| /* This file holds subroutines responsible for drawing inside clustered logic blocks. | |
| * The four main subroutines defined here are draw_internal_alloc_blk(), | |
| * draw_internal_init_blk(), draw_internal_draw_subblk(), and toggle_blk_internal(). | |
| * When VPR graphics initially sets up, draw_internal_alloc_blk() will be called from | |
| * draw.c to allocate space for the structures needed for internal blks drawing. | |
| * Before any drawing, draw_internal_init_blk() will pre-compute a bounding box | |
| * for each sub-block in the pb_graph of every physical block type. When the menu button | |
| * "Blk Internal" is pressed, toggle_blk_internal() will enable internal blocks drawing. | |
| * Then, with each subsequent click on the button, toggle_blk_internal() will propel one | |
| * more level of pbs to be drawn. Draw_internal_draw_subblk() will be called whenever | |
| * new blocks need to be drawn, and this function is responsible for drawing sub-blocks | |
| * from the pre-computed bounding box values. | |
| * | |
| * Author: Long Yu (Mike) Wang | |
| * Date: August 2013, May 2014 | |
| * | |
| * Author: Matthew J.P. Walker | |
| * Date: May 2014 | |
| */ | |
| #include <cstdio> | |
| #include <algorithm> | |
| #include <string.h> | |
| #include "vtr_assert.h" | |
| #include "vtr_memory.h" | |
| using namespace std; | |
| #include "intra_logic_block.h" | |
| #include "globals.h" | |
| #include "atom_netlist.h" | |
| #include "vpr_utils.h" | |
| #include "draw_global.h" | |
| #include "graphics.h" | |
| #include "draw.h" | |
| /************************* Subroutines local to this file. *******************************/ | |
| static void draw_internal_load_coords(int type_descrip_index, t_pb_graph_node *pb_graph_node, | |
| float parent_width, float parent_height); | |
| static void draw_internal_calc_coords(int type_descrip_index, t_pb_graph_node *pb_graph_node, | |
| int num_pb_types, int type_index, int num_pb, int pb_index, | |
| float parent_width, float parent_height, | |
| float *blk_width, float *blk_height); | |
| static int draw_internal_find_max_lvl(t_pb_type pb_type); | |
| static void draw_internal_pb(const ClusterBlockId clb_index, t_pb* pb, const t_bound_box& parent_bbox, const t_type_ptr type); | |
| static bool is_top_lvl_block_highlighted(const ClusterBlockId blk_id, const t_type_ptr type); | |
| void draw_one_logical_connection(const AtomPinId src_pin, const AtomPinId sink_pin); | |
| t_pb* highlight_sub_block_helper(const ClusterBlockId clb_index, t_pb* pb, const t_point& local_pt, int max_depth); | |
| /************************* Subroutine definitions begin *********************************/ | |
| void draw_internal_alloc_blk() { | |
| t_draw_coords *draw_coords; | |
| int i; | |
| t_pb_graph_node *pb_graph_head; | |
| /* Call accessor function to retrieve global variables. */ | |
| draw_coords = get_draw_coords_vars(); | |
| /* Create a vector holding coordinate information for each type of physical logic | |
| * block. | |
| */ | |
| auto& device_ctx = g_vpr_ctx.device(); | |
| draw_coords->blk_info.resize(device_ctx.num_block_types); | |
| for (i = 0; i < device_ctx.num_block_types; ++i) { | |
| /* Empty block has no sub_blocks */ | |
| if (&device_ctx.block_types[i] == device_ctx.EMPTY_TYPE) | |
| continue; | |
| pb_graph_head = device_ctx.block_types[i].pb_graph_head; | |
| /* Create an vector with size equal to the total number of pins for each type | |
| * of physical logic block, in order to uniquely identify each sub-block in | |
| * the pb_graph of that type. | |
| */ | |
| draw_coords->blk_info.at(i).subblk_array.resize(pb_graph_head->total_pb_pins); | |
| } | |
| } | |
| void draw_internal_init_blk() { | |
| /* Call accessor function to retrieve global variables. */ | |
| t_draw_coords* draw_coords = get_draw_coords_vars(); | |
| t_draw_state* draw_state = get_draw_state_vars(); | |
| t_pb_graph_node *pb_graph_head_node; | |
| auto& device_ctx = g_vpr_ctx.device(); | |
| for (int i = 0; i < device_ctx.num_block_types; ++i) { | |
| /* Empty block has no sub_blocks */ | |
| t_type_descriptor& type_desc = device_ctx.block_types[i]; | |
| if (&type_desc == device_ctx.EMPTY_TYPE) | |
| continue; | |
| pb_graph_head_node = type_desc.pb_graph_head; | |
| int type_descriptor_index = type_desc.index; | |
| int num_sub_tiles = type_desc.capacity; | |
| // set the clb dimensions | |
| t_bound_box& clb_bbox = draw_coords->blk_info.at(type_descriptor_index).subblk_array.at(0); | |
| // note, that all clbs of the same type are the same size, | |
| // and that consequently we have *one* model for each type. | |
| clb_bbox.bottom_left() = t_point(0,0); | |
| if (size_t(type_desc.width) > device_ctx.grid.width() || size_t(type_desc.height) > device_ctx.grid.height()) { | |
| // in this case, the clb certainly wont't fit, but this prevents | |
| // an out-of-bounds access, and provides some sort of (probably right) | |
| // value | |
| clb_bbox.top_right() = t_point( | |
| (draw_coords->tile_x[1]-draw_coords->tile_x[0])*(type_desc.width - 1), | |
| (draw_coords->tile_y[1]-draw_coords->tile_y[0])*(type_desc.height - 1) | |
| ); | |
| } else { | |
| clb_bbox.top_right() = t_point( | |
| draw_coords->tile_x[type_desc.width - 1], | |
| draw_coords->tile_y[type_desc.height - 1] | |
| ); | |
| } | |
| clb_bbox.top_right() += t_point( | |
| draw_coords->get_tile_width()/num_sub_tiles, | |
| draw_coords->get_tile_width() | |
| ); | |
| draw_internal_load_coords(type_descriptor_index, pb_graph_head_node, | |
| clb_bbox.get_width(), clb_bbox.get_height()); | |
| /* Determine the max number of sub_block levels in the FPGA */ | |
| draw_state->max_sub_blk_lvl = max(draw_internal_find_max_lvl(*type_desc.pb_type), | |
| draw_state->max_sub_blk_lvl); | |
| } | |
| } | |
| void draw_internal_draw_subblk() { | |
| auto& device_ctx = g_vpr_ctx.device(); | |
| auto& cluster_ctx = g_vpr_ctx.clustering(); | |
| auto& place_ctx = g_vpr_ctx.placement(); | |
| for (size_t i = 0; i < device_ctx.grid.width(); i++) { | |
| for (size_t j = 0; j < device_ctx.grid.height(); j++) { | |
| /* Only the first block of a group should control drawing */ | |
| if (device_ctx.grid[i][j].width_offset > 0 || device_ctx.grid[i][j].height_offset > 0) | |
| continue; | |
| /* Don't draw if tile is empty. This includes corners. */ | |
| if (device_ctx.grid[i][j].type == device_ctx.EMPTY_TYPE) | |
| continue; | |
| int num_sub_tiles = device_ctx.grid[i][j].type->capacity; | |
| for (int k = 0; k < num_sub_tiles; ++k) { | |
| /* Don't draw if block is empty. */ | |
| if (place_ctx.grid_blocks[i][j].blocks[k] == EMPTY_BLOCK_ID || place_ctx.grid_blocks[i][j].blocks[k] == INVALID_BLOCK_ID) | |
| continue; | |
| /* Get block ID */ | |
| ClusterBlockId bnum = place_ctx.grid_blocks[i][j].blocks[k]; | |
| /* Safety check, that physical blocks exists in the CLB */ | |
| if (cluster_ctx.clb_nlist.block_pb(bnum) == nullptr) | |
| continue; | |
| draw_internal_pb(bnum, cluster_ctx.clb_nlist.block_pb(bnum), t_bound_box(0,0,0,0), cluster_ctx.clb_nlist.block_type(bnum)); | |
| } | |
| } | |
| } | |
| } | |
| /* This function traverses through the pb_graph of a certain physical block type and | |
| * finds the maximum sub-block levels for that type. | |
| */ | |
| static int draw_internal_find_max_lvl(t_pb_type pb_type) { | |
| int i, j; | |
| t_mode mode; | |
| int max_levels = 0; | |
| /* If no modes, we have reached the end of pb_graph */ | |
| if (pb_type.num_modes == 0) | |
| return (pb_type.depth); | |
| for (i = 0; i < pb_type.num_modes; ++i) { | |
| mode = pb_type.modes[i]; | |
| for (j = 0; j < mode.num_pb_type_children; ++j) { | |
| max_levels = max(draw_internal_find_max_lvl(mode.pb_type_children[j]), max_levels); | |
| } | |
| } | |
| return max_levels; | |
| } | |
| /* Helper function for initializing bounding box values for each sub-block. This function | |
| * traverses through the pb_graph for a descriptor_type (given by type_descrip_index), and | |
| * calls helper function to compute bounding box values. | |
| */ | |
| static void draw_internal_load_coords(int type_descrip_index, t_pb_graph_node *pb_graph_node, | |
| float parent_width, float parent_height) { | |
| int i, j, k; | |
| t_pb_type *pb_type; | |
| int num_modes, num_children, num_pb; | |
| t_mode mode; | |
| float blk_width = 0.; | |
| float blk_height = 0.; | |
| /* Get information about the pb_type */ | |
| pb_type = pb_graph_node->pb_type; | |
| num_modes = pb_type->num_modes; | |
| /* If no modes, we have reached the end of pb_graph */ | |
| if (num_modes == 0) | |
| return; | |
| for (i = 0; i < num_modes; ++i) { | |
| mode = pb_type->modes[i]; | |
| num_children = mode.num_pb_type_children; | |
| for (j = 0; j < num_children; ++j) { | |
| /* Find the number of instances for each child pb_type. */ | |
| num_pb = mode.pb_type_children[j].num_pb; | |
| for (k = 0; k < num_pb; ++k) { | |
| /* Compute bound box for block. Don't call if pb_type is root-level pb. */ | |
| draw_internal_calc_coords(type_descrip_index, | |
| &pb_graph_node->child_pb_graph_nodes[i][j][k], | |
| num_children, j, num_pb, k, | |
| parent_width, parent_height, | |
| &blk_width, &blk_height); | |
| /* Traverse to next level in the pb_graph */ | |
| draw_internal_load_coords(type_descrip_index, | |
| &pb_graph_node->child_pb_graph_nodes[i][j][k], | |
| blk_width, blk_height); | |
| } | |
| } | |
| } | |
| return; | |
| } | |
| /* Helper function which computes bounding box values for a sub-block. The coordinates | |
| * are relative to the left and bottom corner of the parent block. | |
| */ | |
| static void | |
| draw_internal_calc_coords(int type_descrip_index, t_pb_graph_node *pb_graph_node, | |
| int num_pb_types, int type_index, int num_pb, int pb_index, | |
| float parent_width, float parent_height, | |
| float *blk_width, float *blk_height) | |
| { | |
| float parent_drawing_width, parent_drawing_height; | |
| float sub_tile_x, sub_tile_y; | |
| float child_width, child_height; | |
| auto& device_ctx = g_vpr_ctx.device(); | |
| auto& place_ctx = g_vpr_ctx.placement(); | |
| // get the bbox for this pb type | |
| t_bound_box& pb_bbox = get_draw_coords_vars()->blk_info.at(type_descrip_index).get_pb_bbox_ref(*pb_graph_node); | |
| const float FRACTION_PARENT_PADDING_X = 0.01; | |
| const float NORMAL_FRACTION_PARENT_HEIGHT = 0.90; | |
| float capacity_divisor = 1; | |
| const float FRACTION_PARENT_PADDING_BOTTOM = 0.01; | |
| const float FRACTION_CHILD_MARGIN_X = 0.025; | |
| const float FRACTION_CHILD_MARGIN_Y = 0.04; | |
| int capacity = device_ctx.block_types[type_descrip_index].capacity; | |
| if (capacity > 1 && device_ctx.grid.width() > 0 && device_ctx.grid.height() > 0 && place_ctx.grid_blocks[1][0].usage != 0 | |
| && type_descrip_index == device_ctx.grid[1][0].type->index) { | |
| // that should test for io blocks, and setting capacity_divisor > 1 | |
| // will squish every thing down | |
| capacity_divisor = capacity - 1; | |
| } | |
| /* Draw all child-level blocks in just most of the space inside their parent block. */ | |
| parent_drawing_width = parent_width * (1 - FRACTION_PARENT_PADDING_X*2); | |
| parent_drawing_height = parent_height * (NORMAL_FRACTION_PARENT_HEIGHT / capacity_divisor); | |
| /* The left and bottom corner (inside the parent block) of the space to draw | |
| * child blocks. | |
| */ | |
| sub_tile_x = parent_width * FRACTION_PARENT_PADDING_X; | |
| sub_tile_y = parent_height * FRACTION_PARENT_PADDING_BOTTOM; | |
| /* Divide parent_drawing_width by the number of child types. */ | |
| child_width = parent_drawing_width/num_pb_types; | |
| /* Divide parent_drawing_height by the number of instances of the pb_type. */ | |
| child_height = parent_drawing_height/num_pb; | |
| /* The starting point to draw the physical block. */ | |
| pb_bbox.left() = child_width * type_index + sub_tile_x + FRACTION_CHILD_MARGIN_X * child_width; | |
| pb_bbox.bottom() = child_height * pb_index + sub_tile_y + FRACTION_CHILD_MARGIN_Y * child_height; | |
| /* Leave some space between different pb_types. */ | |
| child_width *= 1 - FRACTION_CHILD_MARGIN_X*2; | |
| /* Leave some space between different instances of the same type. */ | |
| child_height *= 1 - FRACTION_CHILD_MARGIN_Y*2; | |
| /* Endpoint for drawing the pb_type */ | |
| pb_bbox.right() = pb_bbox.left() + child_width; | |
| pb_bbox.top() = pb_bbox.bottom() + child_height; | |
| *blk_width = child_width; | |
| *blk_height = child_height; | |
| return; | |
| } | |
| /* Helper subroutine to draw all sub-blocks. This function traverses through the pb_graph | |
| * which a netlist block can map to, and draws each sub-block inside its parent block. With | |
| * each click on the "Blk Internal" button, a new level is shown. | |
| */ | |
| static void draw_internal_pb(const ClusterBlockId clb_index, t_pb* pb, const t_bound_box& parent_bbox, const t_type_ptr type) { | |
| t_draw_coords* draw_coords = get_draw_coords_vars(); | |
| t_draw_state* draw_state = get_draw_state_vars(); | |
| t_selected_sub_block_info& sel_sub_info = get_selected_sub_block_info(); | |
| t_pb_type* pb_type = pb->pb_graph_node->pb_type; | |
| t_bound_box abs_bbox = draw_coords->get_pb_bbox(clb_index, *pb->pb_graph_node) + parent_bbox.bottom_left(); | |
| // if we've gone too far, don't draw anything | |
| if (pb_type->depth > draw_state->show_blk_internal) { | |
| return; | |
| } | |
| /// first draw box /// | |
| if (pb_type->depth == 0) { | |
| if (!is_top_lvl_block_highlighted(clb_index, type)) { | |
| // if this is a top level pb, and only if it isn't selected (ie. a funny colour), | |
| // overwrite it. (but stil draw the text) | |
| setcolor(WHITE); | |
| fillrect(abs_bbox); | |
| setcolor(BLACK); | |
| setlinestyle(SOLID); | |
| drawrect(abs_bbox); | |
| } | |
| } else { | |
| if (pb->name != nullptr) { | |
| // If block is used, draw it in colour with solid border. | |
| setlinestyle(SOLID); | |
| // type_index indicates what type of block. | |
| const int type_index = type->index; | |
| // determine default background color | |
| if (sel_sub_info.is_selected(pb->pb_graph_node, clb_index)) { | |
| setcolor(SELECTED_COLOR); | |
| } else if (sel_sub_info.is_sink_of_selected(pb->pb_graph_node, clb_index)) { | |
| setcolor(DRIVES_IT_COLOR); | |
| } else if (sel_sub_info.is_source_of_selected(pb->pb_graph_node, clb_index)) { | |
| setcolor(DRIVEN_BY_IT_COLOR); | |
| } else if (pb_type->depth != draw_state->show_blk_internal && pb->child_pbs != nullptr) { | |
| setcolor(WHITE); // draw anthing else that will have a child as white | |
| } else if (type_index < 3) { | |
| setcolor(LIGHTGREY); | |
| } else if (type_index < 3 + MAX_BLOCK_COLOURS) { | |
| setcolor(BISQUE + MAX_BLOCK_COLOURS + type_index - 3); | |
| } else { | |
| setcolor(BISQUE + 2 * MAX_BLOCK_COLOURS - 1); | |
| } | |
| } | |
| else { | |
| // If block is not used, draw as empty block (ie. white | |
| // background with dashed border). | |
| setlinestyle(DASHED); | |
| setcolor(WHITE); | |
| } | |
| fillrect(abs_bbox); | |
| setcolor(BLACK); | |
| drawrect(abs_bbox); | |
| } | |
| /// then draw text /// | |
| if (pb->name != nullptr) { | |
| setfontsize(16); // note: calc_text_xbound(...) assumes this is 16 | |
| if (pb_type->depth == draw_state->show_blk_internal || pb->child_pbs == nullptr) { | |
| // If this pb is at the lowest displayed level, or has no more children, then | |
| // label it in the center with its type and name | |
| int type_len = strlen(pb_type->name); | |
| int name_len = strlen(pb->name); | |
| int tot_len = type_len + name_len; | |
| char* blk_tag = (char *)vtr::malloc((tot_len + 8) * sizeof(char)); | |
| sprintf (blk_tag, "%s(%s)", pb_type->name, pb->name); | |
| drawtext( | |
| t_point(abs_bbox.get_xcenter(), abs_bbox.get_ycenter()), | |
| blk_tag, | |
| abs_bbox | |
| ); | |
| free(blk_tag); | |
| } else { | |
| // else (ie. has chilren, and isn't at the lowest displayed level) | |
| // just label its type, and put it up at the top so we can see it | |
| drawtext( | |
| t_point( | |
| abs_bbox.get_xcenter(), | |
| abs_bbox.top() - (abs_bbox.get_height()) / 15.0 | |
| ), | |
| pb_type->name, | |
| abs_bbox | |
| ); | |
| } | |
| } else { | |
| // If child block is not used, label it only by its type | |
| drawtext( | |
| t_point(abs_bbox.get_xcenter(), abs_bbox.get_ycenter()), | |
| pb_type->name, | |
| abs_bbox | |
| ); | |
| } | |
| /// now recurse on the child pbs. /// | |
| // return if no children, or this is an unusused pb, | |
| // or if going down will be too far down (this one is redundant, but for optimazition) | |
| if(pb->child_pbs == nullptr || pb->name == nullptr | |
| || pb_type->depth == draw_state->show_blk_internal) { | |
| return; | |
| } | |
| int num_child_types = pb->get_num_child_types(); | |
| for (int i = 0; i < num_child_types; ++i) { | |
| if (pb->child_pbs[i] == nullptr) { | |
| continue; | |
| } | |
| int num_pb = pb->get_num_children_of_type(i); | |
| for (int j = 0; j < num_pb; ++j) { | |
| t_pb* child_pb = &pb->child_pbs[i][j]; | |
| VTR_ASSERT(child_pb != nullptr); | |
| t_pb_type* pb_child_type = child_pb->pb_graph_node->pb_type; | |
| // don't go farther if 0 modes | |
| if (pb_child_type == nullptr || pb_child_type->num_modes == 0) { | |
| continue; | |
| } | |
| // now recurse | |
| draw_internal_pb(clb_index, child_pb, abs_bbox, type); | |
| } | |
| } | |
| } | |
| void draw_logical_connections() { | |
| const t_selected_sub_block_info& sel_subblk_info = get_selected_sub_block_info(); | |
| t_draw_state* draw_state = get_draw_state_vars(); | |
| auto& atom_ctx = g_vpr_ctx.atom(); | |
| // iterate over all the atom nets | |
| for(auto net_id : atom_ctx.nlist.nets()) { | |
| AtomPinId driver_pin_id = atom_ctx.nlist.net_driver(net_id); | |
| AtomBlockId src_blk_id = atom_ctx.nlist.pin_block(driver_pin_id); | |
| const t_pb_graph_node* src_pb_gnode = atom_ctx.lookup.atom_pb_graph_node(src_blk_id); | |
| ClusterBlockId src_clb = atom_ctx.lookup.atom_clb(src_blk_id); | |
| bool src_is_selected = sel_subblk_info.is_in_selected_subtree(src_pb_gnode, src_clb); | |
| bool src_is_src_of_selected = sel_subblk_info.is_source_of_selected(src_pb_gnode, src_clb); | |
| // iterate over the sinks | |
| for(auto sink_pin_id : atom_ctx.nlist.net_sinks(net_id)) { | |
| AtomBlockId sink_blk_id = atom_ctx.nlist.pin_block(sink_pin_id); | |
| const t_pb_graph_node* sink_pb_gnode = atom_ctx.lookup.atom_pb_graph_node(sink_blk_id); | |
| ClusterBlockId sink_clb = atom_ctx.lookup.atom_clb(sink_blk_id); | |
| if (src_is_selected && sel_subblk_info.is_sink_of_selected(sink_pb_gnode, sink_clb)) { | |
| setcolor(DRIVES_IT_COLOR); | |
| } else if (src_is_src_of_selected && sel_subblk_info.is_in_selected_subtree(sink_pb_gnode, sink_clb)) { | |
| setcolor(DRIVEN_BY_IT_COLOR); | |
| } else if (draw_state->show_nets == DRAW_LOGICAL_CONNECTIONS && | |
| (draw_state->showing_sub_blocks() || src_clb != sink_clb)) { | |
| setcolor(BLACK); // if showing all, draw the other ones in black | |
| } else { | |
| continue; // not showing all, and not the sperified block, so skip | |
| } | |
| draw_one_logical_connection(driver_pin_id, sink_pin_id); | |
| } | |
| } | |
| } | |
| /** | |
| * Helper function for draw_one_logical_connection(...). | |
| * We return the index of the pin in the context of the *model* | |
| * This is used to determine where to draw connection start/end points | |
| * | |
| * The pin index in the model context is returned via pin_index. | |
| * | |
| * The total number of model pins (either inputs or outputs) is returned via total_pins | |
| * | |
| * The search inputs parameter tells this function to search | |
| * inputs (if true) or outputs (if false). | |
| */ | |
| void find_pin_index_at_model_scope( | |
| const AtomPinId pin_id, const AtomBlockId blk_id, | |
| int* pin_index, int* total_pins) { | |
| auto& atom_ctx = g_vpr_ctx.atom(); | |
| AtomPortId port_id = atom_ctx.nlist.pin_port(pin_id); | |
| const t_model_ports* model_port = atom_ctx.nlist.port_model(port_id); | |
| //Total up the port widths | |
| // Note that we do this on the model since the atom netlist doesn't include unused ports | |
| int pin_cnt = 0; | |
| *pin_index = -1; //initialize | |
| const t_model* model = atom_ctx.nlist.block_model(blk_id); | |
| for (const t_model_ports* port : {model->inputs, model->outputs}) { | |
| while(port) { | |
| if(port == model_port) { | |
| //This is the port the pin is associated with, record it's index | |
| //Get the pin index in the port | |
| int atom_port_index = atom_ctx.nlist.pin_port_bit(pin_id); | |
| //The index of this pin in the model is the pins counted so-far | |
| //(i.e. accross previous ports) plus the index in the port | |
| *pin_index = pin_cnt + atom_port_index; | |
| } | |
| //Running total of model pins seen so-far | |
| pin_cnt += port->size; | |
| port = port->next; | |
| } | |
| } | |
| VTR_ASSERT(*pin_index != -1); | |
| *total_pins = pin_cnt; | |
| } | |
| /** | |
| * Draws ONE logical connection from src_pin in src_lblk to sink_pin in sink_lblk. | |
| * The *_abs_bbox parameters are for mild optmization, as the absolute bbox can be calculated | |
| * more effeciently elsewhere. | |
| */ | |
| void draw_one_logical_connection(const AtomPinId src_pin, const AtomPinId sink_pin) { | |
| t_point src_point = atom_pin_draw_coord(src_pin); | |
| t_point sink_point = atom_pin_draw_coord(sink_pin); | |
| // draw a link connecting the pins. | |
| drawline(src_point.x, src_point.y, | |
| sink_point.x, sink_point.y); | |
| auto& atom_ctx = g_vpr_ctx.atom(); | |
| if (atom_ctx.lookup.atom_clb(atom_ctx.nlist.pin_block(src_pin)) == atom_ctx.lookup.atom_clb(atom_ctx.nlist.pin_block(sink_pin))) { | |
| // if they are in the same clb, put one arrow in the center | |
| float center_x = (src_point.x + sink_point.x) / 2; | |
| float center_y = (src_point.y + sink_point.y) / 2; | |
| draw_triangle_along_line( | |
| center_x, center_y, | |
| src_point.x, sink_point.x, | |
| src_point.y, sink_point.y | |
| ); | |
| } else { | |
| // if they are not, put 2 near each end | |
| draw_triangle_along_line(src_point, sink_point, 0.05); | |
| draw_triangle_along_line(src_point, sink_point, 0.95); | |
| } | |
| } | |
| /* This function checks whether a top-level clb has been highlighted. It does | |
| * so by checking whether the color in this block is default color. | |
| */ | |
| static bool is_top_lvl_block_highlighted(const ClusterBlockId blk_id, const t_type_ptr type) { | |
| t_draw_state *draw_state; | |
| /* Call accessor function to retrieve global variables. */ | |
| draw_state = get_draw_state_vars(); | |
| if (type->index < 3) { | |
| if (draw_state->block_color[blk_id] == LIGHTGREY) | |
| return false; | |
| } else if (type->index < 3 + MAX_BLOCK_COLOURS) { | |
| if (draw_state->block_color[blk_id] == (color_types) (BISQUE + MAX_BLOCK_COLOURS | |
| + type->index - 3)) | |
| return false; | |
| } else { | |
| if (draw_state->block_color[blk_id] == (color_types) (BISQUE + 2 * MAX_BLOCK_COLOURS - 1)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| int highlight_sub_block(const t_point& point_in_clb, ClusterBlockId clb_index, t_pb *pb) { | |
| t_draw_state* draw_state = get_draw_state_vars(); | |
| int max_depth = draw_state->show_blk_internal; | |
| t_pb* new_selected_sub_block = | |
| highlight_sub_block_helper(clb_index, pb, point_in_clb, max_depth); | |
| if (new_selected_sub_block == nullptr) { | |
| get_selected_sub_block_info().clear(); | |
| return 1; | |
| } else { | |
| get_selected_sub_block_info().set(new_selected_sub_block, clb_index); | |
| return 0; | |
| } | |
| } | |
| /** | |
| * The recursive part of highlight_sub_block. finds which pb is under | |
| * the given location. local_pt is relative to the given pb, and pb should | |
| * be in clb. | |
| */ | |
| t_pb* highlight_sub_block_helper( | |
| const ClusterBlockId clb_index, t_pb* pb, const t_point& local_pt, int max_depth) { | |
| t_draw_coords *draw_coords = get_draw_coords_vars(); | |
| t_pb_type* pb_type = pb->pb_graph_node->pb_type; | |
| // check to see if we are past the displayed level, | |
| // if pb has children, | |
| // and if pb is dud | |
| if (pb_type->depth + 1 > max_depth | |
| || pb->child_pbs == nullptr | |
| || pb_type->num_modes == 0) { | |
| return nullptr; | |
| } | |
| int num_child_types = pb->get_num_child_types(); | |
| // Iterate through each type of child pb, and each pb of that type. | |
| for (int i = 0; i < num_child_types; ++i) { | |
| int num_children_of_type = pb->get_num_children_of_type(i); | |
| for (int j = 0; j < num_children_of_type; ++j) { | |
| if (pb->child_pbs[i] == nullptr) { | |
| continue; | |
| } | |
| t_pb* child_pb = &pb->child_pbs[i][j]; | |
| t_pb_graph_node* pb_child_node = child_pb->pb_graph_node; | |
| // get the bbox for this child | |
| const t_bound_box& bbox = draw_coords->get_pb_bbox(clb_index, *pb_child_node); | |
| // If child block is being used, check if it intersects | |
| if (child_pb->name != nullptr && bbox.intersects(local_pt)) { | |
| // check farther down the graph, see if we can find | |
| // something more specific. | |
| t_pb* subtree_result = | |
| highlight_sub_block_helper( | |
| clb_index, child_pb, local_pt - bbox.bottom_left(), max_depth); | |
| if (subtree_result != nullptr) { | |
| // we found something more specific. | |
| return subtree_result; | |
| } else { | |
| // couldn't find something more specific, return parent | |
| return child_pb; | |
| } | |
| } | |
| } | |
| } | |
| return nullptr; | |
| } | |
| t_selected_sub_block_info& get_selected_sub_block_info() { | |
| // used to keep track of the selected sub-block. | |
| static t_selected_sub_block_info selected_sub_block_info; | |
| return selected_sub_block_info; | |
| } | |
| /* | |
| * Begin definition of t_selected_sub_block_info functions. | |
| */ | |
| t_selected_sub_block_info::t_selected_sub_block_info() { | |
| clear(); | |
| } | |
| template<typename HashType> | |
| void add_all_children(const t_pb* pb, const ClusterBlockId clb_index, | |
| std::unordered_set< t_selected_sub_block_info::gnode_clb_pair, HashType>& set) { | |
| if (pb == nullptr) { | |
| return; | |
| } | |
| set.insert(t_selected_sub_block_info::gnode_clb_pair(pb->pb_graph_node, clb_index)); | |
| int num_child_types = pb->get_num_child_types(); | |
| for (int i = 0; i < num_child_types; ++i) { | |
| int num_children_of_type = pb->get_num_children_of_type(i); | |
| for (int j = 0; j < num_children_of_type; ++j) { | |
| add_all_children(&pb->child_pbs[i][j], clb_index, set); | |
| } | |
| } | |
| } | |
| void t_selected_sub_block_info::set(t_pb* new_selected_sub_block, const ClusterBlockId new_containing_block_index) { | |
| selected_pb = new_selected_sub_block; | |
| selected_pb_gnode = (selected_pb == nullptr) ? nullptr : selected_pb->pb_graph_node; | |
| containing_block_index = new_containing_block_index; | |
| sinks.clear(); | |
| sources.clear(); | |
| in_selected_subtree.clear(); | |
| auto& atom_ctx = g_vpr_ctx.atom(); | |
| if (has_selection()) { | |
| add_all_children(selected_pb, containing_block_index, in_selected_subtree); | |
| for (auto blk_id : atom_ctx.nlist.blocks()) { | |
| const ClusterBlockId clb = atom_ctx.lookup.atom_clb(blk_id); | |
| const t_pb_graph_node* pb_graph_node = atom_ctx.lookup.atom_pb_graph_node(blk_id); | |
| // find the atom block that corrisponds to this pb. | |
| if ( is_in_selected_subtree(pb_graph_node, clb) ) { | |
| //Collect the sources of all nets driving this node | |
| for(auto pin_id : atom_ctx.nlist.block_input_pins(blk_id)) { | |
| AtomNetId net_id = atom_ctx.nlist.pin_net(pin_id); | |
| AtomPinId driver_pin_id = atom_ctx.nlist.net_driver(net_id); | |
| AtomBlockId src_blk = atom_ctx.nlist.pin_block(driver_pin_id); | |
| const ClusterBlockId src_clb = atom_ctx.lookup.atom_clb(src_blk); | |
| const t_pb_graph_node* src_pb_graph_node = atom_ctx.lookup.atom_pb_graph_node(src_blk); | |
| sources.insert(gnode_clb_pair(src_pb_graph_node, src_clb)); | |
| } | |
| //Collect the sinks of all nets driven by this node | |
| for(auto pin_id : atom_ctx.nlist.block_output_pins(blk_id)) { | |
| AtomNetId net_id = atom_ctx.nlist.pin_net(pin_id); | |
| for(auto sink_pin_id : atom_ctx.nlist.net_sinks(net_id)) { | |
| AtomBlockId sink_blk = atom_ctx.nlist.pin_block(sink_pin_id); | |
| const ClusterBlockId sink_clb = atom_ctx.lookup.atom_clb(sink_blk); | |
| const t_pb_graph_node* sink_pb_graph_node = atom_ctx.lookup.atom_pb_graph_node(sink_blk); | |
| sinks.insert(gnode_clb_pair(sink_pb_graph_node, sink_clb)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void t_selected_sub_block_info::clear() { | |
| set(nullptr, ClusterBlockId::INVALID()); | |
| } | |
| t_pb* t_selected_sub_block_info::get_selected_pb() const { return selected_pb; } | |
| t_pb_graph_node* t_selected_sub_block_info::get_selected_pb_gnode() const { return selected_pb_gnode; } | |
| ClusterBlockId t_selected_sub_block_info::get_containing_block() const { return containing_block_index; } | |
| bool t_selected_sub_block_info::has_selection() const { | |
| return get_selected_pb_gnode() != nullptr && get_containing_block() != ClusterBlockId::INVALID(); | |
| } | |
| bool t_selected_sub_block_info::is_selected(const t_pb_graph_node* test, const ClusterBlockId clb_index) const { | |
| return get_selected_pb_gnode() == test && get_containing_block() == clb_index; | |
| } | |
| bool t_selected_sub_block_info::is_sink_of_selected(const t_pb_graph_node* test, const ClusterBlockId clb_index) const { | |
| return sinks.find(gnode_clb_pair(test,clb_index)) != sinks.end(); | |
| } | |
| bool t_selected_sub_block_info::is_source_of_selected(const t_pb_graph_node* test, const ClusterBlockId clb_index) const { | |
| return sources.find(gnode_clb_pair(test,clb_index)) != sources.end(); | |
| } | |
| bool t_selected_sub_block_info::is_in_selected_subtree(const t_pb_graph_node* test, const ClusterBlockId clb_index) const { | |
| return in_selected_subtree.find(gnode_clb_pair(test,clb_index)) != in_selected_subtree.end(); | |
| } | |
| /* | |
| * Begin definition of t_selected_sub_block_info::clb_pin_tuple functions. | |
| */ | |
| t_selected_sub_block_info::clb_pin_tuple::clb_pin_tuple(ClusterBlockId clb_index_, const t_pb_graph_node* pb_gnode_) : | |
| clb_index(clb_index_), | |
| pb_gnode(pb_gnode_) { | |
| } | |
| t_selected_sub_block_info::clb_pin_tuple::clb_pin_tuple(const AtomPinId atom_pin) { | |
| auto& atom_ctx = g_vpr_ctx.atom(); | |
| clb_index = atom_ctx.lookup.atom_clb(atom_ctx.nlist.pin_block(atom_pin)); | |
| pb_gnode = atom_ctx.lookup.atom_pb_graph_node(atom_ctx.nlist.pin_block(atom_pin)); | |
| } | |
| bool t_selected_sub_block_info::clb_pin_tuple::operator==(const clb_pin_tuple& rhs) const { | |
| return clb_index == rhs.clb_index | |
| && pb_gnode == rhs.pb_gnode; | |
| } | |
| /* | |
| * Begin definition of t_selected_sub_block_info::gnode_clb_pair functions | |
| */ | |
| t_selected_sub_block_info::gnode_clb_pair::gnode_clb_pair(const t_pb_graph_node* pb_gnode_, const ClusterBlockId clb_index_) : | |
| pb_gnode(pb_gnode_), | |
| clb_index(clb_index_) { | |
| } | |
| bool t_selected_sub_block_info::gnode_clb_pair::operator==(const gnode_clb_pair& rhs) const { | |
| return clb_index == rhs.clb_index | |
| && pb_gnode == rhs.pb_gnode; | |
| } |