| #include <stdio.h> | |
| #include <string.h> | |
| #include <algorithm> | |
| #include <math.h> | |
| #include "vpr_types.h" | |
| #include "vpr_utils.h" | |
| #include "globals.h" | |
| #include "graphics.h" | |
| #include "path_delay.h" | |
| #include "draw.h" | |
| #include <assert.h> | |
| #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 */ |