blob: 40eb8aa0ad914b44fcfc5ba1de38493b536ad754 [file] [log] [blame]
/* 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