diff --git a/vpr/src/analytical_place/global_placer.cpp b/vpr/src/analytical_place/global_placer.cpp index 8e4045ce32..baa8e0f636 100644 --- a/vpr/src/analytical_place/global_placer.cpp +++ b/vpr/src/analytical_place/global_placer.cpp @@ -18,6 +18,10 @@ #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" @@ -352,6 +356,10 @@ PartialPlacement SimPLGlobalPlacer::place() { PartialPlacement best_p_placement(ap_netlist_); double best_ub_hpwl = std::numeric_limits::max(); +#ifndef NO_GRAPHICS + get_draw_state_vars()->set_ap_partial_placement_ref(p_placement); + update_screen(ScreenUpdatePriority::MAJOR, "AP starts", ANALYTICAL_PLACEMENT, nullptr); +#endif // Run the global placer. for (size_t i = 0; i < max_num_iterations_; i++) { float iter_start_time = runtime_timer.elapsed_sec(); @@ -361,12 +369,22 @@ 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_); +#ifndef NO_GRAPHICS + // Per iteration analytical solve display + std::string iter_msg = vtr::string_fmt("AP Iteration %zu after analytical solve", i); + update_screen(ScreenUpdatePriority::MAJOR, iter_msg.c_str(), ANALYTICAL_PLACEMENT, nullptr); +#endif // 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_); +#ifndef NO_GRAPHICS + // Per iteration legalized display + iter_msg = vtr::string_fmt("AP Iteration %zu after partial legalization", i); + update_screen(ScreenUpdatePriority::MAJOR, iter_msg.c_str(), ANALYTICAL_PLACEMENT, nullptr); +#endif // Perform a timing update float timing_update_start_time = runtime_timer.elapsed_sec(); @@ -422,6 +440,8 @@ PartialPlacement SimPLGlobalPlacer::place() { if (hpwl_relative_gap < target_hpwl_relative_gap_) break; + + } // Update the setup slacks. This is performed down here (as well as being @@ -450,6 +470,13 @@ PartialPlacement SimPLGlobalPlacer::place() { *density_manager_, pre_cluster_timing_manager_); + +#ifndef NO_GRAPHICS + // Final display of the last iteration's placement + get_draw_state_vars()->set_ap_partial_placement_ref(p_placement); + update_screen(ScreenUpdatePriority::MAJOR, "Global Placement Complete", ANALYTICAL_PLACEMENT, nullptr); + get_draw_state_vars()->clear_ap_partial_placement_ref(); +#endif // Return the placement from the final iteration. return best_p_placement; } diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 49e8bd438e..079f59bf56 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -386,6 +386,7 @@ enum class e_sched_type { enum pic_type { NO_PICTURE, PLACEMENT, + ANALYTICAL_PLACEMENT, ROUTING }; /* What's on screen? */ diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 23dcb993ca..1c860d1823 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -16,10 +16,12 @@ #include #include #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 @@ -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 @@ -286,7 +290,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) @@ -302,16 +306,30 @@ 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; draw_state->pic_on_screen = pic_on_screen_val; } + // What is this? Always true! bool should_pause = int(priority) >= draw_state->gr_automode; //If there was a state change, we must call ezgl::application::run() to update the buttons. @@ -481,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 + +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(); @@ -491,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(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. */ @@ -624,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(); diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index 294e3d37ff..03a5a9a013 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -52,6 +52,9 @@ void update_screen(ScreenUpdatePriority priority, const char* msg, enum pic_type */ void init_draw_coords(float clb_width, const BlkLocRegistry& blk_loc_registry); +void set_initial_world_ap(); +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 90452e6f1c..0d96c343f8 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 @@ -193,6 +192,60 @@ 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) { + // Only draw at the root of a non-unit tile + int w_off = device_ctx.grid.get_width_offset({x, y, layer}); + int h_off = device_ctx.grid.get_height_offset({x, y, layer}); + if (w_off > 0 || h_off > 0) 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..70357eba7b 100644 --- a/vpr/src/draw/draw_basic.h +++ b/vpr/src/draw/draw_basic.h @@ -29,6 +29,8 @@ * Blocks are drawn in layer order (so that semi-transparent blocks/grids render well)*/ void drawplace(ezgl::renderer* g); +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 68b2772fb0..6a5fb9cfc3 100644 --- a/vpr/src/draw/draw_types.h +++ b/vpr/src/draw/draw_types.h @@ -157,6 +157,8 @@ struct t_draw_layer_display { int alpha = 255; }; +struct PartialPlacement; + /** * @brief Structure used to store variables related to highlighting/drawing * @@ -396,6 +398,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