blob: e72044bba0fd8f02bfc3f1b8903026bb92b0407e [file] [log] [blame] [edit]
/*********************************** Top-level Summary *************************************
This is VPR's main graphics application program. The program interacts with graphics.c,
which provides an API for displaying graphics on both X11 and Win32. The most important
subroutine in this file is drawscreen(), which is a callback function that X11 or Win32
will call whenever the screen needs to be updated. Then, drawscreen() will decide what
drawing subroutines to call depending on whether PLACEMENT or ROUTING is shown on screen
and whether any of the menu buttons has been triggered. As a note, looks into draw_global.c
for understanding the data structures associated with drawing.
Authors: Vaughn Betz, Long Yu (Mike) Wang
Last updated: August 2013
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#include <assert.h>
#include "vpr_types.h"
#include "vpr_utils.h"
#include "globals.h"
#include "graphics.h"
#include "path_delay.h"
#include "draw.h"
#include "read_xml_arch_file.h"
#include "util.h"
#include "draw_types.h"
#include "draw_global.h"
#include "intra_logic_block.h"
#ifdef WIN32 /* For runtime tracking in WIN32. The clock() function defined in time.h will *
* track CPU runtime. */
#include <time.h>
#else /* For X11. The clock() function in time.h will not output correct time difference *
* for X11, because the graphics is processed by the Xserver rather than local CPU, *
* which means tracking CPU time will not be the same as the actual wall clock time. *
* Thus, so use gettimeofday() in sys/time.h to track actual calendar time. */
#include <sys/time.h>
#endif
#ifdef DEBUG
#include "rr_graph.h"
#endif
/****************************** Define Macros *******************************/
#define DEFAULT_RR_NODE_COLOR BLACK
//#define TIME_DRAWSCREEN /* Enable if want to track runtime for drawscreen() */
/************************** File Scope Variables ****************************/
/* Draw_state is globally accessible through global accesssor function
* get_draw_state_vars().
*/
t_draw_state *draw_state;
/* Call global accessor function get_draw_coords_vars() to retrieve
* global variables for coordinates information.
*/
t_draw_coords *draw_coords;
/********************** 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, t_event_buttonPressed button_info);
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, enum color_types color);
static void draw_rr_chany(int inode, int itrack, enum color_types color);
static t_draw_bbox draw_get_rr_chan_bbox(int inode);
static void draw_get_rr_pin_coords(int inode, int iside, int width_offset,
int height_offset, 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_pin_to_pin(int opin, int ipin);
static void draw_rr_switch(float from_x, float from_y, float to_x, float to_y,
boolean buffered);
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 void draw_triangle_along_line(float xend, float yend, float x1, float x2,
float y1, float y2);
static int get_track_num(int inode, int **chanx_track, int **chany_track);
static bool draw_if_net_highlighted (int inet);
static void draw_highlight_fan_in_fan_out(int hit_node);
static void highlight_nets(char *message, int hit_node);
static int draw_check_rr_node_hit (float click_x, float click_y);
static void highlight_rr_nodes(float x, float y);
static void draw_highlight_blocks_color(t_type_ptr type, int bnum);
static void draw_reset_blk_color(int i);
/********************** Subroutine definitions ******************************/
bool is_inode_an_interposer_wire(int inode)
{
int ix = rr_node[inode].xlow;
int itrack = rr_node[inode].ptc_num;
if( rr_node[inode].type==CHANY && itrack >= chan_width.y_list[ix] )
{
return true;
}
else
{
return false;
}
}
/* This Funtion draws an interposer wire
* The interposer wires are shifted up in the channel to show the effect that they are not
* part of the channel tracks.
* Also, the interposer wires are drawn in RED so that it's clear where cuts are happening
*/
void draw_shifted_line(int inode)
{
t_draw_bbox bound_box = draw_get_rr_chan_bbox(inode);
int ix = rr_node[inode].xlow;
int savecolor = getcolor();
int bottom_shift_y = 0;
int top_shift_y = 0;
if(is_inode_an_interposer_wire(inode))
{
setcolor(RED);
bottom_shift_y = draw_coords->tile_width + chan_width.y_list[ix] + 0.25*chan_width.y_list[ix];
top_shift_y = chan_width.y_list[ix] + chan_width.y_list[ix] - 0.25*chan_width.y_list[ix];
}
drawline(bound_box.xleft, bound_box.ybottom+bottom_shift_y, bound_box.xright, bound_box.ytop+top_shift_y);
setcolor(savecolor);
}
void init_graphics_state(boolean show_graphics_val, int gr_automode_val,
enum e_route_type route_type)
{
/* Call accessor functions to retrieve global variables. */
draw_state = get_draw_state_vars();
/* 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. */
draw_state->show_graphics = show_graphics_val;
draw_state->gr_automode = gr_automode_val;
draw_state->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 (!draw_state->show_graphics) /* Graphics turned off */
return;
/* If it's the type of picture displayed has changed, set up the proper *
* buttons. */
if (draw_state->pic_on_screen != pic_on_screen_val) {
if (pic_on_screen_val == PLACEMENT && draw_state->pic_on_screen == NO_PICTURE) {
create_button("Window", "Toggle Nets", toggle_nets);
create_button("Toggle Nets", "Blk Internal", toggle_blk_internal);
}
else if (pic_on_screen_val == ROUTING && draw_state->pic_on_screen == PLACEMENT) {
create_button("Blk Internal", "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 && draw_state->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
&& draw_state->pic_on_screen == NO_PICTURE) {
create_button("Window", "Toggle Nets", toggle_nets);
create_button("Toggle Nets", "Blk Internal", toggle_blk_internal);
create_button("Blk Internal", "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(draw_state->default_message, msg, BUFSIZE);
draw_state->pic_on_screen = pic_on_screen_val;
update_message(msg);
drawscreen();
if (priority >= draw_state->gr_automode) {
event_loop(highlight_blocks, NULL, NULL, drawscreen);
} else {
flushinput();
}
}
static void drawscreen() {
#ifdef TIME_DRAWSCREEN
/* This can be used to test how long it takes for the redrawing routing to finish *
* updating the screen for a given input which would cause the screen to be redrawn.*/
#ifdef WIN32
clock_t drawscreen_begin,drawscreen_end;
drawscreen_begin = clock();
#else /* For X11. The clock() function in time.h does not output correct time difference *
* in Linux, so use gettimeofday() in sys/time.h for accurate runtime tracking. */
struct timeval begin;
gettimeofday(&begin,NULL); /* get start time */
unsigned long begin_time;
begin_time = begin.tv_sec * 1000000 + begin.tv_usec;
#endif
#endif
/* 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();
#ifdef TIME_DRAWSCREEN
#ifdef WIN32
drawscreen_end = clock();
#ifdef CLOCKS_PER_SEC /* This macro has to do with the version of compiler being used */
printf("Drawscreen took %f seconds.\n", (float)(drawscreen_end - drawscreen_begin) / CLOCKS_PER_SEC);
#else
printf("Drawscreen took %f seconds.\n", (float)(drawscreen_end - drawscreen_begin) / CLK_PER_SEC);
#endif
#else /* X11 */
struct timeval end;
gettimeofday(&end,NULL); /* get end time */
unsigned long end_time;
end_time = end.tv_sec * 1000000 + end.tv_usec;
unsigned long time_diff_microsec;
time_diff_microsec = end_time - begin_time;
printf("Drawscreen took %ld microseconds\n", time_diff_microsec);
#endif /* WIN32 */
#endif /* TIME_DRAWSCREEN */
}
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);
if (draw_state->pic_on_screen == PLACEMENT) {
drawplace();
if (draw_state->show_nets) {
drawnets();
}
if (draw_state->show_blk_internal) {
draw_internal_draw_subblk();
}
} else { /* ROUTING on screen */
drawplace();
if (draw_state->show_nets) {
drawroute(ALL_NETS);
} else {
draw_rr();
}
if (draw_state->show_blk_internal) {
draw_internal_draw_subblk();
}
if (draw_state->show_congestion != DRAW_NO_CONGEST) {
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. */
draw_state->show_nets = (draw_state->show_nets == FALSE) ? TRUE : FALSE;
draw_state->draw_rr_toggle = DRAW_NO_RR;
draw_state->show_congestion = DRAW_NO_CONGEST;
update_message(draw_state->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_state->draw_rr_toggle = (enum e_draw_rr_toggle) (((int)draw_state->draw_rr_toggle + 1)
% ((int)DRAW_RR_TOGGLE_MAX));
draw_state->show_nets = FALSE;
draw_state->show_congestion = DRAW_NO_CONGEST;
update_message(draw_state->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;
draw_state->show_nets = FALSE;
draw_state->draw_rr_toggle = DRAW_NO_RR;
draw_state->show_congestion = (enum e_draw_congestion) (((int)draw_state->show_congestion + 1)
% ((int)DRAW_CONGEST_MAX));
if (draw_state->show_congestion == DRAW_NO_CONGEST) {
update_message(draw_state->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(draw_state->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 */
draw_state->block_color[iblk] = MAGENTA;
} else if (num_nets_seen == nets_to_highlight - 1) { /* 2nd last block */
draw_state->block_color[iblk] = YELLOW;
} else if (num_nets_seen < nets_to_highlight) { /* Earlier block */
draw_state->block_color[iblk] = DARKGREEN;
}
if (inet != OPEN) {
num_nets_seen++;
if (num_nets_seen < nets_to_highlight) { /* First nets. */
draw_state->net_color[inet] = DARKGREEN;
} else if (num_nets_seen == nets_to_highlight) {
draw_state->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) {
/* Call accessor functions to retrieve global variables. */
draw_coords = get_draw_coords_vars();
/* Allocate the structures needed to draw the placement and routing. Set *
* up the default colors for blocks and nets. */
draw_coords->tile_x = (float *) my_malloc((nx + 2) * sizeof(float));
draw_coords->tile_y = (float *) my_malloc((ny + 2) * sizeof(float));
/* For sub-block drawings inside clbs */
draw_internal_alloc_blk();
draw_state->net_color = (enum color_types *) my_malloc(
g_clbs_nlist.net.size() * sizeof(enum color_types));
draw_state->block_color = (enum color_types *) my_malloc(
num_blocks * sizeof(enum color_types));
/* Space is allocated for draw_rr_node but not initialized because we do *
* not yet know information about the routing resources. */
draw_state->draw_rr_node = (t_draw_rr_node *) my_malloc(
num_rr_nodes * sizeof(t_draw_rr_node));
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. */
if(draw_coords != NULL) {
free(draw_coords->tile_x);
draw_coords->tile_x = NULL;
free(draw_coords->tile_y);
draw_coords->tile_y = NULL;
}
if(draw_state != NULL) {
free(draw_state->net_color);
draw_state->net_color = NULL;
free(draw_state->block_color);
draw_state->block_color = NULL;
free(draw_state->draw_rr_node);
draw_state->draw_rr_node = 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 (!draw_state->show_graphics)
return; /* -nodisp was selected. */
/* Each time routing is on screen, need to reallocate the color of each *
* rr_node, as the number of rr_nodes may change. */
if (num_rr_nodes != 0) {
draw_state->draw_rr_node = (t_draw_rr_node *) my_realloc(draw_state->draw_rr_node,
(num_rr_nodes) * sizeof(t_draw_rr_node));
for (i = 0; i < num_rr_nodes; i++) {
draw_state->draw_rr_node[i].color = DEFAULT_RR_NODE_COLOR;
draw_state->draw_rr_node[i].node_highlighted = false;
}
}
draw_coords->tile_width = width_val;
draw_coords->pin_size = 0.3;
for (i = 0; i < num_types; ++i) {
draw_coords->pin_size = min(draw_coords->pin_size,
(draw_coords->tile_width / (4.0F * type_descriptors[i].num_pins)));
}
j = 0;
for (i = 0; i < (nx + 1); i++) {
draw_coords->tile_x[i] = (i * draw_coords->tile_width) + j;
j += chan_width.y_list[i] + 1; /* N wires need N+1 units of space */
// to show the interposer wires, we need more space between the blocks
#ifdef INTERPOSER_BASED_ARCHITECTURE
j += (chan_width.y_list[i] + 1);
#endif
}
draw_coords->tile_x[nx + 1] = ((nx + 1) * draw_coords->tile_width) + j;
j = 0;
for (i = 0; i < (ny + 1); ++i) {
draw_coords->tile_y[i] = (i * draw_coords->tile_width) + j;
j += chan_width.x_list[i] + 1;
// to show the interposer wires, we need more space between the blocks
#ifdef INTERPOSER_BASED_ARCHITECTURE
j += (chan_width.x_list[i] + 1);
#endif
}
draw_coords->tile_y[ny + 1] = ((ny + 1) * draw_coords->tile_width) + j;
/* Load coordinates of sub-blocks inside the clbs */
draw_internal_init_blk();
init_world(0.0, draw_coords->tile_y[ny + 1] + draw_coords->tile_width,
draw_coords->tile_x[nx + 1] + draw_coords->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].width_offset > 0 || grid[i][j].height_offset > 0)
continue;
num_sub_tiles = grid[i][j].type->capacity;
sub_tile_step = draw_coords->tile_width / num_sub_tiles;
height = grid[i][j].type->height;
/* Don't draw if tile capacity is zero. This includes corners. */
if (num_sub_tiles == 0)
continue;
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 ((j < 1) || (j > ny)) { /* top and bottom fringes */
/* Only for the top and bottom fringes (ie. io blocks place on top
* and bottom edges of the chip), sub_tiles are placed next to each
* other horizontally, because it is easier to visualize the
* connection from each individual io block to other parts of the
* chip when toggle_nets is selected.
*/
x1 = draw_coords->tile_x[i] + (k * sub_tile_step);
y1 = draw_coords->tile_y[j];
x2 = x1 + sub_tile_step;
y2 = y1 + draw_coords->tile_width;
}
else {
/* Sub_tiles are stacked on top of each other vertically. */
x1 = draw_coords->tile_x[i];
y1 = draw_coords->tile_y[j] + (k * sub_tile_step);
x2 = x1 + draw_coords->tile_width;
y2 = draw_coords->tile_y[j + height - 1] + ((k + 1) * sub_tile_step);
}
/* Look at the tile at start of large block */
bnum = grid[i][j].blocks[k];
/* Fill background for the clb. Do not fill if "show_blk_internal"
* is toggled.
*/
if (bnum != EMPTY && bnum != INVALID) {
setcolor(draw_state->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 && bnum != INVALID) {
drawtext((x1 + x2) / 2.0, (y1 + y2) / 2.0, block[bnum].name,
sub_tile_step);
}
/* Draw text for block type so that user knows what block */
if (grid[i][j].width_offset == 0 && grid[i][j].height_offset == 0) {
if (i > 0 && i <= nx && j > 0 && j <= ny) {
drawtext((x1 + x2) / 2.0, y1 + (draw_coords->tile_width / 4.0),
grid[i][j].type->name, sub_tile_step);
}
}
}
}
}
}
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. */
unsigned ipin, inet;
int 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 < g_clbs_nlist.net.size(); inet++) {
if (g_clbs_nlist.net[inet].is_global)
continue; /* Don't draw global nets. */
setcolor(draw_state->net_color[inet]);
b1 = g_clbs_nlist.net[inet].pins[0].block; /* The DRIVER */
get_block_center(b1, &x1, &y1);
for (ipin = 1; ipin < g_clbs_nlist.net[inet].pins.size(); ipin++) {
b2 = g_clbs_nlist.net[inet].pins[ipin].block;
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 = draw_coords->tile_width / block[bnum].type->capacity;
if ((j < 1) || (j > ny)) { /* Top and bottom fringe */
/* For the top and bottom fringes (ie. io blocks place on top
* and bottom edges of the chip), sub_tiles are placed next to each
* other horizontally. Therefore, for the center of each io
* sub_block, the x-coordinate varies but the y-coordinate is half
* tile_width above the bottom of the tile.
*/
*x = draw_coords->tile_x[i] + (sub_tile_step * (k + 0.5));
*y = draw_coords->tile_y[j] + (draw_coords->tile_width / 2.0);
} else {
/* For everything other than the top and bottom io blocks, sub_tiles
* (if any) are stacked vertically. Therefore, y-coordinate changes
* for each io sub_block but x-coordinate is half way across the
* block.
*/
*x = draw_coords->tile_x[i] + (draw_coords->tile_width / 2.0);
*y = draw_coords->tile_y[j] + (sub_tile_step * (k + 0.5));
}
}
static void draw_congestion(void) {
/* Draws all the overused routing resources (i.e. congestion) in RED. */
int inode, itrack;
setlinewidth(2);
for (inode = 0; inode < num_rr_nodes; inode++) {
if (rr_node[inode].occ > 0) {
switch (rr_node[inode].type) {
case CHANX:
itrack = rr_node[inode].ptc_num;
if (draw_state->show_congestion == DRAW_CONGESTED &&
rr_node[inode].occ > rr_node[inode].capacity) {
draw_rr_chanx(inode, itrack, RED);
}
else if (draw_state->show_congestion == DRAW_CONGESTED_AND_USED) {
if (rr_node[inode].occ > rr_node[inode].capacity)
draw_rr_chanx(inode, itrack, RED);
else
draw_rr_chanx(inode, itrack, BLUE);
}
break;
case CHANY:
itrack = rr_node[inode].ptc_num;
if (draw_state->show_congestion == DRAW_CONGESTED &&
rr_node[inode].occ > rr_node[inode].capacity) {
draw_rr_chany(inode, itrack, RED);
}
else if (draw_state->show_congestion == DRAW_CONGESTED_AND_USED) {
if (rr_node[inode].occ > rr_node[inode].capacity)
draw_rr_chany(inode, itrack, RED);
else
draw_rr_chany(inode, itrack, BLUE);
}
break;
case IPIN:
case OPIN:
if (draw_state->show_congestion == DRAW_CONGESTED &&
rr_node[inode].occ > rr_node[inode].capacity) {
draw_rr_pin(inode, RED);
}
else if (draw_state->show_congestion == DRAW_CONGESTED_AND_USED) {
if (rr_node[inode].occ > rr_node[inode].capacity)
draw_rr_pin(inode, RED);
else
draw_rr_pin(inode, BLUE);
}
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_state->draw_rr_toggle == DRAW_NO_RR) {
setlinewidth(3);
drawroute(HIGHLIGHTED);
setlinewidth(0);
return;
}
setlinestyle(SOLID);
for (inode = 0; inode < num_rr_nodes; inode++) {
if (!draw_state->draw_rr_node[inode].node_highlighted)
{
/* If not highlighted node, assign color based on type. */
switch (rr_node[inode].type) {
case CHANX:
case CHANY:
draw_state->draw_rr_node[inode].color = DEFAULT_RR_NODE_COLOR;
break;
case OPIN:
draw_state->draw_rr_node[inode].color = PINK;
break;
case IPIN:
draw_state->draw_rr_node[inode].color = LIGHTSKYBLUE;
break;
default:
break;
}
}
/* Now call drawing routines to draw the node. */
switch (rr_node[inode].type) {
case SOURCE:
case SINK:
break; /* Don't draw. */
case CHANX:
itrack = rr_node[inode].ptc_num;
draw_rr_chanx(inode, itrack, draw_state->draw_rr_node[inode].color);
draw_rr_edges(inode);
break;
case CHANY:
itrack = rr_node[inode].ptc_num;
draw_rr_chany(inode, itrack, draw_state->draw_rr_node[inode].color);
draw_rr_edges(inode);
break;
case IPIN:
draw_rr_pin(inode, draw_state->draw_rr_node[inode].color);
break;
case OPIN:
draw_rr_pin(inode, draw_state->draw_rr_node[inode].color);
draw_rr_edges(inode);
break;
default:
vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in draw_rr: Unexpected rr_node type: %d.\n", rr_node[inode].type);
}
}
drawroute(HIGHLIGHTED);
}
static void draw_rr_chanx(int inode, int itrack, enum color_types color) {
/* Draws an x-directed channel segment. */
enum {
BUFFSIZE = 80
};
t_draw_bbox bound_box;
float wire_start_y1, wire_start_y2;
int k;
char str[BUFFSIZE];
// For CHANX, bound_box.ybottom is same as bound_box.ytop
bound_box = draw_get_rr_chan_bbox(inode);
setcolor(color);
if (color != DEFAULT_RR_NODE_COLOR) {
// If wire is highlighted, then draw with thicker linewidth.
setlinewidth(3);
drawline(bound_box.xleft, bound_box.ybottom, bound_box.xright, bound_box.ytop);
setlinewidth(0);
}
else
drawline(bound_box.xleft, bound_box.ybottom, bound_box.xright, bound_box.ytop);
wire_start_y1 = bound_box.ybottom - 0.25;
wire_start_y2 = bound_box.ytop + 0.25;
if (rr_node[inode].direction == INC_DIRECTION) {
setlinewidth(2);
setcolor(YELLOW);
/* Draw a line at start of wire to indicate mux */
drawline(bound_box.xleft, wire_start_y1, bound_box.xleft, wire_start_y2);
/* Mux balence numbers */
setcolor(BLACK);
sprintf(str, "%d", rr_node[inode].fan_in);
drawtext(bound_box.xleft, bound_box.ybottom, str, 5);
setcolor(BLACK);
setlinewidth(0);
draw_triangle_along_line(bound_box.xright - 0.15, bound_box.ytop, bound_box.xleft,
bound_box.xright, bound_box.ybottom, bound_box.ytop);
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++) {
bound_box.xright = draw_coords->tile_x[k] + draw_coords->tile_width;
draw_triangle_along_line(bound_box.xright - 0.15, bound_box.ytop, bound_box.xleft,
bound_box.xright, bound_box.ybottom, bound_box.ytop);
bound_box.xright = draw_coords->tile_x[k + 1];
draw_triangle_along_line(bound_box.xright + 0.15, bound_box.ytop, bound_box.xleft,
bound_box.xright, bound_box.ybottom, bound_box.ytop);
}
setcolor(color);
} else if (rr_node[inode].direction == DEC_DIRECTION) {
setlinewidth(2);
setcolor(YELLOW);
drawline(bound_box.xright, wire_start_y1, bound_box.xright, wire_start_y2);
/* Mux balance numbers */
setcolor(BLACK);
sprintf(str, "%d", rr_node[inode].fan_in);
drawtext(bound_box.xright, bound_box.ybottom, str, 5);
setlinewidth(0);
draw_triangle_along_line(bound_box.xleft + 0.15, bound_box.ybottom, bound_box.xright,
bound_box.xleft, bound_box.ytop, bound_box.ybottom);
setcolor(LIGHTGREY);
for (k = rr_node[inode].xhigh; k > rr_node[inode].xlow; k--) {
bound_box.xleft = draw_coords->tile_x[k];
draw_triangle_along_line(bound_box.xleft + 0.15, bound_box.ybottom, bound_box.xright,
bound_box.xleft, bound_box.ytop, bound_box.ybottom);
bound_box.xleft = draw_coords->tile_x[k - 1] + draw_coords->tile_width;
draw_triangle_along_line(bound_box.xleft - 0.15, bound_box.ybottom, bound_box.xright,
bound_box.xleft, bound_box.ytop, bound_box.ybottom);
}
setcolor(color);
}
}
static void draw_rr_chany(int inode, int itrack, enum color_types color) {
/* Draws a y-directed channel segment. */
enum {
BUFFSIZE = 80
};
t_draw_bbox bound_box;
float wire_start_x1, wire_start_x2;
int k;
char str[BUFFSIZE];
// Get the coordinates of the channel wire segment.
// For CHANY, bound_box.xleft is equal to bound_box.xright.
bound_box = draw_get_rr_chan_bbox(inode);
setcolor(color);
if (color != DEFAULT_RR_NODE_COLOR)
{
// If wire is highlighted, then draw with thicker linewidth.
setlinewidth(3);
#ifdef INTERPOSER_BASED_ARCHITECTURE
if(is_inode_an_interposer_wire(inode))
{
draw_shifted_line(inode);
}
else
{
drawline(bound_box.xleft, bound_box.ybottom, bound_box.xright, bound_box.ytop);
}
#else
drawline(bound_box.xleft, bound_box.ybottom, bound_box.xright, bound_box.ytop);
#endif
setlinewidth(0);
}
else
{
#ifdef INTERPOSER_BASED_ARCHITECTURE
if(is_inode_an_interposer_wire(inode))
{
draw_shifted_line(inode);
}
else
{
drawline(bound_box.xleft, bound_box.ybottom, bound_box.xright, bound_box.ytop);
}
#else
drawline(bound_box.xleft, bound_box.ybottom, bound_box.xright, bound_box.ytop);
#endif
}
wire_start_x1 = bound_box.xleft - 0.25;
wire_start_x2 = bound_box.xright + 0.25;
if (rr_node[inode].direction == INC_DIRECTION) {
setlinewidth(2);
setcolor(YELLOW);
drawline(wire_start_x1, bound_box.ybottom, wire_start_x2, bound_box.ybottom);
setcolor(BLACK);
sprintf(str, "%d", rr_node[inode].fan_in);
drawtext(bound_box.xleft, bound_box.ybottom, str, 5);
setcolor(BLACK);
setlinewidth(0);
draw_triangle_along_line(bound_box.xright, bound_box.ytop - 0.15, bound_box.xleft,
bound_box.xright, bound_box.ybottom, bound_box.ytop);
setcolor(LIGHTGREY);
for (k = rr_node[inode].ylow; k < rr_node[inode].yhigh; k++) {
bound_box.ytop = draw_coords->tile_y[k] + draw_coords->tile_width;
draw_triangle_along_line(bound_box.xright, bound_box.ytop - 0.15, bound_box.xleft,
bound_box.xright, bound_box.ybottom, bound_box.ytop);
bound_box.ytop = draw_coords->tile_y[k + 1];
draw_triangle_along_line(bound_box.xright, bound_box.ytop + 0.15, bound_box.xleft,
bound_box.xright, bound_box.ybottom, bound_box.ytop);
}
setcolor(color);
} else if (rr_node[inode].direction == DEC_DIRECTION) {
setlinewidth(2);
setcolor(YELLOW);
drawline(wire_start_x1, bound_box.ytop, wire_start_x2, bound_box.ytop);
setcolor(BLACK);
sprintf(str, "%d", rr_node[inode].fan_in);
drawtext(bound_box.xleft, bound_box.ytop, str, 5);
setcolor(BLACK);
setlinewidth(0);
draw_triangle_along_line(bound_box.xleft, bound_box.ybottom + 0.15, bound_box.xright,
bound_box.xleft, bound_box.ytop, bound_box.ybottom);
setcolor(LIGHTGREY);
for (k = rr_node[inode].yhigh; k > rr_node[inode].ylow; k--) {
bound_box.ybottom = draw_coords->tile_y[k];
draw_triangle_along_line(bound_box.xleft, bound_box.ybottom + 0.15, bound_box.xright,
bound_box.xleft, bound_box.ytop, bound_box.ybottom);
bound_box.ybottom = draw_coords->tile_y[k - 1] + draw_coords->tile_width;
draw_triangle_along_line(bound_box.xleft, bound_box.ybottom - 0.15, bound_box.xright,
bound_box.xleft, bound_box.ytop, bound_box.ybottom);
}
setcolor(color);
}
}
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_state->draw_rr_toggle == DRAW_NODES_RR)
|| (draw_state->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:
if (draw_state->draw_rr_node[inode].color == MAGENTA) {
// If OPIN was clicked on, set color to fan-out
setcolor(draw_state->draw_rr_node[to_node].color);
} else if (draw_state->draw_rr_node[to_node].color == MAGENTA) {
// If CHANX or CHANY got clicked, set color to fan-in
setcolor(draw_state->draw_rr_node[inode].color);
} else
setcolor(PINK);
draw_pin_to_chan_edge(inode, to_node);
break;
case IPIN:
if (draw_state->draw_rr_node[inode].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[to_node].color);
} else if (draw_state->draw_rr_node[to_node].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[inode].color);
} else
setcolor(MEDIUMPURPLE);
draw_pin_to_pin(inode, to_node);
break;
default:
vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in draw_rr_edges: node %d (type: %d) connects to node %d (type: %d).\n",
inode, from_type, to_node, to_type);
break;
}
break;
case CHANX: /* from_type */
switch (to_type) {
case IPIN:
if (draw_state->draw_rr_toggle == DRAW_NODES_AND_SBOX_RR) {
break;
}
if (draw_state->draw_rr_node[to_node].node_highlighted &&
draw_state->draw_rr_node[inode].color == DEFAULT_RR_NODE_COLOR) {
// If the IPIN is clicked on, draw connection to all the CHANX
// wire segments fanning into the pin. If a CHANX wire is clicked
// on, draw only the connection between that wire and the IPIN, with
// the pin fanning out from the wire.
break;
}
if (draw_state->draw_rr_node[inode].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[to_node].color);
} else if (draw_state->draw_rr_node[to_node].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[inode].color);
} else
setcolor(LIGHTSKYBLUE);
draw_pin_to_chan_edge(to_node, inode);
break;
case CHANX:
if (draw_state->draw_rr_node[inode].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[to_node].color);
} else if (draw_state->draw_rr_node[to_node].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[inode].color);
} 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 (draw_state->draw_rr_node[inode].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[to_node].color);
} else if (draw_state->draw_rr_node[to_node].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[inode].color);
} 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_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in draw_rr_edges: node %d (type: %d) connects to node %d (type: %d).\n",
inode, from_type, to_node, to_type);
break;
}
break;
case CHANY: /* from_type */
switch (to_type) {
case IPIN:
if (draw_state->draw_rr_toggle == DRAW_NODES_AND_SBOX_RR) {
break;
}
if (draw_state->draw_rr_node[to_node].node_highlighted &&
draw_state->draw_rr_node[inode].color == DEFAULT_RR_NODE_COLOR) {
// If the IPIN is clicked on, draw connection to all the CHANY
// wire segments fanning into the pin. If a CHANY wire is clicked
// on, draw only the connection between that wire and the IPIN, with
// the pin fanning out from the wire.
break;
}
if (draw_state->draw_rr_node[inode].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[to_node].color);
} else if (draw_state->draw_rr_node[to_node].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[inode].color);
} else
setcolor(LIGHTSKYBLUE);
draw_pin_to_chan_edge(to_node, inode);
break;
case CHANX:
if (draw_state->draw_rr_node[inode].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[to_node].color);
} else if (draw_state->draw_rr_node[to_node].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[inode].color);
} 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 (draw_state->draw_rr_node[inode].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[to_node].color);
} else if (draw_state->draw_rr_node[to_node].color == MAGENTA) {
setcolor(draw_state->draw_rr_node[inode].color);
} 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_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in draw_rr_edges: node %d (type: %d) connects to node %d (type: %d).\n",
inode, from_type, to_node, to_type);
break;
}
break;
default: /* from_type */
vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"draw_rr_edges called with node %d of type %d.\n",
inode, from_type);
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);
}
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;
t_draw_bbox chanx_bbox, chany_bbox;
int chanx_xlow, chany_x, chany_ylow, chanx_y;
/* Get the coordinates of the CHANX and CHANY segments. */
chanx_bbox = draw_get_rr_chan_bbox(chanx_node);
chany_bbox = draw_get_rr_chan_bbox(chany_node);
/* (x1,y1): point on CHANX segment, (x2,y2): point on CHANY segment. */
y1 = chanx_bbox.ybottom;
x2 = chany_bbox.xleft;
chanx_xlow = rr_node[chanx_node].xlow;
chanx_y = rr_node[chanx_node].ylow;
chany_x = rr_node[chany_node].xlow;
chany_ylow = rr_node[chany_node].ylow;
if (chanx_xlow <= chany_x) { /* Can draw connection going right */
/* Connection not at end of the CHANX segment. */
x1 = draw_coords->tile_x[chany_x] + draw_coords->tile_width;
if (rr_node[chanx_node].direction != BI_DIRECTION) {
if (edge_dir == FROM_X_TO_Y) {
if ((chanx_track % 2) == 1) { /* If dec wire, then going left */
x1 = draw_coords->tile_x[chany_x + 1];
}
}
}
} else { /* Must draw connection going left. */
x1 = chanx_bbox.xleft;
}
if (chany_ylow <= chanx_y) { /* Can draw connection going up. */
/* Connection not at end of the CHANY segment. */
y2 = draw_coords->tile_y[chanx_y] + draw_coords->tile_width;
if (rr_node[chany_node].direction != BI_DIRECTION) {
if (edge_dir == FROM_Y_TO_X) {
if ((chany_track % 2) == 1) { /* If dec wire, then going down */
y2 = draw_coords->tile_y[chanx_y + 1];
}
}
}
} else { /* Must draw connection going down. */
y2 = chany_bbox.ybottom;
}
#ifdef INTERPOSER_BASED_ARCHITECTURE
// handle CHANY connections to interposer wires
// connect to the bottom of an interposer wire
if(chany_track >= chan_width.y_list[chany_x])
{
y2 = draw_coords->tile_y[chanx_y] + draw_coords->tile_width + chan_width.y_list[chany_x] + 0.25*chan_width.y_list[chany_x];
}
#endif
drawline(x1, y1, x2, y2);
if (draw_state->draw_rr_toggle != DRAW_ALL_RR) {
if (draw_state->draw_rr_node[chanx_node].node_highlighted) {
float xend, yend;
xend = x2 + (x1 - x2) / 10.;
yend = y2 + (y1 - y2) / 10.;
// Draw arrow showing direction
draw_triangle_along_line(xend, yend, x1, x2, y1, y2);
}
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;
t_draw_bbox from_chan, to_chan;
int from_xlow, to_xlow, from_xhigh, to_xhigh;
// Get the coordinates of the channel wires.
from_chan = draw_get_rr_chan_bbox(from_node);
to_chan = draw_get_rr_chan_bbox(to_node);
/* (x1, y1) point on from_node, (x2, y2) point on to_node. */
y1 = from_chan.ybottom;
y2 = to_chan.ybottom;
from_xlow = rr_node[from_node].xlow;
from_xhigh = rr_node[from_node].xhigh;
to_xlow = rr_node[to_node].xlow;
to_xhigh = rr_node[to_node].xhigh;
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 = from_chan.xleft;
x2 = to_chan.xright;
} 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 = from_chan.xright;
x2 = to_chan.xleft;
}
/* 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. */
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 = to_chan.xleft;
/* since no U-turns from_track must be INC as well */
x1 = draw_coords->tile_x[to_xlow - 1] + draw_coords->tile_width;
} else { /* DEC wire starts at rightmost edge */
assert(from_xhigh > to_xhigh);
x2 = to_chan.xright;
x1 = draw_coords->tile_x[to_xhigh + 1];
}
} else {
if (to_xlow < from_xlow) { /* Draw from left edge of one to other */
x1 = from_chan.xleft;
x2 = draw_coords->tile_x[from_xlow - 1] + draw_coords->tile_width;
} else if (from_xlow < to_xlow) {
x1 = draw_coords->tile_x[to_xlow - 1] + draw_coords->tile_width;
x2 = to_chan.xleft;
} /* 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 = from_chan.xright;
x2 = draw_coords->tile_x[from_xhigh + 1];
} else if (from_xhigh > to_xhigh) {
x1 = draw_coords->tile_x[to_xhigh + 1];
x2 = to_chan.xright;
} else { /* Complete overlap: start and end both align. Draw outside the sbox */
x1 = from_chan.xleft;
x2 = from_chan.xleft + draw_coords->tile_width;
}
}
}
drawline(x1, y1, x2, y2);
if (draw_state->draw_rr_toggle == DRAW_ALL_RR)
draw_rr_switch(x1, y1, x2, y2, switch_inf[switch_type].buffered);
else if (draw_state->draw_rr_node[from_node].node_highlighted) {
float xend, yend;
xend = x2 + (x1 - x2) / 10.;
yend = y2 + (y1 - y2) / 10.;
// Draw arrow showing direction
draw_triangle_along_line(xend, yend, x1, x2, y1, y2);
}
}
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;
t_draw_bbox from_chan, to_chan;
int from_ylow, to_ylow, from_yhigh, to_yhigh, from_x, to_x;
// Get the coordinates of the channel wires.
from_chan = draw_get_rr_chan_bbox(from_node);
to_chan = draw_get_rr_chan_bbox(to_node);
from_x = rr_node[from_node].xlow;
to_x = rr_node[to_node].xlow;
from_ylow = rr_node[from_node].ylow;
from_yhigh = rr_node[from_node].yhigh;
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 = from_chan.xleft;
x2 = to_chan.xleft;
if (to_yhigh < from_ylow) { /* From upper to lower */
y1 = from_chan.ybottom;
y2 = to_chan.ytop;
} else if (to_ylow > from_yhigh) { /* From lower to upper */
y1 = from_chan.ytop;
y2 = to_chan.ybottom;
}
/* 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 */
// for interposer-based architectures, the interposer node will have
// the same y_low and y_high as the cutline y-coordinate
// so, for connections from CHANY wire just below the cut to the interposer node,
// from_ylow can be equal to to_ylow
#ifndef INTERPOSER_BASED_ARCHITECTURE
assert(from_ylow < to_ylow);
#endif
y2 = to_chan.ybottom;
/* since no U-turns from_track must be INC as well */
y1 = draw_coords->tile_y[to_ylow - 1] + draw_coords->tile_width;
} else { /* DEC wire starts at top edge */
// for interposer-based architectures, the interposer node is marked as CHANY
// so, it is possible to have from_yhigh equal to to_yhigh
#ifndef INTERPOSER_BASED_ARCHITECTURE
if (!(from_yhigh > to_yhigh)) {
vpr_printf_info("from_yhigh (%d) !> to_yhigh (%d).\n",
from_yhigh, to_yhigh);
vpr_printf_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_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);
}
#endif
y2 = to_chan.ytop;
y1 = draw_coords->tile_y[to_yhigh + 1];
}
} else {
if (to_ylow < from_ylow) { /* Draw from bottom edge of one to other. */
y1 = from_chan.ybottom;
y2 = draw_coords->tile_y[from_ylow - 1] + draw_coords->tile_width;
} else if (from_ylow < to_ylow) {
y1 = draw_coords->tile_y[to_ylow - 1] + draw_coords->tile_width;
y2 = to_chan.ybottom;
} else if (to_yhigh > from_yhigh) { /* Draw from top edge of one to other. */
y1 = from_chan.ytop;
y2 = draw_coords->tile_y[from_yhigh + 1];
} else if (from_yhigh > to_yhigh) {
y1 = draw_coords->tile_y[to_yhigh + 1];
y2 = to_chan.ytop;
} else { /* Complete overlap: start and end both align. Draw outside the sbox */
y1 = from_chan.ybottom;
y2 = from_chan.ybottom + draw_coords->tile_width;
}
}
}
#ifdef INTERPOSER_BASED_ARCHITECTURE
//handle CHANX connections to and from interposer wires
if(is_inode_an_interposer_wire(from_node))
{
if(rr_node[from_node].direction == DEC_DIRECTION)
{
y1 = draw_coords->tile_y[from_ylow] + draw_coords->tile_width + chan_width.y_list[from_x] + 0.25*chan_width.y_list[from_x];
}
else if(rr_node[from_node].direction == INC_DIRECTION)
{
y1 = draw_coords->tile_y[from_ylow] + draw_coords->tile_width + 2*chan_width.y_list[from_x] - 0.25*chan_width.y_list[from_x];
}
}
if(is_inode_an_interposer_wire(to_node))
{
if(rr_node[to_node].direction == INC_DIRECTION)
{
y2 = draw_coords->tile_y[to_ylow] + draw_coords->tile_width + chan_width.y_list[to_x] + 0.25*chan_width.y_list[to_x];
}
else if(rr_node[to_node].direction == DEC_DIRECTION)
{
y2 = draw_coords->tile_y[to_ylow] + draw_coords->tile_width + 2*chan_width.y_list[to_x] - 0.25*chan_width.y_list[to_x];
}
}
#endif
/* UDSD Modification by WMF End */
drawline(x1, y1, x2, y2);
if (draw_state->draw_rr_toggle == DRAW_ALL_RR)
draw_rr_switch(x1, y1, x2, y2, switch_inf[switch_type].buffered);
else if (draw_state->draw_rr_node[from_node].node_highlighted) {
float xend, yend;
xend = x2 + (x1 - x2) / 10.;
yend = y2 + (y1 - y2) / 10.;
// Draw arrow showing direction
draw_triangle_along_line(xend, yend, x1, x2, y1, y2);
}
}
/* This function computes and returns the boundary coordinates of a channel
* wire segment. This can be used for drawing a wire or determining if a
* wire has been clicked on by the user.
* TODO: Fix this for global routing, currently for detailed only.
*/
static t_draw_bbox draw_get_rr_chan_bbox (int inode) {
t_draw_bbox bound_box;
switch (rr_node[inode].type) {
case CHANX:
bound_box.xleft = draw_coords->tile_x[rr_node[inode].xlow];
bound_box.xright = draw_coords->tile_x[rr_node[inode].xhigh]
+ draw_coords->tile_width;
bound_box.ybottom = draw_coords->tile_y[rr_node[inode].ylow]
+ draw_coords->tile_width + (1. + rr_node[inode].ptc_num);
bound_box.ytop = draw_coords->tile_y[rr_node[inode].ylow]
+ draw_coords->tile_width + (1. + rr_node[inode].ptc_num);
break;
case CHANY:
bound_box.xleft = draw_coords->tile_x[rr_node[inode].xlow]
+ draw_coords->tile_width + (1. + rr_node[inode].ptc_num);
bound_box.xright = draw_coords->tile_x[rr_node[inode].xlow]
+ draw_coords->tile_width + (1. + rr_node[inode].ptc_num);
bound_box.ybottom = draw_coords->tile_y[rr_node[inode].ylow];
bound_box.ytop = draw_coords->tile_y[rr_node[inode].yhigh]
+ draw_coords->tile_width;
break;
default:
bound_box.xleft = -1;
bound_box.xright = -1;
bound_box.ybottom = -1;
bound_box.ytop = -1;
break;
}
return bound_box;
}
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;
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;
int width_offset = grid[i][j].width_offset;
int height_offset = grid[i][j].height_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].width_offset][grid[i][j].height_offset][iside][ipin]) { /* Pin exists on this side. */
draw_get_rr_pin_coords(inode, iside, width_offset, height_offset, &xcen, &ycen);
fillrect(xcen - draw_coords->pin_size, ycen - draw_coords->pin_size,
xcen + draw_coords->pin_size, ycen + draw_coords->pin_size);
sprintf(str, "%d", ipin);
setcolor(BLACK);
drawtext(xcen, ycen, str, 2 * draw_coords->pin_size);
setcolor(color);
}
}
}
static void draw_get_rr_pin_coords(int inode, int iside,
int width_offset, int height_offset,
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 + width_offset;
j = rr_node[inode].ylow + height_offset;
xc = draw_coords->tile_x[i];
yc = draw_coords->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) (draw_coords->tile_width) / (float) (type->num_pins + type->capacity);
offset = (ipin + k + 1) * step;
switch (iside) {
case LEFT:
yc += offset;
break;
case RIGHT:
xc += draw_coords->tile_width;
yc += offset;
break;
case BOTTOM:
xc += offset;
break;
case TOP:
xc += offset;
yc += draw_coords->tile_width;
break;
default:
vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in draw_get_rr_pin_coords: Unexpected iside %d.\n", iside);
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] */
unsigned int inet;
int i, j, inode, prev_node, prev_track, itrack;
short switch_type;
struct s_trace *tptr;
t_rr_type rr_type, prev_type;
if (draw_state->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 < g_clbs_nlist.net.size(); inet++) {
if (g_clbs_nlist.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 && draw_state->net_color[inet] == BLACK)
continue;
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;
if (draw_if_net_highlighted(inet)) {
/* If a net has been highlighted, highlight the whole net in *
* the same color. */
draw_state->draw_rr_node[inode].color = draw_state->net_color[inet];
draw_state->draw_rr_node[inode].node_highlighted = true;
}
else {
/* If not highlighted, draw the node in default color. */
draw_state->draw_rr_node[inode].color = DEFAULT_RR_NODE_COLOR;
}
switch (rr_type) {
case OPIN:
draw_rr_pin(inode, draw_state->draw_rr_node[inode].color);
break;
case IPIN:
draw_rr_pin(inode, draw_state->draw_rr_node[inode].color);
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_state->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, draw_state->draw_rr_node[inode].color);
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_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in drawroute: Unexpected connection from an rr_node of type %d to one of type %d.\n",
prev_type, rr_type);
}
break;
case CHANY:
if (draw_state->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, draw_state->draw_rr_node[inode].color);
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_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in drawroute: Unexpected connection from an rr_node of type %d to one of type %d.\n",
prev_type, rr_type);
}
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_state->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_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in get_track_num: Unexpected node type %d for node %d.\n", rr_type, inode);
return OPEN;
}
}
/* This helper function determines whether a net has been highlighted. The highlighting *
* could be caused by the user clicking on a routing resource, highlight critical path *
* toggled, or fan-in/fan-out of a highlighted node. */
static bool draw_if_net_highlighted (int inet) {
bool highlighted = false;
if (draw_state->net_color[inet] == MAGENTA || draw_state->net_color[inet] == RED
|| draw_state->net_color[inet] == BLUE || draw_state->net_color[inet] == DARKGREEN
|| draw_state->net_color[inet] == CYAN)
highlighted = true;
return highlighted;
}
/* If an rr_node has been clicked on, it will be highlighted in MAGENTA.
* If so, and toggle nets is selected, highlight the whole net in that colour.
*/
static void highlight_nets(char *message, int hit_node) {
unsigned int inet;
struct s_trace *tptr;
for (inet = 0; inet < g_clbs_nlist.net.size(); inet++) {
for (tptr = trace_head[inet]; tptr != NULL; tptr = tptr->next) {
if (draw_state->draw_rr_node[tptr->index].color == MAGENTA) {
draw_state->net_color[inet] = draw_state->draw_rr_node[tptr->index].color;
if (tptr->index == hit_node) {
sprintf(message, "%s || Net: %d (%s)", message, inet,
g_clbs_nlist.net[inet].name);
}
}
else if (draw_state->draw_rr_node[tptr->index].color == WHITE) {
// If node is de-selected.
draw_state->net_color[inet] = BLACK;
break;
}
}
}
update_message(message);
}
/* If an rr_node has been clicked on, it will be either highlighted in MAGENTA,
* or de-highlighted in WHITE. If highlighted, and toggle_rr is selected, highlight
* fan_in into the node in blue and fan_out from the node in red. If de-highlighted,
* de-highlight its fan_in and fan_out.
*/
static void draw_highlight_fan_in_fan_out(int hit_node) {
int iedge, inode;
/* Highlight the fanout nodes in red. */
for (iedge = 0; iedge < rr_node[hit_node].num_edges; iedge++) {
int fanout_node = rr_node[hit_node].edges[iedge];
if (draw_state->draw_rr_node[hit_node].color == MAGENTA) {
// If node is highlighted, highlight its fanout
draw_state->draw_rr_node[fanout_node].color = RED;
draw_state->draw_rr_node[fanout_node].node_highlighted = true;
}
else if (draw_state->draw_rr_node[hit_node].color == WHITE) {
// If node is de-highlighted, de-highlight its fanout
draw_state->draw_rr_node[fanout_node].color = DEFAULT_RR_NODE_COLOR;
draw_state->draw_rr_node[fanout_node].node_highlighted = false;
}
}
/* Highlight the nodes that can fanin to this node in blue. */
for (inode = 0; inode < num_rr_nodes; inode++) {
for (iedge = 0; iedge < rr_node[inode].num_edges; iedge++) {
int fanout_node = rr_node[inode].edges[iedge];
if (fanout_node == hit_node) {
if (draw_state->draw_rr_node[hit_node].color == MAGENTA) {
// If hit_node is highlighted, highlight its fanin
draw_state->draw_rr_node[inode].color = BLUE;
draw_state->draw_rr_node[inode].node_highlighted = true;
}
else if (draw_state->draw_rr_node[hit_node].color == WHITE) {
// If hit_node is de-highlighted, de-highlight its fanin
draw_state->draw_rr_node[inode].color = DEFAULT_RR_NODE_COLOR;
draw_state->draw_rr_node[inode].node_highlighted = false;
}
}
}
}
}
/* This is a helper function for highlight_rr_nodes(). It determines whether
* a routing resource has been clicked on by computing a bounding box for that
* and checking if the mouse click hit inside its bounding box.
*/
static int draw_check_rr_node_hit (float click_x, float click_y) {
int inode;
int hit_node = OPEN;
t_draw_bbox bound_box;
for (inode = 0; inode < num_rr_nodes; inode++) {
switch (rr_node[inode].type) {
case IPIN:
case OPIN:
{
int i = rr_node[inode].xlow;
int j = rr_node[inode].ylow;
t_type_ptr type = grid[i][j].type;
int width_offset = grid[i][j].width_offset;
int height_offset = grid[i][j].height_offset;
int ipin = rr_node[inode].ptc_num;
float xcen, ycen;
int iside;
for (iside = 0; iside < 4; iside++) {
// If pin exists on this side of the block, then get pin coordinates
if (type->pinloc[width_offset][height_offset][iside][ipin]) {
draw_get_rr_pin_coords(inode, iside, width_offset, height_offset,
&xcen, &ycen);
// Now check if we clicked on this pin
if (click_x >= xcen - draw_coords->pin_size &&
click_x <= xcen + draw_coords->pin_size &&
click_y >= ycen - draw_coords->pin_size &&
click_y <= ycen + draw_coords->pin_size) {
hit_node = inode;
return hit_node;
}
}
}
break;
}
case CHANX:
case CHANY:
{
bound_box = draw_get_rr_chan_bbox(inode);
// Check if we clicked on this wire, with 30%
// tolerance outside its boundary
const float tolerance = 0.3;
if (click_x >= bound_box.xleft - tolerance &&
click_x <= bound_box.xright + tolerance &&
click_y >= bound_box.ybottom - tolerance &&
click_y <= bound_box.ytop + tolerance) {
hit_node = inode;
return hit_node;
}
break;
}
default:
break;
}
}
return hit_node;
}
/* This routine is called when the routing resource graph is shown, and someone
* clicks outside a block. That click might represent a click on a wire -- we call
* this routine to determine which wire (if any) was clicked on. If a wire was
* clicked upon, we highlight it in Magenta, and its fanout in red.
*/
static void highlight_rr_nodes(float x, float y) {
int hit_node = OPEN; // i.e. -1, no node selected.
char message[250] = "";
if (draw_state->draw_rr_toggle == DRAW_NO_RR && !draw_state->show_nets) {
update_message(draw_state->default_message);
drawscreen();
return;
}
// Check which rr_node (if any) was clicked on.
hit_node = draw_check_rr_node_hit (x, y);
if (hit_node != OPEN) {
int xlow = rr_node[hit_node].xlow;
int xhigh = rr_node[hit_node].xhigh;
int ylow = rr_node[hit_node].ylow;
int yhigh = rr_node[hit_node].yhigh;
int ptc_num = rr_node[hit_node].ptc_num;
if (draw_state->draw_rr_node[hit_node].color != MAGENTA) {
/* If the node hasn't been clicked on before, highlight it
* in magenta.
*/
draw_state->draw_rr_node[hit_node].color = MAGENTA;
draw_state->draw_rr_node[hit_node].node_highlighted = true;
sprintf(message, "Selected node #%d: %s (%d,%d) -> (%d,%d) track: %d, %d edges, occ: %d, capacity: %d",
hit_node, rr_node[hit_node].rr_get_type_string(),
xlow, ylow, xhigh, yhigh, ptc_num, rr_node[hit_node].num_edges,
rr_node[hit_node].occ, rr_node[hit_node].capacity);
}
else {
/* Using white color to represent de-highlighting (or
* de-selecting) of node.
*/
draw_state->draw_rr_node[hit_node].color = WHITE;
draw_state->draw_rr_node[hit_node].node_highlighted = false;
}
#ifdef DEBUG
print_rr_node(stdout, rr_node, hit_node);
#endif
if (draw_state->draw_rr_toggle != DRAW_NO_RR)
// If rr_graph is shown, highlight the fan-in/fan-outs for
// this node.
draw_highlight_fan_in_fan_out(hit_node);
}
if (hit_node == OPEN) {
update_message(draw_state->default_message);
drawscreen();
return;
}
if (draw_state->show_nets) {
highlight_nets(message, hit_node);
} else
update_message(message);
drawscreen();
}
static void highlight_blocks(float x, float y, t_event_buttonPressed button_info) {
/* 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;
float io_step;
t_type_ptr type;
char msg[BUFSIZE];
/* Control + mouse click to select multiple nets. */
if (!button_info.ctrl_pressed)
deselect_all();
hit = i = j = k = 0;
for (i = 0; i <= (nx + 1) && !hit; i++) {
if (x <= draw_coords->tile_x[i] + draw_coords->tile_width) {
if (x >= draw_coords->tile_x[i]) {
for (j = 0; j <= (ny + 1) && !hit; j++) {
if (grid[i][j].width_offset != 0 || grid[i][j].height_offset != 0)
continue;
type = grid[i][j].type;
if (y <= draw_coords->tile_y[j + type->height - 1]
+ draw_coords->tile_width)
{
if (y >= draw_coords->tile_y[j])
hit = 1;
}
}
}
}
}
i--;
j--;
if (!hit) {
highlight_rr_nodes(x, y);
/* update_message(draw_state->default_message);
drawscreen(); */
return;
}
type = grid[i][j].type;
hit = 0;
if (EMPTY_TYPE == type) {
update_message(draw_state->default_message);
drawscreen();
return;
}
/* The user selected the clb at location (i,j). */
io_step = draw_coords->tile_width / type->capacity;
if ((i < 1) || (i > nx)) /* Vertical columns of IOs */
k = (int) ((y - draw_coords->tile_y[j]) / io_step);
else
k = (int) ((x - draw_coords->tile_x[i]) / io_step);
assert(k < type->capacity);
if (grid[i][j].blocks[k] == EMPTY) {
update_message(draw_state->default_message);
drawscreen();
return;
}
bnum = grid[i][j].blocks[k];
/* Highlight block and fan-in/fan-outs. */
draw_highlight_blocks_color(type, bnum);
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 draw_highlight_blocks_color(t_type_ptr type, int bnum) {
int k, netnum, fanblk, iclass;
unsigned ipin;
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 */
if (draw_state->block_color[bnum] == GREEN) {
/* If block already highlighted, de-highlight the fanout. */
draw_state->net_color[netnum] = BLACK;
for (ipin = 1; ipin < g_clbs_nlist.net[netnum].pins.size(); ipin++) {
fanblk = g_clbs_nlist.net[netnum].pins[ipin].block;
draw_reset_blk_color(fanblk);
}
}
else {
/* Highlight the fanout */
draw_state->net_color[netnum] = RED;
for (ipin = 1; ipin < g_clbs_nlist.net[netnum].pins.size(); ipin++) {
fanblk = g_clbs_nlist.net[netnum].pins[ipin].block;
draw_state->block_color[fanblk] = RED;
}
}
}
else { /* This net is fanin to the block. */
if (draw_state->block_color[bnum] == GREEN) {
/* If block already highlighted, de-highlight the fanin. */
draw_state->net_color[netnum] = BLACK;
fanblk = g_clbs_nlist.net[netnum].pins[0].block; /* DRIVER to net */
draw_reset_blk_color(fanblk);
}
else {
/* Highlight the fanin */
draw_state->net_color[netnum] = BLUE;
fanblk = g_clbs_nlist.net[netnum].pins[0].block; /* DRIVER to net */
draw_state->block_color[fanblk] = BLUE;
}
}
}
if (draw_state->block_color[bnum] == GREEN) {
/* If block already highlighted, de-highlight the selected block. */
draw_reset_blk_color(bnum);
}
else {
/* Highlight the selected block. */
draw_state->block_color[bnum] = GREEN;
}
}
static void deselect_all(void) {
/* Sets the color of all clbs, nets and rr_nodes to the default. */
int i;
/* Create some colour highlighting */
for (i = 0; i < num_blocks; i++) {
draw_reset_blk_color(i);
}
for (i = 0; i < (int) g_clbs_nlist.net.size(); i++)
draw_state->net_color[i] = BLACK;
for (i = 0; i < num_rr_nodes; i++) {
draw_state->draw_rr_node[i].color = DEFAULT_RR_NODE_COLOR;
draw_state->draw_rr_node[i].node_highlighted = false;
}
}
static void draw_reset_blk_color(int i) {
if (block[i].type->index < 3) {
draw_state->block_color[i] = LIGHTGREY;
} else if (block[i].type->index < 3 + MAX_BLOCK_COLOURS) {
draw_state->block_color[i] = (enum color_types) (BISQUE + MAX_BLOCK_COLOURS
+ block[i].type->index - 3);
} else {
draw_state->block_color[i] = (enum color_types) (BISQUE + 2 * MAX_BLOCK_COLOURS
- 1);
}
}
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;
float x1, x2, y1, y2;
int start, end, i;
t_draw_bbox chan_bbox;
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;
type = grid[grid_x][grid_y].type;
/* large block begins at primary tile (offset == 0) */
int width_offset = grid[grid_x][grid_y].width_offset;
int height_offset = grid[grid_x][grid_y].height_offset;
grid_x = grid_x - width_offset;
grid_y = grid_y - height_offset;
int width = grid[grid_x][grid_y].type->width;
int 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;
width_offset = width - 1;
height_offset = height - 1;
draw_pin_off = draw_coords->pin_size;
} else {
assert((grid_y - 1) == chan_ylow);
iside = BOTTOM;
width_offset = 0;
height_offset = 0;
draw_pin_off = -draw_coords->pin_size;
}
assert(grid[grid_x][grid_y].type->pinloc[width_offset][height_offset][iside][pin_num]);
draw_get_rr_pin_coords(pin_node, iside, width_offset, height_offset, &x1, &y1);
chan_bbox = draw_get_rr_chan_bbox(chan_node);
y1 += draw_pin_off;
y2 = chan_bbox.ybottom;
x2 = x1;
if (is_opin(pin_num, type)) {
if (direction == INC_DIRECTION) {
x2 = chan_bbox.xleft;
} else if (direction == DEC_DIRECTION) {
x2 = chan_bbox.xright;
}
}
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 = draw_coords->pin_size;
} else {
assert((grid_x - 1) == chan_xlow);
iside = LEFT;
draw_pin_off = -draw_coords->pin_size;
}
for (i = start; i <= end; i++) {
height_offset = i - grid_y;
assert(height_offset >= 0 && height_offset < 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[width_offset][height_offset][iside][pin_num]) {
break;
}
}
assert(grid[grid_x][grid_y].type->pinloc[width_offset][height_offset][iside][pin_num]);
draw_get_rr_pin_coords(pin_node, iside, width_offset, height_offset, &x1, &y1);
chan_bbox = draw_get_rr_chan_bbox(chan_node);
x1 += draw_pin_off;
x2 = chan_bbox.xleft;
y2 = y1;
if (is_opin(pin_num, type)) {
if (direction == INC_DIRECTION) {
y2 = chan_bbox.ybottom;
} else if (direction == DEC_DIRECTION) {
y2 = chan_bbox.ytop;
}
}
break;
default:
vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__,
"in draw_pin_to_chan_edge: Invalid channel node %d.\n", chan_node);
x1 = x2 = y1 = y2 = OPEN; //Prevents compiler error of variable uninitialized.
}
drawline(x1, y1, x2, y2);
if (direction == BI_DIRECTION || !is_opin(pin_num, type)) {
draw_x(x2, y2, 0.7 * draw_coords->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;
int ipin_grid_x, ipin_grid_y, ipin_pin_num;
int width_offset, height_offset;
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;
width_offset = 0;
height_offset = 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_x = opin_grid_x - grid[opin_grid_x][opin_grid_y].width_offset;
opin_grid_y = opin_grid_y - grid[opin_grid_x][opin_grid_y].height_offset;
opin_pin_num = rr_node[opin_node].ptc_num;
type = grid[opin_grid_x][opin_grid_y].type;
found = FALSE;
for (int width = 0; width < type->width && !found; ++width) {
for (int height = 0; height < type->height && !found; ++height) {
for (iside = (enum e_side)0; iside < 4 && !found; iside = (enum e_side)(iside + 1)) {
/* Find first location of pin */
if (1 == type->pinloc[width][height][iside][opin_pin_num]) {
width_offset = width;
height_offset = height;
pin_side = iside;
found = TRUE;
}
}
}
}
assert(found);
draw_get_rr_pin_coords(opin_node, pin_side, width_offset, height_offset, &x1, &y1);
/* get ipin coordinate */
ipin_grid_x = rr_node[ipin_node].xlow;
ipin_grid_y = rr_node[ipin_node].ylow;
ipin_grid_x = ipin_grid_x - grid[ipin_grid_x][ipin_grid_y].width_offset;
ipin_grid_y = ipin_grid_y - grid[ipin_grid_x][ipin_grid_y].height_offset;
ipin_pin_num = rr_node[ipin_node].ptc_num;
type = grid[ipin_grid_x][ipin_grid_y].type;
found = FALSE;
for (int width = 0; width < type->width && !found; ++width) {
for (int height = 0; height < type->height && !found; ++height) {
for (iside = (enum e_side)0; iside < 4 && !found; iside = (enum e_side)(iside + 1)) {
/* Find first location of pin */
if (1 == type->pinloc[width][height][iside][ipin_pin_num]) {
width_offset = width;
height_offset = height;
pin_side = iside;
found = TRUE;
}
}
}
}
assert(found);
draw_get_rr_pin_coords(ipin_node, pin_side, width_offset, height_offset, &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);
}