diff --git a/doc/src/vpr/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index ea6f253365..b3d482e048 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -256,6 +256,16 @@ General Options **Default:** ``on`` +.. option:: --verify_route_file_switch_id {on | off} + + Verify that the switch IDs in the routing file are consistent with those in the RR Graph. + Set this to false when switch IDs in the routing file may differ from the RR Graph. + For example, when analyzing different timing corners using the same netlist, placement, and routing files, + the RR switch IDs in the RR Graph may differ due to changes in delays. + In such cases, set this option to false so that the switch IDs from the RR Graph are used, and those in the routing file are ignored. + + **Default:** ``on`` + .. option:: --target_utilization Sets the target device utilization. diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 592b973003..7e1c2f8961 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -517,6 +517,8 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->custom_3d_sb_fanin_fanout = Options.custom_3d_sb_fanin_fanout; RouterOpts->with_timing_analysis = Options.timing_analysis; + RouterOpts->verify_route_file_switch_id = Options.verify_route_file_switch_id; + RouterOpts->generate_router_lookahead_report = Options.generate_router_lookahead_report.value(); } diff --git a/vpr/src/base/old_traceback.cpp b/vpr/src/base/old_traceback.cpp index 41bb857f59..de708ca296 100644 --- a/vpr/src/base/old_traceback.cpp +++ b/vpr/src/base/old_traceback.cpp @@ -6,9 +6,9 @@ #include "route_common.h" #include +#include std::pair traceback_from_route_tree_recurr(t_trace* head, t_trace* tail, const RouteTreeNode& node); -bool validate_traceback_recurr(t_trace* trace, std::set& seen_rr_nodes); void free_trace_data(t_trace* tptr); /** Build a route tree from a traceback */ @@ -109,7 +109,7 @@ t_trace* TracebackCompat::traceback_from_route_tree(const RouteTree& tree) { std::tie(head, tail) = traceback_from_route_tree_recurr(nullptr, nullptr, tree.root()); - VTR_ASSERT(validate_traceback(head)); + VTR_ASSERT(validate_and_update_traceback(head)); return head; } @@ -141,70 +141,65 @@ void print_traceback(const t_trace* trace) { VTR_LOG("\n"); } -bool validate_traceback(t_trace* trace) { - std::set seen_rr_nodes; - - return validate_traceback_recurr(trace, seen_rr_nodes); -} - -bool validate_traceback_recurr(t_trace* trace, std::set& seen_rr_nodes) { +bool validate_and_update_traceback(t_trace* trace, bool verify_switch_id /* = true */) { if (!trace) { return true; } - seen_rr_nodes.insert(trace->index); + std::set seen_rr_nodes; + std::stack trace_stack; + trace_stack.push(trace); - t_trace* next = trace->next; + while (!trace_stack.empty()) { + trace = trace_stack.top(); + trace_stack.pop(); + seen_rr_nodes.insert(trace->index); + t_trace* next = trace->next; - if (next) { - if (trace->iswitch == OPEN) { //End of a branch + if (next == nullptr) { + continue; + } - //Verify that the next element (branch point) has been already seen in the traceback so far + if (trace->iswitch == OPEN) { // End of a branch + // Verify that the next element (branch point) has been already seen in the traceback so far if (!seen_rr_nodes.count(next->index)) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback branch point %d not found", next->index); - } else { - //Recurse along the new branch - return validate_traceback_recurr(next, seen_rr_nodes); } - } else { //Midway along branch - - //Check there is an edge connecting trace and next - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; + } else { // Midway along branch + // Check there is an edge connecting trace and next + const auto& rr_graph = g_vpr_ctx.device().rr_graph; bool found = false; for (t_edge_size iedge = 0; iedge < rr_graph.num_edges(RRNodeId(trace->index)); ++iedge) { int to_node = size_t(rr_graph.edge_sink_node(RRNodeId(trace->index), iedge)); - if (to_node == next->index) { found = true; - - //Verify that the switch matches int rr_iswitch = rr_graph.edge_switch(RRNodeId(trace->index), iedge); - if (trace->iswitch != rr_iswitch) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback mismatched switch type: traceback %d rr_graph %d (RR nodes %d -> %d)\n", - trace->iswitch, rr_iswitch, - trace->index, to_node); + if (verify_switch_id) { + // Verify that the switch matches + if (trace->iswitch != rr_iswitch) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback mismatched switch type: traceback %d rr_graph %d (RR nodes %d -> %d)\n", + trace->iswitch, rr_iswitch, + trace->index, to_node); + } + } else { + // Update the switch ID in the traceback to match the RR Graph + trace->iswitch = rr_iswitch; } break; } } - if (!found) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback no RR edge between RR nodes %d -> %d\n", trace->index, next->index); } - - //Recurse - return validate_traceback_recurr(next, seen_rr_nodes); } + // Recurse + trace_stack.push(next); } - VTR_ASSERT(!next); - return true; //End of traceback + return true; } -t_trace* -alloc_trace_data() { +t_trace* alloc_trace_data() { return (t_trace*)malloc(sizeof(t_trace)); } diff --git a/vpr/src/base/old_traceback.h b/vpr/src/base/old_traceback.h index 6c9a9ed258..d0c175622f 100644 --- a/vpr/src/base/old_traceback.h +++ b/vpr/src/base/old_traceback.h @@ -46,4 +46,17 @@ class TracebackCompat { t_trace* alloc_trace_data(); void free_traceback(t_trace* trace); void print_traceback(const t_trace* trace); -bool validate_traceback(t_trace* trace); + +/** + * @brief Validate the integrity of the traceback rooted a trace: it should contain only valid rr nodes, branches in the routing tree + * should link to existing routing, and edges in the traceback should exist in the RRGraph. + * If verify_switch_id is true, this routine also checks that the switch types used in the traceback match those in the + * RRGraph. If verify_switch_id is false, the switch ids (types) are remapped to those in the RRGraph. This switch remapping is + * useful when an RRGraph with a more detailed delay model (and hence more switch types) is used with a prior routing. + * + * @param trace Pointer to the head of the routing trace of the net to validate and update. + * @param verify_switch_id Whether to verify the switch IDs in the traceback. + * + * @return true if the traceback is valid, false otherwise. + */ +bool validate_and_update_traceback(t_trace* trace, bool verify_switch_id = true); diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index f73eb799ce..57c449b6d2 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1637,6 +1637,16 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .default_value("on") .show_in(argparse::ShowIn::HELP_ONLY); + gen_grp.add_argument(args.verify_route_file_switch_id, "--verify_route_file_switch_id") + .help( + "Verify that the switch IDs in the routing file are consistent with those in the RR Graph. " + "Set this to false when switch IDs in the routing file may differ from the RR Graph. " + "For example, when analyzing different timing corners using the same netlist, placement, and routing files, " + "the RR switch IDs in the RR Graph may differ due to changes in delays. " + "In such cases, set this option to false so that the switch IDs from the RR Graph are used, and those in the routing file are ignored.\n") + .default_value("on") + .show_in(argparse::ShowIn::HELP_ONLY); + gen_grp.add_argument(args.target_device_utilization, "--target_utilization") .help( "Sets the target device utilization." diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 2cf86dbaee..f846867af7 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -73,6 +73,7 @@ struct t_options { argparse::ArgValue timing_update_type; argparse::ArgValue CreateEchoFile; argparse::ArgValue verify_file_digests; + argparse::ArgValue verify_route_file_switch_id; argparse::ArgValue device_layout; argparse::ArgValue target_device_utilization; argparse::ArgValue constant_net_method; diff --git a/vpr/src/base/read_route.cpp b/vpr/src/base/read_route.cpp index 28c15debac..fb300b6fd4 100644 --- a/vpr/src/base/read_route.cpp +++ b/vpr/src/base/read_route.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "physical_types_util.h" #include "vtr_assert.h" @@ -39,15 +40,95 @@ #include "old_traceback.h" /*************Functions local to this module*************/ -static void process_route(const Netlist<>& net_list, std::ifstream& fp, const char* filename, int& lineno, bool is_flat); -static void process_nodes(const Netlist<>& net_list, std::ifstream& fp, ClusterNetId inet, const char* filename, int& lineno); -static void process_nets(const Netlist<>& net_list, std::ifstream& fp, ClusterNetId inet, std::string name, std::vector input_tokens, const char* filename, int& lineno, bool is_flat); -static void process_global_blocks(const Netlist<>& net_list, std::ifstream& fp, ClusterNetId inet, const char* filename, int& lineno, bool is_flat); -static void format_coordinates(int& layer_num, int& x, int& y, std::string coord, ClusterNetId net, const char* filename, const int lineno); -static void format_pin_info(std::string& pb_name, std::string& port_name, int& pb_pin_num, const std::string& input); + +/** + * @brief Read the routing file and create the routing tree for each net. + * + * @param net_list The netlist to process. + * @param fp The file stream to read from. + * @param filename The name of the file to read from. + * @param lineno The line number currently being processed. + * @param verify_route_file_switch_id Whether to verify the RR switch IDs in the routing file. + * @param is_flat Whether flat-router is enabled. + */ +static void process_route(const Netlist<>& net_list, + std::ifstream& fp, + const char* filename, + int& lineno, + bool verify_route_file_switch_id, + bool is_flat); + +/** + * @brief Create the routing tree for the net at the given line number in the routing file. + * + * @param net_list The netlist to process. + * @param fp The file stream to read from. + * @param inet The net ID to process. + * @param filename The name of the file to read from. + * @param verify_route_file_switch_id Whether to verify the RR switch IDs in the routing file. + */ +static void process_nodes(const Netlist<>& net_list, + std::ifstream& fp, + ClusterNetId inet, + const char* filename, + bool verify_route_file_switch_id, + int& lineno); + +/** + * @brief Process the net at the given line number in the routing file. + * + * @param net_list The netlist to process. + * @param fp The file stream to read from. + * @param inet The net ID to process. + * @param name The name of the net. + * @param input_tokens The tokens of the net. + * @param filename The name of the file to read from. + * @param lineno The line number currently being processed. + * @param verify_route_file_switch_id Whether to verify the RR switch IDs in the routing file. + * @param is_flat Whether flat-router is enabled. + */ +static void process_nets(const Netlist<>& net_list, + std::ifstream& fp, + ClusterNetId inet, + std::string name, + std::vector input_tokens, + const char* filename, + int& lineno, + bool verify_route_file_switch_id, + bool is_flat); + +/** + * @brief This function goes through all the blocks in a global net and verify + * it with the clustered netlist and the placement + */ +static void process_global_blocks(const Netlist<>& net_list, + std::ifstream& fp, + ClusterNetId inet, + const char* filename, + int& lineno, + bool is_flat); + +static void format_coordinates(int& layer_num, + int& x, + int& y, + std::string coord, + ClusterNetId net, + const char* filename, + const int lineno); + +static void format_pin_info(std::string& pb_name, + std::string& port_name, + int& pb_pin_num, + const std::string& input); + static std::string format_name(std::string name); -static bool check_rr_graph_connectivity(RRNodeId prev_node, RRNodeId node); -void print_route(const Netlist<>& net_list, FILE* fp, bool is_flat); + +static bool check_rr_graph_connectivity(RRNodeId prev_node, + RRNodeId node); + +void print_route(const Netlist<>& net_list, + FILE* fp, + bool is_flat); /*************Global Functions****************************/ @@ -57,7 +138,10 @@ void print_route(const Netlist<>& net_list, FILE* fp, bool is_flat); * Perform a series of verification tests to ensure the netlist, * placement, and routing files match */ -bool read_route(const char* route_file, const t_router_opts& router_opts, bool verify_file_digests, bool is_flat) { +bool read_route(const char* route_file, + const t_router_opts& router_opts, + bool verify_file_digests, + bool is_flat) { auto& device_ctx = g_vpr_ctx.mutable_device(); auto& place_ctx = g_vpr_ctx.placement(); bool flat_router = router_opts.flat_routing; @@ -112,7 +196,7 @@ bool read_route(const char* route_file, const t_router_opts& router_opts, bool v } /* Read in every net */ - process_route(router_net_list, fp, route_file, lineno, is_flat); + process_route(router_net_list, fp, route_file, lineno, router_opts.verify_route_file_switch_id, is_flat); fp.close(); @@ -144,8 +228,12 @@ bool read_route(const char* route_file, const t_router_opts& router_opts, bool v return is_feasible; } -///@brief Walks through every net and add the routing appropriately -static void process_route(const Netlist<>& net_list, std::ifstream& fp, const char* filename, int& lineno, bool is_flat) { +static void process_route(const Netlist<>& net_list, + std::ifstream& fp, + const char* filename, + int& lineno, + bool verify_route_file_switch_id, + bool is_flat) { std::string input; std::vector tokens; while (std::getline(fp, input)) { @@ -158,15 +246,30 @@ static void process_route(const Netlist<>& net_list, std::ifstream& fp, const ch continue; //Skip commented lines } else if (tokens[0] == "Net") { ClusterNetId inet(atoi(tokens[1].c_str())); - process_nets(net_list, fp, inet, tokens[2], tokens, filename, lineno, is_flat); + process_nets(net_list, + fp, + inet, + tokens[2], + tokens, + filename, + lineno, + verify_route_file_switch_id, + is_flat); } } tokens.clear(); } -///@brief Check if the net is global or not, and process appropriately -static void process_nets(const Netlist<>& net_list, std::ifstream& fp, ClusterNetId inet, std::string name, std::vector input_tokens, const char* filename, int& lineno, bool is_flat) { +static void process_nets(const Netlist<>& net_list, + std::ifstream& fp, + ClusterNetId inet, + std::string name, + std::vector input_tokens, + const char* filename, + int& lineno, + bool verify_route_file_switch_id, + bool is_flat) { if (input_tokens.size() > 3 && input_tokens[3] == "global" && input_tokens[4] == "net" && input_tokens[5] == "connecting:") { /* Global net. Never routed. */ @@ -199,12 +302,22 @@ static void process_nets(const Netlist<>& net_list, std::ifstream& fp, ClusterNe name.c_str(), size_t(inet), net_list.net_name(inet).c_str()); } - process_nodes(net_list, fp, inet, filename, lineno); + process_nodes(net_list, + fp, + inet, + filename, + verify_route_file_switch_id, + lineno); } input_tokens.clear(); } -static void process_nodes(const Netlist<>& net_list, std::ifstream& fp, ClusterNetId inet, const char* filename, int& lineno) { +static void process_nodes(const Netlist<>& net_list, + std::ifstream& fp, + ClusterNetId inet, + const char* filename, + bool verify_route_file_switch_id, + int& lineno) { /* Not a global net. Goes through every node and add it into trace.head*/ auto& device_ctx = g_vpr_ctx.mutable_device(); const auto& rr_graph = device_ctx.rr_graph; @@ -396,16 +509,13 @@ static void process_nodes(const Netlist<>& net_list, std::ifstream& fp, ClusterN oldpos = fp.tellg(); } + VTR_ASSERT(validate_and_update_traceback(head_ptr, verify_route_file_switch_id)); + /* Convert to route_tree after reading */ - VTR_ASSERT(validate_traceback(head_ptr)); route_ctx.route_trees[inet] = TracebackCompat::traceback_to_route_tree(head_ptr); free_traceback(head_ptr); } -/** - * @brief This function goes through all the blocks in a global net and verify - * it with the clustered netlist and the placement - */ static void process_global_blocks(const Netlist<>& net_list, std::ifstream& fp, ClusterNetId inet, const char* filename, int& lineno, bool is_flat) { std::string block, bnum_str; int layer_num, x, y; diff --git a/vpr/src/base/read_route.h b/vpr/src/base/read_route.h index 95f9c51649..34b5d65c30 100644 --- a/vpr/src/base/read_route.h +++ b/vpr/src/base/read_route.h @@ -9,5 +9,12 @@ #include "netlist.h" #include "vpr_types.h" -bool read_route(const char* route_file, const t_router_opts& RouterOpts, bool verify_file_digests, bool is_flat); -void print_route(const Netlist<>& net_list, const char* placement_file, const char* route_file, bool is_flat); +bool read_route(const char* route_file, + const t_router_opts& RouterOpts, + bool verify_file_digests, + bool is_flat); + +void print_route(const Netlist<>& net_list, + const char* placement_file, + const char* route_file, + bool is_flat); diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index a40c759787..648d6b740e 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -1152,7 +1152,10 @@ RouteStatus vpr_load_routing(t_vpr_setup& vpr_setup, auto& filename_opts = vpr_setup.FileNameOpts; //Load the routing from a file - bool is_legal = read_route(filename_opts.RouteFile.c_str(), vpr_setup.RouterOpts, filename_opts.verify_file_digests, is_flat); + bool is_legal = read_route(filename_opts.RouteFile.c_str(), + vpr_setup.RouterOpts, + filename_opts.verify_file_digests, + is_flat); const Netlist<>& router_net_list = is_flat ? (const Netlist<>&)g_vpr_ctx.atom().netlist() : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; if (vpr_setup.Timing.timing_analysis_enabled) { //Update timing info diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index b74fef30a0..4a70cb3b4c 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1343,7 +1343,10 @@ struct t_router_opts { bool with_timing_analysis; - // Options related to rr_node reordering, for testing and possible cache optimization + /// Whether to verify the switch IDs in the route file with the RR Graph. + bool verify_route_file_switch_id; + + /// Options related to rr_node reordering, for testing and possible cache optimization e_rr_node_reorder_algorithm reorder_rr_graph_nodes_algorithm = DONT_REORDER; int reorder_rr_graph_nodes_threshold = 0; int reorder_rr_graph_nodes_seed = 1;