| /* 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 |
| */ |
| |
| #ifndef NO_GRAPHICS |
| |
| # include <cstdio> |
| # include <algorithm> |
| # include <string.h> |
| |
| # include "vtr_assert.h" |
| # include "vtr_memory.h" |
| |
| # include "intra_logic_block.h" |
| # include "globals.h" |
| # include "atom_netlist.h" |
| # include "vpr_utils.h" |
| # include "draw_global.h" |
| # include "draw.h" |
| # include "draw_color.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 int draw_internal_find_max_lvl(const t_pb_type& pb_type); |
| 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 bool is_top_lvl_block_highlighted(const ClusterBlockId blk_id, const t_logical_block_type_ptr type); |
| std::vector<AtomBlockId> collect_pb_atoms(const t_pb* pb); |
| void collect_pb_atoms_recurr(const t_pb* pb, std::vector<AtomBlockId>& atoms); |
| t_pb* highlight_sub_block_helper(const ClusterBlockId clb_index, t_pb* pb, const ezgl::point2d& local_pt, int max_depth); |
| |
| # ifndef NO_GRAPHICS |
| static void draw_internal_pb(const ClusterBlockId clb_index, t_pb* pb, const ezgl::rectangle& parent_bbox, const t_logical_block_type_ptr type, ezgl::renderer* g); |
| void draw_atoms_fanin_fanout_flylines(const std::vector<AtomBlockId>& atoms, ezgl::renderer* g); |
| void draw_selected_pb_flylines(ezgl::renderer* g); |
| void draw_one_logical_connection(const AtomPinId src_pin, const AtomPinId sink_pin, ezgl::renderer* g); |
| # endif /* NO_GRAPHICS */ |
| |
| /************************* Subroutine definitions begin *********************************/ |
| |
| void draw_internal_alloc_blk() { |
| t_draw_coords* draw_coords; |
| 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.logical_block_types.size()); |
| |
| for (const auto& type : device_ctx.logical_block_types) { |
| if (physical_tile_type(&type) == device_ctx.EMPTY_TYPE) { |
| continue; |
| } |
| |
| pb_graph_head = type.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(type.index).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 (const auto& type : device_ctx.physical_tile_types) { |
| /* Empty block has no sub_blocks */ |
| if (&type == device_ctx.EMPTY_TYPE) |
| continue; |
| |
| pb_graph_head_node = logical_block_type(&type)->pb_graph_head; |
| int type_descriptor_index = type.index; |
| |
| int num_sub_tiles = type.capacity; |
| |
| // set the clb dimensions |
| ezgl::rectangle& clb_bbox = draw_coords->blk_info.at(type_descriptor_index).subblk_array.at(0); |
| ezgl::point2d bot_left = clb_bbox.bottom_left(); |
| ezgl::point2d top_right = clb_bbox.top_right(); |
| |
| // note, that all clbs of the same type are the same size, |
| // and that consequently we have *one* model for each type. |
| bot_left = {0, 0}; |
| if (size_t(type.width) > device_ctx.grid.width() || size_t(type.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 |
| top_right = ezgl::point2d( |
| (draw_coords->tile_x[1] - draw_coords->tile_x[0]) * (type.width - 1), |
| (draw_coords->tile_y[1] - draw_coords->tile_y[0]) * (type.height - 1)); |
| } else { |
| top_right = ezgl::point2d( |
| draw_coords->tile_x[type.width - 1], |
| draw_coords->tile_y[type.height - 1]); |
| } |
| top_right += ezgl::point2d( |
| draw_coords->get_tile_width() / num_sub_tiles, |
| draw_coords->get_tile_width()); |
| |
| clb_bbox = ezgl::rectangle(bot_left, top_right); |
| draw_internal_load_coords(type_descriptor_index, pb_graph_head_node, |
| clb_bbox.width(), clb_bbox.height()); |
| |
| /* Determine the max number of sub_block levels in the FPGA */ |
| draw_state->max_sub_blk_lvl = std::max(draw_internal_find_max_lvl(*logical_block_type(&type)->pb_type), |
| draw_state->max_sub_blk_lvl); |
| } |
| } |
| |
| # ifndef NO_GRAPHICS |
| void draw_internal_draw_subblk(ezgl::renderer* g) { |
| t_draw_state* draw_state = get_draw_state_vars(); |
| if (!draw_state->show_blk_internal) { |
| return; |
| } |
| 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), ezgl::rectangle({0, 0}, 0, 0), cluster_ctx.clb_nlist.block_type(bnum), g); |
| } |
| } |
| } |
| //Draw the atom-level net flylines for the selected pb |
| //(inputs: blue, outputs: red, internal: orange) |
| draw_selected_pb_flylines(g); |
| } |
| # endif /* NO_GRAPHICS */ |
| |
| /* 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(const 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 = std::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 |
| ezgl::rectangle& 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; |
| double left, bot, right, top; |
| |
| int capacity = device_ctx.physical_tile_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. */ |
| left = child_width * type_index + sub_tile_x + FRACTION_CHILD_MARGIN_X * child_width; |
| bot = 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 */ |
| right = left + child_width; |
| top = bot + child_height; |
| |
| pb_bbox = ezgl::rectangle({right, top}, {left, bot}); |
| |
| *blk_width = child_width; |
| *blk_height = child_height; |
| |
| return; |
| } |
| |
| # ifndef NO_GRAPHICS |
| /* 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 ezgl::rectangle& parent_bbox, const t_logical_block_type_ptr type, ezgl::renderer* g) { |
| 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; |
| ezgl::rectangle temp = draw_coords->get_pb_bbox(clb_index, *pb->pb_graph_node); |
| ezgl::rectangle abs_bbox = temp + 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) |
| |
| g->set_color(ezgl::WHITE); |
| g->fill_rectangle(abs_bbox); |
| g->set_color(ezgl::BLACK); |
| g->set_line_dash(ezgl::line_dash::none); |
| g->draw_rectangle(abs_bbox); |
| } |
| } else { |
| if (pb->name != nullptr) { |
| // If block is used, draw it in colour with solid border. |
| g->set_line_dash(ezgl::line_dash::none); |
| |
| // 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)) { |
| g->set_color(SELECTED_COLOR); |
| } else if (sel_sub_info.is_sink_of_selected(pb->pb_graph_node, clb_index)) { |
| g->set_color(DRIVES_IT_COLOR); |
| } else if (sel_sub_info.is_source_of_selected(pb->pb_graph_node, clb_index)) { |
| g->set_color(DRIVEN_BY_IT_COLOR); |
| } else if (pb_type->depth != draw_state->show_blk_internal && pb->child_pbs != nullptr) { |
| g->set_color(ezgl::WHITE); // draw anything else that will have a child as white |
| } else if (type_index < 3) { |
| g->set_color(blk_LIGHTGREY); |
| } else if (type_index < 3 + MAX_BLOCK_COLOURS) { |
| g->set_color((block_colors[MAX_BLOCK_COLOURS + type_index - 3])); |
| } else { |
| g->set_color((block_colors[2 * MAX_BLOCK_COLOURS - 1])); |
| } |
| } else { |
| // If block is not used, draw as empty block (ie. white |
| // background with dashed border). |
| |
| g->set_line_dash(ezgl::line_dash::asymmetric_5_3); |
| g->set_color(ezgl::WHITE); |
| } |
| g->fill_rectangle(abs_bbox); |
| g->set_color(ezgl::BLACK); |
| g->draw_rectangle(abs_bbox); |
| } |
| |
| /// then draw text /// |
| |
| if (pb->name != nullptr) { |
| g->set_font_size(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); |
| |
| g->draw_text( |
| abs_bbox.center(), |
| blk_tag, |
| abs_bbox.width(), |
| abs_bbox.height()); |
| |
| 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 |
| g->draw_text( |
| ezgl::point2d(abs_bbox.center_x(), |
| abs_bbox.top() - (abs_bbox.height()) / 15.0), |
| pb_type->name, |
| abs_bbox.width(), |
| abs_bbox.height()); |
| } |
| } else { |
| // If child block is not used, label it only by its type |
| g->draw_text( |
| abs_bbox.center(), |
| pb_type->name, |
| abs_bbox.width(), |
| abs_bbox.height()); |
| } |
| |
| /// 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, g); |
| } |
| } |
| } |
| |
| void draw_selected_pb_flylines(ezgl::renderer* g) { |
| t_selected_sub_block_info& sel_sub_info = get_selected_sub_block_info(); |
| |
| const t_pb* pb = sel_sub_info.get_selected_pb(); |
| |
| if (pb) { |
| auto atoms = collect_pb_atoms(pb); |
| draw_atoms_fanin_fanout_flylines(atoms, g); |
| } |
| } |
| |
| void draw_atoms_fanin_fanout_flylines(const std::vector<AtomBlockId>& atoms, ezgl::renderer* g) { |
| std::set<AtomBlockId> atoms_set(atoms.begin(), atoms.end()); |
| |
| auto& atom_nl = g_vpr_ctx.atom().nlist; |
| |
| g->set_line_dash(ezgl::line_dash::none); |
| g->set_line_width(2); |
| |
| for (AtomBlockId blk : atoms) { |
| for (AtomPinId ipin : atom_nl.block_input_pins(blk)) { |
| AtomNetId net = atom_nl.pin_net(ipin); |
| |
| AtomPinId net_driver = atom_nl.net_driver(net); |
| AtomBlockId net_driver_blk = atom_nl.pin_block(net_driver); |
| |
| if (atoms_set.count(net_driver_blk)) { |
| g->set_color(ezgl::ORANGE); //Internal |
| } else { |
| g->set_color(ezgl::BLUE); //External input |
| } |
| |
| ezgl::point2d start = atom_pin_draw_coord(net_driver); |
| ezgl::point2d end = atom_pin_draw_coord(ipin); |
| g->draw_line(start, end); |
| draw_triangle_along_line(g, start, end, 0.95, 40 * DEFAULT_ARROW_SIZE); |
| draw_triangle_along_line(g, start, end, 0.05, 40 * DEFAULT_ARROW_SIZE); |
| } |
| |
| for (AtomPinId opin : atom_nl.block_output_pins(blk)) { |
| AtomNetId net = atom_nl.pin_net(opin); |
| |
| for (AtomPinId net_sink : atom_nl.net_sinks(net)) { |
| AtomBlockId net_sink_blk = atom_nl.pin_block(net_sink); |
| |
| if (atoms_set.count(net_sink_blk)) { |
| g->set_color(ezgl::ORANGE); //Internal |
| } else { |
| g->set_color(ezgl::RED); //External output |
| } |
| |
| ezgl::point2d start = atom_pin_draw_coord(opin); |
| ezgl::point2d end = atom_pin_draw_coord(net_sink); |
| g->draw_line(start, end); |
| draw_triangle_along_line(g, start, end, 0.95, 40 * DEFAULT_ARROW_SIZE); |
| draw_triangle_along_line(g, start, end, 0.05, 40 * DEFAULT_ARROW_SIZE); |
| } |
| } |
| } |
| } |
| # endif /* NO_GRAPHICS */ |
| |
| std::vector<AtomBlockId> collect_pb_atoms(const t_pb* pb) { |
| std::vector<AtomBlockId> atoms; |
| collect_pb_atoms_recurr(pb, atoms); |
| return atoms; |
| } |
| |
| void collect_pb_atoms_recurr(const t_pb* pb, std::vector<AtomBlockId>& atoms) { |
| auto& atom_ctx = g_vpr_ctx.atom(); |
| |
| if (pb->is_primitive()) { |
| //Base case |
| AtomBlockId blk = atom_ctx.lookup.pb_atom(pb); |
| if (blk) { |
| atoms.push_back(blk); |
| } |
| } else { |
| //Recurse |
| VTR_ASSERT_DEBUG(atom_ctx.lookup.pb_atom(pb) == AtomBlockId::INVALID()); |
| |
| for (int itype = 0; itype < pb->get_num_child_types(); ++itype) { |
| for (int ichild = 0; ichild < pb->get_num_children_of_type(itype); ++ichild) { |
| collect_pb_atoms_recurr(&pb->child_pbs[itype][ichild], atoms); |
| } |
| } |
| } |
| } |
| |
| # ifndef NO_GRAPHICS |
| void draw_logical_connections(ezgl::renderer* g) { |
| 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(); |
| |
| g->set_line_dash(ezgl::line_dash::none); |
| |
| // 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)) { |
| g->set_color(DRIVES_IT_COLOR); |
| } else if (src_is_src_of_selected && sel_subblk_info.is_in_selected_subtree(sink_pb_gnode, sink_clb)) { |
| g->set_color(DRIVEN_BY_IT_COLOR); |
| } else if (draw_state->show_nets == DRAW_LOGICAL_CONNECTIONS && (draw_state->showing_sub_blocks() || src_clb != sink_clb)) { |
| g->set_color(ezgl::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, g); |
| } |
| } |
| } |
| # endif /* NO_GRAPHICS */ |
| |
| /** |
| * 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; |
| } |
| |
| # ifndef NO_GRAPHICS |
| /** |
| * 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, ezgl::renderer* g) { |
| ezgl::point2d src_point = atom_pin_draw_coord(src_pin); |
| ezgl::point2d sink_point = atom_pin_draw_coord(sink_pin); |
| |
| // draw a link connecting the pins. |
| g->draw_line(src_point, sink_point); |
| |
| 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(g, |
| 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(g, src_point, sink_point, 0.05); |
| draw_triangle_along_line(g, src_point, sink_point, 0.95); |
| } |
| } |
| # endif /* NO_GRAPHICS */ |
| |
| /* 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_logical_block_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] == blk_LIGHTGREY) |
| return false; |
| } else if (type->index < 3 + MAX_BLOCK_COLOURS) { |
| if (draw_state->block_color[blk_id] == block_colors[MAX_BLOCK_COLOURS + type->index - 3]) |
| // if (draw_state->block_color[blk_id] == to_ezgl_color((color_types)(BISQUE + MAX_BLOCK_COLOURS + type->index - 3))) |
| return false; |
| } else { |
| if (draw_state->block_color[blk_id] == block_colors[2 * MAX_BLOCK_COLOURS - 1]) |
| // if (draw_state->block_color[blk_id] == to_ezgl_color((color_types)(BISQUE + 2 * MAX_BLOCK_COLOURS - 1))) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| int highlight_sub_block(const ezgl::point2d& 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 ezgl::point2d& 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 ezgl::rectangle& 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.contains(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; |
| } |
| |
| #endif // NO_GRAPHICS |