blob: 132bd2af193c3f9bc53ec37ce083113beee6be9e [file] [log] [blame]
#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);
}