Skip to content

Congestion Aware Placement #2672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: openfpga
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions vpr/src/base/SetupVPR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ static void SetupPlacerOpts(const t_options& Options, t_placer_opts* PlacerOpts)
PlacerOpts->recompute_crit_iter = Options.RecomputeCritIter;

PlacerOpts->timing_tradeoff = Options.PlaceTimingTradeoff;
PlacerOpts->congestion_tradeoff = Options.CongestionTradeoff;

/* Depends on PlacerOpts->place_algorithm */
PlacerOpts->delay_offset = Options.place_delay_offset;
Expand Down
26 changes: 21 additions & 5 deletions vpr/src/base/read_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ struct ParsePlaceAlgorithm {
conv_value.set_value(CRITICALITY_TIMING_PLACE);
} else if (str == "slack_timing") {
conv_value.set_value(SLACK_TIMING_PLACE);
} else if (str == "congestion_aware"){
conv_value.set_value(CONGESTION_AWARE_PLACE);
} else {
std::stringstream msg;
msg << "Invalid conversion from '" << str << "' to e_place_algorithm (expected one of: " << argparse::join(default_choices(), ", ") << ")";
Expand All @@ -419,6 +421,8 @@ struct ParsePlaceAlgorithm {
conv_value.set_value("bounding_box");
} else if (val == CRITICALITY_TIMING_PLACE) {
conv_value.set_value("criticality_timing");
} else if (val == CONGESTION_AWARE_PLACE) {
conv_value.set_value("congestion_aware");
} else {
VTR_ASSERT(val == SLACK_TIMING_PLACE);
conv_value.set_value("slack_timing");
Expand All @@ -427,7 +431,7 @@ struct ParsePlaceAlgorithm {
}

std::vector<std::string> default_choices() {
return {"bounding_box", "criticality_timing", "slack_timing"};
return {"bounding_box", "criticality_timing", "slack_timing", "congestion_aware"};
}
};

Expand Down Expand Up @@ -1398,6 +1402,8 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio
" Sets the routing congestion drawing state\n"
" * exit <int>\n"
" Exits VPR with specified exit code\n"
" * set_congestion <int>\n"
" Sets the routing congestion drawing state\n"
"\n"
" Example:\n"
" 'save_graphics place.png; \\\n"
Expand Down Expand Up @@ -2002,9 +2008,10 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio
"Controls which placement algorithm is used. Valid options:\n"
" * bounding_box: Focuses purely on minimizing the bounding box wirelength of the circuit. Turns off timing analysis if specified.\n"
" * criticality_timing: Focuses on minimizing both the wirelength and the connection timing costs (criticality * delay).\n"
" * slack_timing: Focuses on improving the circuit slack values to reduce critical path delay.\n")
" * slack_timing: Focuses on improving the circuit slack values to reduce critical path delay.\n"
" * congestion_aware: Focuses on improving routability.\n")
.default_value("criticality_timing")
.choices({"bounding_box", "criticality_timing", "slack_timing"})
.choices({"bounding_box", "criticality_timing", "slack_timing", "congestion_aware"})
.show_in(argparse::ShowIn::HELP_ONLY);

place_grp.add_argument<e_place_algorithm, ParsePlaceAlgorithm>(args.PlaceQuenchAlgorithm, "--place_quench_algorithm")
Expand All @@ -2014,9 +2021,10 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio
"Valid options:\n"
" * bounding_box: Focuses purely on minimizing the bounding box wirelength of the circuit. Turns off timing analysis if specified.\n"
" * criticality_timing: Focuses on minimizing both the wirelength and the connection timing costs (criticality * delay).\n"
" * slack_timing: Focuses on improving the circuit slack values to reduce critical path delay.\n")
" * slack_timing: Focuses on improving the circuit slack values to reduce critical path delay.\n"
" * congestion_aware: Focuses on improving routability.\n")
.default_value("criticality_timing")
.choices({"bounding_box", "criticality_timing", "slack_timing"})
.choices({"bounding_box", "criticality_timing", "slack_timing", "congestion_aware"})
.show_in(argparse::ShowIn::HELP_ONLY);

place_grp.add_argument(args.PlaceChanWidth, "--place_chan_width")
Expand Down Expand Up @@ -2236,6 +2244,14 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio
" 0.0 focuses completely on wirelength, 1.0 completely on timing")
.default_value("0.5")
.show_in(argparse::ShowIn::HELP_ONLY);

place_timing_grp.add_argument(args.CongestionTradeoff, "--congest_tradeoff")
.help(
"Trade-off control the bouding value for the contestion matrix.\n"
" a value near routing channel width can be a good value.\n"
" a high value let the VPR to ignore the congestion aware placement and continue its own course of action.\n")
.default_value("1.0")
.show_in(argparse::ShowIn::HELP_ONLY);

