Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions vpr/src/analytical_place/analytical_draw_manager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @file
* @author Yulang (Robert) Luo
* @date October 2025
* @brief The definitions of the Analytical Draw Manager class which is used
* to handle graphics updates during analytical placement.
*/

#include "analytical_draw_manager.h"
#include "vpr_types.h"

#ifndef NO_GRAPHICS
#include "draw.h"
#include "draw_global.h"
#include "partial_placement.h"
#endif

AnalyticalDrawManager::AnalyticalDrawManager(const PartialPlacement& p_placement) {
#ifndef NO_GRAPHICS
// Set the analytical placement reference in draw state
get_draw_state_vars()->set_ap_partial_placement_ref(p_placement);
#else
(void)p_placement;
#endif
}

AnalyticalDrawManager::~AnalyticalDrawManager() {
#ifndef NO_GRAPHICS
// Clear the analytical placement reference in draw state
get_draw_state_vars()->clear_ap_partial_placement_ref();
#endif
}

void AnalyticalDrawManager::update_graphics(const std::string& msg) {
#ifndef NO_GRAPHICS
update_screen(ScreenUpdatePriority::MAJOR, msg.c_str(), ANALYTICAL_PLACEMENT, nullptr);
#else
(void)msg;
#endif
}
41 changes: 41 additions & 0 deletions vpr/src/analytical_place/analytical_draw_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once
/**
* @file
* @author Yulang (Robert) Luo
* @date October 2025
* @brief The decalarations of the Analytical Draw Manager class which is used
* to handle graphics updates during analytical placement.
*/

#include <string>

// Forward declarations
class PartialPlacement;

/**
* @class AnalyticalDrawManager
* @brief Manages graphics updates during analytical placement operations.
*
* This class provides a clean interface for updating the screen during
* analytical placement without requiring the placement code to be littered
* with NO_GRAPHICS conditional compilation directives.
*/
class AnalyticalDrawManager {
public:
/**
* @brief Constructor initializes the draw manager with a reference to the
* current partial placement.
*/
explicit AnalyticalDrawManager(const PartialPlacement& p_placement);

/**
* @brief Destructor cleans up the reference in the draw state.
*/
~AnalyticalDrawManager();

/**
* @brief Update screen with current analytical placement state
* @param msg A message to display with the update
*/
void update_graphics(const std::string& msg);
};
16 changes: 16 additions & 0 deletions vpr/src/analytical_place/global_placer.cpp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to keep the drawing code pretty isolated from the AP code as much as possible. In the future, we may decide to make another global placer as well, and I would like to avoid duplicate code.

