| #include <stdio.h> |
| #include <string.h> |
| #include <math.h> |
| #include "util.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> |
| |
| /*************** 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_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. */ |
| |
| |
| /********************** Subroutines local to this module ********************/ |
| |
| static void toggle_nets(void (*drawscreen) (void)); |
| static void toggle_rr(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_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", "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", "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, 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(20); /* 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; |
| 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 = (draw_rr_toggle + 1) % (DRAW_RR_TOGGLE_MAX); |
| show_nets = FALSE; |
| show_congestion = 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; |
| |
| 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)); |
| |
| deselect_all(); /* Set initial colors */ |
| } |
| |
| |
| 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. */ |
| |
| tile_width = width_val; |
| pin_size = 0.3; |
| for(i = 0; i < num_types; ++i) |
| { |
| pin_size = |
| min(pin_size, |
| (tile_width / (4.0 * 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(net[inet].is_global) |
| continue; /* Don't draw global nets. */ |
| |
| setcolor(net_color[inet]); |
| b1 = net[inet].node_block[0]; /* The DRIVER */ |
| get_block_center(b1, &x1, &y1); |
| |
| for(ipin = 1; ipin < (net[inet].num_sinks + 1); ipin++) |
| { |
| b2 = 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; |
| } |
| } |
| } |
| } |
| |
| |
| 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: |
| setcolor(BLACK); |
| itrack = rr_node[inode].ptc_num; |
| draw_rr_chanx(inode, itrack); |
| draw_rr_edges(inode); |
| break; |
| |
| case CHANY: |
| setcolor(BLACK); |
| itrack = rr_node[inode].ptc_num; |
| draw_rr_chany(inode, itrack); |
| draw_rr_edges(inode); |
| break; |
| |
| case IPIN: |
| draw_rr_pin(inode, BLUE); |
| break; |
| |
| case OPIN: |
| draw_rr_pin(inode, RED); |
| setcolor(RED); |
| draw_rr_edges(inode); |
| break; |
| |
| default: |
| printf |
| ("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]; |
| |
| /* Track 0 at bottom edge, closest to "owning" clb. */ |
| |
| x1 = tile_x[rr_node[inode].xlow]; |
| x2 = tile_y[rr_node[inode].xhigh] + tile_width; |
| y = tile_y[rr_node[inode].ylow] + tile_width + 1.0 + itrack; |
| 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]; |
| |
| /* 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; |
| 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; |
| |
| 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; |
| |
| switch (from_type) |
| { |
| |
| case OPIN: |
| switch (to_type) |
| { |
| case CHANX: |
| case CHANY: |
| setcolor(RED); |
| draw_pin_to_chan_edge(inode, to_node); |
| break; |
| |
| default: |
| printf |
| ("Error in draw_rr_edges: node %d (type: %d) connects to \n" |
| "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; |
| } |
| |
| setcolor(BLUE); |
| draw_pin_to_chan_edge(to_node, inode); |
| break; |
| |
| case CHANX: |
| 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: |
| 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: |
| printf |
| ("Error in draw_rr_edges: node %d (type: %d) connects to \n" |
| "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; |
| } |
| |
| setcolor(BLUE); |
| draw_pin_to_chan_edge(to_node, inode); |
| break; |
| |
| case CHANX: |
| 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: |
| 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: |
| printf |
| ("Error in draw_rr_edges: node %d (type: %d) connects to \n" |
| "node %d (type: %d).\n", inode, from_type, |
| to_node, to_type); |
| exit(1); |
| break; |
| } |
| break; |
| |
| default: /* from_type */ |
| printf |
| ("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)) |
| { |
| printf |
| ("from_yhigh (%d) !> to_yhigh (%d).\n", |
| from_yhigh, to_yhigh); |
| printf |
| ("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); |
| printf |
| ("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, iclass, 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); |
| iclass = type->pin_class[ipin]; |
| /* 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: |
| printf("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(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]); |
| 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: |
| printf |
| ("Error in drawroute: Unexpected connection from an \n" |
| "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: |
| printf |
| ("Error in drawroute: Unexpected connection from an \n" |
| "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: |
| printf |
| ("Error in get_track_num: unexpected node type %d for node %d." |
| "\n", rr_type, inode); |
| exit(1); |
| } |
| } |
| |
| |
| 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 = 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) |
| { |
| 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 FB */ |
| 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 <= net[netnum].num_sinks; ipin++) |
| { |
| fanblk = net[netnum].node_block[ipin]; |
| block_color[fanblk] = RED; |
| } |
| } |
| else |
| { /* This net is fanin to the block. */ |
| net_color[netnum] = BLUE; |
| fanblk = 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] = BISQUE + MAX_BLOCK_COLOURS + block[i].type->index - 3; |
| } else { |
| block_color[i] = BISQUE + 2 * MAX_BLOCK_COLOURS - 1; |
| } |
| } |
| |
| for(i = 0; i < num_nets; i++) |
| net_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 = max(start, grid_x); |
| end = 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 = max(start, grid_y); |
| end = 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: |
| printf |
| ("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); |
| } |
| } |
| |
| /* UDSD by AY End */ |