diff --git a/CMakeLists.txt b/CMakeLists.txt index c037f5e834b..dfc90c3e851 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ option(VPR_USE_SERVER "Specify whether vpr enables the server mode" ON) #VPR option --enable_analytic_placer is also required for Analytic Placement option(VPR_ANALYTIC_PLACE "Enable analytic placement in VPR." ON) option(VPR_ENABLE_INTERCHANGE "Enable FPGA interchange." ON) +option(VPR_ENABLE_NOC_SAT_ROUTING "Enable NoC SAT routing." OFF) option(WITH_BLIFEXPLORER "Enable build with blifexplorer" OFF) @@ -220,12 +221,12 @@ endforeach() set(FLEX_BISON_WARN_SUPPRESS_FLAGS "") set(FLEX_BISON_WARN_SUPPRESS_FLAGS_TO_CHECK "-Wno-redundant-decls" #Flex/bison generate code with redundant declarations - "-Wno-switch-default" #Flex/bison generate switch statments w/o default cases + "-Wno-switch-default" #Flex/bison generate switch statements w/o default cases "-Wno-unused-parameter" #Flex produces functions with unused params in re-entrant mode "-Wno-missing-declarations" #Flex misses some declarations in re-entrant mode "-Wimplicit-fallthrough=0" #Bison produces some cases with explicit "-Wno-sign-compare" #Flex generates code which performs some signed/unsigned comparison - "-Wno-null-dereference" #Bison produces some cases with potenetial null derefs + "-Wno-null-dereference" #Bison produces some cases with potential null derefs ) foreach(flag ${FLEX_BISON_WARN_SUPPRESS_FLAGS_TO_CHECK}) CHECK_CXX_COMPILER_FLAG(${flag} CXX_COMPILER_SUPPORTS_${flag}) diff --git a/libs/EXTERNAL/libargparse/src/argparse.hpp b/libs/EXTERNAL/libargparse/src/argparse.hpp index 28626fa2052..15e0237eab3 100644 --- a/libs/EXTERNAL/libargparse/src/argparse.hpp +++ b/libs/EXTERNAL/libargparse/src/argparse.hpp @@ -173,7 +173,7 @@ namespace argparse { //Sets the hlep text Argument& help(std::string help_str); - //Sets the defuault value + //Sets the default value Argument& default_value(const std::string& default_val); Argument& default_value(const std::vector& default_val); Argument& default_value(const std::initializer_list& default_val); diff --git a/libs/libvtrutil/src/vtr_time.cpp b/libs/libvtrutil/src/vtr_time.cpp index a557f186735..5c80ff3126c 100644 --- a/libs/libvtrutil/src/vtr_time.cpp +++ b/libs/libvtrutil/src/vtr_time.cpp @@ -3,6 +3,8 @@ #include "vtr_log.h" #include "vtr_rusage.h" +#include + namespace vtr { int f_timer_depth = 0; @@ -30,7 +32,7 @@ float Timer::delta_max_rss_mib() const { ///@brief Constructor ScopedActionTimer::ScopedActionTimer(std::string action_str) - : action_(action_str) + : action_(std::move(action_str)) , depth_(f_timer_depth++) { } @@ -69,7 +71,7 @@ int ScopedActionTimer::depth() const { ///@brief Constructor ScopedFinishTimer::ScopedFinishTimer(std::string action_str) - : ScopedActionTimer(action_str) { + : ScopedActionTimer(std::move(action_str)) { } ///@brief Destructor @@ -83,7 +85,7 @@ ScopedFinishTimer::~ScopedFinishTimer() { ///@brief Constructor ScopedStartFinishTimer::ScopedStartFinishTimer(std::string action_str) - : ScopedActionTimer(action_str) { + : ScopedActionTimer(std::move(action_str)) { vtr::printf_info("%s%s\n", pad().c_str(), action().c_str()); } diff --git a/libs/libvtrutil/src/vtr_time.h b/libs/libvtrutil/src/vtr_time.h index 2a4d4ec8af0..4e389ef5026 100644 --- a/libs/libvtrutil/src/vtr_time.h +++ b/libs/libvtrutil/src/vtr_time.h @@ -39,7 +39,7 @@ class Timer { ///@brief Scoped time class which prints the time elapsed for the specifid action class ScopedActionTimer : public Timer { public: - ScopedActionTimer(const std::string action); + ScopedActionTimer(std::string action); ~ScopedActionTimer(); void quiet(bool value); @@ -71,7 +71,7 @@ class ScopedActionTimer : public Timer { */ class ScopedFinishTimer : public ScopedActionTimer { public: - ScopedFinishTimer(const std::string action); + ScopedFinishTimer(std::string action); ~ScopedFinishTimer(); }; @@ -91,7 +91,7 @@ class ScopedFinishTimer : public ScopedActionTimer { */ class ScopedStartFinishTimer : public ScopedActionTimer { public: - ScopedStartFinishTimer(const std::string action); + ScopedStartFinishTimer(std::string action); ~ScopedStartFinishTimer(); }; } // namespace vtr diff --git a/vpr/CMakeLists.txt b/vpr/CMakeLists.txt index da92de9bc48..226822084f3 100644 --- a/vpr/CMakeLists.txt +++ b/vpr/CMakeLists.txt @@ -92,6 +92,20 @@ if(${VPR_ANALYTIC_PLACE}) endif(TARGET Eigen3::Eigen) endif() +if (${VPR_ENABLE_NOC_SAT_ROUTING}) + message(STATUS "VPR NoC SAT Routing: Requested") + find_package(ortools CONFIG REQUIRED) + if (TARGET ortools::ortools) + message(STATUS "VPR NoC SAT Routing dependency (or-tools): Found") + message(STATUS "VPR NoC SAT Routing: Enabled") + target_link_libraries(libvpr ortools::ortools) + target_compile_definitions(libvpr PUBLIC -DENABLE_NOC_SAT_ROUTING) + else () + message(STATUS "VPR NoC SAT Routing dependency (or-tools): Not Found (You may need to set CMAKE_PREFIX_PATH in order for CMake to find your OR-Tools installation)") + message(STATUS "VPR NoC SAT Routing: Disabled") + endif (TARGET ortools::ortools) +endif () + set_target_properties(libvpr PROPERTIES PREFIX "") #Avoid extra 'lib' prefix #Specify link-time dependencies @@ -161,7 +175,7 @@ add_executable(vpr ${EXEC_SOURCES}) target_link_libraries(vpr libvpr) -#Supress IPO link warnings if IPO is enabled +#Suppress IPO link warnings if IPO is enabled get_target_property(VPR_USES_IPO vpr INTERPROCEDURAL_OPTIMIZATION) if (VPR_USES_IPO) set_property(TARGET vpr APPEND PROPERTY LINK_FLAGS ${IPO_LINK_WARN_SUPRESS_FLAGS}) @@ -292,7 +306,7 @@ target_link_libraries(test_vpr Catch2::Catch2WithMain libvpr) -#Supress IPO link warnings if IPO is enabled +#Suppress IPO link warnings if IPO is enabled get_target_property(TEST_VPR_USES_IPO vpr INTERPROCEDURAL_OPTIMIZATION) if (TEST_VPR_USES_IPO) set_property(TARGET test_vpr APPEND PROPERTY LINK_FLAGS ${IPO_LINK_WARN_SUPRESS_FLAGS}) diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index af960b93806..af73c8816a1 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -744,9 +744,20 @@ static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts) { NocOpts->noc_latency_constraints_weighting = Options.noc_latency_constraints_weighting; NocOpts->noc_latency_weighting = Options.noc_latency_weighting; NocOpts->noc_congestion_weighting = Options.noc_congestion_weighting; - NocOpts->noc_swap_percentage = Options.noc_swap_percentage; NocOpts->noc_centroid_weight = Options.noc_centroid_weight; + NocOpts->noc_swap_percentage = Options.noc_swap_percentage; + NocOpts->noc_sat_routing_bandwidth_resolution = Options.noc_sat_routing_bandwidth_resolution; + NocOpts->noc_sat_routing_latency_overrun_weighting = Options.noc_sat_routing_latency_overrun_weighting_factor; + NocOpts->noc_sat_routing_congestion_weighting = Options.noc_sat_routing_congestion_weighting_factor; + if (Options.noc_sat_routing_num_workers.provenance() == argparse::Provenance::SPECIFIED) { + NocOpts->noc_sat_routing_num_workers = Options.noc_sat_routing_num_workers; + } else { + NocOpts->noc_sat_routing_num_workers = (int)Options.num_workers; + } + NocOpts->noc_sat_routing_log_search_progress = Options.noc_sat_routing_log_search_progress; NocOpts->noc_placement_file_name = Options.noc_placement_file_name; + + } static void SetupServerOpts(const t_options& Options, t_server_opts* ServerOpts) { diff --git a/vpr/src/base/ShowSetup.cpp b/vpr/src/base/ShowSetup.cpp index 231c4a4547f..024d3c53a93 100644 --- a/vpr/src/base/ShowSetup.cpp +++ b/vpr/src/base/ShowSetup.cpp @@ -802,6 +802,10 @@ static void ShowNocOpts(const t_noc_opts& NocOpts) { VTR_LOG("NocOpts.noc_latency_weighting: %f\n", NocOpts.noc_latency_weighting); VTR_LOG("NocOpts.noc_congestion_weighting: %f\n", NocOpts.noc_congestion_weighting); VTR_LOG("NocOpts.noc_swap_percentage: %d%%\n", NocOpts.noc_swap_percentage); + VTR_LOG("NocOpts.noc_sat_routing_bandwidth_resolution: %d\n", NocOpts.noc_sat_routing_bandwidth_resolution); + VTR_LOG("NocOpts.noc_sat_routing_latency_overrun_weighting: %d\n", NocOpts.noc_sat_routing_latency_overrun_weighting); + VTR_LOG("NocOpts.noc_sat_routing_congestion_weighting: %d\n", NocOpts.noc_sat_routing_congestion_weighting); + VTR_LOG("NocOpts.noc_sat_routing_num_workers: %d\n", NocOpts.noc_sat_routing_num_workers); VTR_LOG("NocOpts.noc_routing_algorithm: %s\n", NocOpts.noc_placement_file_name.c_str()); VTR_LOG("\n"); } \ No newline at end of file diff --git a/vpr/src/base/echo_files.cpp b/vpr/src/base/echo_files.cpp index 9228aa13ae4..9627ba84f96 100644 --- a/vpr/src/base/echo_files.cpp +++ b/vpr/src/base/echo_files.cpp @@ -32,8 +32,7 @@ void setEchoEnabled(bool echo_enabled) { } void setAllEchoFileEnabled(bool value) { - int i; - for (i = 0; i < (int)E_ECHO_END_TOKEN; i++) { + for (int i = 0; i < (int)E_ECHO_END_TOKEN; i++) { echoFileEnabled[i] = value; } } @@ -67,7 +66,7 @@ void alloc_and_load_echo_file_info() { echoFileNames = new char*[(int)E_ECHO_END_TOKEN]; for (auto i = 0; i < (int)E_ECHO_END_TOKEN; i++) { echoFileEnabled[i] = false; - echoFileNames[i] = NULL; + echoFileNames[i] = nullptr; } setAllEchoFileEnabled(getEchoEnabled()); @@ -136,9 +135,8 @@ void alloc_and_load_echo_file_info() { } void free_echo_file_info() { - int i; if (echoFileEnabled != nullptr) { - for (i = 0; i < (int)E_ECHO_END_TOKEN; i++) { + for (int i = 0; i < (int)E_ECHO_END_TOKEN; i++) { if (echoFileNames[i] != nullptr) { delete[] echoFileNames[i]; } @@ -165,7 +163,7 @@ char* getOutputFileName(enum e_output_files ename) { return outputFileNames[(int)ename]; } -void alloc_and_load_output_file_names(const std::string default_name) { +void alloc_and_load_output_file_names(const std::string& default_name) { std::string name; if (outputFileNames == nullptr) { @@ -185,9 +183,8 @@ void alloc_and_load_output_file_names(const std::string default_name) { } void free_output_file_names() { - int i; if (outputFileNames != nullptr) { - for (i = 0; i < (int)E_FILE_END_TOKEN; i++) { + for (int i = 0; i < (int)E_FILE_END_TOKEN; i++) { if (outputFileNames[i] != nullptr) { delete[] outputFileNames[i]; outputFileNames[i] = nullptr; diff --git a/vpr/src/base/echo_files.h b/vpr/src/base/echo_files.h index 548d6a9fe49..ea02d58bc92 100644 --- a/vpr/src/base/echo_files.h +++ b/vpr/src/base/echo_files.h @@ -92,7 +92,7 @@ void free_echo_file_info(); void setOutputFileName(enum e_output_files ename, const char* name, const char* default_name); char* getOutputFileName(enum e_output_files ename); -void alloc_and_load_output_file_names(const std::string default_name); +void alloc_and_load_output_file_names(const std::string& default_name); void free_output_file_names(); #endif diff --git a/vpr/src/base/place_and_route.cpp b/vpr/src/base/place_and_route.cpp index 186193744ce..2701b46cc09 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -65,8 +65,8 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, t_det_routing_arch* det_routing_arch, std::vector& segment_inf, NetPinsMatrix& net_delay, - std::shared_ptr timing_info, - std::shared_ptr delay_calc, + const std::shared_ptr& timing_info, + const std::shared_ptr& delay_calc, bool is_flat) { vtr::vector> best_routing; /* Saves the best routing found so far. */ int current, low, high, final; @@ -101,7 +101,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } - VTR_ASSERT(net_delay.size()); + VTR_ASSERT(!net_delay.empty()); if (det_routing_arch->directionality == BI_DIRECTIONAL) udsd_multiplier = 1; @@ -298,7 +298,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, current = current + current % udsd_multiplier; } - /* The binary search above occassionally does not find the minimum * + /* The binary search above occasionally does not find the minimum * * routeable channel width. Sometimes a circuit that will not route * * in 19 channels will route in 18, due to router flukiness. If * * verify_binary_search is set, the code below will ensure that FPGAs * diff --git a/vpr/src/base/place_and_route.h b/vpr/src/base/place_and_route.h index 7a59fa02795..b4735ed8af4 100644 --- a/vpr/src/base/place_and_route.h +++ b/vpr/src/base/place_and_route.h @@ -36,8 +36,8 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, t_det_routing_arch* det_routing_arch, std::vector& segment_inf, NetPinsMatrix& net_delay, - std::shared_ptr timing_info, - std::shared_ptr delay_calc, + const std::shared_ptr& timing_info, + const std::shared_ptr& delay_calc, bool is_flat); t_chan_width init_chan(int cfactor, diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 7538891b5c0..b4f33cf8025 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1516,7 +1516,7 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio gen_grp.add_argument(args.strict_checks, "--strict_checks") .help( "Controls whether VPR enforces some consistency checks strictly (as errors) or treats them as warnings." - " Usually these checks indicate an issue with either the targetted architecture, or consistency issues" + " Usually these checks indicate an issue with either the targeted architecture, or consistency issues" " with VPR's internal data structures/algorithms (possibly harming optimization quality)." " In specific circumstances on specific architectures these checks may be too restrictive and can be turned off." " However exercise extreme caution when turning this option off -- be sure you completely understand why the issue" @@ -2788,8 +2788,8 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio noc_grp.add_argument(args.noc, "--noc") .help( - "Enables a NoC-driven placer that optimizes the placement of routers on the NoC." - "Also enables an option in the graphical display that can be used to display the NoC on the FPGA." + "Enables a NoC-driven placer that optimizes the placement of routers on the NoC. " + "Also enables an option in the graphical display that can be used to display the NoC on the FPGA. " "This should be on only when the FPGA device contains a NoC and the provided netlist connects to the NoC.") .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); @@ -2805,7 +2805,7 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .help( "Controls the algorithm used by the NoC to route packets.\n" "* xy_routing: Uses the direction oriented routing algorithm. This is recommended to be used with mesh NoC topologies.\n" - "* bfs_routing: Uses the breadth first search algorithm. The objective is to find a route that uses a minimum number of links." + "* bfs_routing: Uses the breadth first search algorithm. The objective is to find a route that uses a minimum number of links. " " This algorithm is not guaranteed to generate deadlock-free traffic flow routes, but can be used with any NoC topology\n" "* west_first_routing: Uses the west-first routing algorithm. This is recommended to be used with mesh NoC topologies.\n" "* north_last_routing: Uses the north-last routing algorithm. This is recommended to be used with mesh NoC topologies.\n" @@ -2818,9 +2818,9 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio noc_grp.add_argument(args.noc_placement_weighting, "--noc_placement_weighting") .help( - "Controls the importance of the NoC placement parameters relative to timing and wirelength of the design." - "This value can be >=0, where 0 would mean the placement is based solely on timing and wirelength." - "A value of 1 would mean noc placement is considered equal to timing and wirelength" + "Controls the importance of the NoC placement parameters relative to timing and wirelength of the design. " + "This value can be >=0, where 0 would mean the placement is based solely on timing and wirelength. " + "A value of 1 would mean noc placement is considered equal to timing and wirelength " "A value greater than 1 would mean the placement is increasingly dominated by NoC parameters.") .default_value("5.0") .show_in(argparse::ShowIn::HELP_ONLY); @@ -2830,7 +2830,7 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio "Controls the importance of minimizing the NoC aggregate bandwidth.\n" "This value can be >=0, where 0 would mean the aggregate bandwidth has no relevance to placement.\n" "Other positive numbers specify the importance of minimizing the NoC aggregate bandwidth to other NoC-related cost terms.\n" - "Weighting factors for NoC-related cost terms are normalized internally. Therefore, their absolute values are not important, and" + "Weighting factors for NoC-related cost terms are normalized internally. Therefore, their absolute values are not important, and " "only their relative ratios determine the importance of each cost term.") .default_value("0.38") .show_in(argparse::ShowIn::HELP_ONLY); @@ -2840,7 +2840,7 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio "Controls the importance of meeting all the NoC traffic flow latency constraints.\n" "This value can be >=0, where 0 would mean the latency constraints have no relevance to placement.\n" "Other positive numbers specify the importance of meeting latency constraints to other NoC-related cost terms.\n" - "Weighting factors for NoC-related cost terms are normalized internally. Therefore, their absolute values are not important, and" + "Weighting factors for NoC-related cost terms are normalized internally. Therefore, their absolute values are not important, and " "only their relative ratios determine the importance of each cost term.") .default_value("0.6") .show_in(argparse::ShowIn::HELP_ONLY); @@ -2850,7 +2850,7 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio "Controls the importance of reducing the latencies of the NoC traffic flows.\n" "This value can be >=0, where 0 would mean the latencies have no relevance to placement.\n" "Other positive numbers specify the importance of minimizing aggregate latency to other NoC-related cost terms.\n" - "Weighting factors for NoC-related cost terms are normalized internally. Therefore, their absolute values are not important, and" + "Weighting factors for NoC-related cost terms are normalized internally. Therefore, their absolute values are not important, and " "only their relative ratios determine the importance of each cost term.") .default_value("0.02") .show_in(argparse::ShowIn::HELP_ONLY); @@ -2860,25 +2860,58 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio "Controls the importance of reducing the congestion of the NoC links.\n" "This value can be >=0, where 0 would mean the congestion has no relevance to placement.\n" "Other positive numbers specify the importance of minimizing congestion to other NoC-related cost terms.\n" - "Weighting factors for NoC-related cost terms are normalized internally. Therefore, their absolute values are not important, and" + "Weighting factors for NoC-related cost terms are normalized internally. Therefore, their absolute values are not important, and " "only their relative ratios determine the importance of each cost term.") .default_value("0.25") .show_in(argparse::ShowIn::HELP_ONLY); - noc_grp.add_argument(args.noc_swap_percentage, "--noc_swap_percentage") + noc_grp.add_argument(args.noc_centroid_weight, "--noc_centroid_weight") .help( "Sets the minimum fraction of swaps attempted by the placer that are NoC blocks." "This value is an integer ranging from 0-100. 0 means NoC blocks will be moved at the same rate as other blocks. 100 means all swaps attempted by the placer are NoC router blocks.") .default_value("0") .show_in(argparse::ShowIn::HELP_ONLY); - - noc_grp.add_argument(args.noc_centroid_weight, "--noc_centroid_weight") + + noc_grp.add_argument(args.noc_swap_percentage, "--noc_swap_percentage") .help( - "Sets the minimum fraction of swaps attempted by the placer that are NoC blocks." + "Sets the minimum fraction of swaps attempted by the placer that are NoC blocks. " "This value is an integer ranging from 0-100. 0 means NoC blocks will be moved at the same rate as other blocks. 100 means all swaps attempted by the placer are NoC router blocks.") .default_value("0") .show_in(argparse::ShowIn::HELP_ONLY); + noc_grp.add_argument(args.noc_sat_routing_bandwidth_resolution, "--noc_sat_routing_bandwidth_resolution") + .help( + "Specifies the resolution by which traffic flow bandwidths are converted into integers in SAT routing algorithm.\n" + "The higher this number is, the more accurate the congestion estimation and aggregate bandwidth minimization is.\n" + "Higher resolution for bandwidth conversion increases the number of variables in the SAT formulation.") + .default_value("128") + .show_in(argparse::ShowIn::HELP_ONLY); + + noc_grp.add_argument(args.noc_sat_routing_latency_overrun_weighting_factor, "--noc_sat_routing_latency_overrun_weighting_factor") + .help( + "Controls the importance of reducing traffic flow latency overrun in SAT routing.") + .default_value("1024") + .show_in(argparse::ShowIn::HELP_ONLY); + + noc_grp.add_argument(args.noc_sat_routing_congestion_weighting_factor, "--noc_sat_routing_congestion_weighting_factor") + .help( + "Controls the importance of reducing the number of congested NoC links in SAT routing.") + .default_value("16384") + .show_in(argparse::ShowIn::HELP_ONLY); + + noc_grp.add_argument(args.noc_sat_routing_num_workers, "--noc_sat_routing_num_workers") + .help( + "The maximum number of parallel threads that the SAT solver can use to explore the solution space.\n" + "If not explicitly specified by the user, VPR will set the number parallel SAT solver workers to the value " + "specified by -j command line option.") + .show_in(argparse::ShowIn::HELP_ONLY); + + noc_grp.add_argument(args.noc_sat_routing_log_search_progress, "--noc_sat_routing_log_search_progress") + .help( + "Print the detailed log of the SAT solver's search progress.") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + noc_grp.add_argument(args.noc_placement_file_name, "--noc_placement_file_name") .help( "Name of the output file that contains the NoC placement information." diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 30211a904bb..7de7f2535b2 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -160,8 +160,13 @@ struct t_options { argparse::ArgValue noc_latency_constraints_weighting; argparse::ArgValue noc_latency_weighting; argparse::ArgValue noc_congestion_weighting; - argparse::ArgValue noc_swap_percentage; argparse::ArgValue noc_centroid_weight; + argparse::ArgValue noc_swap_percentage; + argparse::ArgValue noc_sat_routing_bandwidth_resolution; + argparse::ArgValue noc_sat_routing_latency_overrun_weighting_factor; + argparse::ArgValue noc_sat_routing_congestion_weighting_factor; + argparse::ArgValue noc_sat_routing_num_workers; + argparse::ArgValue noc_sat_routing_log_search_progress; argparse::ArgValue noc_placement_file_name; /* Timing-driven placement options only */ diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index e23e98083cf..3467fc6234e 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -1468,13 +1468,13 @@ void vpr_analysis(const Netlist<>& net_list, //Write the post-synthesis netlist if (vpr_setup.AnalysisOpts.gen_post_synthesis_netlist) { - netlist_writer(atom_ctx.nlist.netlist_name().c_str(), analysis_delay_calc, + netlist_writer(atom_ctx.nlist.netlist_name(), analysis_delay_calc, vpr_setup.AnalysisOpts); } //Write the post-implementation merged netlist if (vpr_setup.AnalysisOpts.gen_post_implementation_merged_netlist) { - merged_netlist_writer(atom_ctx.nlist.netlist_name().c_str(), analysis_delay_calc, vpr_setup.AnalysisOpts); + merged_netlist_writer(atom_ctx.nlist.netlist_name(), analysis_delay_calc, vpr_setup.AnalysisOpts); } //Do power analysis diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index f4f7dba2efa..7e231136aad 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1494,17 +1494,22 @@ struct t_analysis_opts { // used to store NoC specific options, when supplied as an input by the user struct t_noc_opts { - bool noc; ///get_router_grid_position_x(), router_list.begin()->get_router_grid_position_y(), @@ -239,7 +239,7 @@ void draw_noc_links(ezgl::renderer* g, t_logical_block_type_ptr noc_router_logic NocLinkType link_type; // get half the width and height of the noc connection marker - // we will shift the links based on this parameters since the links will be drawn at the boundaries of connection marker instead of the center + // we will shift the links based on these parameters since the links will be drawn at the boundaries of connection marker instead of the center double noc_connection_marker_quarter_width = (noc_connection_marker_bbox.center().x - noc_connection_marker_bbox.bottom_left().x) / 2; double noc_connection_marker_quarter_height = (noc_connection_marker_bbox.center().y - noc_connection_marker_bbox.bottom_left().y) / 2; @@ -293,7 +293,7 @@ void determine_direction_to_shift_noc_links(vtr::vector int number_of_links = list_of_noc_link_shift_directions.size(); - // store the parallel link of wach link we process + // store the parallel link of each link we process NocLinkId parallel_link; // go through all the noc links and assign how the link should be shifted @@ -301,7 +301,7 @@ void determine_direction_to_shift_noc_links(vtr::vector // convert the link to a link id NocLinkId link_id(link); - // only assign a shift direction if we havent already + // only assign a shift direction if we haven't already if (list_of_noc_link_shift_directions[link_id] == NocLinkShift::NO_SHIFT) { // the current link will always have a TOP_shift list_of_noc_link_shift_directions[link_id] = NocLinkShift::TOP_SHIFT; @@ -310,8 +310,8 @@ void determine_direction_to_shift_noc_links(vtr::vector parallel_link = noc_ctx.noc_model.get_parallel_link(link_id); // check first if a legal link id was found - if (parallel_link == INVALID_LINK_ID) { - // if we are here, then a parallel link wasnt found, so that means there is only a single link and there is no need to perform any shifting on the single link + if (parallel_link == NocLinkId::INVALID()) { + // if we are here, then a parallel link wasn't found, so that means there is only a single link and there is no need to perform any shifting on the single link list_of_noc_link_shift_directions[link_id] = NocLinkShift::NO_SHIFT; continue; @@ -334,13 +334,13 @@ NocLinkType determine_noc_link_type(ezgl::point2d link_start_point, ezgl::point2 double x_coord_horizontal_start = link_start_point.x; double y_coord_horizontal_start = link_start_point.y; - double x_coord_horziontal_end = x_coord_horizontal_start + HORIZONTAL_LINE_LENGTH; + double x_coord_horizontal_end = x_coord_horizontal_start + HORIZONTAL_LINE_LENGTH; double y_coord_horizontal_end = link_start_point.y; // stores the link type NocLinkType result = NocLinkType::INVALID_TYPE; - // we can check quickly if the link is vertical or horizontal without calculating the dot product. If it is vertical or horizontal then we just return. Otherwise we have to calculate it. + // we can check quickly if the link is vertical or horizontal without calculating the dot product. If it is vertical or horizontal then we just return. Otherwise, we have to calculate it. // check if the link is vertical by determining if there is any horizontal change if (vtr::isclose(x_coord_end - x_coord_start, 0.0)) { @@ -349,7 +349,7 @@ NocLinkType determine_noc_link_type(ezgl::point2d link_start_point, ezgl::point2 return result; } - // check if the link is horizontal by determinig if there is any vertical shift + // check if the link is horizontal by determining if there is any vertical shift if (vtr::isclose(y_coord_end - y_coord_start, 0.0)) { result = NocLinkType::HORIZONTAL; @@ -361,11 +361,11 @@ NocLinkType determine_noc_link_type(ezgl::point2d link_start_point, ezgl::point2 // get the magnitude of the link double link_magnitude = sqrt(pow(x_coord_end - x_coord_start, 2.0) + pow(y_coord_end - y_coord_start, 2.0)); // get the dot product of the two connecting line - double dot_product_of_link_and_horizontal_line = (x_coord_end - x_coord_start) * (x_coord_horziontal_end - x_coord_horizontal_start) + (y_coord_end - y_coord_start) * (y_coord_horizontal_end - y_coord_horizontal_start); + double dot_product_of_link_and_horizontal_line = (x_coord_end - x_coord_start) * (x_coord_horizontal_end - x_coord_horizontal_start) + (y_coord_end - y_coord_start) * (y_coord_horizontal_end - y_coord_horizontal_start); // calculate the angle double angle = acos(dot_product_of_link_and_horizontal_line / (link_magnitude * HORIZONTAL_LINE_LENGTH)); - // the angle is in the first or fourth quandrant of the unit circle + // the angle is in the first or fourth quadrant of the unit circle if ((angle > 0) && (angle < (PI_RADIAN / 2))) { // if the link is a positive sloped line, then its end point must be higher than its start point (must be higher than the connected horizontal line) if (y_coord_end > y_coord_horizontal_end) { @@ -375,7 +375,7 @@ NocLinkType determine_noc_link_type(ezgl::point2d link_start_point, ezgl::point2 result = NocLinkType::NEGATIVE_SLOPE; } - } else { // the case where the angle is in the 3rd and 4th quandrant of the unit cirle + } else { // the case where the angle is in the 3rd and 4th quadrant of the unit circle // if the link is a positive sloped line, then its end point must be lower than its start point (must be lower than the connected horizontal line) if (y_coord_end < y_coord_horizontal_end) { @@ -415,7 +415,7 @@ void shift_noc_link(noc_link_draw_coords& link_coords, NocLinkShift link_shift_d link_coords.start.x += noc_connection_marker_quarter_width; link_coords.end.x += noc_connection_marker_quarter_width; } - // dont change anything if we arent shifting at all + // don't change anything if we aren't shifting at all break; case NocLinkType::HORIZONTAL: if (link_shift_direction == NocLinkShift::TOP_SHIFT) { @@ -427,7 +427,7 @@ void shift_noc_link(noc_link_draw_coords& link_coords, NocLinkShift link_shift_d link_coords.start.y -= noc_connection_marker_quarter_height; link_coords.end.y -= noc_connection_marker_quarter_height; } - // dont change anything if we arent shifting at all + // don't change anything if we aren't shifting at all break; case NocLinkType::POSITVE_SLOPE: if (link_shift_direction == NocLinkShift::TOP_SHIFT) { @@ -445,7 +445,7 @@ void shift_noc_link(noc_link_draw_coords& link_coords, NocLinkShift link_shift_d link_coords.start.y -= noc_connection_marker_quarter_height; link_coords.end.y -= noc_connection_marker_quarter_height; } - // dont change anything if we arent shifting at all + // don't change anything if we aren't shifting at all break; case NocLinkType::NEGATIVE_SLOPE: if (link_shift_direction == NocLinkShift::TOP_SHIFT) { diff --git a/vpr/src/noc/bfs_routing.cpp b/vpr/src/noc/bfs_routing.cpp index f25f755c1f5..2fdc2650a7d 100644 --- a/vpr/src/noc/bfs_routing.cpp +++ b/vpr/src/noc/bfs_routing.cpp @@ -58,7 +58,7 @@ void BFSRouting::route_flow(NocRouterId src_router_id, routers_to_process.pop(); // get the links leaving the router currently being processed(these represent possible paths to explore in the NoC) - const std::vector& outgoing_links = noc_model.get_noc_router_connections(processing_router); + const std::vector& outgoing_links = noc_model.get_noc_router_outgoing_links(processing_router); // go through the outgoing links of the current router and process the sink routers connected to these links for (auto link : outgoing_links) { diff --git a/vpr/src/noc/channel_dependency_graph.cpp b/vpr/src/noc/channel_dependency_graph.cpp index abc38cffe6c..31bd4052485 100644 --- a/vpr/src/noc/channel_dependency_graph.cpp +++ b/vpr/src/noc/channel_dependency_graph.cpp @@ -4,8 +4,38 @@ #include -ChannelDependencyGraph::ChannelDependencyGraph(size_t n_links, - const vtr::vector>& traffic_flow_routes) { +ChannelDependencyGraph::ChannelDependencyGraph(const NocStorage& noc_model, + const NocTrafficFlows& traffic_flow_storage, + const vtr::vector>& traffic_flow_routes, + const vtr::vector_map& block_locs) { + VTR_ASSERT((size_t)traffic_flow_storage.get_number_of_traffic_flows() == traffic_flow_routes.size()); + + for (auto traffic_flow_id : traffic_flow_storage.get_all_traffic_flow_id()) { + const auto& traffic_flow = traffic_flow_storage.get_single_noc_traffic_flow(traffic_flow_id); + const auto& traffic_flow_route = traffic_flow_routes[traffic_flow_id]; + + // get the source and destination logical router blocks in the current traffic flow + ClusterBlockId logical_source_router_block_id = traffic_flow.source_router_cluster_id; + ClusterBlockId logical_sink_router_block_id = traffic_flow.sink_router_cluster_id; + + // get the ids of the hard router blocks where the logical router cluster blocks have been placed + NocRouterId src_router_id = noc_model.get_router_at_grid_location(block_locs[logical_source_router_block_id].loc); + NocRouterId dst_router_id = noc_model.get_router_at_grid_location(block_locs[logical_sink_router_block_id].loc); + + const NocLink& first_link = noc_model.get_single_noc_link(traffic_flow_route.front()); + VTR_ASSERT(first_link.get_source_router() == src_router_id); + const NocLink& last_link = noc_model.get_single_noc_link(traffic_flow_route.back()); + VTR_ASSERT(last_link.get_sink_router() == dst_router_id); + + for (size_t i = 0; i < traffic_flow_route.size() - 1; i++) { + const NocLink& noc_link1 = noc_model.get_single_noc_link(traffic_flow_route[i]); + const NocLink& noc_link2 = noc_model.get_single_noc_link(traffic_flow_route[i + 1]); + VTR_ASSERT(noc_link1.get_sink_router() == noc_link2.get_source_router()); + } + } + + const size_t n_links = noc_model.get_noc_links().size(); + adjacency_list_.clear(); // In channel dependency graph, vertices represent NoC links. // reserve enough space so that all vertices can store their outgoing neighbors diff --git a/vpr/src/noc/channel_dependency_graph.h b/vpr/src/noc/channel_dependency_graph.h index 4b19607b40a..54bcb26ab3a 100644 --- a/vpr/src/noc/channel_dependency_graph.h +++ b/vpr/src/noc/channel_dependency_graph.h @@ -24,6 +24,8 @@ #include "vtr_vector.h" #include "noc_data_types.h" +#include "noc_storage.h" +#include "noc_traffic_flows.h" class ChannelDependencyGraph { public: @@ -36,8 +38,10 @@ class ChannelDependencyGraph { * @param traffic_flow_routes The route of each traffic flow generated * by a routing algorithm. */ - ChannelDependencyGraph(size_t n_links, - const vtr::vector>& traffic_flow_routes); + ChannelDependencyGraph(const NocStorage& noc_model, + const NocTrafficFlows& traffic_flow_storage, + const vtr::vector>& traffic_flow_routes, + const vtr::vector_map& block_locs); /** * @brief Checks whether CDG has any cycles. A cycle implies that diff --git a/vpr/src/noc/negative_first_routing.cpp b/vpr/src/noc/negative_first_routing.cpp index 23cff958638..eaec531a554 100644 --- a/vpr/src/noc/negative_first_routing.cpp +++ b/vpr/src/noc/negative_first_routing.cpp @@ -107,4 +107,40 @@ TurnModelRouting::Direction NegativeFirstRouting::select_next_direction(const st } return selected_direction; -} \ No newline at end of file +} + +bool NegativeFirstRouting::is_turn_legal(const std::array, 3>& noc_routers) const { + const int x1 = noc_routers[0].get().get_router_grid_position_x(); + const int y1 = noc_routers[0].get().get_router_grid_position_y(); + + const int x2 = noc_routers[1].get().get_router_grid_position_x(); + const int y2 = noc_routers[1].get().get_router_grid_position_y(); + + const int x3 = noc_routers[2].get().get_router_grid_position_x(); + const int y3 = noc_routers[2].get().get_router_grid_position_y(); + + // check if the given routers can be traversed one after another + VTR_ASSERT(x2 == x1 || y2 == y1); + VTR_ASSERT(x3 == x2 || y3 == y2); + + // going back to the first router is not allowed + if (x1 == x3 && y1 == y3) { + return false; + } + + /* In negative-first routing algorithm, a traffic flow + * can't take a downward turn if it is travelling toward right direction. + */ + if (x2 > x1 && y3 < y2) { + return false; + } + + /* In negative-first routing algorithm, a traffic flow + * can't take a left turn if it is travelling upwards. + */ + if (y2 > y1 && x3 < x2) { + return false; + } + + return true; +} diff --git a/vpr/src/noc/negative_first_routing.h b/vpr/src/noc/negative_first_routing.h index f4a7cb6e9a7..3a465e3cc28 100644 --- a/vpr/src/noc/negative_first_routing.h +++ b/vpr/src/noc/negative_first_routing.h @@ -65,6 +65,8 @@ class NegativeFirstRouting : public TurnModelRouting { NocRouterId curr_router_id, NocTrafficFlowId traffic_flow_id, const NocStorage& noc_model) override; + + bool is_turn_legal(const std::array, 3>& noc_routers) const override; }; #endif //VTR_NEGATIVE_FIRST_ROUTING_H diff --git a/vpr/src/noc/noc_storage.cpp b/vpr/src/noc/noc_storage.cpp index 2f4f1ac7a45..f3fe939eecc 100644 --- a/vpr/src/noc/noc_storage.cpp +++ b/vpr/src/noc/noc_storage.cpp @@ -1,21 +1,29 @@ #include "noc_storage.h" +#include "vtr_assert.h" +#include "vpr_error.h" + + +#include NocStorage::NocStorage() { clear_noc(); } // getters for the NoC +const std::vector& NocStorage::get_noc_router_outgoing_links(NocRouterId id) const { + return router_outgoing_links_list[id]; +} -const std::vector& NocStorage::get_noc_router_connections(NocRouterId id) const { - return router_link_list[id]; +const std::vector& NocStorage::get_noc_router_incoming_links(NocRouterId id) const { + return router_incoming_links_list[id]; } const vtr::vector& NocStorage::get_noc_routers() const { return router_storage; } -int NocStorage::get_number_of_noc_routers(void) const { +int NocStorage::get_number_of_noc_routers() const { return router_storage.size(); } @@ -124,7 +132,8 @@ void NocStorage::add_link(NocRouterId source, NocRouterId sink, double bandwidth link_storage.emplace_back(added_link_id, source, sink, bandwidth, latency); - router_link_list[source].push_back(added_link_id); + router_outgoing_links_list[source].push_back(added_link_id); + router_incoming_links_list[sink].push_back(added_link_id); } void NocStorage::set_noc_link_bandwidth(double link_bandwidth) { @@ -158,38 +167,49 @@ bool NocStorage::remove_link(NocRouterId src_router_id, NocRouterId sink_router_ // check if the src router for the link to remove exists (within the id ranges). Otherwise, there is no point looking for the link if ((size_t)src_router_id < router_storage.size()) { // get all the outgoing links of the provided source router - std::vector* source_router_outgoing_links = &router_link_list[src_router_id]; + std::vector& source_router_outgoing_links = router_outgoing_links_list[src_router_id]; + std::vector& sink_router_incoming_links = router_incoming_links_list[sink_router_id]; - // keeps track of the position of each outgoing link for the provided src router. When the id of the link to remove is found, this index can be used to remove it from the outgoing link vector. - int outgoing_link_index = 0; + const NocLinkId link_to_be_removed_id = get_single_noc_link_id(src_router_id, sink_router_id); + link_removed_status = (link_to_be_removed_id != NocLinkId::INVALID()); - // go through each outgoing link of the source router and see if there is a link that also has the corresponding sink router. - // Save this link index and remove it - for (auto outgoing_link_id = source_router_outgoing_links->begin(); outgoing_link_id != source_router_outgoing_links->end(); outgoing_link_id++) { - // check to see if the current link id matches the id of the link to remove - if (link_storage[*outgoing_link_id].get_sink_router() == sink_router_id) { - // found the link we need to remove, so we delete it here - //change the link to be invalid - link_storage[*outgoing_link_id].set_source_router(NocRouterId::INVALID()); - link_storage[*outgoing_link_id].set_sink_router(NocRouterId::INVALID()); - link_storage[*outgoing_link_id].set_bandwidth_usage(-1); + auto it = std::remove(source_router_outgoing_links.begin(), + source_router_outgoing_links.end(), + link_to_be_removed_id); - // removing this link as an outgoing link from the source router - source_router_outgoing_links->erase(source_router_outgoing_links->begin() + outgoing_link_index); + if (it == source_router_outgoing_links.end()) { + VTR_LOG_WARN("No link could be found among outgoing links of source router with id(%d) " + "that that connects to the sink router with id (%d).\n", + (size_t)src_router_id, + (size_t)sink_router_id); + } - // indicate that the link to remove has been found and deleted - link_removed_status = true; + source_router_outgoing_links.erase(it, source_router_outgoing_links.end()); - break; - } + it = std::remove(sink_router_incoming_links.begin(), + sink_router_incoming_links.end(), + link_to_be_removed_id); - outgoing_link_index++; + if (it == sink_router_incoming_links.end()) { + VTR_LOG_WARN("No link could be found among incoming links of sink router with id(%d) " + "that that connects to the source router with id (%d).\n", + (size_t)sink_router_id, + (size_t)src_router_id); } + + sink_router_incoming_links.erase(it, sink_router_incoming_links.end()); + + link_storage[link_to_be_removed_id].set_source_router(NocRouterId::INVALID()); + link_storage[link_to_be_removed_id].set_sink_router(NocRouterId::INVALID()); + link_storage[link_to_be_removed_id].set_bandwidth_usage(-1); + } // if a link was not removed then throw warning message if (!link_removed_status) { - VTR_LOG_WARN("No link could be found that has a source router with id: '%d' and sink router with id:'%d'.\n", (size_t)src_router_id, (size_t)sink_router_id); + VTR_LOG_WARN("No link could be found that has a source router with id: '%d' and sink router with id:'%d'.\n", + (size_t)src_router_id, + (size_t)sink_router_id); } return link_removed_status; @@ -225,7 +245,8 @@ void NocStorage::finished_building_noc() { void NocStorage::clear_noc() { router_storage.clear(); link_storage.clear(); - router_link_list.clear(); + router_outgoing_links_list.clear(); + router_incoming_links_list.clear(); grid_location_to_router_id.clear(); built_noc = false; @@ -253,7 +274,8 @@ int NocStorage::convert_router_id(NocRouterId id) const { void NocStorage::make_room_for_noc_router_link_list() { VTR_ASSERT_MSG(!built_noc, "NoC already built, cannot modify further."); - router_link_list.resize(router_storage.size()); + router_outgoing_links_list.resize(router_storage.size()); + router_incoming_links_list.resize(router_storage.size()); } NocLinkId NocStorage::get_parallel_link(NocLinkId current_link) const { @@ -262,12 +284,12 @@ NocLinkId NocStorage::get_parallel_link(NocLinkId current_link) const { NocRouterId curr_sink_router = link_storage[current_link].get_sink_router(); // get the link list of the sink router - const std::vector* sink_router_links = &(router_link_list[curr_sink_router]); + const std::vector& sink_router_links = router_outgoing_links_list[curr_sink_router]; - NocLinkId parallel_link = INVALID_LINK_ID; + NocLinkId parallel_link = NocLinkId::INVALID(); // go through the links of the sink router and the link that has the current source router as the sink router of the link is the parallel link we are looking for - for (auto sink_router_link : *sink_router_links) { + for (auto sink_router_link : sink_router_links) { if (link_storage[sink_router_link].get_sink_router() == curr_source_router) { parallel_link = sink_router_link; break; @@ -312,7 +334,7 @@ void NocStorage::echo_noc(char* file_name) const { fprintf(fp, "Equivalent Physical Tile Grid Position -> (%d,%d)\n", router.get_router_grid_position_x(), router.get_router_grid_position_y()); fprintf(fp, "Router Connections (destination router id, link bandwidth, link latency) ->"); - auto& router_connections = this->get_noc_router_connections(this->convert_router_id(router.get_router_user_id())); + auto& router_connections = this->get_noc_router_outgoing_links(this->convert_router_id(router.get_router_user_id())); // go through the outgoing links of the current router and print the connecting router for (auto router_connection : router_connections) { diff --git a/vpr/src/noc/noc_storage.h b/vpr/src/noc/noc_storage.h index 8e10aad4260..bbefe9b4fa9 100644 --- a/vpr/src/noc/noc_storage.h +++ b/vpr/src/noc/noc_storage.h @@ -35,7 +35,6 @@ * */ -#include #include #include #include @@ -43,13 +42,6 @@ #include "vtr_vector.h" #include "noc_router.h" #include "noc_link.h" -#include "vtr_assert.h" -#include "vpr_error.h" -#include "echo_files.h" -// \cond -// represents the id of a link that does not exist in the NoC -constexpr NocLinkId INVALID_LINK_ID(-1); -// \endcond class NocStorage { private: @@ -61,9 +53,15 @@ class NocStorage { * @brief Stores outgoing links for each router in the NoC. These * links can be used by the router to communicate to other routers * in the NoC. - * */ - vtr::vector> router_link_list; + vtr::vector> router_outgoing_links_list; + + /** + * @brief Stores incoming links for each router in the NoC. These + * links can be used by the router to communicate to other routers + * in the NoC. + */ + vtr::vector> router_incoming_links_list; /** Contains all the links in the NoC*/ vtr::vector link_storage; @@ -76,7 +74,6 @@ class NocStorage { * are dense since it is used to index the routers. The datastructure * below is a conversiont able that maps the user router IDs to the * corresponding internal ones. - * */ std::unordered_map router_id_conversion_table; @@ -178,13 +175,23 @@ class NocStorage { /** * @brief Gets a vector of outgoing links for a given router - * in the NoC. THe link vector cannot be modified. + * in the NoC. The link vector cannot be modified. * * @param id A unique identifier that represents a router * @return A vector of links. The links are represented by a unique * identifier. */ - const std::vector& get_noc_router_connections(NocRouterId id) const; + const std::vector& get_noc_router_outgoing_links(NocRouterId id) const; + + /** + * @brief Gets a vector of incoming links for a given router + * in the NoC. + * + * @param id A unique identifier that represents a router + * @return A vector of links. The links are represented by a unique + * identifier. + */ + const std::vector& get_noc_router_incoming_links(NocRouterId id) const; /** * @brief Get all the routers in the NoC. The routers themselves cannot diff --git a/vpr/src/noc/noc_traffic_flows.cpp b/vpr/src/noc/noc_traffic_flows.cpp index 9d80f4c692f..795a0316cb6 100644 --- a/vpr/src/noc/noc_traffic_flows.cpp +++ b/vpr/src/noc/noc_traffic_flows.cpp @@ -9,7 +9,7 @@ NocTrafficFlows::NocTrafficFlows() { // getters for the traffic flows -int NocTrafficFlows::get_number_of_traffic_flows(void) const { +int NocTrafficFlows::get_number_of_traffic_flows() const { return noc_traffic_flows.size(); } @@ -33,7 +33,7 @@ const std::vector& NocTrafficFlows::get_traffic_flows_associat } } -int NocTrafficFlows::get_number_of_routers_used_in_traffic_flows(void) { +int NocTrafficFlows::get_number_of_routers_used_in_traffic_flows() { return traffic_flows_associated_to_router_blocks.size(); } @@ -49,11 +49,11 @@ const vtr::vector>& NocTrafficFlows::ge return traffic_flow_routes; } -const std::vector& NocTrafficFlows::get_router_clusters_in_netlist(void) const { +const std::vector& NocTrafficFlows::get_router_clusters_in_netlist() const { return router_cluster_in_netlist; } -const std::vector& NocTrafficFlows::get_all_traffic_flow_id(void) const { +const std::vector& NocTrafficFlows::get_all_traffic_flow_id() const { return noc_traffic_flows_ids; } @@ -84,8 +84,6 @@ void NocTrafficFlows::create_noc_traffic_flow(const std::string& source_router_m // now add the new traffic flow to flows associated with the current source and sink router add_traffic_flow_to_associated_routers(curr_traffic_flow_id, source_router_cluster_id); add_traffic_flow_to_associated_routers(curr_traffic_flow_id, sink_router_cluster_id); - - return; } void NocTrafficFlows::set_router_cluster_in_netlist(const std::vector& routers_cluster_id_in_netlist) { @@ -98,18 +96,16 @@ void NocTrafficFlows::set_router_cluster_in_netlist(const std::vectorsecond.emplace_back(traffic_flow_id); } - - return; } void NocTrafficFlows::echo_noc_traffic_flows(char* file_name) { @@ -208,6 +200,4 @@ void NocTrafficFlows::echo_noc_traffic_flows(char* file_name) { } vtr::fclose(fp); - - return; } \ No newline at end of file diff --git a/vpr/src/noc/noc_traffic_flows.h b/vpr/src/noc/noc_traffic_flows.h index dac054d4bef..7db9041a05d 100644 --- a/vpr/src/noc/noc_traffic_flows.h +++ b/vpr/src/noc/noc_traffic_flows.h @@ -154,7 +154,7 @@ class NocTrafficFlows { * @return int An integer that represents the number of unique traffic * flows within the NoC. */ - int get_number_of_traffic_flows(void) const; + int get_number_of_traffic_flows() const; /** * @brief Given a unique id of a traffic flow (t_noc_traffic_flow) @@ -192,7 +192,7 @@ class NocTrafficFlows { * @return int The total number of unique routers used in * the traffic flows provided by the user. */ - int get_number_of_routers_used_in_traffic_flows(void); + int get_number_of_routers_used_in_traffic_flows(); /** * @brief Gets the routed path of traffic flow. This cannot be @@ -229,13 +229,13 @@ class NocTrafficFlows { * @return a vector ([0..num_logical_router-1]) where each entry gives the clusterBlockId * of a logical NoC router. Used for fast lookups in the placer. */ - const std::vector& get_router_clusters_in_netlist(void) const; + const std::vector& get_router_clusters_in_netlist() const; /** * @return provides access to all traffic flows' ids to allow a range-based * loop through all traffic flows, used in noc_place_utils.cpp functions. */ - const std::vector& get_all_traffic_flow_id(void) const; + const std::vector& get_all_traffic_flow_id() const; // setters @@ -294,14 +294,14 @@ class NocTrafficFlows { * */ - void finished_noc_traffic_flows_setup(void); + void finished_noc_traffic_flows_setup(); /** * @brief Resets the class by clearing internal * datastructures. * */ - void clear_traffic_flows(void); + void clear_traffic_flows(); /** * @brief Given a block from the clustered netlist, determine @@ -331,6 +331,20 @@ class NocTrafficFlows { * traffic flow information */ void echo_noc_traffic_flows(char* file_name); + + + /** + * @brief Defines the latency constraint of a traffic flow + * when not provided by the user. + * + * This value has to be significantly larger than latencies + * seen within the NoC so that the net effect in the placement + * cost is 0 (the latency constraint has no effect since there is none). + * Since the traffic flow latencies will be in nanoseconds, + * setting this value to 1 second which is significantly larger + * than what will be seen in the NoC. + */ + static constexpr double DEFAULT_MAX_TRAFFIC_FLOW_LATENCY = 1.; }; #endif \ No newline at end of file diff --git a/vpr/src/noc/north_last_routing.cpp b/vpr/src/noc/north_last_routing.cpp index ebf4e655896..7bfc58791c0 100644 --- a/vpr/src/noc/north_last_routing.cpp +++ b/vpr/src/noc/north_last_routing.cpp @@ -96,4 +96,35 @@ TurnModelRouting::Direction NorthLastRouting::select_next_direction(const std::v } return selected_direction; -} \ No newline at end of file +} + +bool NorthLastRouting::is_turn_legal(const std::array, 3>& noc_routers) const { + const int x1 = noc_routers[0].get().get_router_grid_position_x(); + const int y1 = noc_routers[0].get().get_router_grid_position_y(); + + const int x2 = noc_routers[1].get().get_router_grid_position_x(); + const int y2 = noc_routers[1].get().get_router_grid_position_y(); + + const int x3 = noc_routers[2].get().get_router_grid_position_x(); + const int y3 = noc_routers[2].get().get_router_grid_position_y(); + + // check if the given routers can be traversed one after another + VTR_ASSERT(x2 == x1 || y2 == y1); + VTR_ASSERT(x3 == x2 || y3 == y2); + + // going back to the first router is not allowed + if (x1 == x3 && y1 == y3) { + return false; + } + + /* In the north-last algorithm, once the north direction is taken, no other + * direction can be followed. Therefore, if the first link moves upward, the + * second one cannot move horizontally. The case where the second link goes + * back to the first router was checked in the previous if statement. + */ + if (y2 > y1 && x2 != x3) { + return false; + } + + return true; +} diff --git a/vpr/src/noc/north_last_routing.h b/vpr/src/noc/north_last_routing.h index e9e877e68b4..61aaf8caa4b 100644 --- a/vpr/src/noc/north_last_routing.h +++ b/vpr/src/noc/north_last_routing.h @@ -66,6 +66,8 @@ class NorthLastRouting : public TurnModelRouting { NocRouterId curr_router_id, NocTrafficFlowId traffic_flow_id, const NocStorage& noc_model) override; + + bool is_turn_legal(const std::array, 3>& noc_routers) const override; }; #endif //VTR_NORTH_LAST_ROUTING_H diff --git a/vpr/src/noc/odd_even_routing.cpp b/vpr/src/noc/odd_even_routing.cpp index 8c660e5b02c..68a6b59a479 100644 --- a/vpr/src/noc/odd_even_routing.cpp +++ b/vpr/src/noc/odd_even_routing.cpp @@ -34,10 +34,13 @@ const std::vector& OddEvenRouting::get_legal_direct const auto curr_router_pos = curr_router.get_router_physical_location(); const auto dst_router_pos = dst_router.get_router_physical_location(); - // get the compressed location for source, current, and destination NoC routers - auto compressed_src_loc = get_compressed_loc_approx(compressed_noc_grid,t_pl_loc{src_router_pos, 0}, num_layers)[src_router_pos.layer_num]; - auto compressed_curr_loc = get_compressed_loc_approx(compressed_noc_grid,t_pl_loc{curr_router_pos, 0}, num_layers)[curr_router_pos.layer_num]; - auto compressed_dst_loc = get_compressed_loc_approx(compressed_noc_grid,t_pl_loc{dst_router_pos, 0}, num_layers)[dst_router_pos.layer_num]; + /* get the compressed location for source, current, and destination NoC routers + * Odd-even routing algorithm restricts turn based on whether the current NoC router + * in an odd or even NoC column. This information can be extracted from the NoC compressed grid. + */ + auto compressed_src_loc = get_compressed_loc_approx(compressed_noc_grid, t_pl_loc{src_router_pos, 0}, num_layers)[src_router_pos.layer_num]; + auto compressed_curr_loc = get_compressed_loc_approx(compressed_noc_grid, t_pl_loc{curr_router_pos, 0}, num_layers)[curr_router_pos.layer_num]; + auto compressed_dst_loc = get_compressed_loc_approx(compressed_noc_grid, t_pl_loc{dst_router_pos, 0}, num_layers)[dst_router_pos.layer_num]; // clear returned legal directions from the previous call returned_legal_direction.clear(); @@ -151,3 +154,67 @@ bool OddEvenRouting::is_odd(int number) { bool OddEvenRouting::is_even(int number) { return (number % 2) == 0; } + +bool OddEvenRouting::is_turn_legal(const std::array, 3>& noc_routers) const { + // used to access NoC compressed grid + const auto& place_ctx = g_vpr_ctx.placement(); + // used to get NoC logical block type + const auto& cluster_ctx = g_vpr_ctx.clustering(); + // used to get the clustered block ID of a NoC router + auto& noc_ctx = g_vpr_ctx.noc(); + // get number of layers + const int num_layers = g_vpr_ctx.device().grid.get_num_layers(); + + const int x1 = noc_routers[0].get().get_router_grid_position_x(); + const int y1 = noc_routers[0].get().get_router_grid_position_y(); + + const int x2 = noc_routers[1].get().get_router_grid_position_x(); + const int y2 = noc_routers[1].get().get_router_grid_position_y(); + + const int x3 = noc_routers[2].get().get_router_grid_position_x(); + const int y3 = noc_routers[2].get().get_router_grid_position_y(); + + // check if the given routers can be traversed one after another + VTR_ASSERT(x2 == x1 || y2 == y1); + VTR_ASSERT(x3 == x2 || y3 == y2); + + // get the position of the second NoC routers + const auto router2_pos = noc_routers[1].get().get_router_physical_location(); + + // Get the logical block type for router + const auto router_block_type = cluster_ctx.clb_nlist.block_type(noc_ctx.noc_traffic_flows_storage.get_router_clusters_in_netlist()[0]); + + // Get the compressed grid for NoC + const auto& compressed_noc_grid = place_ctx.compressed_block_grids[router_block_type->index]; + + // get the compressed location of the second NoC router + auto compressed_2_loc = get_compressed_loc(compressed_noc_grid, t_pl_loc{router2_pos, 0}, num_layers)[router2_pos.layer_num]; + + // going back to the first router is not allowed + if (x1 == x3 && y1 == y3) { + return false; + } + + // check if the turn is compatible with odd-even routing algorithm turn restrictions + if (is_odd(compressed_2_loc.x)) { + if (y2 != y1 && x3 < x2) { + return false; + } + } else { // even column + if (x2 > x1 && y2 != y3) { + return false; + } + } + + return true; +} + + + + + + + + + + diff --git a/vpr/src/noc/odd_even_routing.h b/vpr/src/noc/odd_even_routing.h index 512b3db965b..deaf67f9c10 100644 --- a/vpr/src/noc/odd_even_routing.h +++ b/vpr/src/noc/odd_even_routing.h @@ -35,6 +35,9 @@ class OddEvenRouting : public TurnModelRouting{ NocRouterId curr_router_id, NocTrafficFlowId traffic_flow_id, const NocStorage& noc_model) override; + + bool is_turn_legal(const std::array, 3>& noc_routers) const override; + /** * Checks whether the given umber is odd. * @param number An integer number diff --git a/vpr/src/noc/read_xml_noc_traffic_flows_file.cpp b/vpr/src/noc/read_xml_noc_traffic_flows_file.cpp index 07bd53be7ce..3f12c02ed2d 100644 --- a/vpr/src/noc/read_xml_noc_traffic_flows_file.cpp +++ b/vpr/src/noc/read_xml_noc_traffic_flows_file.cpp @@ -3,7 +3,7 @@ void read_xml_noc_traffic_flows_file(const char* noc_flows_file) { // start by checking that the provided file is a ".flows" file - if (vtr::check_file_name_extension(noc_flows_file, ".flows") == false) { + if (!vtr::check_file_name_extension(noc_flows_file, ".flows")) { VPR_FATAL_ERROR(VPR_ERROR_OTHER, "NoC traffic flows file '%s' has an unknown extension. Expecting .flows for NoC traffic flow files.", noc_flows_file); } @@ -72,8 +72,6 @@ void read_xml_noc_traffic_flows_file(const char* noc_flows_file) { if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_NOC_TRAFFIC_FLOWS)) { noc_ctx.noc_traffic_flows_storage.echo_noc_traffic_flows(getEchoFileName(E_ECHO_NOC_TRAFFIC_FLOWS)); } - - return; } void process_single_flow(pugi::xml_node single_flow_tag, @@ -125,8 +123,6 @@ void process_single_flow(pugi::xml_node single_flow_tag, traffic_flow_bandwidth, max_traffic_flow_latency, traffic_flow_priority); - - return; } double get_traffic_flow_bandwidth(pugi::xml_node single_flow_tag, const pugiutil::loc_data& loc_data) { @@ -144,7 +140,7 @@ double get_traffic_flow_bandwidth(pugi::xml_node single_flow_tag, const pugiutil double get_max_traffic_flow_latency(pugi::xml_node single_flow_tag, const pugiutil::loc_data& loc_data) { // "set to large value, indicating no constraint - double max_traffic_flow_latency = DEFAULT_MAX_TRAFFIC_FLOW_LATENCY; + double max_traffic_flow_latency = NocTrafficFlows::DEFAULT_MAX_TRAFFIC_FLOW_LATENCY; // holds the latency value as a string so that it can be used to convert to a floating point value (this is done so that scientific notation is supported) std::string max_traffic_flow_latency_intermediate_val; @@ -194,8 +190,6 @@ void verify_traffic_flow_router_modules(const std::string& source_router_name, c // Cannot have the source and sink routers have the same name (they need to be different). A flow cant go to a single router. vpr_throw(VPR_ERROR_OTHER, loc_data.filename_c_str(), loc_data.line(single_flow_tag), "Source and sink NoC routers cannot be the same modules."); } - - return; } void verify_traffic_flow_properties(double traffic_flow_bandwidth, double max_traffic_flow_latency, int traffic_flow_priority, pugi::xml_node single_flow_tag, const pugiutil::loc_data& loc_data) { @@ -213,8 +207,6 @@ void verify_traffic_flow_properties(double traffic_flow_bandwidth, double max_tr if (traffic_flow_priority <= 0) { vpr_throw(VPR_ERROR_OTHER, loc_data.filename_c_str(), loc_data.line(single_flow_tag), "The traffic flow priorities expected to be positive, non-zero integer values."); } - - return; } ClusterBlockId get_router_module_cluster_id(const std::string& router_module_name, @@ -254,8 +246,6 @@ void check_traffic_flow_router_module_type(const std::string& router_module_name if (!is_tile_compatible(noc_router_tile_type, router_module_logical_type)) { vpr_throw(VPR_ERROR_OTHER, loc_data.filename_c_str(), loc_data.line(single_flow_tag), "The supplied module name '%s' is not a NoC router.", router_module_name.c_str()); } - - return; } t_physical_tile_type_ptr get_physical_type_of_noc_router_tile(const DeviceContext& device_ctx, NocContext& noc_ctx) { diff --git a/vpr/src/noc/read_xml_noc_traffic_flows_file.h b/vpr/src/noc/read_xml_noc_traffic_flows_file.h index 55cecc38bc1..7abd685d82e 100644 --- a/vpr/src/noc/read_xml_noc_traffic_flows_file.h +++ b/vpr/src/noc/read_xml_noc_traffic_flows_file.h @@ -39,11 +39,6 @@ // identifier when an integer conversion failed while reading an attribute value in an xml file constexpr int NUMERICAL_ATTRIBUTE_CONVERSION_FAILURE = -1; -// defines the latency constraint of a traffic flow when not provided by the user -// This value has to be significantly larger than latencies seen within the NoC so that the net effect in the placement cost is 0 (the latency constraint has no effect since there is none) -// Since the traffic flow latencies will be in nanoseconds, setting this value to 1 second which is significantly larger that what will be seen in the NoC -constexpr double DEFAULT_MAX_TRAFFIC_FLOW_LATENCY = 1.; - // defines the priority of a traffic flow when not specified by a user constexpr int DEFAULT_TRAFFIC_FLOW_PRIORITY = 1; diff --git a/vpr/src/noc/sat_routing.cpp b/vpr/src/noc/sat_routing.cpp new file mode 100644 index 00000000000..354620c6cfa --- /dev/null +++ b/vpr/src/noc/sat_routing.cpp @@ -0,0 +1,679 @@ +#ifdef ENABLE_NOC_SAT_ROUTING + + +#include "sat_routing.h" +#include "turn_model_routing.h" + +#include "globals.h" +#include "vtr_time.h" + +#include + +#include "ortools/sat/cp_model.h" +#include "ortools/sat/cp_model.pb.h" +#include "ortools/sat/cp_model_solver.h" + +namespace orsat = operations_research::sat; + +/** + * For each traffic flow and NoC link pair, we create a boolean variable. + * When a variable associated with traffic flow t and NoC link l is set, + * it means that t is routed through l. + */ +typedef std::unordered_map, orsat::BoolVar> t_flow_link_var_map; + + +/** + * @brief Creates a boolean variable for each (traffic flow, link) pair. + * It also create integer variables for latency-constrained traffic flows. + * + * @param cp_model The CP model builder object. Created variables are added + * to this model builder object. + * @param flow_link_vars The created boolean variables for (traffic flow, link) pairs + * are stored in this container to be used for adding constraints and defining objective + * functions. + * @param latency_overrun_vars The created integer variables for latency-constrained + * traffic flows are added to this container for future use, e.g. adding constraints + * and defining objective functions. + */ +static void create_flow_link_vars(orsat::CpModelBuilder& cp_model, + t_flow_link_var_map& flow_link_vars, + std::map& latency_overrun_vars); + +/** + * @brief Translates a latency constraint for a traffic flow to the maximum number + * of links that the traffic flow can traverse without violating the latency constraint. + * + * This translation is possible only when all NoC routers or links have the same latency. + * NoC routers can have a different latency than NoC links, but all router (or links) must + * have the same latency. + * + * @param traffic_flow_id The unique ID of the latency-constrained traffic flow. + * @return The maximum number links that can travelled by the given traffic flow without + * violating the latency constraint. + */ +static int comp_max_number_of_traversed_links(NocTrafficFlowId traffic_flow_id); + +/** + * @brief Performs an outer product between traffic_flow_ids and noc_link_ids + * and returns all boolean variables for the resulting (traffic flow, link) pairs. + * + * @param map The container that stores all boolean variables for all + * (traffic flow, link) pairs. + * @param traffic_flow_ids Traffic flows whose boolean variables are requested. + * @param noc_link_ids NoC links whose boolean variables are requested. + * @return A vector of boolean variables for the requested (traffic flow, link) pairs. + */ +static std::vector get_flow_link_vars(const t_flow_link_var_map& map, + const std::vector& traffic_flow_ids, + const std::vector& noc_link_ids); + +/** + * @brief Adds constraints for latency_overrun_vars variables to make sure + * that they count the number of extra links a traffic flow is traversing + * beyond what its latency constraint allows. For example, if a traffic flow + * latency constraint allows the traffic flow to traverse at most 3 links, + * but 4 (traffic flow, link) boolean variables are activated for this traffic flow, + * the corresponding integer variable in latency_overrun_vars should take a value + * of 1 because an extra link beyond the maximum 3 links has been activated. + * + * @param cp_model The CP model builder object. New constraints are added + * to this model builder object. + * @param flow_link_vars Boolean variable container for (traffic flow, link) pairs. + * @param latency_overrun_vars The integer variables that are to be constrained. + */ +static void constrain_latency_overrun_vars(orsat::CpModelBuilder& cp_model, + t_flow_link_var_map& flow_link_vars, + std::map& latency_overrun_vars); + +/** + * @brief Forbids specific turns that traffic flows can take. + * Turn model routing algorithms forbid specific turns in a mesh topology + * to make sure that deadlock does not happen. A turn can be determined by + * specifying two consecutive links. A turn can be forbidden in the SAT + * formulation by making sure that at most one of two consecutive links + * that specify a turn is activated. + * + * @param flow_link_vars Boolean variable container for (traffic flow, link) pairs. + * @param cp_model The CP model builder object. New constraints are added + * to this model builder object. + */ +static void forbid_illegal_turns(t_flow_link_var_map& flow_link_vars, + orsat::CpModelBuilder& cp_model); + +/** + * @brief Creates a boolean variable for each link to indicate + * whether it is congested. This function adds some constraints + * to enforce congested_link_vars boolean variables to be set to 1 + * when their corresponding link congested. + * + * Since the SAT solver cannot work with floating point numbers, + * all link and traffic flow bandwidths are quantized. + * + * @param congested_link_vars To be filled with created boolean variables + * to indicate whether links are congested. + * @param flow_link_vars Boolean variable container for (traffic flow, link) pairs. + * @param cp_model The CP model builder object. Created variables are added + * to this model builder object. Constraints are also added to this object. + * @param bandwidth_resolution Specifies the resolution by which bandwidth + * values are quantized. + */ +static void create_congested_link_vars(vtr::vector& congested_link_vars, + t_flow_link_var_map& flow_link_vars, + orsat::CpModelBuilder& cp_model, + int bandwidth_resolution); + +/** + * @brief Quantize traffic flow bandwidths. The maximum NoC link bandwidth is + * quantized to the specified bandwidth resolution, and traffic flow bandwidths + * are quantized accordingly. + * + * @param bandwidth_resolution The resolution by which traffic flow bandwidth are + * quantized. Quantized bandwidths increment by link_bandwidth/resolution. + * @return A vector of quantized traffic flow bandwidths. + */ +static vtr::vector quantize_traffic_flow_bandwidths(int bandwidth_resolution); + +/** + * @brief Adds constraints to ensure that activated (traffic flow, link) + * boolean variables for a specific traffic flow create a continuous route + * starting from the source router and arriving at the destination. + * + * @param flow_link_vars Boolean variable container for (traffic flow, link) pairs. + * @param cp_model The CP model builder object. New constraints are added + * to this model builder object. + */ +static void add_continuity_constraints(t_flow_link_var_map& flow_link_vars, + orsat::CpModelBuilder& cp_model); + +/** + * @brief Creates a linear expression to be minimized by the SAT solver. + * This objective function is a linear combination of latency overrun, + * the number of congested links, and the quantized aggregate bandwidth. + * + * @param cp_model The CP model builder object. New constraints are added + * to this model builder object. + * @param flow_link_vars Boolean variable container for (traffic flow, link) pairs. + * @param latency_overrun_vars Integer variables for latency-constrained + * traffic flows. Each integer variable shows how many extra links a constrained + * traffic flow has traversed beyond what its latency constraint allows. + * @param congested_link_vars Boolean variables indicating whether a link is + * congested or not. + * @param bandwidth_resolution The resolution by which traffic flow bandwidths + * are quantized. + * @param latency_overrun_weight Specifies the importance of minimizing latency overrun + * for latency-constrained traffic flows. + * @param congestion_weight Specifies the importance of avoiding congestion in links. + * @param minimize_aggregate_bandwidth Specifies whether the objective includes an + * aggregate bandwidth term. + * + * @return A linear expression including latency overrun, the number of congested links, + * and the aggregate bandwidth; + */ +static orsat::LinearExpr create_objective(orsat::CpModelBuilder& cp_model, + t_flow_link_var_map& flow_link_vars, + std::map& latency_overrun_vars, + vtr::vector& congested_link_vars, + int bandwidth_resolution, + int latency_overrun_weight, + int congestion_weight, + bool minimize_aggregate_bandwidth); + +/** + * @brief Converts the activated (traffic flow, link) boolean variables to + * traffic flow routes. + * + * Note that traffic flow routes are not sorted in traversal order. Each + * traffic flow route contains NoC links that constitute the route, but these + * links may not stored in the route traversal order. For re-ordering traffic flow + * routes in route traversal order, call sort_noc_links_in_chain_order() function. + * + * @param flow_link_vars Boolean variable container for (traffic flow, link) pairs. + * @param response The SAT solver's response. This object is used to query the values + * of (traffic flow, link) variables. + * @return Traffic flow routes. NoC links may not be stored in the route traversal order. + */ +static vtr::vector> convert_vars_to_routes(t_flow_link_var_map& flow_link_vars, + const orsat::CpSolverResponse& response); + +/** + * @brief Sorts the given NoC links so that they can traversed one after another. + * + * After the SAT solver returns a solution, boolean (traffic flow, link) variables + * can be translated into traffic flow routes. The SAT solver does not have a notion + * traversal order. Therefore, activated NoC links for a traffic flow are stored in + * an arbitrary order. This function can reorder the activated links in the route + * traversal order. + * + * @param links NoC links that form a continuous route. NoC links can be stored + * in an order different than the traversal order. + * @return Sorted links in traversal order. + */ +static std::vector sort_noc_links_in_chain_order(const std::vector& links); + + +static std::vector get_flow_link_vars(const t_flow_link_var_map& map, + const std::vector& traffic_flow_ids, + const std::vector& noc_link_ids) { + std::vector results; + for (auto traffic_flow_id : traffic_flow_ids) { + for (auto noc_link_id : noc_link_ids) { + auto it = map.find({traffic_flow_id, noc_link_id}); + if (it != map.end()) { + results.push_back(it->second); + } + } + } + + return results; +} + +static void forbid_illegal_turns(t_flow_link_var_map& flow_link_vars, + orsat::CpModelBuilder& cp_model) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& traffic_flow_storage = noc_ctx.noc_traffic_flows_storage; + + auto noc_routing_alg = dynamic_cast (noc_ctx.noc_flows_router.get()); + // ensure that the routing algorithm is a turn model algorithm + VTR_ASSERT(noc_routing_alg != nullptr); + + // forbid illegal turns based on the routing algorithm + // this includes 180 degree turns + for (const auto& [link1, link2] : noc_routing_alg->get_all_illegal_turns(noc_ctx.noc_model)) { + for (auto traffic_flow_id : traffic_flow_storage.get_all_traffic_flow_id()) { + auto& first_var = flow_link_vars[{traffic_flow_id, link1}]; + auto& second_var = flow_link_vars[{traffic_flow_id, link2}]; + // at most one of two consecutive links that form a turn can be activated + cp_model.AddBoolOr({first_var.Not(), second_var.Not()}); + } + } +} + +static vtr::vector quantize_traffic_flow_bandwidths(int bandwidth_resolution) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& traffic_flow_storage = noc_ctx.noc_traffic_flows_storage; + + //TODO: support heterogeneous bandwidth + const auto& noc_links = noc_ctx.noc_model.get_noc_links(); + const double link_bandwidth = noc_links.front().get_bandwidth(); + auto it = std::adjacent_find(noc_links.begin(), noc_links.end(), [](const NocLink& a, const NocLink& b){ + return a.get_bandwidth() != b.get_bandwidth(); + }); + + if (it != noc_links.end()) { + const NocLink& first_link = *it; + const NocLink& second_link = *(it + 1); + VTR_LOG_ERROR( + "SAT router assumes all NoC links have the same bandwidth. " + "NoC links %d and %d have different bandwidth: %g and %g", + (size_t)first_link.get_link_id(), (size_t)second_link.get_link_id(), + first_link.get_bandwidth(), second_link.get_bandwidth()); + } + + vtr::vector rescaled_traffic_flow_bandwidths; + rescaled_traffic_flow_bandwidths.resize(traffic_flow_storage.get_number_of_traffic_flows()); + + // rescale traffic flow bandwidths + for (auto traffic_flow_id : traffic_flow_storage.get_all_traffic_flow_id()) { + const auto& traffic_flow = traffic_flow_storage.get_single_noc_traffic_flow(traffic_flow_id); + double bandwidth = traffic_flow.traffic_flow_bandwidth; + int rescaled_bandwidth = (int)std::floor((bandwidth / link_bandwidth) * bandwidth_resolution); + rescaled_traffic_flow_bandwidths[traffic_flow_id] = rescaled_bandwidth; + } + + return rescaled_traffic_flow_bandwidths; +} + +static void create_congested_link_vars(vtr::vector& congested_link_vars, + t_flow_link_var_map& flow_link_vars, + orsat::CpModelBuilder& cp_model, + int bandwidth_resolution) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& traffic_flow_storage = noc_ctx.noc_traffic_flows_storage; + + // quantize traffic flow bandwidth + vtr::vector rescaled_traffic_flow_bandwidths = quantize_traffic_flow_bandwidths(bandwidth_resolution); + + // go over all NoC links and create a boolean variable for each one to indicate if it is congested + for (const auto& noc_link : noc_ctx.noc_model.get_noc_links()) { + const NocLinkId noc_link_id = noc_link.get_link_id(); + orsat::LinearExpr bandwidth_load; + + // compute the total bandwidth routed through this link + for (auto traffic_flow_id : traffic_flow_storage.get_all_traffic_flow_id()) { + orsat::BoolVar binary_var = flow_link_vars[{traffic_flow_id, noc_link_id}]; + bandwidth_load += orsat::LinearExpr::Term(binary_var, rescaled_traffic_flow_bandwidths[traffic_flow_id]); + } + + orsat::BoolVar congested = cp_model.NewBoolVar(); + cp_model.AddLessOrEqual(bandwidth_load, bandwidth_resolution).OnlyEnforceIf(congested.Not()); + cp_model.AddGreaterThan(bandwidth_load, bandwidth_resolution).OnlyEnforceIf(congested); + congested_link_vars.push_back(congested); + } +} + +static void add_continuity_constraints(t_flow_link_var_map& flow_link_vars, + orsat::CpModelBuilder& cp_model) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& traffic_flow_storage = noc_ctx.noc_traffic_flows_storage; + const auto& place_ctx = g_vpr_ctx.placement(); + + // constrain the links that can be activated for each traffic flow in a way that they + // form a continuous route + for (auto traffic_flow_id : traffic_flow_storage.get_all_traffic_flow_id()) { + const auto& traffic_flow = traffic_flow_storage.get_single_noc_traffic_flow(traffic_flow_id); + + // get the source and destination logical router blocks in the current traffic flow + ClusterBlockId logical_source_router_block_id = traffic_flow.source_router_cluster_id; + ClusterBlockId logical_sink_router_block_id = traffic_flow.sink_router_cluster_id; + + // get the ids of the hard router blocks where the logical router cluster blocks have been placed + NocRouterId source_router_id = noc_ctx.noc_model.get_router_at_grid_location(place_ctx.block_locs[logical_source_router_block_id].loc); + NocRouterId sink_router_id = noc_ctx.noc_model.get_router_at_grid_location(place_ctx.block_locs[logical_sink_router_block_id].loc); + + // exactly one outgoing link of the source must be selected + const auto& src_outgoing_link_ids = noc_ctx.noc_model.get_noc_router_outgoing_links(source_router_id); + auto src_outgoing_vars = get_flow_link_vars(flow_link_vars, {traffic_flow_id}, src_outgoing_link_ids); + cp_model.AddExactlyOne(src_outgoing_vars); + + // exactly one incoming link of the sink must be selected + const auto& dst_incoming_link_ids = noc_ctx.noc_model.get_noc_router_incoming_links(sink_router_id); + auto dst_incoming_vars = get_flow_link_vars(flow_link_vars, {traffic_flow_id}, dst_incoming_link_ids); + cp_model.AddExactlyOne(dst_incoming_vars); + + // each NoC router has at most one incoming and one outgoing link activated + for (const auto& noc_router : noc_ctx.noc_model.get_noc_routers()) { + const int noc_router_user_id = noc_router.get_router_user_id(); + const NocRouterId noc_router_id = noc_ctx.noc_model.convert_router_id(noc_router_user_id); + + // the links connected to source and destination routers have already been constrained + if (noc_router_id == source_router_id || noc_router_id == sink_router_id) { + continue; + } + + + // for each intermediate router, at most one incoming link can be activated to route this traffic flow + const auto& incoming_links = noc_ctx.noc_model.get_noc_router_incoming_links(noc_router_id); + auto incoming_vars = get_flow_link_vars(flow_link_vars, {traffic_flow_id}, incoming_links); + cp_model.AddAtMostOne(incoming_vars); + + // for each intermediate router, at most one outgoing link can be activated to route this traffic flow + const auto& outgoing_links = noc_ctx.noc_model.get_noc_router_outgoing_links(noc_router_id); + auto outgoing_vars = get_flow_link_vars(flow_link_vars, {traffic_flow_id}, outgoing_links); + cp_model.AddAtMostOne(outgoing_vars); + + // count the number of activated incoming links for this traffic flow + orsat::LinearExpr incoming_vars_sum = orsat::LinearExpr::Sum(incoming_vars); + + // count the number of activated outgoing links for this traffic flow + orsat::LinearExpr outgoing_vars_sum = orsat::LinearExpr::Sum(outgoing_vars); + + /* the number activated incoming and outgoing links must be equal/ + * Either they are both 0, meaning that this NoC routers is not on the + * traffic flow router, or they are both 1, implying the traffic flow route + * goes through this NoC router.*/ + cp_model.AddEquality(incoming_vars_sum, outgoing_vars_sum); + } + } +} + +static std::vector sort_noc_links_in_chain_order(const std::vector& links) { + std::vector route; + if (links.empty()) { + return route; + } + + const auto& noc_model = g_vpr_ctx.noc().noc_model; + + // Create a map to find pairs by their first element + std::unordered_map src_map; + std::unordered_map is_dst; + for (const auto l : links) { + NocRouterId src_router_id = noc_model.get_single_noc_link(l).get_source_router(); + NocRouterId dst_router_id = noc_model.get_single_noc_link(l).get_sink_router(); + src_map[src_router_id] = l; + is_dst[dst_router_id] = true; + } + + // Find the starting pair (whose first element is not a second element of any pair) + auto it = links.begin(); + for (; it != links.end(); ++it) { + NocRouterId src_router_id = noc_model.get_single_noc_link(*it).get_source_router(); + if (is_dst.find(src_router_id) == is_dst.end()) { + break; + } + } + + // Reconstruct the chain starting from the found starting pair + auto current = *it; + while (true) { + route.push_back(current); + NocRouterId dst_router_id = noc_model.get_single_noc_link(current).get_sink_router(); + auto nextIt = src_map.find(dst_router_id); + if (nextIt == src_map.end()) { + break; // End of chain + } + current = nextIt->second; + } + + VTR_ASSERT(route.size() == links.size()); + + return route; +} + +static vtr::vector> convert_vars_to_routes(t_flow_link_var_map& flow_link_vars, + const orsat::CpSolverResponse& response) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& traffic_flow_storage = noc_ctx.noc_traffic_flows_storage; + + VTR_ASSERT(response.status() == orsat::CpSolverStatus::FEASIBLE || + response.status() == orsat::CpSolverStatus::OPTIMAL); + + vtr::vector> routes; + routes.resize(traffic_flow_storage.get_number_of_traffic_flows()); + + for (auto& [key, var] : flow_link_vars) { + auto [traffic_flow_id, noc_link_id] = key; + bool value = orsat::SolutionBooleanValue(response, var); + if (value) { + routes[traffic_flow_id].push_back(noc_link_id); + } + } + + for (auto& route : routes) { + route = sort_noc_links_in_chain_order(route); + } + + return routes; +} + +static void create_flow_link_vars(orsat::CpModelBuilder& cp_model, + t_flow_link_var_map& flow_link_vars, + std::map& latency_overrun_vars) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& noc_model = noc_ctx.noc_model; + const auto& traffic_flow_storage = noc_ctx.noc_traffic_flows_storage; + // used to access NoC compressed grid + const auto& place_ctx = g_vpr_ctx.placement(); + // used to get NoC logical block type + const auto& cluster_ctx = g_vpr_ctx.clustering(); + + // Get the logical block type for router + const auto router_block_type = cluster_ctx.clb_nlist.block_type(noc_ctx.noc_traffic_flows_storage.get_router_clusters_in_netlist()[0]); + + // Get the compressed grid for NoC + const auto& compressed_noc_grid = place_ctx.compressed_block_grids[router_block_type->index]; + + size_t max_n_cols = std::max_element(compressed_noc_grid.compressed_to_grid_x.begin(), compressed_noc_grid.compressed_to_grid_x.end(), + [](const std::vector& a, const std::vector& b) { + return a.size() < b.size(); + })->size(); + + size_t max_n_rows = std::max_element(compressed_noc_grid.compressed_to_grid_y.begin(), compressed_noc_grid.compressed_to_grid_y.end(), + [](const std::vector& a, const std::vector& b) { + return a.size() < b.size(); + })->size(); + + /* For specifying the domain, assume that the longest traffic flow route starts from + * one corner and terminates at the opposite corner. Assuming minimal routing, such a + * route traversed around H+W links, where H and W are the number of rows and columns + * of the mesh grid. + */ + operations_research::Domain latency_overrun_domain(0, (int)(max_n_rows + max_n_cols)); + + // create boolean variables for each traffic flow and link pair + // create integer variables for traffic flows with constrained latency + for (auto traffic_flow_id : traffic_flow_storage.get_all_traffic_flow_id()) { + const auto& traffic_flow = traffic_flow_storage.get_single_noc_traffic_flow(traffic_flow_id); + + // create an integer variable for each latency-constrained traffic flow + if (traffic_flow.max_traffic_flow_latency < NocTrafficFlows::DEFAULT_MAX_TRAFFIC_FLOW_LATENCY) { + latency_overrun_vars[traffic_flow_id] = cp_model.NewIntVar(latency_overrun_domain); + } + + // create (traffic flow, NoC link) pair boolean variables + for (const auto& noc_link : noc_model.get_noc_links()) { + const NocLinkId noc_link_id = noc_link.get_link_id(); + flow_link_vars[{traffic_flow_id, noc_link_id}] = cp_model.NewBoolVar(); + } + } +} + +static int comp_max_number_of_traversed_links(NocTrafficFlowId traffic_flow_id) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& noc_model = noc_ctx.noc_model; + const auto& traffic_flow_storage = noc_ctx.noc_traffic_flows_storage; + + const auto& traffic_flow = traffic_flow_storage.get_single_noc_traffic_flow(traffic_flow_id); + + const auto& noc_links = noc_model.get_noc_links(); + const auto& noc_routers = noc_model.get_noc_routers(); + const double noc_link_latency = noc_model.get_noc_link_latency(); + const double noc_router_latency = noc_model.get_noc_router_latency(); + + auto router_it = std::find_if(noc_routers.begin(), noc_routers.end(), [noc_router_latency](const NocRouter& r) { + return (noc_router_latency != r.get_latency()); + }); + + if (router_it != noc_routers.end()) { + VTR_LOG_ERROR( + "SAT router assumes all NoC routers have the same latency. " + "NoC router with the user if %d has a different latency (%g) than the NoC-wide router latency (%g).\n", + router_it->get_router_user_id(), router_it->get_latency()); + } + + auto link_it = std::find_if(noc_links.begin(), noc_links.end(), [noc_link_latency](const NocLink& l) { + return (noc_link_latency != l.get_latency()); + }); + + if (link_it != noc_links.end()) { + VTR_LOG_ERROR( + "SAT router assumes all NoC links have the same latency. " + "NoC link %d has a different latency (%g) than the NoC-wide link latency (%g).\n", + (size_t)link_it->get_link_id(), link_it->get_latency()); + } + + const double traffic_flow_latency_constraint = traffic_flow.max_traffic_flow_latency; + + VTR_ASSERT(traffic_flow_latency_constraint < NocTrafficFlows::DEFAULT_MAX_TRAFFIC_FLOW_LATENCY); + + int n_max_links = std::floor((traffic_flow_latency_constraint - noc_router_latency) / (noc_link_latency + noc_router_latency)); + + return n_max_links; +} + +static void constrain_latency_overrun_vars(orsat::CpModelBuilder& cp_model, + t_flow_link_var_map& flow_link_vars, + std::map& latency_overrun_vars) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& noc_model = noc_ctx.noc_model; + + for (auto& [traffic_flow_id, latency_overrun_var] : latency_overrun_vars) { + int n_max_links = comp_max_number_of_traversed_links(traffic_flow_id); + // get all boolean variables for this traffic flow + auto link_vars = get_flow_link_vars(flow_link_vars, {traffic_flow_id}, + {noc_model.get_noc_links().keys().begin(), noc_model.get_noc_links().keys().end()}); + + orsat::LinearExpr latency_overrun_expr; + // count the number of activated links for this traffic flow + latency_overrun_expr += orsat::LinearExpr::Sum(link_vars); + // subtract the maximum number of permissible links from the number of activated links + latency_overrun_expr -= n_max_links; + + // if latency_overrun_expr is non-positive, the latency constraint is met and + // latency_overrun_var should be zero. Otherwise, should be equal to latency_overrun_expr + // This is like pushing latency_overrun_expr through a ReLU function to get latency_overrun_var. + cp_model.AddMaxEquality(latency_overrun_var, {latency_overrun_expr, 0}); + } +} + +static orsat::LinearExpr create_objective(orsat::CpModelBuilder& cp_model, + t_flow_link_var_map& flow_link_vars, + std::map& latency_overrun_vars, + vtr::vector& congested_link_vars, + int bandwidth_resolution, + int latency_overrun_weight, + int congestion_weight, + bool minimize_aggregate_bandwidth) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& traffic_flow_storage = noc_ctx.noc_traffic_flows_storage; + + // use the current routing solution as a hint for the SAT solver + // This will help the solver by giving a good starting point and tighter initial lower bound on the objective function + for (auto traffic_flow_id : traffic_flow_storage.get_all_traffic_flow_id()) { + for (auto route_link_id : traffic_flow_storage.get_traffic_flow_route(traffic_flow_id)) { + cp_model.AddHint(flow_link_vars[{traffic_flow_id, route_link_id}], true); + } + } + + orsat::LinearExpr latency_overrun_sum; + for (auto& [traffic_flow_id, latency_overrun_var] : latency_overrun_vars) { + latency_overrun_sum += latency_overrun_var; + } + latency_overrun_sum *= latency_overrun_weight; + + auto rescaled_traffic_flow_bandwidths = quantize_traffic_flow_bandwidths(bandwidth_resolution); + orsat::LinearExpr agg_bw_expr; + if (minimize_aggregate_bandwidth) { + for (auto& [key, var] : flow_link_vars) { + auto [traffic_flow_id, noc_link_id] = key; + agg_bw_expr += orsat::LinearExpr::Term(var, rescaled_traffic_flow_bandwidths[traffic_flow_id]); + } + } else { + agg_bw_expr = 0; + } + + + orsat::LinearExpr congested_link_sum = orsat::LinearExpr::Sum(congested_link_vars); + congested_link_sum *= congestion_weight; + + orsat::LinearExpr objective = latency_overrun_sum + agg_bw_expr + congested_link_sum; + return objective; +} + + +vtr::vector> noc_sat_route(bool minimize_aggregate_bandwidth, + const t_noc_opts& noc_opts, + int seed) { + vtr::ScopedStartFinishTimer timer("NoC SAT Routing"); + + // Used to add variables and constraints to a CP-SAT model + orsat::CpModelBuilder cp_model; + + /* For each traffic flow and NoC link pair, we create a boolean variable. + * When a variable associated with traffic flow t and NoC link l is set, + * it means that t is routed through l.*/ + t_flow_link_var_map flow_link_vars; + + /* A boolean variable is associated with each NoC link to indicate + * whether it is congested.*/ + vtr::vector link_congested_vars; + + /* Each traffic flow latency constraint is translated to how many NoC links + * the traffic flow can traverse without violating the constraint. + * These integer variables specify the number of additional links traversed + * beyond the maximum allowed number of links. + */ + std::map latency_overrun_vars; + + create_flow_link_vars(cp_model, flow_link_vars, latency_overrun_vars); + + constrain_latency_overrun_vars(cp_model, flow_link_vars, latency_overrun_vars); + + forbid_illegal_turns(flow_link_vars, cp_model); + + create_congested_link_vars(link_congested_vars, flow_link_vars, cp_model, noc_opts.noc_sat_routing_bandwidth_resolution); + + add_continuity_constraints(flow_link_vars, cp_model); + + auto objective = create_objective(cp_model, flow_link_vars, latency_overrun_vars, link_congested_vars, + noc_opts.noc_sat_routing_bandwidth_resolution, + noc_opts.noc_sat_routing_latency_overrun_weighting, + noc_opts.noc_sat_routing_congestion_weighting, + minimize_aggregate_bandwidth); + + cp_model.Minimize(objective); + + orsat::SatParameters sat_params; + if (noc_opts.noc_sat_routing_num_workers > 0) { + sat_params.set_num_workers(noc_opts.noc_sat_routing_num_workers); + } + sat_params.set_random_seed(seed); + sat_params.set_log_search_progress(noc_opts.noc_sat_routing_log_search_progress); + + orsat::Model model; + model.Add(NewSatParameters(sat_params)); + + orsat::CpSolverResponse response = orsat::SolveCpModel(cp_model.Build(), &model); + + if (response.status() == orsat::CpSolverStatus::FEASIBLE || + response.status() == orsat::CpSolverStatus::OPTIMAL) { + auto routes = convert_vars_to_routes(flow_link_vars, response); + return routes; + } + + // when no feasible solution was found, return an empty vector + return {}; +} + +#endif //ENABLE_NOC_SAT_ROUTING \ No newline at end of file diff --git a/vpr/src/noc/sat_routing.h b/vpr/src/noc/sat_routing.h new file mode 100644 index 00000000000..945b5a0aabe --- /dev/null +++ b/vpr/src/noc/sat_routing.h @@ -0,0 +1,71 @@ +#ifndef VTR_SATROUTING_H +#define VTR_SATROUTING_H + +/** + * @file + * @brief SAT formulation of NoC traffic flow routing. + * + * This file implements a SAT formulation of NoC routing problem. + * Each (traffic flow, link) pair is associated with boolean variable. + * When one of these boolean variables are set by the SAT solver, it means + * that the corresponding traffic flow is routed through the corresponding link. + * To ensure that traffic flow routes are continuous, deadlock-free, with + * minimum link congestion, several constraints are added to the SAT formulation. + * + * For more details refer to the following paper: + * The Road Less Traveled: Congestion-Aware NoC Placement and Packet Routing for FPGAs + */ + +#ifdef ENABLE_NOC_SAT_ROUTING + +#include +#include + +#include "noc_data_types.h" +#include "vpr_types.h" +#include "vtr_hash.h" +#include "vtr_vector.h" +#include "clustered_netlist_fwd.h" + +/** + * @brief Assuming that logical NoC routers are fixed, + * this function formulates routing traffic flows as a SAT + * problem and tries to find a deadlock-free and congestion-free + * routing with the minimum aggregate bandwidth while meeting + * traffic flow latency constraints. + * + * @param minimize_aggregate_bandwidth Indicates whether the SAT solver + * should minimize the aggregate bandwidth or not. A congestion-free + * and deadlock-free solution can be found faster if the solver does not + * need to minimize the aggregate bandwidth. + * @param seed An integer seed to initialize the SAT solver. + * @return The generated routes for all traffic flows. + */ +vtr::vector> noc_sat_route(bool minimize_aggregate_bandwidth, + const t_noc_opts& noc_opts, + int seed); + +namespace std { + +template<> +struct hash> { + /** + * @brief Generates a hash value for a (NocTrafficFlowId, NocLinkId) pair. + * + * This hash function is used to store SAT boolean variables for each + * (traffic flow, link) pair in a hash map. + * + * @param flow_link A (traffic flow, link) pair whose hash value is desired. + * @return The computed hash value. + */ + std::size_t operator()(const std::pair& flow_link) const noexcept { + std::size_t seed = std::hash{}(flow_link.first); + vtr::hash_combine(seed, flow_link.second); + return seed; + } +}; +} // namespace std + + +#endif +#endif \ No newline at end of file diff --git a/vpr/src/noc/turn_model_routing.cpp b/vpr/src/noc/turn_model_routing.cpp index c831ee8cb6e..802a2fa5104 100644 --- a/vpr/src/noc/turn_model_routing.cpp +++ b/vpr/src/noc/turn_model_routing.cpp @@ -98,7 +98,7 @@ NocLinkId TurnModelRouting::move_to_next_router(NocRouterId& curr_router_id, bool visited_next_router = false; // get all the outgoing links for the current router - const auto& router_connections = noc_model.get_noc_router_connections(curr_router_id); + const auto& router_connections = noc_model.get_noc_router_outgoing_links(curr_router_id); // go through each outgoing link and determine whether the link leads towards the intended route direction for (auto connecting_link : router_connections) { @@ -233,3 +233,39 @@ TurnModelRouting::Direction TurnModelRouting::select_direction_other_than(const // if there was not any direction different from "other_than", return INVALID return TurnModelRouting::Direction::INVALID; } + +std::vector> TurnModelRouting::get_all_illegal_turns(const NocStorage& noc_model) const { + std::vector> illegal_turns; + + /* Iterate over all sets of three routers that can be traversed in sequence. + * Check if traversing these three routes involves any turns, and if so, + * check if the resulting turn is illegal under the restrictions of a turn model + * routing algorithm. Store all illegal turns and return them. + */ + + for (const auto& noc_router : noc_model.get_noc_routers()) { + const int noc_router_user_id = noc_router.get_router_user_id(); + const NocRouterId noc_router_id = noc_model.convert_router_id(noc_router_user_id); + VTR_ASSERT(noc_router_id != NocRouterId::INVALID()); + const auto& first_noc_link_ids = noc_model.get_noc_router_outgoing_links(noc_router_id); + + for (auto first_noc_link_id : first_noc_link_ids) { + const NocLink& first_noc_link = noc_model.get_single_noc_link(first_noc_link_id); + const NocRouterId second_noc_router_id = first_noc_link.get_sink_router(); + const NocRouter& second_noc_router = noc_model.get_single_noc_router(second_noc_router_id); + const auto& second_noc_link_ids = noc_model.get_noc_router_outgoing_links(second_noc_router_id); + + for (auto second_noc_link_id : second_noc_link_ids) { + const NocLink& second_noc_link = noc_model.get_single_noc_link(second_noc_link_id); + const NocRouterId third_noc_router_id = second_noc_link.get_sink_router(); + const NocRouter& third_noc_router = noc_model.get_single_noc_router(third_noc_router_id); + if (!is_turn_legal({noc_router, second_noc_router, third_noc_router})) { + illegal_turns.emplace_back(first_noc_link_id, second_noc_link_id); + } + } + } + } + + return illegal_turns; +} + diff --git a/vpr/src/noc/turn_model_routing.h b/vpr/src/noc/turn_model_routing.h index 9de5a3f60c8..68d64f4e12f 100644 --- a/vpr/src/noc/turn_model_routing.h +++ b/vpr/src/noc/turn_model_routing.h @@ -52,6 +52,7 @@ */ #include "noc_routing.h" +#include class TurnModelRouting : public NocRouting { public: @@ -90,6 +91,17 @@ class TurnModelRouting : public NocRouting { std::vector& flow_route, const NocStorage& noc_model) override; + /** + * @brief Turn model algorithms forbid specific turns in the mesh topology + * to guarantee deadlock-freedom. This function finds all illegal turns + * implied by a turn model routing algorithm. + * + * @param noc_model Contains NoC router and link connectivity information. + * @return A vector of std::pair. In each pair, + * a traffic flow cannot traverse the second link after the first link. + */ + std::vector> get_all_illegal_turns(const NocStorage& noc_model) const; + protected: /** * @brief This enum describes the all the possible @@ -237,6 +249,17 @@ class TurnModelRouting : public NocRouting { const NocStorage& noc_model) = 0; + /** + * @brief Determines whether a turn specified by 3 NoC routers visited in the turn + * is legal. Turn model routing algorithms forbid specific turns in the mesh topology + * to guarantee deadlock-freedom. In addition to turns forbidden by the turn model algorithm, + * 180-degree turns are also illegal. + * + * @param noc_routers Three NoC routers visited in a turn. + * @return True if the turn is legal, otherwise false. + */ + virtual bool is_turn_legal(const std::array, 3>& noc_routers) const = 0; + protected: // get_legal_directions() return a reference to this vector to avoid allocating a new vector // each time it is called diff --git a/vpr/src/noc/west_first_routing.cpp b/vpr/src/noc/west_first_routing.cpp index e3308b5b176..5fc4a13e696 100644 --- a/vpr/src/noc/west_first_routing.cpp +++ b/vpr/src/noc/west_first_routing.cpp @@ -93,4 +93,35 @@ TurnModelRouting::Direction WestFirstRouting::select_next_direction(const std::v } return selected_direction; -} \ No newline at end of file +} + +bool WestFirstRouting::is_turn_legal(const std::array, 3>& noc_routers) const { + const int x1 = noc_routers[0].get().get_router_grid_position_x(); + const int y1 = noc_routers[0].get().get_router_grid_position_y(); + + const int x2 = noc_routers[1].get().get_router_grid_position_x(); + const int y2 = noc_routers[1].get().get_router_grid_position_y(); + + const int x3 = noc_routers[2].get().get_router_grid_position_x(); + const int y3 = noc_routers[2].get().get_router_grid_position_y(); + + // check if the given routers can be traversed one after another + VTR_ASSERT(x2 == x1 || y2 == y1); + VTR_ASSERT(x3 == x2 || y3 == y2); + + // going back to the first router is not allowed + if (x1 == x3 && y1 == y3) { + return false; + } + + /* In the west-first routing algorithm, once the traffic flow + * moved in a vertical direction, it is no longer allowed to move + * towards west. Therefore, if the first link was travelling in a + * vertical direction, the second link can't move towards left. + */ + if (y2 != y1 && x3 < x2) { + return false; + } + + return true; +} diff --git a/vpr/src/noc/west_first_routing.h b/vpr/src/noc/west_first_routing.h index a88e90ea7ac..17b3fa9cc74 100644 --- a/vpr/src/noc/west_first_routing.h +++ b/vpr/src/noc/west_first_routing.h @@ -65,6 +65,8 @@ class WestFirstRouting : public TurnModelRouting { NocRouterId curr_router_id, NocTrafficFlowId traffic_flow_id, const NocStorage& noc_model) override; + + bool is_turn_legal(const std::array, 3>& noc_routers) const override; }; #endif //VTR_WEST_FIRST_ROUTING_H diff --git a/vpr/src/noc/xy_routing.cpp b/vpr/src/noc/xy_routing.cpp index 31edef2b5b4..f3f1f7e4dfa 100644 --- a/vpr/src/noc/xy_routing.cpp +++ b/vpr/src/noc/xy_routing.cpp @@ -80,4 +80,31 @@ TurnModelRouting::Direction XYRouting::select_next_direction(const std::vector, 3>& noc_routers) const { + const int x1 = noc_routers[0].get().get_router_grid_position_x(); + const int y1 = noc_routers[0].get().get_router_grid_position_y(); + + const int x2 = noc_routers[1].get().get_router_grid_position_x(); + const int y2 = noc_routers[1].get().get_router_grid_position_y(); + + const int x3 = noc_routers[2].get().get_router_grid_position_x(); + const int y3 = noc_routers[2].get().get_router_grid_position_y(); + + // check if the given routers can be traversed one after another + VTR_ASSERT(x2 == x1 || y2 == y1); + VTR_ASSERT(x3 == x2 || y3 == y2); + + // going back to the first router is not allowed + if (x1 == x3 && y1 == y3) { + return false; + } + + // if the first move is vertical, the second one can't be horizontal + if (y1 != y2 && x2 != x3) { + return false; + } + + return true; } \ No newline at end of file diff --git a/vpr/src/noc/xy_routing.h b/vpr/src/noc/xy_routing.h index 43da6be4881..199855710fe 100644 --- a/vpr/src/noc/xy_routing.h +++ b/vpr/src/noc/xy_routing.h @@ -105,6 +105,7 @@ class XYRouting : public TurnModelRouting { NocTrafficFlowId traffic_flow_id, const NocStorage& noc_model) override; + bool is_turn_legal(const std::array, 3>& noc_routers) const override; private: const std::vector x_axis_directions {TurnModelRouting::Direction::LEFT, TurnModelRouting::Direction::RIGHT}; diff --git a/vpr/src/place/initial_noc_placement.cpp b/vpr/src/place/initial_noc_placement.cpp index e039d3b4ee4..2cf2bf3f1f3 100644 --- a/vpr/src/place/initial_noc_placement.cpp +++ b/vpr/src/place/initial_noc_placement.cpp @@ -4,7 +4,11 @@ #include "noc_place_utils.h" #include "noc_place_checkpoint.h" #include "place_constraints.h" + +#include "sat_routing.h" + #include "vtr_math.h" +#include "vtr_time.h" #include #include @@ -253,11 +257,11 @@ static void noc_routers_anneal(const t_noc_opts& noc_opts) { } void initial_noc_placement(const t_noc_opts& noc_opts, const t_placer_opts& placer_opts) { + vtr::ScopedStartFinishTimer timer("Initial NoC Placement"); auto& noc_ctx = g_vpr_ctx.noc(); // Get all the router clusters const std::vector& router_blk_ids = noc_ctx.noc_traffic_flows_storage.get_router_clusters_in_netlist(); - // Holds all the routers that are not fixed into a specific location by constraints std::vector unfixed_routers; @@ -279,7 +283,7 @@ void initial_noc_placement(const t_noc_opts& noc_opts, const t_placer_opts& plac place_noc_routers_randomly(unfixed_routers, placer_opts.seed); // populate internal data structures to maintain route, bandwidth usage, and latencies - initial_noc_routing(); + initial_noc_routing({}); // Run the simulated annealing optimizer for NoC routers noc_routers_anneal(noc_opts); diff --git a/vpr/src/place/initial_placement.cpp b/vpr/src/place/initial_placement.cpp index e462b5b6f1e..50d65da2aa9 100644 --- a/vpr/src/place/initial_placement.cpp +++ b/vpr/src/place/initial_placement.cpp @@ -501,7 +501,7 @@ static void update_blk_type_first_loc(int blk_type_column_index, t_logical_block_type_ptr block_type, const t_pl_macro& pl_macro, std::vector* blk_types_empty_locs_in_grid) { //check if dense placement could place macro successfully - if (blk_type_column_index == -1 || blk_types_empty_locs_in_grid->size() <= abs(blk_type_column_index)) { + if (blk_type_column_index == -1 || blk_types_empty_locs_in_grid->size() <= (size_t)abs(blk_type_column_index)) { return; } diff --git a/vpr/src/place/noc_place_checkpoint.cpp b/vpr/src/place/noc_place_checkpoint.cpp index e0f41dc94f0..3e11d9c8eb6 100644 --- a/vpr/src/place/noc_place_checkpoint.cpp +++ b/vpr/src/place/noc_place_checkpoint.cpp @@ -68,7 +68,7 @@ void NoCPlacementCheckpoint::restore_checkpoint(t_placer_costs& costs) { } // Re-initialize routes and static variables that keep track of NoC-related costs - reinitialize_noc_routing(costs); + reinitialize_noc_routing(costs, {}); } bool NoCPlacementCheckpoint::is_valid() const { diff --git a/vpr/src/place/noc_place_utils.cpp b/vpr/src/place/noc_place_utils.cpp index f2c6be0fb8b..0b9e274b85b 100644 --- a/vpr/src/place/noc_place_utils.cpp +++ b/vpr/src/place/noc_place_utils.cpp @@ -12,6 +12,10 @@ #include "place_constraints.h" #include "move_transactions.h" +#ifdef ENABLE_NOC_SAT_ROUTING +#include "sat_routing.h" +#endif + #include /********************** Variables local to noc_place_utils.c pp***************************/ @@ -54,40 +58,56 @@ static bool select_random_router_cluster(ClusterBlockId& b_from, static std::vector find_affected_links_by_flow_reroute(std::vector& prev_links, std::vector& curr_links); -void initial_noc_routing() { +void initial_noc_routing(const vtr::vector>& new_traffic_flow_routes) { // need to update the link usages within after routing all the traffic flows // also need to route all the traffic flows and store them auto& noc_ctx = g_vpr_ctx.mutable_noc(); NocTrafficFlows& noc_traffic_flows_storage = noc_ctx.noc_traffic_flows_storage; + VTR_ASSERT(new_traffic_flow_routes.size() == (size_t)noc_traffic_flows_storage.get_number_of_traffic_flows() || + new_traffic_flow_routes.empty()); + /* We need all the traffic flow ids to be able to access them. The range * of traffic flow ids go from 0 to the total number of traffic flows within * the NoC. - * go through all the traffic flows and route them. Then once routed, update the links used in the routed traffic flows with their usages + * Go through all the traffic flows and route them. Then once routed, + * update the links used in the routed traffic flows with their usages */ for (const auto& traffic_flow_id : noc_traffic_flows_storage.get_all_traffic_flow_id()) { const t_noc_traffic_flow& curr_traffic_flow = noc_traffic_flows_storage.get_single_noc_traffic_flow(traffic_flow_id); - // update the traffic flow route based on where the router cluster blocks are placed - std::vector& curr_traffic_flow_route = route_traffic_flow(traffic_flow_id, noc_ctx.noc_model, noc_traffic_flows_storage, *noc_ctx.noc_flows_router); + /* Update the traffic flow route based on where the router cluster blocks are placed. + * If the caller has not provided traffic flow routes, route traffic flow, otherwise use the provided route. + */ + const std::vector& curr_traffic_flow_route = new_traffic_flow_routes.empty() + ? route_traffic_flow(traffic_flow_id, noc_ctx.noc_model, noc_traffic_flows_storage, *noc_ctx.noc_flows_router) + : new_traffic_flow_routes[traffic_flow_id]; + + if (!new_traffic_flow_routes.empty()) { + noc_traffic_flows_storage.get_mutable_traffic_flow_route(traffic_flow_id) = curr_traffic_flow_route; + } // update the links used in the found traffic flow route, links' bandwidth should be incremented since the traffic flow is routed update_traffic_flow_link_usage(curr_traffic_flow_route, noc_ctx.noc_model, 1, curr_traffic_flow.traffic_flow_bandwidth); } } -void reinitialize_noc_routing(t_placer_costs& costs) { +void reinitialize_noc_routing(t_placer_costs& costs, + const vtr::vector>& new_traffic_flow_routes) { // used to access NoC links and modify them auto& noc_ctx = g_vpr_ctx.mutable_noc(); + VTR_ASSERT((size_t)noc_ctx.noc_traffic_flows_storage.get_number_of_traffic_flows() == new_traffic_flow_routes.size() || + new_traffic_flow_routes.empty()); + // Zero out bandwidth usage for all links for (auto& noc_link : noc_ctx.noc_model.get_mutable_noc_links()) { noc_link.set_bandwidth_usage(0.0); } // Route traffic flows and update link bandwidth usage - initial_noc_routing(); + initial_noc_routing(new_traffic_flow_routes); // Initialize traffic_flow_costs costs.noc_cost_terms.aggregate_bandwidth = comp_noc_aggregate_bandwidth_cost(); @@ -206,7 +226,10 @@ std::vector& route_traffic_flow(NocTrafficFlowId traffic_flow_id, return curr_traffic_flow_route; } -void update_traffic_flow_link_usage(const std::vector& traffic_flow_route, NocStorage& noc_model, int inc_or_dec, double traffic_flow_bandwidth) { +void update_traffic_flow_link_usage(const std::vector& traffic_flow_route, + NocStorage& noc_model, + int inc_or_dec, + double traffic_flow_bandwidth) { // go through the links within the traffic flow route and update their bandwidth usage for (auto& link_in_route_id : traffic_flow_route) { // get the link to update and its current bandwidth @@ -890,15 +913,67 @@ bool noc_routing_has_cycle() { // get all traffic flow routes const auto& traffic_flow_routes = noc_ctx.noc_traffic_flows_storage.get_all_traffic_flow_routes(); - // get the total number of NoC links - const size_t num_noc_links = noc_ctx.noc_model.get_number_of_noc_links(); + bool has_cycle = noc_routing_has_cycle(traffic_flow_routes); + + return has_cycle; +} + +bool noc_routing_has_cycle(const vtr::vector>& routes) { + const auto& noc_ctx = g_vpr_ctx.noc(); + const auto& place_ctx = g_vpr_ctx.placement(); + + ChannelDependencyGraph channel_dependency_graph(noc_ctx.noc_model, + noc_ctx.noc_traffic_flows_storage, + routes, + place_ctx.block_locs); - ChannelDependencyGraph channel_dependency_graph(num_noc_links, traffic_flow_routes); bool has_cycles = channel_dependency_graph.has_cycles(); return has_cycles; } +#ifdef ENABLE_NOC_SAT_ROUTING +void invoke_sat_router(t_placer_costs& costs, const t_noc_opts& noc_opts, int seed) { + + auto traffic_flow_routes = noc_sat_route(true, noc_opts, seed); + + if (!traffic_flow_routes.empty()) { + bool has_cycle = noc_routing_has_cycle(traffic_flow_routes); + if (has_cycle) { + VTR_LOG("SAT NoC routing has cycles.\n"); + } + + reinitialize_noc_routing(costs, traffic_flow_routes); + + print_noc_costs("\nNoC Placement Costs after SAT routing", costs, noc_opts); + + } else { + VTR_LOG("SAT routing failed.\n"); + } +} +#endif + +void print_noc_costs(std::string_view header, const t_placer_costs& costs, const t_noc_opts& noc_opts) { + VTR_LOG("%s. " + "cost: %g, " + "aggregate_bandwidth_cost: %g, " + "latency_cost: %g, " + "n_met_latency_constraints: %d, " + "latency_overrun_cost: %g, " + "congestion_cost: %g, " + "accum_congested_ratio: %g, " + "n_congested_links: %d \n", + header.data(), + calculate_noc_cost(costs.noc_cost_terms, costs.noc_cost_norm_factors, noc_opts), + costs.noc_cost_terms.aggregate_bandwidth, + costs.noc_cost_terms.latency, + get_number_of_traffic_flows_with_latency_cons_met(), + costs.noc_cost_terms.latency_overrun, + costs.noc_cost_terms.congestion, + get_total_congestion_bandwidth_ratio(), + get_number_of_congested_noc_links()); +} + static std::vector find_affected_links_by_flow_reroute(std::vector& prev_links, std::vector& curr_links) { // Sort both link containers diff --git a/vpr/src/place/noc_place_utils.h b/vpr/src/place/noc_place_utils.h index 0563a22f7bf..a0e675ea7d7 100644 --- a/vpr/src/place/noc_place_utils.h +++ b/vpr/src/place/noc_place_utils.h @@ -1,6 +1,7 @@ #ifndef NOC_PLACE_UTILS_H #define NOC_PLACE_UTILS_H +#include #include "move_utils.h" #include "place_util.h" @@ -38,23 +39,27 @@ struct TrafficFlowPlaceCost { }; /** - * @brief Routes all the traffic flows within the NoC and updates the link usage - * for all links. This should be called after initial placement, where all the - * logical NoC router blocks have been placed for the first time and no traffic - * flows have been routed yet. This function should also only be used once as - * its intended use is to initialize the routes for all the traffic flows. - * - * This is different from a complete re-route of the traffic flows as this - * function assumes the traffic flows have not been routed yet, whereas a - * complete re-route function assumes the traffic flows have already been - * routed. This is why this function should only be used once. - * + * @brief Initializes the link bandwidth usage for all NoC links. + * + * If traffic flow routes are not passed to this function, it uses a NoC routing algorithm + * to route all traffic flows. The caller can prevent this function from routing traffic flows + * by passing routes for all traffic flows. This should be called after initial placement, + * where all the logical NoC router blocks have been placed for the first time and no traffic + * flows have been routed yet. In this case an empty vector should be passed to the function. + * This function can also be called after modification of traffic flow routes. For example, + * NoC SAT routing algorithm generates new traffic flow routes to avoid congestion. The routes + * generate by the SAT router should be passed to this function + * + * + * @param new_traffic_flow_routes Traffic flow routes used to initialize link bandwidth utilization. + * If an empty vector is passed, this function uses a routing algorithm to route traffic flows. */ -void initial_noc_routing(); +void initial_noc_routing(const vtr::vector>& new_traffic_flow_routes); /** - * @brief Zeros out all link bandwidth usage an re-routes traffic flows. - * Initializes static variables in noc_place_utils.cpp that are used to + * @brief Re-initializes all link bandwidth usages by either re-routing + * all traffic flows or using the provided traffic flow routes. This functions + * also initializes static variables in noc_place_utils.cpp that are used to * keep track of NoC-related costs. * * This function should be called when a placement checkpoint is restored. @@ -63,9 +68,15 @@ void initial_noc_routing(); * traffic flow routes, and static variable in noc_place_utils.cpp are no * longer valid and need to be re-initialized. * + * This function should be called after NoC SAT routing algorithm returns its + * traffic flow routes. + * * @param costs Used to get aggregate bandwidth and latency costs. + * @param new_traffic_flow_routes Traffic flow routes used to initialize link bandwidth utilization. +* If an empty vector is passed, this function uses a routing algorithm to route traffic flows. */ -void reinitialize_noc_routing(t_placer_costs& costs); +void reinitialize_noc_routing(t_placer_costs& costs, + const vtr::vector>& new_traffic_flow_routes); /** * @brief Goes through all the cluster blocks that were moved @@ -474,7 +485,6 @@ std::vector get_top_n_congestion_ratios(int n); * initialize the datastructures here. * * This should be called before starting the simulated annealing placement. - * */ void allocate_and_load_noc_placement_structs(); @@ -534,7 +544,6 @@ e_create_move propose_router_swap(t_pl_blocks_to_be_moved& blocks_affected, floa * information. * */ - void write_noc_placement_file(const std::string& file_name); /** @@ -552,4 +561,34 @@ void write_noc_placement_file(const std::string& file_name); */ bool noc_routing_has_cycle(); +/** + * @brief Check if the channel dependency graph created from the given traffic flow routes + * has any cycles. + * @param routes The user provided traffic flow routes. + * @return True if there is any cycles in the channel dependency graph. + */ +bool noc_routing_has_cycle(const vtr::vector>& routes); + +/** + * @brief Invokes NoC SAT router and print new NoC cost terms after SAT router + * solved the NoC routing problem. + * + * @param costs To be updated with new NoC-related cost terms after traffic flow routes + * generated by the SAT router replace the old traffic flow routes. + * @param noc_opts Contains NoC-related cost weighting factor used in the SAT router. + * @param seed The initialization seed used in the SAT solver. + */ +#ifdef ENABLE_NOC_SAT_ROUTING +void invoke_sat_router(t_placer_costs& costs, const t_noc_opts& noc_opts, int seed); +#endif + +/** + * @brief Prints NoC related costs terms and metrics. + * + * @param header The string with which the report starts. + * @param costs Contains NoC-related cost terms. + * @param noc_opts Used to compute total NoC cost. + */ +void print_noc_costs(std::string_view header, const t_placer_costs& costs, const t_noc_opts& noc_opts); + #endif \ No newline at end of file diff --git a/vpr/src/place/place.cpp b/vpr/src/place/place.cpp index 6c71635e24b..357b8532016 100644 --- a/vpr/src/place/place.cpp +++ b/vpr/src/place/place.cpp @@ -821,28 +821,11 @@ void try_place(const Netlist<>& net_list, placer_opts.place_algorithm, noc_opts); - //Initial pacement statistics + //Initial placement statistics VTR_LOG("Initial placement cost: %g bb_cost: %g td_cost: %g\n", costs.cost, costs.bb_cost, costs.timing_cost); if (noc_opts.noc) { - VTR_LOG( - "NoC Placement Costs. " - "cost: %g, " - "aggregate_bandwidth_cost: %g, " - "latency_cost: %g, " - "n_met_latency_constraints: %d, " - "latency_overrun_cost: %g, " - "congestion_cost: %g, " - "accum_congested_ratio: %g, " - "n_congested_links: %d \n", - calculate_noc_cost(costs.noc_cost_terms, costs.noc_cost_norm_factors, noc_opts), - costs.noc_cost_terms.aggregate_bandwidth, - costs.noc_cost_terms.latency, - get_number_of_traffic_flows_with_latency_cons_met(), - costs.noc_cost_terms.latency_overrun, - costs.noc_cost_terms.congestion, - get_total_congestion_bandwidth_ratio(), - get_number_of_congested_noc_links()); + print_noc_costs("Initial NoC Placement Costs", costs, noc_opts); } if (placer_opts.place_algorithm.is_timing_driven()) { VTR_LOG( @@ -874,26 +857,7 @@ void try_place(const Netlist<>& net_list, sprintf(msg, "Initial Placement. Cost: %g BB Cost: %g TD Cost %g \t Channel Factor: %d", costs.cost, costs.bb_cost, costs.timing_cost, width_fac); - if (noc_opts.noc) { - sprintf(msg, - "\nInitial NoC Placement Costs. " - "cost: %g, " - "aggregate_bandwidth_cost: %g, " - "latency_cost: %g, " - "n_met_latency_constraints: %d, " - "latency_overrun_cost: %g, " - "congestion_cost: %g, " - "accum_congested_ratio: %g, " - "n_congested_links: %d \n", - calculate_noc_cost(costs.noc_cost_terms, costs.noc_cost_norm_factors, noc_opts), - costs.noc_cost_terms.aggregate_bandwidth, - costs.noc_cost_terms.latency, - get_number_of_traffic_flows_with_latency_cons_met(), - costs.noc_cost_terms.latency_overrun, - costs.noc_cost_terms.congestion, - get_total_congestion_bandwidth_ratio(), - get_number_of_congested_noc_links()); - } + //Draw the initial placement update_screen(ScreenUpdatePriority::MAJOR, msg, PLACEMENT, timing_info); @@ -1203,44 +1167,16 @@ void try_place(const Netlist<>& net_list, costs.bb_cost, costs.timing_cost); // print the noc costs info if (noc_opts.noc) { - sprintf(msg, - "\nNoC Placement Costs. " - "cost: %g, " - "aggregate_bandwidth_cost: %g, " - "latency_cost: %g, " - "n_met_latency_constraints: %d, " - "latency_overrun_cost: %g, " - "congestion_cost: %g, " - "accum_congested_ratio: %g, " - "n_congested_links: %d \n", - calculate_noc_cost(costs.noc_cost_terms, costs.noc_cost_norm_factors, noc_opts), - costs.noc_cost_terms.aggregate_bandwidth, - costs.noc_cost_terms.latency, - get_number_of_traffic_flows_with_latency_cons_met(), - costs.noc_cost_terms.latency_overrun, - costs.noc_cost_terms.congestion, - get_total_congestion_bandwidth_ratio(), - get_number_of_congested_noc_links()); + print_noc_costs("\nNoC Placement Costs", costs, noc_opts); - VTR_LOG( - "\nNoC Placement Costs. " - "cost: %g, " - "aggregate_bandwidth_cost: %g, " - "latency_cost: %g, " - "n_met_latency_constraints: %d, " - "latency_overrun_cost: %g, " - "congestion_cost: %g, " - "accum_congested_ratio: %g, " - "n_congested_links: %d \n", - calculate_noc_cost(costs.noc_cost_terms, costs.noc_cost_norm_factors, noc_opts), - costs.noc_cost_terms.aggregate_bandwidth, - costs.noc_cost_terms.latency, - get_number_of_traffic_flows_with_latency_cons_met(), - costs.noc_cost_terms.latency_overrun, - costs.noc_cost_terms.congestion, - get_total_congestion_bandwidth_ratio(), - get_number_of_congested_noc_links()); +#ifdef ENABLE_NOC_SAT_ROUTING + if (costs.noc_cost_terms.congestion > 0.0) { + VTR_LOG("NoC routing configuration is congested. Invoking the SAT NoC router.\n"); + invoke_sat_router(costs, noc_opts, placer_opts.seed); + } +#endif //ENABLE_NOC_SAT_ROUTING } + update_screen(ScreenUpdatePriority::MAJOR, msg, PLACEMENT, timing_info); // Print out swap statistics print_resources_utilization(); @@ -3400,7 +3336,7 @@ static void update_bb(ClusterNetId net_id, bb_edge_new.xmax = curr_bb_edge->xmax - 1; bb_coord_new.xmax = curr_bb_coord->xmax; } - } else { /* Move to left, old postion was not at xmax. */ + } else { /* Move to left, old position was not at xmax. */ bb_coord_new.xmax = curr_bb_coord->xmax; bb_edge_new.xmax = curr_bb_edge->xmax; } diff --git a/vpr/src/place/place_checkpoint.cpp b/vpr/src/place/place_checkpoint.cpp index 73d49e6e80c..86af65142c3 100644 --- a/vpr/src/place/place_checkpoint.cpp +++ b/vpr/src/place/place_checkpoint.cpp @@ -63,7 +63,7 @@ void restore_best_placement(t_placement_checkpoint& placement_checkpoint, * and need to be re-computed from scratch. */ if (noc_opts.noc) { - reinitialize_noc_routing(costs); + reinitialize_noc_routing(costs, {}); } VTR_LOG("\nCheckpoint restored\n"); diff --git a/vpr/test/test_noc_place_utils.cpp b/vpr/test/test_noc_place_utils.cpp index 81cc83bcf56..80023cf8b81 100644 --- a/vpr/test/test_noc_place_utils.cpp +++ b/vpr/test/test_noc_place_utils.cpp @@ -194,7 +194,7 @@ TEST_CASE("test_initial_noc_placement", "[noc_place_utils]") { } // now call the test function - initial_noc_routing(); + initial_noc_routing({}); // now verify the function by comparing the link bandwidths in the noc model (should have been updated by the test function) to the golden set int number_of_links = golden_link_bandwidths.size(); @@ -389,7 +389,7 @@ TEST_CASE("test_initial_comp_cost_functions", "[noc_place_utils]") { // assume this works // this is needed to set up the global noc packet router and also global datastructures - initial_noc_routing(); + initial_noc_routing({}); SECTION("test_comp_noc_aggregate_bandwidth_cost") { //initialize all the cost calculator datastructures @@ -676,7 +676,7 @@ TEST_CASE("test_find_affected_noc_routers_and_update_noc_costs, test_commit_noc_ // assume this works // this is needed to set up the global noc packet router and also global datastructures - initial_noc_routing(); + initial_noc_routing({}); // datastructure below will store the bandwidth usages of all the links // and will be updated throughout this test. @@ -1540,7 +1540,7 @@ TEST_CASE("test_revert_noc_traffic_flow_routes", "[noc_place_utils]") { // assume this works // this is needed to set up the global noc packet router and also global datastructures - initial_noc_routing(); + initial_noc_routing({}); // datastructure below will store the bandwidth usages of all the links // and will be updated throughout this test. diff --git a/vpr/test/test_noc_storage.cpp b/vpr/test/test_noc_storage.cpp index 0578c438dac..2f24b4a6542 100644 --- a/vpr/test/test_noc_storage.cpp +++ b/vpr/test/test_noc_storage.cpp @@ -270,7 +270,7 @@ TEST_CASE("test_router_link_list", "[vpr_noc]") { source = (NocRouterId)id; // get the router connections from the - const std::vector& router_links = test_noc.get_noc_router_connections(source); + const std::vector& router_links = test_noc.get_noc_router_outgoing_links(source); // get the size of the current router connection list connection_size = golden_set[source].size(); @@ -356,7 +356,7 @@ TEST_CASE("test_remove_link", "[vpr_noc]") { // variable to keep track of whether the link was deleted from the vector outgoing links of its source router bool link_removed_from_outgoing_vector = true; - auto& outgoing_links = test_noc.get_noc_router_connections(link_to_remove_src_router); + auto& outgoing_links = test_noc.get_noc_router_outgoing_links(link_to_remove_src_router); // go through all the outgoing links of the source router in the link we removed and check that the link does not exist there as well. for (auto outgoing_link : outgoing_links) { // get the current outgoing link diff --git a/vpr/test/test_setup_noc.cpp b/vpr/test/test_setup_noc.cpp index 0d36fa2af93..4c6f0a3942a 100644 --- a/vpr/test/test_setup_noc.cpp +++ b/vpr/test/test_setup_noc.cpp @@ -687,7 +687,7 @@ TEST_CASE("test_create_noc_links", "[vpr_setup_noc]") { router_connection = noc_info.router_list[router_id - 1].connection_list.begin(); - for (auto noc_link = noc_model.get_noc_router_connections(current_source_router_id).begin(); noc_link != noc_model.get_noc_router_connections(current_source_router_id).end(); noc_link++) { + for (auto noc_link = noc_model.get_noc_router_outgoing_links(current_source_router_id).begin(); noc_link != noc_model.get_noc_router_outgoing_links(current_source_router_id).end(); noc_link++) { // get the connecting link const NocLink& connecting_link = noc_model.get_single_noc_link(*noc_link); diff --git a/vtr_flow/parse/parse_config/vpr_noc.txt b/vtr_flow/parse/parse_config/vpr_noc.txt index 51b7c194712..5a916529cac 100644 --- a/vtr_flow/parse/parse_config/vpr_noc.txt +++ b/vtr_flow/parse/parse_config/vpr_noc.txt @@ -18,3 +18,11 @@ NoC_latency_overrun;vpr.out;NoC Placement Costs. cost: .*, aggregate_bandwidth_c NoC_congested_bw;vpr.out;NoC Placement Costs. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: .*, n_met_latency_constraints: .*, latency_overrun_cost: .*, congestion_cost: (.*), accum_congested_ratio: .*, n_congested_links: .* NoC_congestion_ratio;vpr.out;NoC Placement Costs. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: .*, n_met_latency_constraints: .*, latency_overrun_cost: .*, congestion_cost: .*, accum_congested_ratio: (.*), n_congested_links: .* NoC_n_congested_links;vpr.out;NoC Placement Costs. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: .*, n_met_latency_constraints: .*, latency_overrun_cost: .*, congestion_cost: .*, accum_congested_ratio: .*, n_congested_links: (.*) +SAT_agg_bandwidth;vpr.out;NoC Placement Costs after SAT routing. cost: .*, aggregate_bandwidth_cost: (.*), latency_cost: .*, n_met_latency_constraints: .*, latency_overrun_cost: .*, congestion_cost: .*, accum_congested_ratio: .*, n_congested_links: .* +SAT_latency;vpr.out;NoC Placement Costs after SAT routing. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: (.*), n_met_latency_constraints: .*, latency_overrun_cost: .*, congestion_cost: .*, accum_congested_ratio: .*, n_congested_links: .* +SAT_n_met_latency_constraints;vpr.out;NoC Placement Costs after SAT routing. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: .*, n_met_latency_constraints: (.*), latency_overrun_cost: .*, congestion_cost: .*, accum_congested_ratio: .*, n_congested_links: .* +SAT_latency_overrun;vpr.out;NoC Placement Costs after SAT routing. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: .*, n_met_latency_constraints: .*, latency_overrun_cost: (.*), congestion_cost: .*, accum_congested_ratio: .*, n_congested_links: .* +SAT_congested_bw;vpr.out;NoC Placement Costs after SAT routing. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: .*, n_met_latency_constraints: .*, latency_overrun_cost: .*, congestion_cost: (.*), accum_congested_ratio: .*, n_congested_links: .* +SAT_congestion_ratio;vpr.out;NoC Placement Costs after SAT routing. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: .*, n_met_latency_constraints: .*, latency_overrun_cost: .*, congestion_cost: .*, accum_congested_ratio: (.*), n_congested_links: .* +SAT_n_congested_links;vpr.out;NoC Placement Costs after SAT routing. cost: .*, aggregate_bandwidth_cost: .*, latency_cost: .*, n_met_latency_constraints: .*, latency_overrun_cost: .*, congestion_cost: .*, accum_congested_ratio: .*, n_congested_links: (.*) +