I suggest that you create a new class in the analytical_place directory for drawing. Something like APDrawManager or something. This class would then have methods for drawing the pre and post legalized placements. This would remove the global accesses from this method. The global placer can then take this as an argument, which it can use to draw whenever it needs.

Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
#include <limits>
#include <memory>
#include <vector>
#include "analytical_draw_manager.h"
#include "PreClusterTimingManager.h"
#include "analytical_solver.h"
#include "ap_flow_enums.h"
#include "ap_netlist.h"
#include "ap_netlist_fwd.h"
#include "atom_netlist.h"
#include "device_grid.h"
#include "draw.h"
#ifndef NO_GRAPHICS
#include "draw_global.h"
#endif
#include "flat_placement_bins.h"
#include "flat_placement_density_manager.h"
#include "globals.h"
Expand Down Expand Up @@ -352,6 +357,10 @@ PartialPlacement SimPLGlobalPlacer::place() {
PartialPlacement best_p_placement(ap_netlist_);
double best_ub_hpwl = std::numeric_limits<double>::max();

// Initialize graphics for analytical placement, setting the reference in
// the draw state.
AnalyticalDrawManager draw_manager(p_placement);

// Run the global placer.
for (size_t i = 0; i < max_num_iterations_; i++) {
float iter_start_time = runtime_timer.elapsed_sec();
Expand All @@ -361,12 +370,18 @@ PartialPlacement SimPLGlobalPlacer::place() {
solver_->solve(i, p_placement);
float solver_end_time = runtime_timer.elapsed_sec();
double lb_hpwl = p_placement.get_hpwl(ap_netlist_);

// Update graphics after analytical solver
draw_manager.update_graphics("Iteration " + std::to_string(i) + " After Solver");

// Run the legalizer.
float legalizer_start_time = runtime_timer.elapsed_sec();
partial_legalizer_->legalize(p_placement);
float legalizer_end_time = runtime_timer.elapsed_sec();
double ub_hpwl = p_placement.get_hpwl(ap_netlist_);

// Update graphics after legalizer
draw_manager.update_graphics("Iteration " + std::to_string(i) + " After Legalizer");

// Perform a timing update
float timing_update_start_time = runtime_timer.elapsed_sec();
Expand Down Expand Up @@ -450,6 +465,7 @@ PartialPlacement SimPLGlobalPlacer::place() {
*density_manager_,
pre_cluster_timing_manager_);


// Return the placement from the final iteration.
return best_p_placement;
}
1 change: 1 addition & 0 deletions vpr/src/base/vpr_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ enum class e_sched_type {
enum pic_type {
NO_PICTURE,
PLACEMENT,
ANALYTICAL_PLACEMENT,
ROUTING
};
/* What's on screen? */
Expand Down
133 changes: 88 additions & 45 deletions vpr/src/draw/draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
#include <cstring>
#include <cmath>
#include "draw.h"
#include "draw_types.h"
#include "timing_info.h"
#include "physical_types.h"

#include "move_utils.h"
#include "vpr_types.h"

#ifndef NO_GRAPHICS

Expand Down Expand Up @@ -174,63 +176,65 @@ static void draw_main_canvas(ezgl::renderer* g) {
t_draw_state* draw_state = get_draw_state_vars();

g->set_font_size(14);
if (draw_state->pic_on_screen != ANALYTICAL_PLACEMENT) {
draw_block_pin_util();
drawplace(g);
draw_internal_draw_subblk(g);

draw_block_pin_util();
drawplace(g);
draw_internal_draw_subblk(g);
if (draw_state->pic_on_screen == ROUTING) { // ROUTING on screen

if (draw_state->pic_on_screen == ROUTING) { // ROUTING on screen
draw_rr(g);

draw_rr(g);
if (draw_state->show_nets && draw_state->draw_nets == DRAW_ROUTED_NETS) {
draw_route(ALL_NETS, g);

if (draw_state->show_nets && draw_state->draw_nets == DRAW_ROUTED_NETS) {
draw_route(ALL_NETS, g);

if (draw_state->highlight_fan_in_fan_out) {
draw_route(HIGHLIGHTED, g);
if (draw_state->highlight_fan_in_fan_out) {
draw_route(HIGHLIGHTED, g);
}
}
}

draw_congestion(g);
draw_congestion(g);

draw_routing_costs(g);
draw_routing_costs(g);

draw_router_expansion_costs(g);
draw_router_expansion_costs(g);

draw_routing_util(g);
draw_routing_util(g);

draw_routing_bb(g);
}
draw_routing_bb(g);
}

draw_placement_macros(g);
draw_placement_macros(g);

#ifndef NO_SERVER
if (g_vpr_ctx.server().gate_io.is_running()) {
const ServerContext& server_ctx = g_vpr_ctx.server(); // shortcut
draw_crit_path_elements(server_ctx.crit_paths, server_ctx.crit_path_element_indexes, server_ctx.draw_crit_path_contour, g);
} else {
draw_crit_path(g);
}
if (g_vpr_ctx.server().gate_io.is_running()) {
const ServerContext& server_ctx = g_vpr_ctx.server(); // shortcut
draw_crit_path_elements(server_ctx.crit_paths, server_ctx.crit_path_element_indexes, server_ctx.draw_crit_path_contour, g);
} else {
draw_crit_path(g);
}
#else
draw_crit_path(g);
draw_crit_path(g);
#endif /* NO_SERVER */

draw_logical_connections(g);
draw_logical_connections(g);

draw_selected_pb_flylines(g);
draw_selected_pb_flylines(g);

draw_noc(g);
draw_noc(g);

if (draw_state->draw_partitions) {
highlight_all_regions(g);
draw_constrained_atoms(g);
}
if (draw_state->draw_partitions) {
highlight_all_regions(g);
draw_constrained_atoms(g);
}

if (draw_state->color_map) {
draw_color_map_legend(*draw_state->color_map, g);
draw_state->color_map.reset(); //Free color map in preparation for next redraw
if (draw_state->color_map) {
draw_color_map_legend(*draw_state->color_map, g);
draw_state->color_map.reset(); //Free color map in preparation for next redraw
}
} else {
draw_analytical_place(g);
}

if (draw_state->auto_proceed) {
//Automatically exit the event loop, so user's don't need to manually click proceed

Expand Down Expand Up @@ -281,7 +285,7 @@ void update_screen(ScreenUpdatePriority priority, const char* msg, enum pic_type
* value controls whether or not the Proceed button must be clicked to *
* continue. Saves the pic_on_screen_val to allow pan and zoom redraws. */
t_draw_state* draw_state = get_draw_state_vars();

strcpy(draw_state->default_message, msg);

if (!draw_state->show_graphics)
Expand All @@ -297,10 +301,23 @@ void update_screen(ScreenUpdatePriority priority, const char* msg, enum pic_type

state_change = true;

if (draw_state->show_graphics) {
if (pic_on_screen_val == ANALYTICAL_PLACEMENT) {
set_initial_world_ap();
} else {
set_initial_world();
}
}

if (draw_state->pic_on_screen == NO_PICTURE) {
// Only add the canvas the first time we open graphics
application.add_canvas("MainCanvas", draw_main_canvas,
initial_world);
application.add_canvas("MainCanvas", draw_main_canvas, initial_world);
} else {
// TODO: will this ever be null?
auto canvas = application.get_canvas(application.get_main_canvas_id());
if (canvas != nullptr) {
canvas->get_camera().set_world(initial_world);
}
}

draw_state->setup_timing_info = setup_timing_info;
Expand Down Expand Up @@ -482,7 +499,18 @@ void init_draw_coords(float clb_width, const BlkLocRegistry& blk_loc_registry) {
//Margin beyond edge of the drawn device to extend the visible world
//Setting this to > 0.0 means 'Zoom Fit' leave some fraction of white
//space around the device edges
#else
(void)clb_width;
(void)blk_loc_registry;
#endif /* NO_GRAPHICS */
}

#ifndef NO_GRAPHICS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be careful with this ifdef. You are not defining these functions when graphics is disabled. However, they are still available in the header file...

I would put the ifdef inside of the method and have the method do nothing when graphics is not enabled.


void set_initial_world() {
constexpr float VISIBLE_MARGIN = 0.01;
t_draw_coords* draw_coords = get_draw_coords_vars();
const DeviceContext& device_ctx = g_vpr_ctx.device();

float draw_width = draw_coords->tile_x[device_ctx.grid.width() - 1]
+ draw_coords->get_tile_width();
Expand All @@ -492,14 +520,24 @@ void init_draw_coords(float clb_width, const BlkLocRegistry& blk_loc_registry) {
initial_world = ezgl::rectangle(
{-VISIBLE_MARGIN * draw_width, -VISIBLE_MARGIN * draw_height},
{(1. + VISIBLE_MARGIN) * draw_width, (1. + VISIBLE_MARGIN)
* draw_height});
#else
(void)clb_width;
(void)blk_loc_registry;
#endif /* NO_GRAPHICS */
* draw_height});
}

#ifndef NO_GRAPHICS
void set_initial_world_ap() {
constexpr float VISIBLE_MARGIN = 0.01f;
const DeviceContext& device_ctx = g_vpr_ctx.device();

const size_t grid_w = device_ctx.grid.width();
const size_t grid_h = device_ctx.grid.height();


float draw_width = static_cast<float>(grid_w);
float draw_height = static_cast<float>(grid_h);

initial_world = ezgl::rectangle(
{-VISIBLE_MARGIN * draw_width, -VISIBLE_MARGIN * draw_height},
{(1.f + VISIBLE_MARGIN) * draw_width, (1.f + VISIBLE_MARGIN) * draw_height});
}

int get_track_num(int inode, const vtr::OffsetMatrix<int>& chanx_track, const vtr::OffsetMatrix<int>& chany_track) {
/* Returns the track number of this routing resource node. */
Expand Down Expand Up @@ -625,6 +663,11 @@ void act_on_mouse_press(ezgl::application* app, GdkEventButton* event, double x,
* fanins and fanouts are highlighted when you click on a block *
* attached to them. */

if (get_draw_state_vars()->pic_on_screen == ANALYTICAL_PLACEMENT) {
// No selection in analytical placement mode yet
return;
}

/* Control + mouse click to select multiple nets. */
if (!(event->state & GDK_CONTROL_MASK))
deselect_all();
Expand Down
14 changes: 14 additions & 0 deletions vpr/src/draw/draw.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ void update_screen(ScreenUpdatePriority priority, const char* msg, enum pic_type
*/
void init_draw_coords(float clb_width, const BlkLocRegistry& blk_loc_registry);

/**
* @brief Set the intial_world ezgl::rectangle for analytical placement
*
* This function sets graphic initial dimensions so there are no gaps between blocks
*/
void set_initial_world_ap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new methods need comments. Also see my prior comments on their definitions.


/**
* @brief Set the intial_world ezgl::rectangle for default
*
* This function sets graphic initial dimensions so there are gaps between blocks
*/
void set_initial_world();

/* Sets the static show_graphics and gr_automode variables to the *
* desired values. They control if graphics are enabled and, if so, *
* how often the user is prompted for input. */
Expand Down
Loading