#include #include #include "util.h" #include "vpr_types.h" #include "globals.h" #include "route_export.h" #include "check_route.h" #include "rr_graph.h" #include "check_rr_graph.h" #include "read_xml_arch_file.h" /* mrFPGA: Xifan TANG */ #include "mrfpga_globals.h" /* end */ /******************** 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 ** clb_opins_used_locally); static void check_locally_used_clb_opins(t_ivec ** clb_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 ** clb_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; vpr_printf(TIO_MESSAGE_INFO, "\n"); vpr_printf(TIO_MESSAGE_INFO, "Checking 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(clb_opins_used_locally); valid = feasible_routing(); if (valid == FALSE) { vpr_printf(TIO_MESSAGE_ERROR, "Error in check_route -- routing resources are overused.\n"); exit(1); } check_locally_used_clb_opins(clb_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 = std::max(max_pins, (clb_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 (clb_net[inet].is_global || clb_net[inet].num_sinks == 0) /* Skip global nets. */ continue; for (ipin = 0; ipin < (clb_net[inet].num_sinks + 1); ipin++) pin_done[ipin] = FALSE; /* Check the SOURCE of the net. */ tptr = trace_head[inet]; if (tptr == NULL) { vpr_printf(TIO_MESSAGE_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) { vpr_printf(TIO_MESSAGE_ERROR, "in check_route: node %d does not link into existing routing for net %d.\n", inode, inet); exit(1); } } else { connects = check_adjacent(prev_node, inode); if (!connects) { vpr_printf(TIO_MESSAGE_ERROR, "in check_route: found non-adjacent segments in traceback while checking net %d.\n", inet); 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. */ vpr_printf(TIO_MESSAGE_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) { vpr_printf(TIO_MESSAGE_ERROR, "in check_route: net %d does not end with a SINK.\n", inet); exit(1); } for (ipin = 0; ipin < (clb_net[inet].num_sinks + 1); ipin++) { if (pin_done[ipin] == FALSE) { vpr_printf(TIO_MESSAGE_ERROR, "in check_route: net %d does not connect to pin %d.\n", inet, ipin); exit(1); } } reset_flags(inet, connected_to_route); } /* End for each net */ free(pin_done); free(connected_to_route); vpr_printf(TIO_MESSAGE_INFO, "Completed routing consistency check successfully.\n"); vpr_printf(TIO_MESSAGE_INFO, "\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 < (clb_net[inet].num_sinks + 1); ipin++) { /* All net SINKs */ if (clb_net[inet].node_block[ipin] == bnum) { node_block_pin = clb_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 clb 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) { vpr_printf(TIO_MESSAGE_ERROR, "in check_sink: found %d terminals of net %d of pad %d at location (%d, %d).\n", ifound, inet, ptc_num, i, j); exit(1); } if (ifound < 1) { vpr_printf(TIO_MESSAGE_ERROR, "in check_sink: node %d does not connect to any terminal of net %s #%d.\n" "This error is usually caused by incorrectly specified logical equivalence in your architecture file.\n" "You should try to respecify what pins are equivalent or turn logical equivalence off.\n", inode, clb_net[inet].name, 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) { vpr_printf(TIO_MESSAGE_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 = clb_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) { vpr_printf(TIO_MESSAGE_ERROR, "in check_source: net SOURCE is in wrong location (%d,%d).\n", i, j); exit(1); } node_block_pin = clb_net[inet].node_block_pin[0]; iclass = type->pin_class[node_block_pin]; if (ptc_num != iclass) { vpr_printf(TIO_MESSAGE_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) { vpr_printf(TIO_MESSAGE_ERROR, "in check_switch: rr_node %d left via switch type %d.\n", inode, switch_type); vpr_printf(TIO_MESSAGE_ERROR, "\tSwitch 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) { vpr_printf(TIO_MESSAGE_ERROR, "in check_switch: rr_node %d is a SINK, but attempts 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. * Special case: direct OPIN to IPIN connections need not be adjacent. These * represent specially-crafted connections such as carry-chains or more advanced * blocks where adjacency is overridden by the architect */ 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++; /* Xifan TANG: fully_capable network dirty hack: * more rules, if they belong to the same port, we think they are adjacent */ } else if (OPEN != rr_node[from_node].net_num) { num_adj++; } } break; case SINK: /* SINKS are adjacent to not connected */ break; case OPIN: if(to_type == CHANX || to_type == CHANY) { /* Xifan TANG: a dirty hack for pin_equivalence auto detect */ from_grid_type = grid[from_xlow][from_ylow].type; if (TRUE == from_grid_type->output_ports_eq_auto_detect) { num_adj = 1; break; } /* END */ /* Original VPR */ num_adj += pin_and_chan_adjacent(from_node, to_node); } else { assert(to_type == IPIN); /* direct OPIN to IPIN connections not necessarily adjacent */ return TRUE; /* Special case, direct OPIN to IPIN connections need not be adjacent */ } 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) { /* mrFPGA: Xifan TANG */ if (is_stack) { if (from_xlow == to_xlow) { if (to_yhigh == from_ylow-1 || from_yhigh == to_ylow-1) { num_adj++; } } } else { /* end */ /* Original VPR */ 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 */ } /* 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) { /* mrFPGA: Xifan TANG */ if (is_stack) { if (from_ylow == to_ylow) { if (to_xhigh == from_xlow-1 || from_xhigh == to_xlow-1) { num_adj++; } } } else { /* end */ /* Original VPR */ 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 */ } /* 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); vpr_printf(TIO_MESSAGE_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; /* mrFPGA: Xifan TANG */ if (is_stack) { if (rr_node[chanx_node].xlow > rr_node[chany_node].xhigh + 1 || rr_node[chanx_node].xhigh < rr_node[chany_node].xlow) { return 0; } if (rr_node[chany_node].ylow > rr_node[chanx_node].yhigh + 1 || rr_node[chany_node].yhigh < rr_node[chanx_node].ylow) { return 0; } return 1; } /* end */ /* Original VPR */ 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); /* end */ } 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) { /* mrFPGA: Xifan TANG */ if (is_stack) { for (i = 0; i < pin_grid_type->height; i++) { /* CHANX below CLB */ if (pin_grid_type->pinloc[i][BOTTOM][pin_ptc] == 1 && pin_yhigh > chan_ylow && pin_ylow <= chan_yhigh + 1 && pin_xlow == chan_xlow && pin_xlow == chan_xhigh) { num_adj++; } /* CHANX above CLB */ if (pin_grid_type->pinloc[i][TOP][pin_ptc] == 1 && pin_yhigh >= chan_ylow && pin_ylow <= chan_yhigh && pin_xlow == chan_xlow && pin_xlow == chan_xhigh) { num_adj++; } } } else { /* end */ if (chan_ylow == pin_yhigh) { /* CHANX above CLB */ 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 CLB */ 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) { /* mrFPGA: Xifan TANG */ if (is_stack) { for (i = 0; i < pin_grid_type->height; i++) { /* CHANY to right of CLB */ if (pin_grid_type->pinloc[i][RIGHT][pin_ptc] == 1 && pin_ylow <= chan_yhigh && pin_yhigh >= chan_ylow && pin_xlow >= chan_xlow && pin_xlow <= chan_xhigh) { num_adj++; } /* CHANY to left of CLB */ if (pin_grid_type->pinloc[i][LEFT][pin_ptc] == 1 && pin_ylow <= chan_yhigh && pin_yhigh >= chan_ylow && pin_xlow > chan_xlow && pin_xlow <= chan_xhigh + 1) { num_adj++; } } } else { /* end */ for (i = 0; i < pin_grid_type->height; i++) { if (chan_xlow == pin_xhigh) { /* CHANY to right of CLB */ 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 CLB */ 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 ** clb_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 (clb_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 CLB * * (CLB outputs used up by being directly wired to subblocks used only * * locally). */ for (iblk = 0; iblk < num_blocks; iblk++) { /* Xifan TANG: Bypass pin equivalence auto detect block */ if (TRUE == block[iblk].type->output_ports_eq_auto_detect) { continue; } /* Original VPR */ for (iclass = 0; iclass < block[iblk].type->num_class; iclass++) { num_local_opins = clb_opins_used_locally[iblk][iclass].nelem; /* Will always be 0 for pads or SINK classes. */ for (ipin = 0; ipin < num_local_opins; ipin++) { inode = clb_opins_used_locally[iblk][iclass].list[ipin]; rr_node[inode].occ++; } } } } static void check_locally_used_clb_opins(t_ivec ** clb_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++) { /* Xifan TANG: do not check the class when pin equivalence auto-detect is turned on */ if (TRUE == block[iblk].type->output_ports_eq_auto_detect) { continue; } for (iclass = 0; iclass < block[iblk].type->num_class; iclass++) { num_local_opins = clb_opins_used_locally[iblk][iclass].nelem; /* Always 0 for pads and for SINK classes */ for (ipin = 0; ipin < num_local_opins; ipin++) { inode = clb_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) { vpr_printf(TIO_MESSAGE_ERROR, "in check_locally_used_opins: block #%d (%s)\n", iblk, block[iblk].name); vpr_printf(TIO_MESSAGE_ERROR, "\tClass %d local OPIN is wrong rr_type -- rr_node #%d of type %d.\n", iclass, inode, rr_type); exit(1); } ipin = rr_node[inode].ptc_num; if (block[iblk].type->pin_class[ipin] != iclass) { vpr_printf(TIO_MESSAGE_ERROR, "in check_locally_used_opins: block #%d (%s):\n", iblk, block[iblk].name); vpr_printf(TIO_MESSAGE_ERROR, "\tExpected class %d local OPIN has class %d -- rr_node #: %d.\n", iclass, block[iblk].type->pin_class[ipin], inode); /* Xifan TANG: reduce this error to warning when multi- fan_in is found */ //if (1 == rr_node[inode].fan_in) { 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) { vpr_printf(TIO_MESSAGE_ERROR, "in check_node_and_range: rr_node #%d is out of legal, range (0 to %d).\n", inode, num_rr_nodes - 1); exit(1); } check_node(inode, route_type); }