place_timing_grp.add_argument(args.RecomputeCritIter, "--recompute_crit_iter")
.help("Controls how many temperature updates occur between timing analysis during placement")
Expand Down
1 change: 1 addition & 0 deletions vpr/src/base/read_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ struct t_options {

/* Timing-driven placement options only */
argparse::ArgValue<float> PlaceTimingTradeoff;
argparse::ArgValue<float> CongestionTradeoff;
argparse::ArgValue<int> RecomputeCritIter;
argparse::ArgValue<int> inner_loop_recompute_divider;
argparse::ArgValue<int> quench_recompute_divider;
Expand Down
6 changes: 4 additions & 2 deletions vpr/src/base/vpr_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,8 @@ struct t_annealing_sched {
enum e_place_algorithm {
BOUNDING_BOX_PLACE,
CRITICALITY_TIMING_PLACE,
SLACK_TIMING_PLACE
SLACK_TIMING_PLACE,
CONGESTION_AWARE_PLACE
};

enum e_place_bounding_box_mode {
Expand Down Expand Up @@ -1093,7 +1094,7 @@ class t_place_algorithm {

///@brief Check if the algorithm belongs to the timing driven category.
inline bool is_timing_driven() const {
return algo == CRITICALITY_TIMING_PLACE || algo == SLACK_TIMING_PLACE;
return algo == CRITICALITY_TIMING_PLACE || algo == SLACK_TIMING_PLACE || algo== CONGESTION_AWARE_PLACE;
}

///@brief Accessor: returns the underlying e_place_algorithm enum value.
Expand Down Expand Up @@ -1231,6 +1232,7 @@ struct t_placer_opts {
t_place_algorithm place_algorithm;
t_place_algorithm place_quench_algorithm;
float timing_tradeoff;
float congestion_tradeoff;
float place_cost_exp;
int place_chan_width;
enum e_pad_loc_type pad_loc_type;
Expand Down
2 changes: 2 additions & 0 deletions vpr/src/place/move_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ struct MoveOutcomeStats {
float delta_cost_norm = std::numeric_limits<float>::quiet_NaN();
float delta_bb_cost_norm = std::numeric_limits<float>::quiet_NaN();
float delta_timing_cost_norm = std::numeric_limits<float>::quiet_NaN();
float delta_cong_cost_norm = std::numeric_limits<float>::quiet_NaN();

float delta_bb_cost_abs = std::numeric_limits<float>::quiet_NaN();
float delta_timing_cost_abs = std::numeric_limits<float>::quiet_NaN();
float delta_cong_cost_abs = std::numeric_limits<float>::quiet_NaN();

e_move_result outcome = ABORTED;
float elapsed_time = std::numeric_limits<float>::quiet_NaN();
Expand Down
95 changes: 94 additions & 1 deletion vpr/src/place/net_cost_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ enum class NetUpdateState {

const int MAX_FANOUT_CROSSING_COUNT = 50;

double cong_matrix[400][400];
Copy link
Contributor

Choose a reason for hiding this comment

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

Needs comments on what these are, what the overall congestion modeling approach is.
The approach seems to be very similar to a placement cost function that I wrote in my PhD and called NON_LINEAR_CONGESTION. At the time I measured that code it had little gain in routability for architectures with all channels the same width (in various chip regions) and the CPU time was high. Not sure if the trade-off is different with this code, but adding QoR data on some larger circuits would be important to check.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, hard-coding 400 is not good.

double cong_matrix_new[400][400];

/**
* @brief Crossing counts for nets with different #'s of pins. From
* ICCAD 94 pp. 690 - 695 (with linear interpolation applied by me).
Expand Down Expand Up @@ -1842,6 +1845,59 @@ static double get_net_wirelength_estimate(ClusterNetId net_id, const t_bb& bb) {
return (ncost);
}

void get_cong_matrix(ClusterNetId net_id, const t_bb& bb) {
/* Finds the cost due to one net by looking at its coordinate bounding *
* box. */
// auto& cluster_ctx = g_vpr_ctx.clustering();

/* Could insert a check for xmin == xmax. In that case, assume *
* connection will be made with no bends and hence no x-cost. *
* Same thing for y-cost. */

/* Cost = wire length along channel * cross_count / average *
* channel capacity. Do this for x, then y direction and add. */
for(int i=bb.xmin;i<bb.xmax;i++){
for(int j=bb.ymin;j<bb.ymax;j++){
cong_matrix[i][j] += get_net_wirelength_estimate(net_id,bb)/double((bb.xmax - bb.xmin + 1)*(bb.ymax - bb.ymin + 1));
}
}
}


double get_cong_cost(double chan_width) {
auto& device_ctx = g_vpr_ctx.device();
double max = 0.0;
double avg = 1e-4,var=0.0;
double num = 0.0;
double max_width = chan_width;
for(int i=0;i<int(device_ctx.grid.width());i++){
for(int j=0;j<int(device_ctx.grid.height());j++){
if(max<cong_matrix_new[i][j]){
max = cong_matrix_new[i][j];
}
}
}

for(int i=0;i<int(device_ctx.grid.width());i++){
for(int j=0;j<int(device_ctx.grid.height());j++){
if(cong_matrix_new[i][j]>max_width){
avg+=cong_matrix_new[i][j]-max_width;
num+=1.0;
}
}
}

for(int i=0;i<int(device_ctx.grid.width());i++){
for(int j=0;j<int(device_ctx.grid.height());j++){
double var_var=cong_matrix_new[i][j]-avg;
var_var = var_var*var_var;
var += var_var;
}
}
var = var/double((device_ctx.grid.width()*device_ctx.grid.height()));
return avg;
}

static double get_net_wirelength_from_layer_bb(ClusterNetId /* net_id */,
const std::vector<t_2D_bb>& bb,
const vtr::NdMatrixProxy<int, 1> layer_pin_sink_count) {
Expand Down Expand Up @@ -1954,11 +2010,21 @@ void find_affected_nets_and_update_costs(
set_bb_delta_cost(bb_delta_c);
}

double comp_bb_cost(e_cost_methods method) {
double comp_bb_cost(e_cost_methods method, const t_place_algorithm& place_algorithm) {
double cost = 0;
double expected_wirelength = 0.0;
auto& cluster_ctx = g_vpr_ctx.clustering();
auto& place_move_ctx = g_placer_ctx.mutable_move();
auto& device_ctx = g_vpr_ctx.device();
// VTR_LOG("\n\n\nwidth = %d and height= %d\n\n\n",device_ctx.grid.width(), device_ctx.grid.height());
if(place_algorithm == CONGESTION_AWARE_PLACE){
for(int i = 0; i < int(device_ctx.grid.width()); i++){
for(int j = 0; j < int(device_ctx.grid.height()); j++){
cong_matrix[i][j] = 0.0;
// cong_matrix_new[i][j] = 0.0;
}
}
}

for (auto net_id : cluster_ctx.clb_nlist.nets()) { /* for each net ... */
if (!cluster_ctx.clb_nlist.net_is_ignored(net_id)) { /* Do only if not ignored. */
Expand All @@ -1976,13 +2042,35 @@ double comp_bb_cost(e_cost_methods method) {
place_move_ctx.num_sink_pin_layer[size_t(net_id)]);
}

if(place_algorithm == CONGESTION_AWARE_PLACE){
get_cong_matrix(net_id, place_move_ctx.bb_coords[net_id]);
}

pl_net_cost.net_cost[net_id] = get_net_cost(net_id, place_move_ctx.bb_coords[net_id]);
cost += pl_net_cost.net_cost[net_id];
if (method == CHECK)
expected_wirelength += get_net_wirelength_estimate(net_id, place_move_ctx.bb_coords[net_id]);
}
}

if(place_algorithm == CONGESTION_AWARE_PLACE){
for(int i = 0; i < int(device_ctx.grid.width()); i++){
for(int j = 0; j < int(device_ctx.grid.height()); j++){
cong_matrix_new[i][j] = cong_matrix[i][j];
}
}
}

// cost = get_cong_cost();
if(place_algorithm == CONGESTION_AWARE_PLACE){
for(int i=0;i<int(device_ctx.grid.width());i++){
for(int j=0;j<int(device_ctx.grid.height());j++){
VTR_LOG("%4.0f\t",cong_matrix[i][j]);
}
VTR_LOG("\n");
}
}

if (method == CHECK) {
VTR_LOG("\n");
VTR_LOG("BB estimate of min-dist (placement) wire length: %.0f\n",
Expand Down Expand Up @@ -2084,8 +2172,13 @@ void recompute_costs_from_scratch(const t_placer_opts& placer_opts,
};

double new_bb_cost = recompute_bb_cost();
double new_cong_cost = 0.0;
if(placer_opts.place_algorithm == CONGESTION_AWARE_PLACE){
new_cong_cost = get_cong_cost(placer_opts.congestion_tradeoff);
}
check_and_print_cost(new_bb_cost, costs->bb_cost, "bb_cost");
costs->bb_cost = new_bb_cost;
costs->cong_cost = new_cong_cost;

if (placer_opts.place_algorithm.is_timing_driven()) {
double new_timing_cost = 0.;
Expand Down
6 changes: 5 additions & 1 deletion vpr/src/place/net_cost_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ void find_affected_nets_and_update_costs(
* @param method
* @return The bounding box cost of the placement, computed by the 3D method.
*/
double comp_bb_cost(e_cost_methods method);
double comp_bb_cost(e_cost_methods method, const t_place_algorithm& place_algorithm);

/**
* @brief Finds the bb cost from scratch (based on per-layer BB).
Expand Down Expand Up @@ -147,3 +147,7 @@ void init_try_swap_net_cost_structs(size_t num_nets, bool cube_bb);
* @brief Free (layer_)ts_bb_edge_new, (layer_)ts_bb_coord_new, ts_layer_sink_pin_count, and ts_nets_to_update data structures.
*/
void free_try_swap_net_cost_structs();

void get_cong_matrix(ClusterNetId net_id, const t_bb& bb);

double get_cong_cost(double chan_width);
Loading
Loading