#include #include #include #include #include "vpr_types.h" #include "vpr_utils.h" #include "globals.h" #include "graphics.h" #include "path_delay.h" #include "draw.h" #include #include "read_xml_arch_file.h" #include "util.h" #ifdef DEBUG #include "rr_graph.h" #endif /*************** Types local to this module *********************************/ #define MAX_BLOCK_COLOURS 5 enum e_draw_rr_toggle { DRAW_NO_RR = 0, DRAW_ALL_RR, DRAW_ALL_BUT_BUFFERS_RR, DRAW_NODES_AND_SBOX_RR, DRAW_NODES_RR, DRAW_RR_TOGGLE_MAX }; enum e_draw_net_type { ALL_NETS, HIGHLIGHTED }; enum e_edge_dir { FROM_X_TO_Y, FROM_Y_TO_X }; /* Chanx to chany or vice versa? */ /****************** Variables local to this module. *************************/ static boolean show_nets = FALSE; /* Show nets of placement or routing? */ /* Controls drawing of routing resources on screen, if pic_on_screen is * * ROUTING. */ /* Can toggle to DRAW_NO_RR;*/ static enum e_draw_rr_toggle draw_rr_toggle = DRAW_NO_RR; /* UDSD by AY */ static enum e_route_type draw_route_type; /* Controls if congestion is shown, when ROUTING is on screen. */ static boolean show_congestion = FALSE; static boolean show_defects = FALSE; /* Show defective stuff */ static boolean show_graphics; /* Graphics enabled or not? */ static char default_message[BUFSIZE]; /* Default screen message on screen */ static int gr_automode; /* Need user input after: 0: each t, * * 1: each place, 2: never */ static enum pic_type pic_on_screen = NO_PICTURE; /* What do I draw? */ static float *tile_x, *tile_y; /* The left and bottom coordinates of each grid_tile in the FPGA. * * tile_x[0..nx+1] and tile_y[0..ny+1]. * * COORDINATE SYSTEM goes from (0,0) at the lower left corner to * * (tile_x[nx+1]+tile_width, tile_y[ny+1]+tile_width) in the * * upper right corner. */ static float tile_width, pin_size; /* Drawn width (and height) of a grid_tile, and the half-width or half-height of * * a pin, respectiviely. Set when init_draw_coords is called. */ static enum color_types *net_color, *block_color; /* Color in which each block and net should be drawn. * * [0..num_nets-1] and [0..num_blocks-1], respectively. */ static float line_fuz = 0.3; static const char *name_type[] = { "SOURCE", "SINK", "IPIN", "OPIN", "CHANX", "CHANY", "INTRA_CLUSTER_EDGE" }; static float *x_rr_node_left = NULL; static float *x_rr_node_right = NULL; static float *y_rr_node_top = NULL; static float *y_rr_node_bottom = NULL; static enum color_types *rr_node_color = NULL; static int old_num_rr_nodes = 0; /********************** Subroutines local to this module ********************/ static void toggle_nets(void (*drawscreen)(void)); static void toggle_rr(void (*drawscreen)(void)); static void toggle_defects(void (*drawscreen)(void)); static void toggle_congestion(void (*drawscreen)(void)); static void highlight_crit_path(void (*drawscreen_ptr)(void)); static void drawscreen(void); static void redraw_screen(void); static void drawplace(void); static void drawnets(void); static void drawroute(enum e_draw_net_type draw_net_type); static void draw_congestion(void); static void highlight_blocks(float x, float y); static void get_block_center(int bnum, float *x, float *y); static void deselect_all(void); static void draw_rr(void); static void draw_rr_edges(int from_node); static void draw_rr_pin(int inode, enum color_types color); static void draw_rr_chanx(int inode, int itrack); static void draw_rr_chany(int inode, int itrack); static void get_rr_pin_draw_coords(int inode, int iside, int ioff, float *xcen, float *ycen); static void draw_pin_to_chan_edge(int pin_node, int chan_node); static void draw_pin_to_pin(int opin, int ipin); static void draw_x(float x, float y, float size); static void draw_chany_to_chany_edge(int from_node, int from_track, int to_node, int to_track, short switch_type); static void draw_chanx_to_chanx_edge(int from_node, int from_track, int to_node, int to_track, short switch_type); static void draw_chanx_to_chany_edge(int chanx_node, int chanx_track, int chany_node, int chany_track, enum e_edge_dir edge_dir, short switch_type); static int get_track_num(int inode, int **chanx_track, int **chany_track); static void draw_rr_switch(float from_x, float from_y, float to_x, float to_y, boolean buffered); static void draw_triangle_along_line(float xend, float yend, /* UDSD by AY */ float x1, float x2, /* UDSD by AY */ float y1, float y2); /* UDSD by AY */ /********************** Subroutine definitions ******************************/ void set_graphics_state(boolean show_graphics_val, int gr_automode_val, enum e_route_type route_type) { /* Sets the static show_graphics and gr_automode variables to the * * desired values. They control if graphics are enabled and, if so, * * how often the user is prompted for input. */ show_graphics = show_graphics_val; gr_automode = gr_automode_val; draw_route_type = route_type; } void update_screen(int priority, char *msg, enum pic_type pic_on_screen_val, boolean crit_path_button_enabled) { /* Updates the screen if the user has requested graphics. The priority * * value controls whether or not the Proceed button must be clicked to * * continue. Saves the pic_on_screen_val to allow pan and zoom redraws. */ if (!show_graphics) /* Graphics turned off */ return; /* If it's the type of picture displayed has changed, set up the proper * * buttons. */ if (pic_on_screen != pic_on_screen_val) { if (pic_on_screen_val == PLACEMENT && pic_on_screen == NO_PICTURE) { create_button("Window", "Toggle Nets", toggle_nets); } else if (pic_on_screen_val == ROUTING && pic_on_screen == PLACEMENT) { create_button("Toggle Nets", "Toggle RR", toggle_rr); create_button("Toggle RR", "Tog Defects", toggle_defects); create_button("Toggle RR", "Congestion", toggle_congestion); if (crit_path_button_enabled) { create_button("Congestion", "Crit. Path", highlight_crit_path); } } else if (pic_on_screen_val == PLACEMENT && pic_on_screen == ROUTING) { destroy_button("Toggle RR"); destroy_button("Congestion"); if (crit_path_button_enabled) { destroy_button("Crit. Path"); } } else if (pic_on_screen_val == ROUTING && pic_on_screen == NO_PICTURE) { create_button("Window", "Toggle Nets", toggle_nets); create_button("Toggle Nets", "Toggle RR", toggle_rr); create_button("Toggle RR", "Tog Defects", toggle_defects); create_button("Tog Defects", "Congestion", toggle_congestion); if (crit_path_button_enabled) { create_button("Congestion", "Crit. Path", highlight_crit_path); } } } /* Save the main message. */ my_strncpy(default_message, msg, BUFSIZE); pic_on_screen = pic_on_screen_val; update_message(msg); drawscreen(); if (priority >= gr_automode) { event_loop(highlight_blocks, NULL, NULL, drawscreen); } else { flushinput(); } } static void drawscreen() { /* This is the screen redrawing routine that event_loop assumes exists. * * It erases whatever is on screen, then calls redraw_screen to redraw * * it. */ clearscreen(); redraw_screen(); } static void redraw_screen() { /* The screen redrawing routine called by drawscreen and * * highlight_blocks. Call this routine instead of drawscreen if * * you know you don't need to erase the current graphics, and want * * to avoid a screen "flash". */ setfontsize(14); /* UDSD Modification by WMF */ if (pic_on_screen == PLACEMENT) { drawplace(); if (show_nets) { drawnets(); } } else { /* ROUTING on screen */ drawplace(); if (show_nets) { drawroute(ALL_NETS); } else { draw_rr(); } if (show_congestion) { draw_congestion(); } } } static void toggle_nets(void (*drawscreen_ptr)(void)) { /* Enables/disables drawing of nets when a the user clicks on a button. * * Also disables drawing of routing resources. See graphics.c for details * * of how buttons work. */ show_nets = (show_nets == FALSE) ? TRUE : FALSE; draw_rr_toggle = DRAW_NO_RR; show_congestion = FALSE; update_message(default_message); drawscreen_ptr(); } static void toggle_rr(void (*drawscreen_ptr)(void)) { /* Cycles through the options for viewing the routing resources available * * in an FPGA. If a routing isn't on screen, the routing graph hasn't been * * built, and this routine doesn't switch the view. Otherwise, this routine * * switches to the routing resource view. Clicking on the toggle cycles * * through the options: DRAW_NO_RR, DRAW_ALL_RR, DRAW_ALL_BUT_BUFFERS_RR, * * DRAW_NODES_AND_SBOX_RR, and DRAW_NODES_RR. */ draw_rr_toggle = (enum e_draw_rr_toggle) (((int)draw_rr_toggle + 1) % ((int)DRAW_RR_TOGGLE_MAX)); show_nets = FALSE; show_congestion = FALSE; update_message(default_message); drawscreen_ptr(); } static void toggle_defects(void (*drawscreen_ptr)(void)) { show_defects = (show_defects == FALSE) ? TRUE : FALSE; update_message(default_message); drawscreen_ptr(); } static void toggle_congestion(void (*drawscreen_ptr)(void)) { /* Turns the congestion display on and off. */ char msg[BUFSIZE]; int inode, num_congested; show_nets = FALSE; draw_rr_toggle = DRAW_NO_RR; show_congestion = (show_congestion == FALSE) ? TRUE : FALSE; if (!show_congestion) { update_message(default_message); } else { num_congested = 0; for (inode = 0; inode < num_rr_nodes; inode++) { if (rr_node[inode].occ > rr_node[inode].capacity) { num_congested++; } } sprintf(msg, "%d routing resources are overused.", num_congested); update_message(msg); } drawscreen_ptr(); } static void highlight_crit_path(void (*drawscreen_ptr)(void)) { /* Highlights all the blocks and nets on the critical path. */ t_linked_int *critical_path_head, *critical_path_node; int inode, iblk, inet, num_nets_seen; static int nets_to_highlight = 1; char msg[BUFSIZE]; if (nets_to_highlight == 0) { /* Clear the display of all highlighting. */ nets_to_highlight = 1; deselect_all(); update_message(default_message); drawscreen_ptr(); return; } critical_path_head = allocate_and_load_critical_path(); critical_path_node = critical_path_head; num_nets_seen = 0; while (critical_path_node != NULL) { inode = critical_path_node->data; get_tnode_block_and_output_net(inode, &iblk, &inet); if (num_nets_seen == nets_to_highlight) { /* Last block */ block_color[iblk] = MAGENTA; } else if (num_nets_seen == nets_to_highlight - 1) { /* 2nd last block */ block_color[iblk] = YELLOW; } else if (num_nets_seen < nets_to_highlight) { /* Earlier block */ block_color[iblk] = DARKGREEN; } if (inet != OPEN) { num_nets_seen++; if (num_nets_seen < nets_to_highlight) { /* First nets. */ net_color[inet] = DARKGREEN; } else if (num_nets_seen == nets_to_highlight) { net_color[inet] = CYAN; /* Last (new) net. */ } } critical_path_node = critical_path_node->next; } if (nets_to_highlight == num_nets_seen) { nets_to_highlight = 0; sprintf(msg, "All %d nets on the critical path highlighted.", num_nets_seen); } else { sprintf(msg, "First %d nets on the critical path highlighted.", nets_to_highlight); nets_to_highlight++; } free_int_list(&critical_path_head); update_message(msg); drawscreen_ptr(); } void alloc_draw_structs(void) { /* Allocate the structures needed to draw the placement and routing. Set * * up the default colors for blocks and nets. */ tile_x = (float *) my_malloc((nx + 2) * sizeof(float)); tile_y = (float *) my_malloc((ny + 2) * sizeof(float)); net_color = (enum color_types *) my_malloc( num_nets * sizeof(enum color_types)); block_color = (enum color_types *) my_malloc( num_blocks * sizeof(enum color_types)); x_rr_node_left = (float *) my_malloc(num_rr_nodes * sizeof(float)); x_rr_node_right = (float *) my_malloc(num_rr_nodes * sizeof(float)); y_rr_node_top = (float *) my_malloc(num_rr_nodes * sizeof(float)); y_rr_node_bottom = (float *) my_malloc(num_rr_nodes * sizeof(float)); rr_node_color = (enum color_types *) my_malloc( num_rr_nodes * sizeof(enum color_types)); deselect_all(); /* Set initial colors */ } void free_draw_structs(void) { /* Free everything allocated by alloc_draw_structs. Called after close_graphics() * * in vpr_api.c. * * For safety, set all the array pointers to NULL in case any data * structure gets freed twice. */ free(tile_x); tile_x = NULL; free(tile_y); tile_y = NULL; free(net_color); net_color = NULL; free(block_color); block_color = NULL; free(x_rr_node_left); x_rr_node_left = NULL; free(x_rr_node_right); x_rr_node_right = NULL; free(y_rr_node_top); y_rr_node_top = NULL; free(y_rr_node_bottom); y_rr_node_bottom = NULL; free(rr_node_color); rr_node_color = NULL; } void init_draw_coords(float width_val) { /* Load the arrays containing the left and bottom coordinates of the clbs * * forming the FPGA. tile_width_val sets the width and height of a drawn * * clb. */ int i; int j; if (!show_graphics) return; /* -nodisp was selected. */ if (num_rr_nodes != old_num_rr_nodes) { x_rr_node_left = (float *) my_realloc(x_rr_node_left, (num_rr_nodes) * sizeof(float)); x_rr_node_right = (float *) my_realloc(x_rr_node_right, (num_rr_nodes) * sizeof(float)); y_rr_node_top = (float *) my_realloc(y_rr_node_top, (num_rr_nodes) * sizeof(float)); y_rr_node_bottom = (float *) my_realloc(y_rr_node_bottom, (num_rr_nodes) * sizeof(float)); rr_node_color = (enum color_types *) my_realloc(rr_node_color, (num_rr_nodes) * sizeof(enum color_types)); for (i = 0; i < num_rr_nodes; i++) { x_rr_node_left[i] = -1; x_rr_node_right[i] = -1; y_rr_node_top[i] = -1; y_rr_node_bottom[i] = -1; rr_node_color[i] = BLACK; } } tile_width = width_val; pin_size = 0.3; for (i = 0; i < num_types; ++i) { pin_size = std::min(pin_size, (tile_width / (4.0F * type_descriptors[i].num_pins))); } j = 0; for (i = 0; i < (nx + 1); i++) { tile_x[i] = (i * tile_width) + j; j += chan_width_y[i] + 1; /* N wires need N+1 units of space */ } tile_x[nx + 1] = ((nx + 1) * tile_width) + j; j = 0; for (i = 0; i < (ny + 1); ++i) { tile_y[i] = (i * tile_width) + j; j += chan_width_x[i] + 1; } tile_y[ny + 1] = ((ny + 1) * tile_width) + j; init_world(0.0, tile_y[ny + 1] + tile_width, tile_x[nx + 1] + tile_width, 0.0); } static void drawplace(void) { /* Draws the blocks placed on the proper clbs. Occupied blocks are darker colours * * while empty ones are lighter colours and have a dashed border. */ float sub_tile_step; float x1, y1, x2, y2; int i, j, k, bnum; int num_sub_tiles; int height; setlinewidth(0); for (i = 0; i <= (nx + 1); i++) { for (j = 0; j <= (ny + 1); j++) { /* Only the first block of a group should control drawing */ if (grid[i][j].offset > 0) continue; /* Don't draw corners */ if (((i < 1) || (i > nx)) && ((j < 1) || (j > ny))) continue; num_sub_tiles = grid[i][j].type->capacity; sub_tile_step = tile_width / num_sub_tiles; height = grid[i][j].type->height; if (num_sub_tiles < 1) { setcolor(BLACK); setlinestyle(DASHED); drawrect(tile_x[i], tile_y[j], tile_x[i] + tile_width, tile_y[j] + tile_width); draw_x(tile_x[i] + (tile_width / 2), tile_y[j] + (tile_width / 2), (tile_width / 2)); } for (k = 0; k < num_sub_tiles; ++k) { /* Graphics will look unusual for multiple height and capacity */ assert(height == 1 || num_sub_tiles == 1); /* Get coords of current sub_tile */ if ((i < 1) || (i > nx)) { /* left and right fringes */ x1 = tile_x[i]; y1 = tile_y[j] + (k * sub_tile_step); x2 = x1 + tile_width; y2 = y1 + sub_tile_step; } else if ((j < 1) || (j > ny)) { /* top and bottom fringes */ x1 = tile_x[i] + (k * sub_tile_step); y1 = tile_y[j]; x2 = x1 + sub_tile_step; y2 = y1 + tile_width; } else { assert(num_sub_tiles <= 1); /* Need to change draw code to support */ x1 = tile_x[i]; y1 = tile_y[j]; x2 = x1 + tile_width; y2 = tile_y[j + height - 1] + tile_width; } /* Look at the tile at start of large block */ bnum = grid[i][j].blocks[k]; /* Draw background */ if (bnum != EMPTY) { setcolor(block_color[bnum]); fillrect(x1, y1, x2, y2); } else { /* colour empty blocks a particular colour depending on type */ if (grid[i][j].type->index < 3) { setcolor(WHITE); } else if (grid[i][j].type->index < 3 + MAX_BLOCK_COLOURS) { setcolor(BISQUE + grid[i][j].type->index - 3); } else { setcolor(BISQUE + MAX_BLOCK_COLOURS - 1); } fillrect(x1, y1, x2, y2); } setcolor(BLACK); setlinestyle((EMPTY == bnum) ? DASHED : SOLID); drawrect(x1, y1, x2, y2); /* Draw text if the space has parts of the netlist */ if (bnum != EMPTY) { drawtext((x1 + x2) / 2.0, (y1 + y2) / 2.0, block[bnum].name, tile_width); } /* Draw text for block type so that user knows what block */ if (grid[i][j].offset == 0) { if (i > 0 && i <= nx && j > 0 && j <= ny) { drawtext((x1 + x2) / 2.0, y1 + (tile_width / 4.0), grid[i][j].type->name, tile_width); } } } } } } static void drawnets(void) { /* This routine draws the nets on the placement. The nets have not * * yet been routed, so we just draw a chain showing a possible path * * for each net. This gives some idea of future congestion. */ int inet, ipin, b1, b2; float x1, y1, x2, y2; setlinestyle(SOLID); setlinewidth(0); /* Draw the net as a star from the source to each sink. Draw from centers of * * blocks (or sub blocks in the case of IOs). */ for (inet = 0; inet < num_nets; inet++) { if (clb_net[inet].is_global) continue; /* Don't draw global nets. */ setcolor(net_color[inet]); b1 = clb_net[inet].node_block[0]; /* The DRIVER */ get_block_center(b1, &x1, &y1); for (ipin = 1; ipin < (clb_net[inet].num_sinks + 1); ipin++) { b2 = clb_net[inet].node_block[ipin]; get_block_center(b2, &x2, &y2); drawline(x1, y1, x2, y2); /* Uncomment to draw a chain instead of a star. */ /* x1 = x2; */ /* y1 = y2; */ } } } static void get_block_center(int bnum, float *x, float *y) { /* This routine finds the center of block bnum in the current placement, * * and returns it in *x and *y. This is used in routine shownets. */ int i, j, k; float sub_tile_step; i = block[bnum].x; j = block[bnum].y; k = block[bnum].z; sub_tile_step = tile_width / block[bnum].type->capacity; if ((i < 1) || (i > nx)) { /* Left and right fringe */ *x = tile_x[i] + (sub_tile_step * (k + 0.5)); } else { *x = tile_x[i] + (tile_width / 2.0); } if ((j < 1) || (j > ny)) { /* Top and bottom fringe */ *y = tile_y[j] + (sub_tile_step * (k + 0.5)); } else { *y = tile_y[j] + (tile_width / 2.0); } } static void draw_congestion(void) { /* Draws all the overused routing resources (i.e. congestion) in RED. */ int inode, itrack; setcolor(RED); setlinewidth(2); for (inode = 0; inode < num_rr_nodes; inode++) { if (rr_node[inode].occ > rr_node[inode].capacity) { switch (rr_node[inode].type) { case CHANX: itrack = rr_node[inode].ptc_num; draw_rr_chanx(inode, itrack); break; case CHANY: itrack = rr_node[inode].ptc_num; draw_rr_chany(inode, itrack); break; case IPIN: case OPIN: draw_rr_pin(inode, RED); break; default: break; } } } } void draw_rr(void) { /* Draws the routing resources that exist in the FPGA, if the user wants * * them drawn. */ int inode, itrack; if (draw_rr_toggle == DRAW_NO_RR) { setlinewidth(3); drawroute(HIGHLIGHTED); setlinewidth(0); return; } setlinestyle(SOLID); setlinewidth(0); for (inode = 0; inode < num_rr_nodes; inode++) { switch (rr_node[inode].type) { case SOURCE: case SINK: break; /* Don't draw. */ case CHANX: if (show_defects && (rr_node[inode].capacity <= 0)) setcolor(RED); else setcolor(BLACK); if (show_defects && (rr_node[inode].occ > 0)) setcolor(CYAN); itrack = rr_node[inode].ptc_num; draw_rr_chanx(inode, itrack); draw_rr_edges(inode); break; case CHANY: if (show_defects && (rr_node[inode].capacity <= 0)) setcolor(RED); else setcolor(BLACK); if (show_defects && (rr_node[inode].occ > 0)) setcolor(CYAN); itrack = rr_node[inode].ptc_num; draw_rr_chany(inode, itrack); draw_rr_edges(inode); break; case IPIN: if (show_defects) { if (rr_node[inode].capacity < 0) draw_rr_pin(inode, RED); else if (rr_node[inode].occ > 0) draw_rr_pin(inode, CYAN); else draw_rr_pin(inode, BLACK); } else draw_rr_pin(inode, BLUE); break; case OPIN: if (show_defects) { if (rr_node[inode].capacity < 0) draw_rr_pin(inode, RED); else if (rr_node[inode].occ > 0) draw_rr_pin(inode, CYAN); else draw_rr_pin(inode, BLACK); setcolor(BLACK); } else { draw_rr_pin(inode, RED); setcolor(RED); } setcolor(RED); draw_rr_edges(inode); break; default: vpr_printf(TIO_MESSAGE_ERROR, "in draw_rr: Unexpected rr_node type: %d.\n", rr_node[inode].type); exit(1); } } setlinewidth(3); drawroute(HIGHLIGHTED); setlinewidth(0); } static void draw_rr_chanx(int inode, int itrack) { /* Draws an x-directed channel segment. */ enum { BUFFSIZE = 80 }; float x1, x2, y; float y1, y2; /* UDSD by AY */ int k; /* UDSD by AY */ char str[BUFFSIZE]; int savecolor; /* Track 0 at bottom edge, closest to "owning" clb. */ x1 = tile_x[rr_node[inode].xlow]; x2 = tile_x[rr_node[inode].xhigh] + tile_width; y = tile_y[rr_node[inode].ylow] + tile_width + 1.0 + itrack; x_rr_node_left[inode] = x1; x_rr_node_right[inode] = x2; y_rr_node_bottom[inode] = y - line_fuz; y_rr_node_top[inode] = y + line_fuz; if (rr_node_color[inode] != BLACK) { savecolor = getcolor(); setcolor(rr_node_color[inode]); setlinewidth(3); drawline(x1, y, x2, y); setlinewidth(0); setcolor(savecolor); } else { drawline(x1, y, x2, y); } /* UDSD by AY Start */ y1 = y - 0.25; y2 = y + 0.25; if (rr_node[inode].direction == INC_DIRECTION) { setlinewidth(2); setcolor(YELLOW); drawline(x1, y1, x1, y2); /* Draw a line at start of wire to indicate mux */ /* Mux balence numbers */ setcolor(BLACK); sprintf(str, "%d", rr_node[inode].fan_in); drawtext(x1, y, str, 5); setcolor(BLACK); setlinewidth(0); draw_triangle_along_line(x2 - 0.15, y, x1, x2, y, y); setcolor(LIGHTGREY); /* TODO: this looks odd, why does it ignore final block? does this mean nothing appears with L=1 ? */ for (k = rr_node[inode].xlow; k < rr_node[inode].xhigh; k++) { x2 = tile_x[k] + tile_width; draw_triangle_along_line(x2 - 0.15, y, x1, x2, y, y); x2 = tile_x[k + 1]; draw_triangle_along_line(x2 + 0.15, y, x1, x2, y, y); } setcolor(BLACK); } else if (rr_node[inode].direction == DEC_DIRECTION) { setlinewidth(2); setcolor(YELLOW); drawline(x2, y1, x2, y2); /* Mux balance numbers */ setcolor(BLACK); sprintf(str, "%d", rr_node[inode].fan_in); drawtext(x2, y, str, 5); setlinewidth(0); draw_triangle_along_line(x1 + 0.15, y, x2, x1, y, y); setcolor(LIGHTGREY); for (k = rr_node[inode].xhigh; k > rr_node[inode].xlow; k--) { x1 = tile_x[k]; draw_triangle_along_line(x1 + 0.15, y, x2, x1, y, y); x1 = tile_x[k - 1] + tile_width; draw_triangle_along_line(x1 - 0.15, y, x2, x1, y, y); } setcolor(BLACK); } /* UDSD by AY End */ } static void draw_rr_chany(int inode, int itrack) { /* Draws a y-directed channel segment. */ enum { BUFFSIZE = 80 }; float x, y1, y2; float x1, x2; /* UDSD by AY */ int k; /* UDSD by AY */ char str[BUFFSIZE]; int savecolor; /* Track 0 at left edge, closest to "owning" clb. */ x = tile_x[rr_node[inode].xlow] + tile_width + 1. + itrack; y1 = tile_y[rr_node[inode].ylow]; y2 = tile_y[rr_node[inode].yhigh] + tile_width; x_rr_node_left[inode] = x - line_fuz; x_rr_node_right[inode] = x + line_fuz; y_rr_node_bottom[inode] = y1; y_rr_node_top[inode] = y2; if (rr_node_color[inode] != BLACK) { savecolor = getcolor(); setcolor(rr_node_color[inode]); setlinewidth(3); drawline(x, y1, x, y2); setlinewidth(0); setcolor(savecolor); } else { drawline(x, y1, x, y2); } /* UDSD by AY Start */ x1 = x - 0.25; x2 = x + 0.25; if (rr_node[inode].direction == INC_DIRECTION) { setlinewidth(2); setcolor(YELLOW); drawline(x1, y1, x2, y1); /* UDSD Modifications by WMF Begin */ setcolor(BLACK); sprintf(str, "%d", rr_node[inode].fan_in); drawtext(x, y1, str, 5); setcolor(BLACK); /* UDSD Modifications by WMF End */ setlinewidth(0); draw_triangle_along_line(x, y2 - 0.15, x, x, y1, y2); setcolor(LIGHTGREY); for (k = rr_node[inode].ylow; k < rr_node[inode].yhigh; k++) { y2 = tile_y[k] + tile_width; draw_triangle_along_line(x, y2 - 0.15, x, x, y1, y2); y2 = tile_y[k + 1]; draw_triangle_along_line(x, y2 + 0.15, x, x, y1, y2); } setcolor(BLACK); } else if (rr_node[inode].direction == DEC_DIRECTION) { setlinewidth(2); setcolor(YELLOW); drawline(x1, y2, x2, y2); /* UDSD Modifications by WMF Begin */ setcolor(BLACK); sprintf(str, "%d", rr_node[inode].fan_in); drawtext(x, y2, str, 5); setcolor(BLACK); /* UDSD Modifications by WMF End */ setlinewidth(0); draw_triangle_along_line(x, y1 + 0.15, x, x, y2, y1); setcolor(LIGHTGREY); for (k = rr_node[inode].yhigh; k > rr_node[inode].ylow; k--) { y1 = tile_y[k]; draw_triangle_along_line(x, y1 + 0.15, x, x, y2, y1); y1 = tile_y[k - 1] + tile_width; draw_triangle_along_line(x, y1 - 0.15, x, x, y2, y1); } setcolor(BLACK); } /* UDSD by AY End */ } static void draw_rr_edges(int inode) { /* Draws all the edges that the user wants shown between inode and what it * * connects to. inode is assumed to be a CHANX, CHANY, or OPIN. */ t_rr_type from_type, to_type; int iedge, to_node, from_ptc_num, to_ptc_num; short switch_type; boolean defective = FALSE; from_type = rr_node[inode].type; if ((draw_rr_toggle == DRAW_NODES_RR) || (draw_rr_toggle == DRAW_NODES_AND_SBOX_RR && from_type == OPIN)) { return; /* Nothing to draw. */ } from_ptc_num = rr_node[inode].ptc_num; for (iedge = 0; iedge < rr_node[inode].num_edges; iedge++) { to_node = rr_node[inode].edges[iedge]; to_type = rr_node[to_node].type; to_ptc_num = rr_node[to_node].ptc_num; if (show_defects) defective = (boolean)(switch_inf[rr_node[inode].switches[iedge]].R < 0); switch (from_type) { case OPIN: switch (to_type) { case CHANX: case CHANY: if (show_defects) { if (defective) setcolor(RED); else setcolor(BLACK); } else setcolor(RED); draw_pin_to_chan_edge(inode, to_node); break; case IPIN: setcolor(RED); draw_pin_to_pin(inode, to_node); break; default: vpr_printf(TIO_MESSAGE_ERROR, "in draw_rr_edges: node %d (type: %d) connects to node %d (type: %d).\n", inode, from_type, to_node, to_type); exit(1); break; } break; case CHANX: /* from_type */ switch (to_type) { case IPIN: if (draw_rr_toggle == DRAW_NODES_AND_SBOX_RR) { break; } if (show_defects) { if (defective) setcolor(RED); else setcolor(BLACK); } else setcolor(BLUE); draw_pin_to_chan_edge(to_node, inode); break; case CHANX: if (show_defects) { if (defective) setcolor(RED); else setcolor(BLACK); } else setcolor(DARKGREEN); switch_type = rr_node[inode].switches[iedge]; draw_chanx_to_chanx_edge(inode, from_ptc_num, to_node, to_ptc_num, switch_type); break; case CHANY: if (show_defects) { if (defective) setcolor(RED); else setcolor(BLACK); } else setcolor(DARKGREEN); switch_type = rr_node[inode].switches[iedge]; draw_chanx_to_chany_edge(inode, from_ptc_num, to_node, to_ptc_num, FROM_X_TO_Y, switch_type); break; default: vpr_printf(TIO_MESSAGE_ERROR, "in draw_rr_edges: node %d (type: %d) connects to node %d (type: %d).\n", inode, from_type, to_node, to_type); exit(1); break; } break; case CHANY: /* from_type */ switch (to_type) { case IPIN: if (draw_rr_toggle == DRAW_NODES_AND_SBOX_RR) { break; } if (show_defects) { if (defective) setcolor(RED); else setcolor(BLACK); } else setcolor(BLUE); draw_pin_to_chan_edge(to_node, inode); break; case CHANX: if (show_defects) { if (defective) setcolor(RED); else setcolor(BLACK); } else setcolor(DARKGREEN); switch_type = rr_node[inode].switches[iedge]; draw_chanx_to_chany_edge(to_node, to_ptc_num, inode, from_ptc_num, FROM_Y_TO_X, switch_type); break; case CHANY: if (show_defects) { if (defective) setcolor(RED); else setcolor(BLACK); } else setcolor(DARKGREEN); switch_type = rr_node[inode].switches[iedge]; draw_chany_to_chany_edge(inode, from_ptc_num, to_node, to_ptc_num, switch_type); break; default: vpr_printf(TIO_MESSAGE_ERROR, "in draw_rr_edges: node %d (type: %d) connects to node %d (type: %d).\n", inode, from_type, to_node, to_type); exit(1); break; } break; default: /* from_type */ vpr_printf(TIO_MESSAGE_ERROR, "draw_rr_edges called with node %d of type %d.\n", inode, from_type); exit(1); break; } } /* End of for each edge loop */ } static void draw_x(float x, float y, float size) { /* Draws an X centered at (x,y). The width and height of the X are each * * 2 * size. */ drawline(x - size, y + size, x + size, y - size); drawline(x - size, y - size, x + size, y + size); } /* UDSD Modifications by WMF: Thank God Andy fixed this. */ static void draw_chanx_to_chany_edge(int chanx_node, int chanx_track, int chany_node, int chany_track, enum e_edge_dir edge_dir, short switch_type) { /* Draws an edge (SBOX connection) between an x-directed channel and a * * y-directed channel. */ float x1, y1, x2, y2; int chanx_y, chany_x, chanx_xlow, chany_ylow; chanx_y = rr_node[chanx_node].ylow; chanx_xlow = rr_node[chanx_node].xlow; chany_x = rr_node[chany_node].xlow; chany_ylow = rr_node[chany_node].ylow; /* (x1,y1): point on CHANX segment, (x2,y2): point on CHANY segment. */ y1 = tile_y[chanx_y] + tile_width + 1. + chanx_track; x2 = tile_x[chany_x] + tile_width + 1. + chany_track; if (chanx_xlow <= chany_x) { /* Can draw connection going right */ x1 = tile_x[chany_x] + tile_width; /* UDSD by AY Start */ if (rr_node[chanx_node].direction != BI_DIRECTION) { if (edge_dir == FROM_X_TO_Y) { if ((chanx_track % 2) == 1) { /* UDSD Modifications by WMF: If dec wire, then going left */ x1 = tile_x[chany_x + 1]; } } } /* UDSD by AY End */ } else { /* Must draw connection going left. */ x1 = tile_x[chanx_xlow]; } if (chany_ylow <= chanx_y) { /* Can draw connection going up. */ y2 = tile_y[chanx_y] + tile_width; /* UDSD by AY Start */ if (rr_node[chany_node].direction != BI_DIRECTION) { if (edge_dir == FROM_Y_TO_X) { if ((chany_track % 2) == 1) { /* UDSD Modifications by WMF: If dec wire, then going down */ y2 = tile_y[chanx_y + 1]; } } } /* UDSD by AY End */ } else { /* Must draw connection going down. */ y2 = tile_y[chany_ylow]; } drawline(x1, y1, x2, y2); if (draw_rr_toggle != DRAW_ALL_RR) return; if (edge_dir == FROM_X_TO_Y) draw_rr_switch(x1, y1, x2, y2, switch_inf[switch_type].buffered); else draw_rr_switch(x2, y2, x1, y1, switch_inf[switch_type].buffered); } static void draw_chanx_to_chanx_edge(int from_node, int from_track, int to_node, int to_track, short switch_type) { /* Draws a connection between two x-channel segments. Passing in the track * * numbers allows this routine to be used for both rr_graph and routing * * drawing. */ float x1, x2, y1, y2; int from_y, to_y, from_xlow, to_xlow, from_xhigh, to_xhigh; from_y = rr_node[from_node].ylow; from_xlow = rr_node[from_node].xlow; from_xhigh = rr_node[from_node].xhigh; to_y = rr_node[to_node].ylow; to_xlow = rr_node[to_node].xlow; to_xhigh = rr_node[to_node].xhigh; /* (x1, y1) point on from_node, (x2, y2) point on to_node. */ y1 = tile_y[from_y] + tile_width + 1 + from_track; y2 = tile_y[to_y] + tile_width + 1 + to_track; if (to_xhigh < from_xlow) { /* From right to left */ /* UDSD Note by WMF: could never happen for INC wires, unless U-turn. For DEC * wires this handles well */ x1 = tile_x[from_xlow]; x2 = tile_x[to_xhigh] + tile_width; } else if (to_xlow > from_xhigh) { /* From left to right */ /* UDSD Note by WMF: could never happen for DEC wires, unless U-turn. For INC * wires this handles well */ x1 = tile_x[from_xhigh] + tile_width; x2 = tile_x[to_xlow]; } /* Segments overlap in the channel. Figure out best way to draw. Have to * * make sure the drawing is symmetric in the from rr and to rr so the edges * * will be drawn on top of each other for bidirectional connections. */ /* UDSD Modification by WMF Begin */ else { if (rr_node[to_node].direction != BI_DIRECTION) { /* must connect to to_node's wire beginning at x2 */ if (to_track % 2 == 0) { /* INC wire starts at leftmost edge */ assert(from_xlow < to_xlow); x2 = tile_x[to_xlow]; /* since no U-turns from_track must be INC as well */ x1 = tile_x[to_xlow - 1] + tile_width; } else { /* DEC wire starts at rightmost edge */ assert(from_xhigh > to_xhigh); x2 = tile_x[to_xhigh] + tile_width; x1 = tile_x[to_xhigh + 1]; } } else { if (to_xlow < from_xlow) { /* Draw from left edge of one to other */ x1 = tile_x[from_xlow]; x2 = tile_x[from_xlow - 1] + tile_width; } else if (from_xlow < to_xlow) { x1 = tile_x[to_xlow - 1] + tile_width; x2 = tile_x[to_xlow]; } /* The following then is executed when from_xlow == to_xlow */ else if (to_xhigh > from_xhigh) { /* Draw from right edge of one to other */ x1 = tile_x[from_xhigh] + tile_width; x2 = tile_x[from_xhigh + 1]; } else if (from_xhigh > to_xhigh) { x1 = tile_x[to_xhigh + 1]; x2 = tile_x[to_xhigh] + tile_width; } else { /* Complete overlap: start and end both align. Draw outside the sbox */ x1 = tile_x[from_xlow]; x2 = tile_x[from_xlow] + tile_width; } } } /* UDSD Modification by WMF End */ drawline(x1, y1, x2, y2); if (draw_rr_toggle == DRAW_ALL_RR) draw_rr_switch(x1, y1, x2, y2, switch_inf[switch_type].buffered); } static void draw_chany_to_chany_edge(int from_node, int from_track, int to_node, int to_track, short switch_type) { /* Draws a connection between two y-channel segments. Passing in the track * * numbers allows this routine to be used for both rr_graph and routing * * drawing. */ float x1, x2, y1, y2; int from_x, to_x, from_ylow, to_ylow, from_yhigh, to_yhigh; from_x = rr_node[from_node].xlow; from_ylow = rr_node[from_node].ylow; from_yhigh = rr_node[from_node].yhigh; to_x = rr_node[to_node].xlow; to_ylow = rr_node[to_node].ylow; to_yhigh = rr_node[to_node].yhigh; /* (x1, y1) point on from_node, (x2, y2) point on to_node. */ x1 = tile_x[from_x] + tile_width + 1 + from_track; x2 = tile_x[to_x] + tile_width + 1 + to_track; if (to_yhigh < from_ylow) { /* From upper to lower */ y1 = tile_y[from_ylow]; y2 = tile_y[to_yhigh] + tile_width; } else if (to_ylow > from_yhigh) { /* From lower to upper */ y1 = tile_y[from_yhigh] + tile_width; y2 = tile_y[to_ylow]; } /* Segments overlap in the channel. Figure out best way to draw. Have to * * make sure the drawing is symmetric in the from rr and to rr so the edges * * will be drawn on top of each other for bidirectional connections. */ /* UDSD Modification by WMF Begin */ else { if (rr_node[to_node].direction != BI_DIRECTION) { if (to_track % 2 == 0) { /* INC wire starts at bottom edge */ assert(from_ylow < to_ylow); y2 = tile_y[to_ylow]; /* since no U-turns from_track must be INC as well */ y1 = tile_y[to_ylow - 1] + tile_width; } else { /* DEC wire starts at top edge */ if (!(from_yhigh > to_yhigh)) { vpr_printf(TIO_MESSAGE_INFO, "from_yhigh (%d) !> to_yhigh (%d).\n", from_yhigh, to_yhigh); vpr_printf(TIO_MESSAGE_INFO, "from is (%d, %d) to (%d, %d) track %d.\n", rr_node[from_node].xhigh, rr_node[from_node].yhigh, rr_node[from_node].xlow, rr_node[from_node].ylow, rr_node[from_node].ptc_num); vpr_printf(TIO_MESSAGE_INFO, "to is (%d, %d) to (%d, %d) track %d.\n", rr_node[to_node].xhigh, rr_node[to_node].yhigh, rr_node[to_node].xlow, rr_node[to_node].ylow, rr_node[to_node].ptc_num); exit(1); } y2 = tile_y[to_yhigh] + tile_width; y1 = tile_y[to_yhigh + 1]; } } else { if (to_ylow < from_ylow) { /* Draw from bottom edge of one to other. */ y1 = tile_y[from_ylow]; y2 = tile_y[from_ylow - 1] + tile_width; } else if (from_ylow < to_ylow) { y1 = tile_y[to_ylow - 1] + tile_width; y2 = tile_y[to_ylow]; } else if (to_yhigh > from_yhigh) { /* Draw from top edge of one to other. */ y1 = tile_y[from_yhigh] + tile_width; y2 = tile_y[from_yhigh + 1]; } else if (from_yhigh > to_yhigh) { y1 = tile_y[to_yhigh + 1]; y2 = tile_y[to_yhigh] + tile_width; } else { /* Complete overlap: start and end both align. Draw outside the sbox */ y1 = tile_y[from_ylow]; y2 = tile_y[from_ylow] + tile_width; } } } /* UDSD Modification by WMF End */ drawline(x1, y1, x2, y2); if (draw_rr_toggle == DRAW_ALL_RR) draw_rr_switch(x1, y1, x2, y2, switch_inf[switch_type].buffered); } static void draw_rr_switch(float from_x, float from_y, float to_x, float to_y, boolean buffered) { /* Draws a buffer (triangle) or pass transistor (circle) on the edge * * connecting from to to, depending on the status of buffered. The drawing * * is closest to the from_node, since it reflects the switch type of from. */ const float switch_rad = 0.15; float magnitude, xcen, ycen, xdelta, ydelta, xbaseline, ybaseline; float xunit, yunit; t_point poly[3]; xcen = from_x + (to_x - from_x) / 10.; ycen = from_y + (to_y - from_y) / 10.; if (!buffered) { /* Draw a circle for a pass transistor */ drawarc(xcen, ycen, switch_rad, 0., 360.); } else { /* Buffer */ xdelta = to_x - from_x; ydelta = to_y - from_y; magnitude = sqrt(xdelta * xdelta + ydelta * ydelta); xunit = xdelta / magnitude; yunit = ydelta / magnitude; poly[0].x = xcen + xunit * switch_rad; poly[0].y = ycen + yunit * switch_rad; xbaseline = xcen - xunit * switch_rad; ybaseline = ycen - yunit * switch_rad; /* Recall: perpendicular vector to the unit vector along the switch (xv, yv) * * is (yv, -xv). */ poly[1].x = xbaseline + yunit * switch_rad; poly[1].y = ybaseline - xunit * switch_rad; poly[2].x = xbaseline - yunit * switch_rad; poly[2].y = ybaseline + xunit * switch_rad; fillpoly(poly, 3); } } static void draw_rr_pin(int inode, enum color_types color) { /* Draws an IPIN or OPIN rr_node. Note that the pin can appear on more * * than one side of a clb. Also note that this routine can change the * * current color to BLACK. */ int ipin, i, j, iside, ioff; float xcen, ycen; char str[BUFSIZE]; t_type_ptr type; i = rr_node[inode].xlow; j = rr_node[inode].ylow; ipin = rr_node[inode].ptc_num; type = grid[i][j].type; ioff = grid[i][j].offset; setcolor(color); /* TODO: This is where we can hide fringe physical pins and also identify globals (hide, color, show) */ for (iside = 0; iside < 4; iside++) { if (type->pinloc[grid[i][j].offset][iside][ipin]) { /* Pin exists on this side. */ get_rr_pin_draw_coords(inode, iside, ioff, &xcen, &ycen); fillrect(xcen - pin_size, ycen - pin_size, xcen + pin_size, ycen + pin_size); sprintf(str, "%d", ipin); setcolor(BLACK); drawtext(xcen, ycen, str, 2 * pin_size); setcolor(color); } } } static void get_rr_pin_draw_coords(int inode, int iside, int ioff, float *xcen, float *ycen) { /* Returns the coordinates at which the center of this pin should be drawn. * * inode gives the node number, and iside gives the side of the clb or pad * * the physical pin is on. */ int i, j, k, ipin, pins_per_sub_tile; float offset, xc, yc, step; t_type_ptr type; i = rr_node[inode].xlow; j = rr_node[inode].ylow + ioff; /* Need correct tile of block */ xc = tile_x[i]; yc = tile_y[j]; ipin = rr_node[inode].ptc_num; type = grid[i][j].type; pins_per_sub_tile = grid[i][j].type->num_pins / grid[i][j].type->capacity; k = ipin / pins_per_sub_tile; /* Since pins numbers go across all sub_tiles in a block in order * we can treat as a block box for this step */ /* For each sub_tile we need and extra padding space */ step = (float) (tile_width) / (float) (type->num_pins + type->capacity); offset = (ipin + k + 1) * step; switch (iside) { case LEFT: yc += offset; break; case RIGHT: xc += tile_width; yc += offset; break; case BOTTOM: xc += offset; break; case TOP: xc += offset; yc += tile_width; break; default: vpr_printf(TIO_MESSAGE_ERROR, "in get_rr_pin_draw_coords: Unexpected iside %d.\n", iside); exit(1); break; } *xcen = xc; *ycen = yc; } static void drawroute(enum e_draw_net_type draw_net_type) { /* Draws the nets in the positions fixed by the router. If draw_net_type is * * ALL_NETS, draw all the nets. If it is HIGHLIGHTED, draw only the nets * * that are not coloured black (useful for drawing over the rr_graph). */ /* Next free track in each channel segment if routing is GLOBAL */ static int **chanx_track = NULL; /* [1..nx][0..ny] */ static int **chany_track = NULL; /* [0..nx][1..ny] */ int inet, i, j, inode, prev_node, prev_track, itrack; short switch_type; struct s_trace *tptr; t_rr_type rr_type, prev_type; if (draw_route_type == GLOBAL) { /* Allocate some temporary storage if it's not already available. */ if (chanx_track == NULL) { chanx_track = (int **) alloc_matrix(1, nx, 0, ny, sizeof(int)); } if (chany_track == NULL) { chany_track = (int **) alloc_matrix(0, nx, 1, ny, sizeof(int)); } for (i = 1; i <= nx; i++) for (j = 0; j <= ny; j++) chanx_track[i][j] = (-1); for (i = 0; i <= nx; i++) for (j = 1; j <= ny; j++) chany_track[i][j] = (-1); } setlinestyle(SOLID); /* Now draw each net, one by one. */ for (inet = 0; inet < num_nets; inet++) { if (clb_net[inet].is_global) /* Don't draw global nets. */ continue; if (trace_head[inet] == NULL) /* No routing. Skip. (Allows me to draw */ continue; /* partially complete routes). */ if (draw_net_type == HIGHLIGHTED && net_color[inet] == BLACK) continue; setcolor(net_color[inet]); tptr = trace_head[inet]; /* SOURCE to start */ inode = tptr->index; rr_type = rr_node[inode].type; for (;;) { prev_node = inode; prev_type = rr_type; switch_type = tptr->iswitch; tptr = tptr->next; inode = tptr->index; rr_type = rr_node[inode].type; switch (rr_type) { case OPIN: draw_rr_pin(inode, net_color[inet]); break; case IPIN: draw_rr_pin(inode, net_color[inet]); if(rr_node[prev_node].type == OPIN) { draw_pin_to_pin(prev_node, inode); } else { prev_track = get_track_num(prev_node, chanx_track, chany_track); draw_pin_to_chan_edge(inode, prev_node); } break; case CHANX: if (draw_route_type == GLOBAL) chanx_track[rr_node[inode].xlow][rr_node[inode].ylow]++; itrack = get_track_num(inode, chanx_track, chany_track); draw_rr_chanx(inode, itrack); switch (prev_type) { case CHANX: prev_track = get_track_num(prev_node, chanx_track, chany_track); draw_chanx_to_chanx_edge(prev_node, prev_track, inode, itrack, switch_type); break; case CHANY: prev_track = get_track_num(prev_node, chanx_track, chany_track); draw_chanx_to_chany_edge(inode, itrack, prev_node, prev_track, FROM_Y_TO_X, switch_type); break; case OPIN: draw_pin_to_chan_edge(prev_node, inode); break; default: vpr_printf(TIO_MESSAGE_ERROR, "in drawroute: Unexpected connection from an rr_node of type %d to one of type %d.\n", prev_type, rr_type); exit(1); } break; case CHANY: if (draw_route_type == GLOBAL) chany_track[rr_node[inode].xlow][rr_node[inode].ylow]++; itrack = get_track_num(inode, chanx_track, chany_track); draw_rr_chany(inode, itrack); switch (prev_type) { case CHANX: prev_track = get_track_num(prev_node, chanx_track, chany_track); draw_chanx_to_chany_edge(prev_node, prev_track, inode, itrack, FROM_X_TO_Y, switch_type); break; case CHANY: prev_track = get_track_num(prev_node, chanx_track, chany_track); draw_chany_to_chany_edge(prev_node, prev_track, inode, itrack, switch_type); break; case OPIN: draw_pin_to_chan_edge(prev_node, inode); break; default: vpr_printf(TIO_MESSAGE_ERROR, "in drawroute: Unexpected connection from an rr_node of type %d to one of type %d.\n", prev_type, rr_type); exit(1); } break; default: break; } if (rr_type == SINK) { /* Skip the next segment */ tptr = tptr->next; if (tptr == NULL) break; inode = tptr->index; rr_type = rr_node[inode].type; } } /* End loop over traceback. */ } /* End for (each net) */ } static int get_track_num(int inode, int **chanx_track, int **chany_track) { /* Returns the track number of this routing resource node. */ int i, j; t_rr_type rr_type; if (draw_route_type == DETAILED) return (rr_node[inode].ptc_num); /* GLOBAL route stuff below. */ rr_type = rr_node[inode].type; i = rr_node[inode].xlow; /* NB: Global rr graphs must have only unit */ j = rr_node[inode].ylow; /* length channel segments. */ switch (rr_type) { case CHANX: return (chanx_track[i][j]); case CHANY: return (chany_track[i][j]); default: vpr_printf(TIO_MESSAGE_ERROR, "in get_track_num: Unexpected node type %d for node %d.\n", rr_type, inode); exit(1); } } static void highlight_nets(char *message) { int inet; struct s_trace *tptr; for (inet = 0; inet < num_nets; inet++) { for (tptr = trace_head[inet]; tptr != NULL; tptr = tptr->next) { if (rr_node_color[tptr->index] != BLACK) { net_color[inet] = rr_node_color[tptr->index]; sprintf(message, "%s || Net:%d %d", message, inet, trace_head[inet]->index); break; } } } update_message(message); } static void highlight_rr_nodes(float x, float y) { int inode; int hit = 0; char message[250] = ""; int edge; if (draw_rr_toggle == DRAW_NO_RR && !show_nets) { update_message(default_message); drawscreen(); return; } for (inode = 0; inode < num_rr_nodes; inode++) { if (x >= x_rr_node_left[inode] && x <= x_rr_node_right[inode] && y >= y_rr_node_bottom[inode] && y <= y_rr_node_top[inode]) { t_rr_type rr_type = rr_node[inode].type; int xlow = rr_node[inode].xlow; int xhigh = rr_node[inode].xhigh; int ylow = rr_node[inode].ylow; int yhigh = rr_node[inode].yhigh; int ptc_num = rr_node[inode].ptc_num; rr_node_color[inode] = MAGENTA; sprintf(message, "%s%s %d: %s (%d,%d) -> (%d,%d) track: %d", message, (hit ? " | " : ""), inode, name_type[rr_type], xlow, ylow, xhigh, yhigh, ptc_num); #ifdef DEBUG print_rr_node(stdout, rr_node, inode); #endif for (edge = 0; edge < rr_node[inode].num_edges; edge++) { if (rr_node_color[rr_node[inode].edges[edge]] == BLACK && rr_node[rr_node[inode].edges[edge]].capacity > rr_node[rr_node[inode].edges[edge]].occ) rr_node_color[rr_node[inode].edges[edge]] = GREEN; else if (rr_node_color[rr_node[inode].edges[edge]] == BLACK && rr_node[rr_node[inode].edges[edge]].capacity == rr_node[rr_node[inode].edges[edge]].occ) rr_node_color[rr_node[inode].edges[edge]] = BLUE; } hit = 1; } } if (!hit) { update_message(default_message); drawscreen(); return; } if (show_nets) { highlight_nets(message); } else update_message(message); drawscreen(); } static void highlight_blocks(float x, float y) { /* This routine is called when the user clicks in the graphics area. * * It determines if a clb was clicked on. If one was, it is * * highlighted in green, it's fanin nets and clbs are highlighted in * * blue and it's fanout is highlighted in red. If no clb was * * clicked on (user clicked on white space) any old highlighting is * * removed. Note that even though global nets are not drawn, their * * fanins and fanouts are highlighted when you click on a block * * attached to them. */ int i, j, k, hit, bnum, ipin, netnum, fanblk; int iclass; float io_step; t_type_ptr type; char msg[BUFSIZE]; deselect_all(); hit = i = j = k = 0; for (i = 0; i <= (nx + 1) && !hit; i++) { if (x <= tile_x[i] + tile_width) { if (x >= tile_x[i]) { for (j = 0; j <= (ny + 1) && !hit; j++) { if (grid[i][j].offset != 0) continue; type = grid[i][j].type; if (y <= tile_y[j + type->height - 1] + tile_width) { if (y >= tile_y[j]) hit = 1; } } } } } i--; j--; if (!hit) { highlight_rr_nodes(x, y); /* update_message(default_message); drawscreen(); */ return; } type = grid[i][j].type; hit = 0; if (EMPTY_TYPE == type) { update_message(default_message); drawscreen(); return; } /* The user selected the clb at location (i,j). */ io_step = tile_width / type->capacity; if ((i < 1) || (i > nx)) /* Vertical columns of IOs */ k = (int) ((y - tile_y[j]) / io_step); else k = (int) ((x - tile_x[i]) / io_step); assert(k < type->capacity); if (grid[i][j].blocks[k] == EMPTY) { update_message(default_message); drawscreen(); return; } bnum = grid[i][j].blocks[k]; /* Highlight fanin and fanout. */ for (k = 0; k < type->num_pins; k++) { /* Each pin on a CLB */ netnum = block[bnum].nets[k]; if (netnum == OPEN) continue; iclass = type->pin_class[k]; if (type->class_inf[iclass].type == DRIVER) { /* Fanout */ net_color[netnum] = RED; for (ipin = 1; ipin <= clb_net[netnum].num_sinks; ipin++) { fanblk = clb_net[netnum].node_block[ipin]; block_color[fanblk] = RED; } } else { /* This net is fanin to the block. */ net_color[netnum] = BLUE; fanblk = clb_net[netnum].node_block[0]; /* DRIVER to net */ block_color[fanblk] = BLUE; } } block_color[bnum] = GREEN; /* Selected block. */ sprintf(msg, "Block %d (%s) at (%d, %d) selected.", bnum, block[bnum].name, i, j); update_message(msg); drawscreen(); /* Need to erase screen. */ } static void deselect_all(void) { /* Sets the color of all clbs and nets to the default. */ int i; /* Create some colour highlighting */ for (i = 0; i < num_blocks; i++) { if (block[i].type->index < 3) { block_color[i] = LIGHTGREY; } else if (block[i].type->index < 3 + MAX_BLOCK_COLOURS) { block_color[i] = (enum color_types) (BISQUE + MAX_BLOCK_COLOURS + block[i].type->index - 3); } else { block_color[i] = (enum color_types) (BISQUE + 2 * MAX_BLOCK_COLOURS - 1); } } for (i = 0; i < num_nets; i++) net_color[i] = BLACK; for (i = 0; i < num_rr_nodes; i++) rr_node_color[i] = BLACK; } /* UDSD by AY Start */ static void draw_triangle_along_line(float xend, float yend, float x1, float x2, float y1, float y2) { float switch_rad = 0.15; float xdelta, ydelta; float magnitude; float xunit, yunit; float xbaseline, ybaseline; t_point poly[3]; xdelta = x2 - x1; ydelta = y2 - y1; magnitude = sqrt(xdelta * xdelta + ydelta * ydelta); xunit = xdelta / magnitude; yunit = ydelta / magnitude; poly[0].x = xend + xunit * switch_rad; poly[0].y = yend + yunit * switch_rad; xbaseline = xend - xunit * switch_rad; ybaseline = yend - yunit * switch_rad; poly[1].x = xbaseline + yunit * switch_rad; poly[1].y = ybaseline - xunit * switch_rad; poly[2].x = xbaseline - yunit * switch_rad; poly[2].y = ybaseline + xunit * switch_rad; fillpoly(poly, 3); } static void draw_pin_to_chan_edge(int pin_node, int chan_node) { /* This routine draws an edge from the pin_node to the chan_node (CHANX or * * CHANY). The connection is made to the nearest end of the track instead * * of perpundicular to the track to symbolize a single-drive connection. * * If mark_conn is TRUE, draw a box where the pin connects to the track * * (useful for drawing the rr graph) */ /* TODO: Fix this for global routing, currently for detailed only */ t_rr_type chan_type; int grid_x, grid_y, pin_num, chan_xlow, chan_ylow, ioff, height; float x1, x2, y1, y2; int start, end, i; int itrack; float xend, yend; float draw_pin_off; enum e_direction direction; enum e_side iside; t_type_ptr type; direction = rr_node[chan_node].direction; grid_x = rr_node[pin_node].xlow; grid_y = rr_node[pin_node].ylow; pin_num = rr_node[pin_node].ptc_num; chan_type = rr_node[chan_node].type; itrack = rr_node[chan_node].ptc_num; type = grid[grid_x][grid_y].type; ioff = grid[grid_x][grid_y].offset; /* large block begins at primary tile (offset == 0) */ grid_y = grid_y - ioff; height = grid[grid_x][grid_y].type->height; chan_ylow = rr_node[chan_node].ylow; chan_xlow = rr_node[chan_node].xlow; start = -1; end = -1; switch (chan_type) { case CHANX: start = rr_node[chan_node].xlow; end = rr_node[chan_node].xhigh; if (is_opin(pin_num, type)) { if (direction == INC_DIRECTION) { end = rr_node[chan_node].xlow; } else if (direction == DEC_DIRECTION) { start = rr_node[chan_node].xhigh; } } start = std::max(start, grid_x); end = std::min(end, grid_x); /* Width is 1 always */ assert(end >= start); /* Make sure we are nearby */ if ((grid_y + height - 1) == chan_ylow) { iside = TOP; ioff = height - 1; draw_pin_off = pin_size; } else { assert((grid_y - 1) == chan_ylow); iside = BOTTOM; ioff = 0; draw_pin_off = -pin_size; } assert(grid[grid_x][grid_y].type->pinloc[ioff][iside][pin_num]); get_rr_pin_draw_coords(pin_node, iside, ioff, &x1, &y1); y1 += draw_pin_off; y2 = tile_y[rr_node[chan_node].ylow] + tile_width + 1. + itrack; x2 = x1; if (is_opin(pin_num, type)) { if (direction == INC_DIRECTION) { x2 = tile_x[rr_node[chan_node].xlow]; } else if (direction == DEC_DIRECTION) { x2 = tile_x[rr_node[chan_node].xhigh] + tile_width; } } break; case CHANY: start = rr_node[chan_node].ylow; end = rr_node[chan_node].yhigh; if (is_opin(pin_num, type)) { if (direction == INC_DIRECTION) { end = rr_node[chan_node].ylow; } else if (direction == DEC_DIRECTION) { start = rr_node[chan_node].yhigh; } } start = std::max(start, grid_y); end = std::min(end, (grid_y + height - 1)); /* Width is 1 always */ assert(end >= start); /* Make sure we are nearby */ if ((grid_x) == chan_xlow) { iside = RIGHT; draw_pin_off = pin_size; } else { assert((grid_x - 1) == chan_xlow); iside = LEFT; draw_pin_off = -pin_size; } for (i = start; i <= end; i++) { ioff = i - grid_y; assert(ioff >= 0 && ioff < type->height); /* Once we find the location, break out, this will leave ioff pointing * to the correct offset. If an offset is not found, the assertion after * this will fail. With the correct routing graph, the assertion will not * be triggered. This also takes care of connecting a wire once to multiple * physical pins on the same side. */ if (grid[grid_x][grid_y].type->pinloc[ioff][iside][pin_num]) { break; } } assert(grid[grid_x][grid_y].type->pinloc[ioff][iside][pin_num]); get_rr_pin_draw_coords(pin_node, iside, ioff, &x1, &y1); x1 += draw_pin_off; x2 = tile_x[chan_xlow] + tile_width + 1 + itrack; y2 = y1; if (is_opin(pin_num, type)) { if (direction == INC_DIRECTION) { y2 = tile_y[rr_node[chan_node].ylow]; } else if (direction == DEC_DIRECTION) { y2 = tile_y[rr_node[chan_node].yhigh] + tile_width; } } break; default: vpr_printf(TIO_MESSAGE_ERROR, "in draw_pin_to_chan_edge: Invalid channel node %d.\n", chan_node); exit(1); } drawline(x1, y1, x2, y2); if (direction == BI_DIRECTION || !is_opin(pin_num, type)) { draw_x(x2, y2, 0.7 * pin_size); } else { xend = x2 + (x1 - x2) / 10.; yend = y2 + (y1 - y2) / 10.; draw_triangle_along_line(xend, yend, x1, x2, y1, y2); } } static void draw_pin_to_pin(int opin_node, int ipin_node) { /* This routine draws an edge from the opin rr node to the ipin rr node */ int opin_grid_x, opin_grid_y, opin_pin_num, opin; int ipin_grid_x, ipin_grid_y, ipin_pin_num, ipin; int ofs, pin_ofs; boolean found; float x1, x2, y1, y2; float xend, yend; enum e_side iside, pin_side; t_type_ptr type; assert(rr_node[opin_node].type == OPIN); assert(rr_node[ipin_node].type == IPIN); iside = (enum e_side)0; x1 = y1 = x2 = y2 = 0; pin_ofs = 0; pin_side = TOP; /* get opin coordinate */ opin_grid_x = rr_node[opin_node].xlow; opin_grid_y = rr_node[opin_node].ylow; opin_grid_y = opin_grid_y - grid[opin_grid_x][opin_grid_y].offset; opin = rr_node[opin_node].ptc_num; opin_pin_num = rr_node[opin_node].ptc_num; type = grid[opin_grid_x][opin_grid_y].type; found = FALSE; for (ofs = 0; ofs < type->height && !found; ++ofs) { for (iside = (enum e_side)0; iside < 4 && !found; iside = (enum e_side)(iside + 1)) { /* Find first location of pin */ if (1 == type->pinloc[ofs][iside][opin]) { pin_ofs = ofs; pin_side = iside; found = TRUE; } } } assert(found); get_rr_pin_draw_coords(opin_node, pin_side, pin_ofs, &x1, &y1); /* get ipin coordinate */ ipin_grid_x = rr_node[ipin_node].xlow; ipin_grid_y = rr_node[ipin_node].ylow; ipin_grid_y = ipin_grid_y - grid[ipin_grid_x][ipin_grid_y].offset; ipin = rr_node[ipin_node].ptc_num; ipin_pin_num = rr_node[ipin_node].ptc_num; type = grid[ipin_grid_x][ipin_grid_y].type; found = FALSE; for (ofs = 0; ofs < type->height && !found; ++ofs) { for (iside = (enum e_side)0; iside < 4 && !found; iside = (enum e_side)(iside + 1)) { /* Find first location of pin */ if (1 == type->pinloc[ofs][iside][ipin]) { pin_ofs = ofs; pin_side = iside; found = TRUE; } } } assert(found); get_rr_pin_draw_coords(ipin_node, pin_side, pin_ofs, &x2, &y2); drawline(x1, y1, x2, y2); xend = x2 + (x1 - x2) / 10.; yend = y2 + (y1 - y2) / 10.; draw_triangle_along_line(xend, yend, x1, x2, y1, y2); } /* UDSD by AY End */