From 4877829ecbdd6603b4fe55f6fa636f4defa27a4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 09:54:26 +0000 Subject: [PATCH 01/23] Bump libs/EXTERNAL/libcatch2 from `4e8d92b` to `33e24b1` Bumps [libs/EXTERNAL/libcatch2](https://github.com/catchorg/Catch2) from `4e8d92b` to `33e24b1`. - [Release notes](https://github.com/catchorg/Catch2/releases) - [Commits](https://github.com/catchorg/Catch2/compare/4e8d92bf02f7d1c8006a0e7a5ecabd8e62d98502...33e24b14fcf95c1c31c8d6b68f445ebb12e026cc) --- updated-dependencies: - dependency-name: libs/EXTERNAL/libcatch2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- libs/EXTERNAL/libcatch2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/EXTERNAL/libcatch2 b/libs/EXTERNAL/libcatch2 index 4e8d92bf02f..33e24b14fcf 160000 --- a/libs/EXTERNAL/libcatch2 +++ b/libs/EXTERNAL/libcatch2 @@ -1 +1 @@ -Subproject commit 4e8d92bf02f7d1c8006a0e7a5ecabd8e62d98502 +Subproject commit 33e24b14fcf95c1c31c8d6b68f445ebb12e026cc From e434d147a9bd9f0e196aaa774a4008ab1eca868e Mon Sep 17 00:00:00 2001 From: amin1377 Date: Fri, 2 Aug 2024 08:53:38 -0400 Subject: [PATCH 02/23] [vpr][rr_graph] add new switch types when rr graph is read and run-flat is enabled --- vpr/src/route/rr_graph.cpp | 76 +++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/vpr/src/route/rr_graph.cpp b/vpr/src/route/rr_graph.cpp index f0e8e6f4b16..b184d304b05 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -271,7 +271,8 @@ static void connect_src_sink_to_pins(RRGraphBuilder& rr_graph_builder, const int j, t_rr_edge_info_set& rr_edges_to_create, const int delayless_switch, - t_physical_tile_type_ptr physical_type_ptr); + t_physical_tile_type_ptr physical_type_ptr, + bool is_remapped); static void alloc_and_load_tile_rr_graph(RRGraphBuilder& rr_graph_builder, std::map& arch_sw_inf_map, @@ -374,6 +375,8 @@ static void add_pb_edges(RRGraphBuilder& rr_graph_builder, t_logical_block_type_ptr logical_block, const t_pb* pb, const t_cluster_pin_chain& nodes_to_collapse, + float R_minW_nmos, + float R_minW_pmos, int rel_cap, int layer, int i, @@ -741,6 +744,20 @@ void create_rr_graph(const t_graph_type graph_type, } if (is_flat) { + short delayless_switch = OPEN; + if (load_rr_graph) { + const auto& rr_switches = device_ctx.rr_graph.rr_switch(); + for (int switch_id = 0; switch_id < rr_switches.size(); switch_id++){ + const auto& rr_switch = rr_switches[RRSwitchId(switch_id)]; + if (rr_switch.name.find("delayless") != std::string::npos) { + delayless_switch = static_cast(switch_id); + break; + } + } + } else { + delayless_switch = det_routing_arch->delayless_switch; + } + VTR_ASSERT(delayless_switch != OPEN); build_intra_cluster_rr_graph(graph_type, grid, block_types, @@ -2021,6 +2038,9 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder /* If Fc gets clipped, this will be flagged to true */ *Fc_clipped = false; + /* This function is called to build the general routing graph resoruces. Thus, the edges are not remapped yet.*/ + bool is_remapped = false; + int num_edges = 0; /* Connection SINKS and SOURCES to their pins - Initializing IPINs/OPINs. */ for (int layer = 0; layer < grid.get_num_layers(); ++layer) { @@ -2053,7 +2073,8 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder j, rr_edges_to_create, delayless_switch, - physical_tile); + physical_tile, + is_remapped); //Create the actual SOURCE->OPIN, IPIN->SINK edges uniquify_edges(rr_edges_to_create); @@ -2270,7 +2291,8 @@ static void alloc_and_load_intra_cluster_rr_graph(RRGraphBuilder& rr_graph_build j, rr_edges_to_create, delayless_switch, - physical_tile); + physical_tile, + load_rr_graph); //Create the actual SOURCE->OPIN, IPIN->SINK edges uniquify_edges(rr_edges_to_create); @@ -2454,7 +2476,8 @@ static void connect_src_sink_to_pins(RRGraphBuilder& rr_graph_builder, const int j, t_rr_edge_info_set& rr_edges_to_create, const int delayless_switch, - t_physical_tile_type_ptr physical_type_ptr) { + t_physical_tile_type_ptr physical_type_ptr, + bool is_remapped) { for (auto class_num : class_num_vec) { const auto& pin_list = get_pin_list_from_class_physical_num(physical_type_ptr, class_num); auto class_type = get_class_type_from_class_physical_num(physical_type_ptr, class_num); @@ -2474,11 +2497,11 @@ static void connect_src_sink_to_pins(RRGraphBuilder& rr_graph_builder, auto pin_type = get_pin_type_from_pin_physical_num(physical_type_ptr, pin_num); if (class_type == DRIVER) { VTR_ASSERT(pin_type == DRIVER); - rr_edges_to_create.emplace_back(class_rr_node_id, pin_rr_node_id, delayless_switch, false); + rr_edges_to_create.emplace_back(class_rr_node_id, pin_rr_node_id, delayless_switch, is_remapped); } else { VTR_ASSERT(class_type == RECEIVER); VTR_ASSERT(pin_type == RECEIVER); - rr_edges_to_create.emplace_back(pin_rr_node_id, class_rr_node_id, delayless_switch, false); + rr_edges_to_create.emplace_back(pin_rr_node_id, class_rr_node_id, delayless_switch, is_remapped); } } } @@ -2682,6 +2705,8 @@ static void build_cluster_internal_edges(RRGraphBuilder& rr_graph_builder, logical_block, pb, nodes_to_collapse, + R_minW_nmos, + R_minW_pmos, rel_cap, layer, i, @@ -2714,6 +2739,8 @@ static void add_pb_edges(RRGraphBuilder& rr_graph_builder, t_logical_block_type_ptr logical_block, const t_pb* pb, const t_cluster_pin_chain& nodes_to_collapse, + float R_minW_nmos, + float R_minW_pmos, int rel_cap, int layer, int i, @@ -2773,23 +2800,15 @@ static void add_pb_edges(RRGraphBuilder& rr_graph_builder, conn_pin_physical_num); if (is_remapped) { - bool found = false; + auto& all_sw_inf = g_vpr_ctx.mutable_device().all_sw_inf; float delay = g_vpr_ctx.device().all_sw_inf.at(sw_idx).Tdel(); - const auto& rr_switches = rr_graph_builder.rr_switch(); - for (int sw_id = 0; sw_id < (int)rr_switches.size(); sw_id++) { - const auto& rr_switch = rr_switches[RRSwitchId(sw_id)]; - if (rr_switch.intra_tile) { - if (rr_switch.Tdel == delay) { - sw_idx = sw_id; - found = true; - break; - } - } - } - // If the graph is loaded from a file, we expect that all sw types are already listed there since currently, we are not doing any further - // Optimization. If the optimization done when the rr graph file was generated is different from the current optimization, in the case that - // these optimizations create different RR switches, this VTR ASSERT can be removed. - VTR_ASSERT(found); + bool is_new_sw; + std::tie(is_new_sw, sw_idx) = find_create_intra_cluster_sw(rr_graph_builder, + all_sw_inf, + R_minW_nmos, + R_minW_pmos, + is_remapped, + delay); } rr_edges_to_create.emplace_back(parent_pin_node_id, conn_pin_node_id, sw_idx, is_remapped); } @@ -2960,19 +2979,10 @@ static void add_chain_node_fan_in_edges(RRGraphBuilder& rr_graph_builder, is_rr_sw_id, delay); - if (!is_rr_sw_id && is_new_sw) { - // Currently we assume that if rr graph is read from a file, we shouldn't get into this block - VTR_ASSERT(!load_rr_graph); - // The internal edges are added after switch_fanin_remap is initialized; thus, if a new arch_sw is added, - // switch _fanin_remap should be updated. - t_rr_switch_inf rr_sw_inf = create_rr_switch_from_arch_switch(create_internal_arch_sw(delay), - R_minW_nmos, - R_minW_pmos); - auto rr_sw_id = rr_graph_builder.add_rr_switch(rr_sw_inf); - // If rr graph is loaded from a file, switch_fanin_remap is going to be empty + if (is_new_sw) { if (!load_rr_graph) { auto& switch_fanin_remap = g_vpr_ctx.mutable_device().switch_fanin_remap; - switch_fanin_remap.push_back({{UNDEFINED, size_t(rr_sw_id)}}); + switch_fanin_remap.push_back({{UNDEFINED, size_t(sw_id)}}); } } From 9642bb4ea9a05344dd19177eb35464e37dfe5170 Mon Sep 17 00:00:00 2001 From: amin1377 Date: Fri, 2 Aug 2024 09:45:05 -0400 Subject: [PATCH 03/23] fix typo --- vpr/src/route/rr_graph.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vpr/src/route/rr_graph.cpp b/vpr/src/route/rr_graph.cpp index b184d304b05..37dde2fbee2 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -747,7 +747,7 @@ void create_rr_graph(const t_graph_type graph_type, short delayless_switch = OPEN; if (load_rr_graph) { const auto& rr_switches = device_ctx.rr_graph.rr_switch(); - for (int switch_id = 0; switch_id < rr_switches.size(); switch_id++){ + for (int switch_id = 0; switch_id < static_cast(rr_switches.size()); switch_id++){ const auto& rr_switch = rr_switches[RRSwitchId(switch_id)]; if (rr_switch.name.find("delayless") != std::string::npos) { delayless_switch = static_cast(switch_id); @@ -762,7 +762,7 @@ void create_rr_graph(const t_graph_type graph_type, grid, block_types, device_ctx.rr_graph, - det_routing_arch->delayless_switch, + delayless_switch, det_routing_arch->R_minW_nmos, det_routing_arch->R_minW_pmos, mutable_device_ctx.rr_graph_builder, @@ -794,7 +794,7 @@ void create_rr_graph(const t_graph_type graph_type, // When this function is called in any stage other than routing, the is_flat flag passed to this function is false, regardless of the flag passed // through command line. So, the graph corresponding to global resources will be created and written down to file if needed. During routing, if flat-routing // is enabled, intra-cluster resources will be added to the graph, but this new bigger graph will not be written down. - if (!det_routing_arch->write_rr_graph_filename.empty() && !is_flat) { + if (!det_routing_arch->write_rr_graph_filename.empty()) { write_rr_graph(&mutable_device_ctx.rr_graph_builder, &mutable_device_ctx.rr_graph, device_ctx.physical_tile_types, From f66b4bbf685010a4a2e5ab701667f84521973f9e Mon Sep 17 00:00:00 2001 From: amin1377 Date: Fri, 2 Aug 2024 10:31:04 -0400 Subject: [PATCH 04/23] don't write down flat rr graph --- vpr/src/route/rr_graph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpr/src/route/rr_graph.cpp b/vpr/src/route/rr_graph.cpp index 37dde2fbee2..81c759f9708 100644 --- a/vpr/src/route/rr_graph.cpp +++ b/vpr/src/route/rr_graph.cpp @@ -794,7 +794,7 @@ void create_rr_graph(const t_graph_type graph_type, // When this function is called in any stage other than routing, the is_flat flag passed to this function is false, regardless of the flag passed // through command line. So, the graph corresponding to global resources will be created and written down to file if needed. During routing, if flat-routing // is enabled, intra-cluster resources will be added to the graph, but this new bigger graph will not be written down. - if (!det_routing_arch->write_rr_graph_filename.empty()) { + if (!det_routing_arch->write_rr_graph_filename.empty() && !is_flat) { write_rr_graph(&mutable_device_ctx.rr_graph_builder, &mutable_device_ctx.rr_graph, device_ctx.physical_tile_types, From 77141072d6b3cd49a0f4da7b53faeab7cfe36c35 Mon Sep 17 00:00:00 2001 From: MohamedElgammal Date: Mon, 5 Aug 2024 15:40:03 -0400 Subject: [PATCH 05/23] Adding a script that control the tuning runs with multiple tuned parameters and efficiently parse the resulting results --- vtr_flow/scripts/tuning_runs/README.md | 24 +++ vtr_flow/scripts/tuning_runs/control_runs.py | 204 +++++++++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 vtr_flow/scripts/tuning_runs/README.md create mode 100755 vtr_flow/scripts/tuning_runs/control_runs.py diff --git a/vtr_flow/scripts/tuning_runs/README.md b/vtr_flow/scripts/tuning_runs/README.md new file mode 100644 index 00000000000..eebbcdf2670 --- /dev/null +++ b/vtr_flow/scripts/tuning_runs/README.md @@ -0,0 +1,24 @@ +A script used to run tuning experiments with multiple parameters. + +Steps to use: +============= + 1) edit the first section of the script by setting `params_dict` dictionary to the parameters that you want to sweep and the corresponding values that you want to try. If you want the resulting spreadheet to include specific metrics, set `keep_metrics_only` variable to `True` and the metrics that you care about in `parsed_metrics`. If you want the full parsed result sheet, set `keep_metrics_only` to `False` + + 2) run the script as follows: +''' +python control_runs.py --generate +''' + +This will edit the `config.txt` file of this task adding several lines `script_params_list_add` for each of the combinations of the input params + + 3) Launch the task using `run_vtr_task.py` script + 4) When the run is done, run the script to parse the results as follows: +''' +python control_runs.py --parse +''' + +The script will generate 3 csv files in the runXXX idrectory of the task as follows: + - `full_res.csv` that exactly matches parse_results.txt but in csv format + - `avg_seed.csv` that averages the results of the each circuit with one set of parameters over the different seed values + - `geomean_res.csv` that geometrically average the results of all the circuits over the same set of parameters + - `summary.xlsx` that merges all the previously mentioned sheets in a single spreadsheet diff --git a/vtr_flow/scripts/tuning_runs/control_runs.py b/vtr_flow/scripts/tuning_runs/control_runs.py new file mode 100755 index 00000000000..5588a2a937b --- /dev/null +++ b/vtr_flow/scripts/tuning_runs/control_runs.py @@ -0,0 +1,204 @@ +import itertools +import os +import sys +import csv +import pandas as pd +import numpy as np +from scipy import stats + +# Define the global dictionary +params_dict = { + "--seed": [1, 2], + "--place_algorithm": ["criticality_timing"], + "--place_agent_epsilon": [0.3] +} + +# Set to True if you only care about specific metrics +keep_metrics_only = True +parsed_metrics = ["num_io", "num_LAB"] + + +def safe_gmean(series): + series = series.replace({0: np.nan}) + return stats.gmean(series.dropna()) + +def generate_combinations(params_dict): + keys = list(params_dict.keys()) + values = list(params_dict.values()) + combinations = list(itertools.product(*values)) + + lines = [] + for combination in combinations: + params_str = ' '.join(f"{key} {value}" for key, value in zip(keys, combination)) + lines.append(f"script_params_list_add={params_str}\n") + return lines + +def parse_results(input_path, params_dict): + # Find the runXXX directory with the largest XXX + run_dirs = [d for d in os.listdir(input_path) if d.startswith("run") and d[3:].isdigit()] + if not run_dirs: + print("No runXXX directories found in the specified input path.") + sys.exit(1) + + largest_run_dir = max(run_dirs, key=lambda d: int(d[3:])) + largest_run_path = os.path.join(input_path, largest_run_dir) + + # Path to parse_results.txt and full_res.csv + parse_results_path = os.path.join(largest_run_path, "parse_results.txt") + full_res_csv_path = os.path.join(largest_run_path, "full_res.csv") + + if not os.path.exists(parse_results_path): + print(f"{parse_results_path} not found.") + sys.exit(1) + + # Read the parse_results.txt file and write to full_res.csv + with open(parse_results_path, "r") as txt_file, open(full_res_csv_path, "w", newline='') as csv_file: + reader = csv.reader(txt_file, delimiter='\t') + writer = csv.writer(csv_file) + + headers = next(reader) + script_params_index = headers.index("script_params") + + # Create new headers with params_dict keys + new_headers = headers[:script_params_index] + list(params_dict.keys()) + headers[script_params_index + 1:] + writer.writerow(new_headers) + + for row in reader: + script_params_value = row[script_params_index] + script_params_dict = parse_script_params(script_params_value, params_dict) + new_row = row[:script_params_index] + [script_params_dict.get(key, '') for key in params_dict.keys()] + row[script_params_index + 1:] + writer.writerow(new_row) + + print(f"Converted {parse_results_path} to {full_res_csv_path}") + + # Generate avg_seed.csv if --seed column exists + generate_avg_seed_csv(full_res_csv_path, largest_run_path) + print(f"Generated average seed results") + + # Generate gmean_res.csv + generate_geomean_res_csv(os.path.join(largest_run_path, "avg_seed.csv"), largest_run_path, params_dict) + print(f"Generated geometric average results over all the circuits") + + generate_xlsx(largest_run_path) + print(f"Generated xlsx that merges all the result csv files") + +def generate_xlsx(largest_run_path): + csv_files = [os.path.join(largest_run_path, "full_res.csv"), + os.path.join(largest_run_path, "avg_seed.csv"), + os.path.join(largest_run_path, "geomean_res.csv")] + sheet_names = ["Full res", "Avg. seeds", "Summary"] + output_excel_file = os.path.join(largest_run_path, "summary.xlsx") + # Create an Excel writer object + with pd.ExcelWriter(output_excel_file) as writer: + for csv_file, sheet_name in zip(csv_files, sheet_names): + # Read each CSV file + df = pd.read_csv(csv_file) + + # Write each DataFrame to a different sheet + df.to_excel(writer, sheet_name=sheet_name, index=False) + +def parse_script_params(script_params, params_dict): + parsed_params = {key: '' for key in params_dict.keys()} + + parts = script_params.split('_') + i = 0 + + while i < len(parts): + for key in params_dict.keys(): + key_parts = key.split('_') + key_length = len(key_parts) + + if parts[i:i+key_length] == key_parts: + value_parts = [] + j = i + key_length + + while j < len(parts) and not any(parts[j:j+len(k.split('_'))] == k.split('_') for k in params_dict.keys()): + value_parts.append(parts[j]) + j += 1 + + parsed_params[key] = '_'.join(value_parts) + i = j - 1 + break + + i += 1 + + return parsed_params + +def generate_avg_seed_csv(full_res_csv_path, output_dir): + + df = pd.read_csv(full_res_csv_path) + + if keep_metrics_only: + col_to_keep = ['circuit', 'arch'] + col_to_keep.extend(list(params_dict.keys())) + col_to_keep.extend(parsed_metrics) + df = df.drop(columns=[col for col in df.columns if col not in col_to_keep]) + + # Check if '--seed' column is present + if '--seed' in df.columns: + # Determine the grouping keys: ['circuit', 'arch'] + keys from params_dict that are present in the dataframe + grouping_keys = ['circuit', 'arch'] + [key for key in params_dict.keys() if key in df.columns and key != "--seed"] + + # Group by specified keys and compute the mean for numeric columns + df_grouped = df.groupby(grouping_keys).mean(numeric_only=True).reset_index() + + # Drop the '--seed' column if it exists + if '--seed' in df_grouped.columns: + df_grouped.drop(columns=['--seed'], inplace=True) + else: + df_grouped = df + + # Save the resulting dataframe to a CSV file + avg_seed_csv_path = os.path.join(output_dir, "avg_seed.csv") + df_grouped.to_csv(avg_seed_csv_path, index=False) + +def generate_geomean_res_csv(full_res_csv_path, output_dir, params_dict): + df = pd.read_csv(full_res_csv_path) + + param_columns = [key for key in params_dict.keys() if key != '--seed'] + non_param_columns = [col for col in df.columns if col not in param_columns] + + geomean_df = df.groupby(param_columns).agg( + {col: (lambda x: '' if x.dtype == 'object' else safe_gmean(x)) for col in non_param_columns} + ).reset_index() + + geomean_df.drop(columns=['circuit'], inplace=True) + geomean_df.drop(columns=['arch'], inplace=True) + + geomean_res_csv_path = os.path.join(output_dir, "geomean_res.csv") + geomean_df.to_csv(geomean_res_csv_path, index=False) + +def main(): + if len(sys.argv) < 3: + print("Usage: script.py