diff --git a/vpr/src/analytical_place/ap_draw_manager.cpp b/vpr/src/analytical_place/ap_draw_manager.cpp new file mode 100644 index 0000000000..754ac18946 --- /dev/null +++ b/vpr/src/analytical_place/ap_draw_manager.cpp @@ -0,0 +1,49 @@ +/** + * @file + * @author Yulang (Robert) Luo + * @date October 2025 + * @brief The definitions of the AP Draw Manager class which is used + * to handle graphics updates during analytical placement. + */ + +#include "ap_draw_manager.h" +#include "vpr_types.h" + +#ifndef NO_GRAPHICS +#include "draw.h" +#include "draw_global.h" +#include "partial_placement.h" +#endif + +APDrawManager::APDrawManager(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 +} + +APDrawManager::~APDrawManager() { +#ifndef NO_GRAPHICS + // Clear the analytical placement reference in draw state + get_draw_state_vars()->clear_ap_partial_placement_ref(); +#endif +} + +void APDrawManager::update_graphics(unsigned int iteration, enum APDrawType draw_type) { +#ifndef NO_GRAPHICS + std::string msg; + if (draw_type == APDrawType::Solver) { + msg = "Analytical Placement Solver - Iteration: " + std::to_string(iteration); + } else if (draw_type == APDrawType::Legalizer) { + msg = "Analytical Placement Legalizer - Iteration: " + std::to_string(iteration); + } else { + msg = "Analytical Placement"; + } + update_screen(ScreenUpdatePriority::MAJOR, msg.c_str(), e_pic_type::ANALYTICAL_PLACEMENT, nullptr); +#else + (void)iteration; + (void)draw_type; +#endif +} \ No newline at end of file diff --git a/vpr/src/analytical_place/ap_draw_manager.h b/vpr/src/analytical_place/ap_draw_manager.h new file mode 100644 index 0000000000..7c89b438e7 --- /dev/null +++ b/vpr/src/analytical_place/ap_draw_manager.h @@ -0,0 +1,47 @@ +#pragma once +/** + * @file + * @author Yulang (Robert) Luo + * @date October 2025 + * @brief The decalarations of the AP Draw Manager class which is used + * to handle graphics updates during analytical placement. + */ + +#include + +// Forward declarations +class PartialPlacement; + +// Types to indicate the type of drawing operation +enum class APDrawType { + Solver, + Legalizer +}; + +/** + * @class APDrawManager + * @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 APDrawManager { +public: + /** + * @brief Constructor initializes the draw manager with a reference to the + * current partial placement. + */ + explicit APDrawManager(const PartialPlacement& p_placement); + + /** + * @brief Destructor cleans up the reference in the draw state. + */ + ~APDrawManager(); + + /** + * @brief Update screen with current analytical placement state + * @param msg A message to display with the update + */ + void update_graphics(unsigned int iteration, enum APDrawType draw_type); +}; diff --git a/vpr/src/analytical_place/global_placer.cpp b/vpr/src/analytical_place/global_placer.cpp index 8e4045ce32..8913b60edb 100644 --- a/vpr/src/analytical_place/global_placer.cpp +++ b/vpr/src/analytical_place/global_placer.cpp @@ -11,6 +11,7 @@ #include #include #include +#include "ap_draw_manager.h" #include "PreClusterTimingManager.h" #include "analytical_solver.h" #include "ap_flow_enums.h" @@ -352,6 +353,10 @@ PartialPlacement SimPLGlobalPlacer::place() { PartialPlacement best_p_placement(ap_netlist_); double best_ub_hpwl = std::numeric_limits::max(); + // Initialize graphics for analytical placement, setting the reference in + // the draw state. + APDrawManager 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(); @@ -361,12 +366,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(i, APDrawType::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(i, APDrawType::Legalizer); // Perform a timing update float timing_update_start_time = runtime_timer.elapsed_sec(); diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index e1133b64ee..4bd8b1557d 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -387,6 +387,7 @@ enum class e_sched_type { enum class e_pic_type { NO_PICTURE, PLACEMENT, + ANALYTICAL_PLACEMENT, ROUTING }; diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 99ff4e6796..08105c5e9e 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -18,10 +18,12 @@ #include "draw.h" #include "draw_interposer.h" +#include "draw_types.h" #include "timing_info.h" #include "physical_types.h" #include "move_utils.h" +#include "vpr_types.h" #ifndef NO_GRAPHICS @@ -176,65 +178,71 @@ 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 != e_pic_type::ANALYTICAL_PLACEMENT) { + draw_block_pin_util(); + drawplace(g); + draw_internal_draw_subblk(g); - draw_interposer_cuts(g); + draw_interposer_cuts(g); - 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 == e_pic_type::ROUTING) { // ROUTING on screen + if (draw_state->pic_on_screen == e_pic_type::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 @@ -288,7 +296,7 @@ void update_screen(ScreenUpdatePriority priority, * value controls whether or not the Proceed button must be clicked to * * continue. Saves the pic_on_screen_val to allow pan and zoom redraws. */ t_draw_state* draw_state = get_draw_state_vars(); - + strcpy(draw_state->default_message, msg); if (!draw_state->show_graphics) @@ -304,9 +312,23 @@ void update_screen(ScreenUpdatePriority priority, state_change = true; + if (draw_state->show_graphics) { + if (pic_on_screen_val == e_pic_type::ANALYTICAL_PLACEMENT) { + set_initial_world_ap(); + } else { + set_initial_world(); + } + } + if (draw_state->pic_on_screen == e_pic_type::NO_PICTURE) { // Only add the canvas the first time we open graphics 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; @@ -486,7 +508,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 + +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(); @@ -496,14 +529,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(grid_w); + float draw_height = static_cast(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& chanx_track, const vtr::OffsetMatrix& chany_track) { /* Returns the track number of this routing resource node. */ @@ -629,6 +672,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 == e_pic_type::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(); diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index e64fee1ee1..b2a8fcd14b 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -55,6 +55,20 @@ void update_screen(ScreenUpdatePriority priority, */ 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(); + +/** + * @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. */ diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 9dfa2440ab..f120c6d5a0 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -12,11 +12,9 @@ #include "physical_types_util.h" #include "vtr_assert.h" -#include "vtr_ndoffsetmatrix.h" #include "vtr_color_map.h" #include "vpr_utils.h" -#include "vpr_error.h" #include "globals.h" #include "draw_color.h" @@ -29,6 +27,7 @@ #include "move_utils.h" #include "route_export.h" #include "tatum/report/TimingPathCollector.hpp" +#include "partial_placement.h" //To process key presses we need the X11 keysym definitions, //which are unavailable when building with MINGW @@ -191,6 +190,56 @@ void drawplace(ezgl::renderer* g) { } } +void draw_analytical_place(ezgl::renderer* g) { + // Draw a tightly packed view of the device grid using only device context info. + t_draw_state* draw_state = get_draw_state_vars(); + const DeviceContext& device_ctx = g_vpr_ctx.device(); + + g->set_line_dash(ezgl::line_dash::none); + g->set_line_width(0); + + int total_layers = device_ctx.grid.get_num_layers(); + for (int layer = 0; layer < total_layers; ++layer) { + const auto& layer_disp = draw_state->draw_layer_display[layer]; + if (!layer_disp.visible) continue; + + for (int x = 0; x < (int)device_ctx.grid.width(); ++x) { + for (int y = 0; y < (int)device_ctx.grid.height(); ++y) { + if (device_ctx.grid.is_root_location({x, y, layer}) == false) continue; + t_physical_tile_type_ptr type = device_ctx.grid.get_physical_type({x, y, layer}); + if (type->capacity == 0) continue; + + ezgl::point2d bl{static_cast(x), static_cast(y)}; + ezgl::point2d tr{static_cast(x + type->width), static_cast(y + type->height)}; + + ezgl::color fill_color = get_block_type_color(type); + g->set_color(fill_color, layer_disp.alpha); + g->fill_rectangle(bl, tr); + + if (draw_state->draw_block_outlines) { + g->set_color(ezgl::BLACK, layer_disp.alpha); + g->draw_rectangle(bl, tr); + } + } + } + } + + const double half_size = 0.05; + + const PartialPlacement* ap_pp = draw_state->get_ap_partial_placement_ref(); + // The reference should be set in the beginning of analytial placement. + VTR_ASSERT(ap_pp != nullptr); + for (const auto& [blk_id, x] : ap_pp->block_x_locs.pairs()) { + double y = ap_pp->block_y_locs[blk_id]; + + ezgl::point2d bl{x - half_size, y - half_size}; + ezgl::point2d tr{x + half_size, y + half_size}; + + g->set_color(ezgl::BLACK); + g->fill_rectangle(bl, tr); + } +} + /* This routine draws the nets on the placement. The nets have not * * yet been routed, so we just draw a chain showing a possible path * * for each net. This gives some idea of future congestion. */ diff --git a/vpr/src/draw/draw_basic.h b/vpr/src/draw/draw_basic.h index cab85c311e..a0560c0be7 100644 --- a/vpr/src/draw/draw_basic.h +++ b/vpr/src/draw/draw_basic.h @@ -29,6 +29,11 @@ * Blocks are drawn in layer order (so that semi-transparent blocks/grids render well)*/ void drawplace(ezgl::renderer* g); +/** This function draws the analytical placement from the PartialPlacement object, it + * also draws the architecture grid and the blocks from device_ctx. + */ +void draw_analytical_place(ezgl::renderer* g); + /** This routine draws the nets on the placement. The nets have not * yet been routed, so we just draw a chain showing a possible path * for each net. This gives some idea of future congestion. diff --git a/vpr/src/draw/draw_types.h b/vpr/src/draw/draw_types.h index 7d8937cf0d..5223af9c75 100644 --- a/vpr/src/draw/draw_types.h +++ b/vpr/src/draw/draw_types.h @@ -166,6 +166,8 @@ struct t_draw_layer_display { int alpha = 255; }; +struct PartialPlacement; + /** * @brief Structure used to store variables related to highlighting/drawing * @@ -405,6 +407,19 @@ struct t_draw_state { * @brief Stores a reference to NoC link bandwidth utilization to be used in the graphics codes. */ std::optional>> noc_link_bandwidth_usages_ref_; + + /** + * @brief Stores a temporary reference to the Analytical Placement partial placement (best placement). + * @details This is set by the AP global placer just before drawing and cleared immediately after. + * Only a reference is stored to avoid copying and lifetime issues. + */ + std::optional> ap_partial_placement_ref_; + +public: + // Set/clear/get the AP partial placement reference used during AP drawing + void set_ap_partial_placement_ref(const PartialPlacement& p) { ap_partial_placement_ref_ = std::cref(p); } + void clear_ap_partial_placement_ref() { ap_partial_placement_ref_.reset(); } + const PartialPlacement* get_ap_partial_placement_ref() const { return ap_partial_placement_ref_ ? &ap_partial_placement_ref_->get() : nullptr; } }; /* For each cluster type, this structure stores drawing