diff --git a/.github/scripts/hostsetup.sh b/.github/scripts/hostsetup.sh index 48f56a066a9..2dfd0c141c5 100755 --- a/.github/scripts/hostsetup.sh +++ b/.github/scripts/hostsetup.sh @@ -69,7 +69,9 @@ apt install -y \ default-jdk \ g++-9 \ gcc-9 \ - wget + wget \ + openssl \ + libssl-dev # installing the latest version of cmake apt install -y apt-transport-https ca-certificates gnupg diff --git a/.github/scripts/install_dependencies.sh b/.github/scripts/install_dependencies.sh index e0acc3255db..6f936c0cd60 100755 --- a/.github/scripts/install_dependencies.sh +++ b/.github/scripts/install_dependencies.sh @@ -56,7 +56,8 @@ sudo apt install -y \ clang-13 \ clang-14 \ clang-format-14 \ - libtbb-dev + libtbb-dev \ + openssl pip install -r requirements.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5199c7c42cb..7babcd42360 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ option(VTR_ENABLE_SANITIZE "Enable address/leak/undefined-behaviour sanitizers ( option(VTR_ENABLE_PROFILING "Enable performance profiler (gprof)" OFF) option(VTR_ENABLE_COVERAGE "Enable code coverage tracking (gcov)" OFF) option(VTR_ENABLE_DEBUG_LOGGING "Enable debug logging" OFF) +option(VTR_ENABLE_VERSION "Enable version number up-to-date during compilation" ON) option(VTR_ENABLE_VERBOSE "Enable increased debug verbosity" OFF) option(SPEC_CPU "Enable SPEC CPU v8 support" OFF) diff --git a/doc/src/vpr/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index 41ace87adb2..d6905231db9 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -358,13 +358,13 @@ Use the options below to override this default naming behaviour. .. seealso:: :ref:`Routing Resource XML File `. -.. option:: --read_vpr_constraints +.. option:: --read_vpr_constraints ::...: - Reads the :ref:`floorplanning constraints ` that packing and placement must respect from the specified XML file. + Reads the :ref:`floorplanning constraints ` that packing and placement must respect from the specified XML file; and/or reads the :ref:`global route constraints ` that router must respect from the specified XML file. Multiple files are allowed and should be seperated with a colomn char. .. option:: --write_vpr_constraints - Writes out new :ref:`floorplanning constraints ` based on current placement to the specified XML file. + Writes out new :ref:`floorplanning constraints ` based on current placement to the specified XML file; and/or writes out new :ref:`global route constraints ` based on current global routecounstraints to the specified XML file. Note that a single combined file is written to even there are multiple input constraint files read in. .. option:: --read_router_lookahead diff --git a/doc/src/vpr/route_constraints.rst b/doc/src/vpr/route_constraints.rst new file mode 100644 index 00000000000..fe921cd5a23 --- /dev/null +++ b/doc/src/vpr/route_constraints.rst @@ -0,0 +1,44 @@ + +VPR Route Constraints +========================= +.. _vpr_constraints_file: +VPR supports running flows with route constraints. Route constraints are set on global signals to specify if they should be routed or not. For example, a user may want to route a specific internal clock even clock modeling option is set to not route it. + +.. note:: The constraint specified in this file overrides the setting of option "--clock_modeling" if it is specified. A message will be issued in such case: "Route constraint(s) detected and will override clock modeling setting". + +The route constraints should be specified by the user using an XML constraints file format, as described in the section below. + +A Constraints File Example +-------------------------- + +.. code-block:: xml + :caption: An example of a route constraints file in XML format. + :linenos: + + + + + + + + + + +.. _end: + +.. note:: The "route_model" in constraint specified in this file only support "ideal" and "route" only. + +Constraints File Format +----------------------- + +VPR has a specific XML format which must be used when creating a route constraints file. The purpose of this constraints file is to specify + +#. The signals that should be constrained for routing +#. The route model for such signals + +The file is passed as an input to VPR when running with route constraints. When the file is read in, its information is used to guide VPR route or not route such signals. + +.. note:: Use the VPR option :vpr:option:`--read_vpr_constraints` to specify the VPR route constraints file that is to be loaded. + +.. note:: Wildcard names of signals are supported to specify a list of signals. The wildcard expression should follow the C/C++ regexpr rule. + diff --git a/install_apt_packages.sh b/install_apt_packages.sh index 533fdf608d5..17d9aa49425 100755 --- a/install_apt_packages.sh +++ b/install_apt_packages.sh @@ -9,7 +9,9 @@ sudo apt-get install -y \ bison \ flex \ python3-dev \ - python3-venv + python3-venv \ + openssl \ + libssl-dev # Required for graphics sudo apt-get install -y \ diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 094f7694cad..a02d6ae1cc7 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -9,6 +9,8 @@ link_libraries(${ADDITIONAL_FLAGS}) add_subdirectory(libarchfpga) add_subdirectory(libvtrutil) add_subdirectory(liblog) +add_subdirectory(libencrypt) +add_subdirectory(libdecrypt) add_subdirectory(libpugiutil) add_subdirectory(libvqm) add_subdirectory(librtlnumber) diff --git a/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp b/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp index 9383257cecb..4f35b27b7a5 100644 --- a/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp +++ b/libs/EXTERNAL/libcatch2/src/catch2/internal/catch_fatal_condition_handler.cpp @@ -202,7 +202,7 @@ namespace Catch { FatalConditionHandler::FatalConditionHandler() { assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); if (altStackSize == 0) { - altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); + altStackSize = std::max(static_cast(/*SIGSTKSZ*/32768), minStackSizeForErrors); } altStackMem = new char[altStackSize](); } diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp index 609b0c0b03e..69c9ba07a83 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.cpp @@ -99,6 +99,15 @@ void TimingReporter::report_timing_setup(std::ostream& os, report_timing(os, paths); } +void TimingReporter::report_timing_setup(std::vector& paths, + std::ostream& os, + const SetupTimingAnalyzer& setup_analyzer, + size_t npaths) const { + paths = path_collector_.collect_worst_setup_timing_paths(timing_graph_, setup_analyzer, npaths); + + report_timing(os, paths); +} + void TimingReporter::report_timing_hold(std::string filename, const HoldTimingAnalyzer& hold_analyzer, size_t npaths) const { @@ -114,6 +123,15 @@ void TimingReporter::report_timing_hold(std::ostream& os, report_timing(os, paths); } +void TimingReporter::report_timing_hold(std::vector& paths, + std::ostream& os, + const HoldTimingAnalyzer& hold_analyzer, + size_t npaths) const { + paths = path_collector_.collect_worst_hold_timing_paths(timing_graph_, hold_analyzer, npaths); + + report_timing(os, paths); +} + void TimingReporter::report_skew_setup(std::string filename, const SetupTimingAnalyzer& setup_analyzer, size_t nworst) const { diff --git a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp index 1569aa6d704..590e3bca690 100644 --- a/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp +++ b/libs/EXTERNAL/libtatum/libtatum/tatum/TimingReporter.hpp @@ -64,9 +64,11 @@ class TimingReporter { public: void report_timing_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_setup(std::vector& paths, std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_hold(std::string filename, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_timing_hold(std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; + void report_timing_hold(std::vector& paths, std::ostream& os, const tatum::HoldTimingAnalyzer& hold_analyzer, size_t npaths=REPORT_TIMING_DEFAULT_NPATHS) const; void report_skew_setup(std::string filename, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; void report_skew_setup(std::ostream& os, const tatum::SetupTimingAnalyzer& setup_analyzer, size_t nworst=REPORT_TIMING_DEFAULT_NPATHS) const; diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index c00d65bfb41..2999b3357ce 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -166,8 +166,11 @@ enum e_pin_type { enum e_interconnect { COMPLETE_INTERC = 1, DIRECT_INTERC = 2, - MUX_INTERC = 3 + MUX_INTERC = 3, + NUM_INTERC_TYPES /* Invalid type */ }; +/* String version of interconnect types. Use for debugging messages */ +constexpr std::array INTERCONNECT_TYPE_STRING = {{"unknown", "complete", "direct", "mux"}}; /* Orientations. */ enum e_side : unsigned char { @@ -1501,7 +1504,7 @@ enum e_directionality { BI_DIRECTIONAL }; /* X_AXIS: Data that describes an x-directed wire segment (CHANX) * - * Y_AXIS: Data that describes an y-directed wire segment (CHANY) * + * Y_AXIS: Data that describes an y-directed wire segment (CHANY) * * BOTH_AXIS: Data that can be applied to both x-directed and y-directed wire segment */ enum e_parallel_axis { X_AXIS, @@ -1554,7 +1557,7 @@ enum e_Fc_type { * Cmetal: Capacitance of a routing track, per unit logic block length. * * Rmetal: Resistance of a routing track, per unit logic block length. * * (UDSD by AY) drivers: How do signals driving a routing track connect to * - * the track? + * the track? * seg_index: The index of the segment as stored in the appropriate Segs list* * Upon loading the architecture, we use this field to keep track * * the segment's index in the unified segment_inf vector. This is * @@ -1611,12 +1614,12 @@ constexpr std::array SWITCH_T /* Constant/Reserved names for switches in architecture XML * Delayless switch: - * The zero-delay switch created by VPR internally + * The zero-delay switch created by VPR internally * This is a special switch just to ease CAD algorithms * It is mainly used in - * - the edges between SOURCE and SINK nodes in routing resource graphs + * - the edges between SOURCE and SINK nodes in routing resource graphs * - the edges in CLB-to-CLB connections (defined by in arch XML) - * + * */ constexpr const char* VPR_DELAYLESS_SWITCH_NAME = "__vpr_delayless_switch__"; @@ -1958,12 +1961,19 @@ struct t_arch { char* architecture_id; //Secure hash digest of the architecture file to uniquely identify this architecture + /* Xifan Tang: options for tileable routing architectures */ + bool tileable; + bool shrink_boundary; + bool through_channel; + t_chan_width_dist Chans; enum e_switch_block_type SBType; + enum e_switch_block_type SBSubType; std::vector switchblocks; float R_minW_nmos; float R_minW_pmos; int Fs; + int subFs; float grid_logic_tile_area; std::vector Segments; t_arch_switch_inf* Switches = nullptr; diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index fa605911698..978865ec6de 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -120,6 +120,11 @@ static std::pair ProcessPinString(pugi::xml_node Locations, T type, const char* pin_loc_string, const pugiutil::loc_data& loc_data); +template +static std::pair ProcessInstanceString(pugi::xml_node Locations, + T type, + const char* pin_loc_string, + const pugiutil::loc_data& loc_data); /* Process XML hierarchy */ static void ProcessTiles(pugi::xml_node Node, @@ -312,10 +317,10 @@ void XmlReadArch(const char* ArchFile, pugi::xml_node Next; ReqOpt POWER_REQD, SWITCHBLOCKLIST_REQD; - if (vtr::check_file_name_extension(ArchFile, ".xml") == false) { + if ((vtr::check_file_name_extension(ArchFile, ".xml") == false) && (vtr::check_file_name_extension(ArchFile, ".xml") == false)) { VTR_LOG_WARN( "Architecture file '%s' may be in incorrect format. " - "Expecting .xml format for architecture files.\n", + "Expecting .xml or .xmle format for architecture files.\n", ArchFile); } @@ -627,10 +632,15 @@ static void LoadPinLoc(pugi::xml_node Locations, &sub_tile, token.c_str(), loc_data); - + /* Get the offset in the capacity range */ + auto capacity_range = ProcessInstanceString(Locations, + &sub_tile, + token.c_str(), + loc_data); + VTR_ASSERT(0 <= capacity_range.first && capacity_range.second < sub_tile_capacity); for (int pin_num = pin_range.first; pin_num < pin_range.second; ++pin_num) { VTR_ASSERT(pin_num < (int)sub_tile.sub_tile_to_tile_pin_indices.size() / sub_tile_capacity); - for (int capacity = 0; capacity < sub_tile_capacity; ++capacity) { + for (int capacity = capacity_range.first; capacity <= capacity_range.second; ++capacity) { int sub_tile_pin_index = pin_num + capacity * sub_tile.num_phy_pins / sub_tile_capacity; int physical_pin_index = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_index]; type->pinloc[width][height][side][physical_pin_index] = true; @@ -660,6 +670,99 @@ static void LoadPinLoc(pugi::xml_node Locations, } } +/* Parse the string to extract instance range, e.g., io[4:7] -> (4, 7) + * If no instance range is explicitly defined, we assume the range of type capacity, i.e., (0, capacity - 1) */ +template +static std::pair ProcessInstanceString(pugi::xml_node Locations, + T type, + const char* pin_loc_string, + const pugiutil::loc_data& loc_data) { + int num_tokens; + auto tokens = GetTokensFromString(pin_loc_string, &num_tokens); + + int token_index = 0; + auto token = tokens[token_index]; + + if (token.type != TOKEN_STRING || 0 != strcmp(token.data, type->name)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Wrong physical type name of the port: %s\n", pin_loc_string); + } + + token_index++; + token = tokens[token_index]; + + int first_inst = 0; + int last_inst = type->capacity.total() - 1; + + /* If there is a dot, such as io.input[0:3], it indicates the full range of the capacity, the default value should be returned */ + if (token.type == TOKEN_DOT) { + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, last_inst); + } + + /* If the string contains index for capacity range, e.g., io[3:3].in[0:5], we skip the capacity range here. */ + if (token.type != TOKEN_OPEN_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No open square bracket present: %s\n", pin_loc_string); + } + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_INT) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No integer to indicate least significant instance index: %s\n", pin_loc_string); + } + + first_inst = vtr::atoi(token.data); + + token_index++; + token = tokens[token_index]; + + // Single pin is specified + if (token.type != TOKEN_COLON) { + if (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No closing bracket: %s\n", pin_loc_string); + } + + token_index++; + + if (token_index != num_tokens) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "instance of pin location should be completed, but more tokens are present: %s\n", pin_loc_string); + } + + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, first_inst); + } + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_INT) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No integer to indicate most significant instance index: %s\n", pin_loc_string); + } + + last_inst = vtr::atoi(token.data); + + token_index++; + token = tokens[token_index]; + + if (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "No closed square bracket: %s\n", pin_loc_string); + } + + if (first_inst > last_inst) { + std::swap(first_inst, last_inst); + } + + freeTokens(tokens, num_tokens); + return std::make_pair(first_inst, last_inst); +} + template static std::pair ProcessPinString(pugi::xml_node Locations, T type, @@ -679,6 +782,20 @@ static std::pair ProcessPinString(pugi::xml_node Locations, token_index++; token = tokens[token_index]; + /* If the string contains index for capacity range, e.g., io[3:3].in[0:5], we skip the capacity range here. */ + if (token.type == TOKEN_OPEN_SQUARE_BRACKET) { + while (token.type != TOKEN_CLOSE_SQUARE_BRACKET) { + token_index++; + token = tokens[token_index]; + if (token_index == num_tokens) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Found an open '[' but miss close ']' of the port: %s\n", pin_loc_string); + } + } + token_index++; + token = tokens[token_index]; + } + if (token.type != TOKEN_DOT) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), "No dot is present to separate type name and port name: %s\n", pin_loc_string); @@ -2403,7 +2520,11 @@ static void ProcessLayout(pugi::xml_node layout_tag, t_arch* arch, const pugiuti VTR_ASSERT(layout_tag.name() == std::string("layout")); //Expect no attributes on - expect_only_attributes(layout_tag, {}, loc_data); + //expect_only_attributes(layout_tag, {}, loc_data); + + arch->tileable = get_attribute(layout_tag, "tileable", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->shrink_boundary = get_attribute(layout_tag, "shrink_boundary", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->through_channel = get_attribute(layout_tag, "through_channel", loc_data, ReqOpt::OPTIONAL).as_bool(false); //Count the number of or tags size_t auto_layout_cnt = 0; @@ -2811,8 +2932,9 @@ static void ProcessDevice(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& // tag Cur = get_single_child(Node, "switch_block", loc_data); - expect_only_attributes(Cur, {"type", "fs"}, loc_data); + expect_only_attributes(Cur, {"type", "fs", "sub_type", "sub_fs"}, loc_data); Prop = get_attribute(Cur, "type", loc_data).value(); + /* Parse attribute 'type', representing the major connectivity pattern for switch blocks */ if (strcmp(Prop, "wilton") == 0) { arch->SBType = WILTON; } else if (strcmp(Prop, "universal") == 0) { @@ -2826,9 +2948,31 @@ static void ProcessDevice(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), "Unknown property %s for switch block type x\n", Prop); } + /* Parse attribute 'sub_type', representing the minor connectivity pattern for switch blocks + * If not specified, the 'sub_type' is the same as major type + * This option is only valid for tileable routing resource graph builder + * Note that sub_type does not support custom switch block pattern!!! + * If 'sub_type' is specified, the custom switch block for 'type' is not allowed! + */ + std::string sub_type_str = get_attribute(Cur, "sub_type", loc_data, BoolToReqOpt(false)).as_string(""); + if (!sub_type_str.empty()) { + if (sub_type_str == std::string("wilton")) { + arch->SBSubType = WILTON; + } else if (sub_type_str == std::string("universal")) { + arch->SBSubType = UNIVERSAL; + } else if (sub_type_str == std::string("subset")) { + arch->SBSubType = SUBSET; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), + "Unknown property %s for switch block subtype x\n", sub_type_str.c_str()); + } + } else { + arch->SBSubType = arch->SBType; + } ReqOpt CUSTOM_SWITCHBLOCK_REQD = BoolToReqOpt(!custom_switch_block); arch->Fs = get_attribute(Cur, "fs", loc_data, CUSTOM_SWITCHBLOCK_REQD).as_int(3); + arch->subFs = get_attribute(Cur, "sub_fs", loc_data, BoolToReqOpt(false)).as_int(arch->Fs); Cur = get_single_child(Node, "default_fc", loc_data, ReqOpt::OPTIONAL); if (Cur) { @@ -3393,8 +3537,8 @@ static void ProcessPinLocations(pugi::xml_node Locations, //Verify that all top-level pins have had their locations specified - //Record all the specified pins - std::map> port_pins_with_specified_locations; + //Record all the specified pins, (capacity, port_name, index) + std::map>> port_pins_with_specified_locations; for (int l = 0; l < num_of_avail_layer; ++l) { for (int w = 0; w < PhysicalTileType->width; ++w) { for (int h = 0; h < PhysicalTileType->height; ++h) { @@ -3403,10 +3547,22 @@ static void ProcessPinLocations(pugi::xml_node Locations, InstPort inst_port(token.c_str()); //A pin specification should contain only the block name, and not any instance count information + //A pin specification may contain instance count, but should be in the range of capacity + int inst_lsb = 0; + int inst_msb = SubTile->capacity.total() - 1; if (inst_port.instance_low_index() != InstPort::UNSPECIFIED || inst_port.instance_high_index() != InstPort::UNSPECIFIED) { - archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), - "Pin location specification '%s' should not contain an instance range (should only be the block name)", - token.c_str()); + /* Extract range numbers */ + inst_lsb = inst_port.instance_low_index(); + inst_msb = inst_port.instance_high_index(); + if (inst_lsb > inst_msb) { + std::swap(inst_lsb, inst_msb); + } + /* Check if we have a valid range */ + if (inst_lsb < 0 || inst_msb > SubTile->capacity.total() - 1) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Pin location specification '%s' contain an out-of-range instance. Expect [%d:%d]", + token.c_str(), 0, SubTile->capacity.total() - 1); + } } //Check that the block name matches @@ -3443,9 +3599,11 @@ static void ProcessPinLocations(pugi::xml_node Locations, VTR_ASSERT(pin_low_idx >= 0); VTR_ASSERT(pin_high_idx >= 0); - for (int ipin = pin_low_idx; ipin <= pin_high_idx; ++ipin) { - //Record that the pin has it's location specified - port_pins_with_specified_locations[inst_port.port_name()].insert(ipin); + for (int iinst = inst_lsb + SubTile->capacity.low; iinst <= inst_msb + SubTile->capacity.low; ++iinst) { + for (int ipin = pin_low_idx; ipin <= pin_high_idx; ++ipin) { + //Record that the pin has it's location specified + port_pins_with_specified_locations[iinst][inst_port.port_name()].insert(ipin); + } } } } @@ -3454,13 +3612,15 @@ static void ProcessPinLocations(pugi::xml_node Locations, } //Check for any pins missing location specs - for (const auto& port : SubTile->ports) { - for (int ipin = 0; ipin < port.num_pins; ++ipin) { - if (!port_pins_with_specified_locations[port.name].count(ipin)) { - //Missing - archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), - "Pin '%s.%s[%d]' has no pin location specified (a location is required for pattern=\"custom\")", - SubTile->name, port.name, ipin); + for (int iinst = SubTile->capacity.low; iinst < SubTile->capacity.high; ++iinst) { + for (const auto& port : SubTile->ports) { + for (int ipin = 0; ipin < port.num_pins; ++ipin) { + if (!port_pins_with_specified_locations[iinst][port.name].count(ipin)) { + //Missing + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Locations), + "Pin '%s[%d].%s[%d]' has no pin location specificed (a location is required for pattern=\"custom\")", + SubTile->name, iinst, port.name, ipin); + } } } } diff --git a/libs/libdecrypt/ README.md b/libs/libdecrypt/ README.md new file mode 100644 index 00000000000..e6a4193bf43 --- /dev/null +++ b/libs/libdecrypt/ README.md @@ -0,0 +1,98 @@ + # libdecrypt + +libdecrypt is a C++ library for decrypting encrypted files. It provides a simple interface to decrypt files using OpenSSL for encryption and decryption operations and pugixml for XML parsing. + +## Features + +- Decrypts encrypted files using RSA encryption algorithm. +- Supports loading private key from PEM string. +- Retrieves encrypted data and session key from XML file. +- Decrypts session key using RSA private key. +- Decrypts XML string using the decrypted session key. +- Outputs the decrypted content as a string. + +## Installation + +1. Build the library using CMake + + cd libdecrypt + mkdir build + cd build + cmake .. + make +2. Install the library (optional): + + make install +## Dependencies + +Make sure you have the following dependencies installed on your system: + +- **OpenSSL**: The library depends on OpenSSL for encryption and decryption operations. Make sure you have OpenSSL installed on your system. + +- **pugixml**: The library uses pugixml for XML parsing. It is included as a submodule in the project. + +## Usage + +To use the `libdecrypt` library in your C++ projects, follow the steps below: + +1. Include the `decryption.h` header file in your source code: + + ```cpp + #include "decryption.h" + +2. Create a `Decryption` object with the path to the encrypted file: + + std::string encryptedFile = "path/to/encrypted/file"; + Decryption decryption(encryptedFile); + +3. Decrypt the contents of the encrypted file: + + decryption.decryptFile(); + +4. Retrieve the decrypted content as a string: + + std::string decryptedContent = decryption.getDecryptedContent(); + +5. Optional: Write the decrypted content to a file: + + std::ofstream outputFile("output.txt"); + outputFile << decryptedContent; + outputFile.close(); + +## Configuration + +To configure `libdecrypt` for your specific use case, follow these steps: + +### Private Key: + +- **Option 1: Define `PRIVATE_KEY` in the code:** + - Locate the `decryption.h` file in the project. + - Uncomment the `#define PRIVATE_KEY` line. + - Replace the dummy private key string with your actual private key in PEM format. + +- **Option 2: Provide a `private_key.pem` file:** + - Place your private key file in the project directory. + - Make sure the file is named `private_key.pem`. + - `libdecrypt` will automatically load the private key from this file. + +### Passphrase: + +- **Option 1: Define `PASSPHRASE` in the code:** + - Locate the `decryption.h` file in the project. + - Uncomment the `#define PASSPHRASE` line. + - Replace the empty string with your actual passphrase. + +- **Option 2: Provide a `config.txt` file:** + - Create a plain text file named `config.txt` in the project directory. + - Write your passphrase in the file. + - `libdecrypt` will read the passphrase from this file. + +Make sure to configure the private key and passphrase according to your specific requirements before using the `libdecrypt` library. + +## License + +This project is licensed under the MIT License + +## Contributing + +Contributions are welcome! If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request. We appreciate your contributions to make this project better. diff --git a/libs/libdecrypt/CMakeLists.txt b/libs/libdecrypt/CMakeLists.txt new file mode 100644 index 00000000000..3eaeab363c6 --- /dev/null +++ b/libs/libdecrypt/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.16) +project(libdecrypt) + +file(READ "private_key.pem" PRIVATE_KEY_CONTENTS) +file(READ "config.txt" PASSPHRASE_CONTENTS) + +string(REPLACE "\n" "\\n" PRIVATE_KEY_CONTENTS "${PRIVATE_KEY_CONTENTS}") +string(REPLACE "\n" "\\n" PASSPHRASE_CONTENTS "${PASSPHRASE_CONTENTS}") +set(PRIVATE_KEY "${PRIVATE_KEY_CONTENTS}") +set(PASS_PHRASE "${PASSPHRASE_CONTENTS}") +add_compile_definitions(PASS_PHRASE="${PASS_PHRASE}") +add_compile_definitions(PRIVATE_KEY="${PRIVATE_KEY}") +find_package(PkgConfig REQUIRED) +pkg_search_module(OPENSSL REQUIRED openssl) + +if( OPENSSL_FOUND ) + include_directories(${OPENSSL_INCLUDE_DIRS}) + message(STATUS "Using OpenSSL ${OPENSSL_VERSION}") +else() + message("SSL not found") + # Error; with REQUIRED, pkg_search_module() will throw an error by it's own +endif() +# Source files and library +file(GLOB_RECURSE LIB_SOURCES src/*.cpp) +file(GLOB_RECURSE LIB_HEADERS src/*.hpp src/*.h) +set(LIB_INCLUDE_DIRS "") +foreach (_headerFile ${LIB_HEADERS}) + get_filename_component(_dir ${_headerFile} PATH) + list(APPEND LIB_INCLUDE_DIRS ${_dir}) +endforeach () +list(REMOVE_DUPLICATES LIB_INCLUDE_DIRS) + + + +# Create the library +add_library(libdecrypt STATIC + ${LIB_HEADERS} + ${LIB_SOURCES}) +target_include_directories(libdecrypt PUBLIC ${LIB_INCLUDE_DIRS}) +set_target_properties(libdecrypt PROPERTIES PREFIX "") # Avoid extra 'lib' prefix + +target_link_libraries(libdecrypt + libpugixml + ${OPENSSL_LIBRARIES} +) +install(TARGETS libdecrypt DESTINATION bin) +install(FILES ${LIB_HEADERS} DESTINATION include/libdecrypt) \ No newline at end of file diff --git a/libs/libdecrypt/config.txt b/libs/libdecrypt/config.txt new file mode 100644 index 00000000000..85df50785d6 --- /dev/null +++ b/libs/libdecrypt/config.txt @@ -0,0 +1 @@ +abcd \ No newline at end of file diff --git a/libs/libdecrypt/private_key.pem b/libs/libdecrypt/private_key.pem new file mode 100644 index 00000000000..f93621227a2 --- /dev/null +++ b/libs/libdecrypt/private_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIxiWq9/gKEI4CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCM12wmpHSRMA8no6WSnyiRBIIE +0HWEpZCWG230mIezxiEQGtR/W0HliJIU/fkJukt7hyqt6ECwcr9te80KIcqftUsH +64d8GBwCAfIpP5gDtojsm2XwM6acdGyFO4ZFyF2yO+kO9kdUVPQbe6Um2wYFGpXq +wMeMclkL+ZRxhttu6j9RsB9iQKqh13/Nw2x7y2xCDK0lkbHKprdO466C92Vqygvb +6Yz7VoawwJT9Wpn7Z6//vSQ1QI7ePf2P1CrZp+z0Nr+kJqxCoSuQ7wavnsEgF+pA +qqx9WRU6twtG97LHeOeYoZZmSN5i3KAFiwWIlxka96VTl86lREPzDJYa4pHR2FNm +186rrKjAIlDlF9UC6zZMIGUtNdo5miuhpjR6BUFQlDIMW6y2nubyj3awBVvM7F45 +QDh0Mzr4x0Ohqaw5+CphkcUKozRHLXA5/31bJLNIGYB6J3uRdJAVVr1R4ouNeI2U ++2Z60WhMIDROVIXn4AwdoukiUy2uBHNPJp8HPyX+aD7I6FgmcLtuZWrjCnOYU44I +8yBVDGbFb/dsjaJbNDNfyl4db6RcPlOjIhIv7kPF3T4AAY2VRZAGQJBNPZ5XO0U9 +nK5q5wgyagsiIWnIhtx7k9texnnVJpOmaF2mS3Wh/PyxJ01wav8yd5TYu8V8vbUV +7dcM/qIUkkYr1Q6llXrSOeA6kGdOfbRSDqgu+9n3NfH4T5msKQQN6u6b1Z/0ayev +pJgFjaxZITh+7T20BEgULuuv3+sCNYPpEmf+fi7V8AKjjdB3iol2XIFKtKYzInwP +Jb/sageAS/u0MHYt4s9XMi69998CAnlt+qZD2wekI6/AOXyxXxkx9WLaxP6Gb6L+ +e/EEui/gKw+AjKlwHrQHFTo2byJy0dOMfJDaCdb8TXouGLrw0iDcoVm/mxN/66WH +ncFSeVqiYOl7lc2y9cfmMNffdw8E92ab6k1bxy4wdmcskVqXsUskveQ4IbAxdaOp +R6B/FZuRvUpjIHlKb0wpmLxvaY1i1bWDXfE5hbIv6zDSMAdgpLeE9Om1xdhOvm2p +dndJguGR6Nk8tYGvJFXREwLxdEU5z3/Cdv/hTV2kmdz1I/JKcEB2cmgqLV1jBpIl +Mzebwl7Dwtxc2VYIDgrij3bU2ekRw1s6dV+RlJdbEX+j5/pxi/sh67szP0JZ4Lfk +87Pk1pmUksqKc6ybU+KCqHmTvlKu1fMrAj4DVG2qLMKl3nVXLnYCm8J7jh5CNXZe +/Qptee/KaT86bP4POQ6w8fKwF2EMT0HrvL5tUoHt6cwH8Hn6pHmrYRI8dbaG9PH2 +wtOvgzRzCalkCh0hAJDrYehnmDCMTFxDC0y8O3l5Ngz/0vhXEsiMUoEk4J9ZbkuZ +yjLl8DSeA8tcCh5XmQ99QMrhdtPCcak+LmbmbEVPWV91FYowcvTTegC9qaJvE5HK +T5X/JkJtw2ZzC/P0CJuAnfLD+mDBayb71/44PqlzOnuPB/6qqldktw4utH0Yjmdx +kri2neh2tJswe/d8pm2g6RJXquxgmszIBh7O6AZ38hjQVVAiQbxuUviB8K4Q7w3B +bTEB2xM7/4uW/QJbPShAA9pd6iMVm8dWMqXJ3zLg+P+qUGQbI9XKx2Ramsy91Xk6 +o6hYZncsAZoVO6HnCoH7WhBozoyFKiZlA9WqqMGL83lU +-----END ENCRYPTED PRIVATE KEY----- diff --git a/libs/libdecrypt/src/config.h b/libs/libdecrypt/src/config.h new file mode 100644 index 00000000000..c01a0dce535 --- /dev/null +++ b/libs/libdecrypt/src/config.h @@ -0,0 +1,9 @@ + +#ifndef CONFIG_H +#define CONFIG_H + +#define PRIVATE_KEY "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIxiWq9/gKEI4CAggA\nMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCM12wmpHSRMA8no6WSnyiRBIIE\n0HWEpZCWG230mIezxiEQGtR/W0HliJIU/fkJukt7hyqt6ECwcr9te80KIcqftUsH\n64d8GBwCAfIpP5gDtojsm2XwM6acdGyFO4ZFyF2yO+kO9kdUVPQbe6Um2wYFGpXq\nwMeMclkL+ZRxhttu6j9RsB9iQKqh13/Nw2x7y2xCDK0lkbHKprdO466C92Vqygvb\n6Yz7VoawwJT9Wpn7Z6//vSQ1QI7ePf2P1CrZp+z0Nr+kJqxCoSuQ7wavnsEgF+pA\nqqx9WRU6twtG97LHeOeYoZZmSN5i3KAFiwWIlxka96VTl86lREPzDJYa4pHR2FNm\n186rrKjAIlDlF9UC6zZMIGUtNdo5miuhpjR6BUFQlDIMW6y2nubyj3awBVvM7F45\nQDh0Mzr4x0Ohqaw5+CphkcUKozRHLXA5/31bJLNIGYB6J3uRdJAVVr1R4ouNeI2U\n+2Z60WhMIDROVIXn4AwdoukiUy2uBHNPJp8HPyX+aD7I6FgmcLtuZWrjCnOYU44I\n8yBVDGbFb/dsjaJbNDNfyl4db6RcPlOjIhIv7kPF3T4AAY2VRZAGQJBNPZ5XO0U9\nnK5q5wgyagsiIWnIhtx7k9texnnVJpOmaF2mS3Wh/PyxJ01wav8yd5TYu8V8vbUV\n7dcM/qIUkkYr1Q6llXrSOeA6kGdOfbRSDqgu+9n3NfH4T5msKQQN6u6b1Z/0ayev\npJgFjaxZITh+7T20BEgULuuv3+sCNYPpEmf+fi7V8AKjjdB3iol2XIFKtKYzInwP\nJb/sageAS/u0MHYt4s9XMi69998CAnlt+qZD2wekI6/AOXyxXxkx9WLaxP6Gb6L+\ne/EEui/gKw+AjKlwHrQHFTo2byJy0dOMfJDaCdb8TXouGLrw0iDcoVm/mxN/66WH\nncFSeVqiYOl7lc2y9cfmMNffdw8E92ab6k1bxy4wdmcskVqXsUskveQ4IbAxdaOp\nR6B/FZuRvUpjIHlKb0wpmLxvaY1i1bWDXfE5hbIv6zDSMAdgpLeE9Om1xdhOvm2p\ndndJguGR6Nk8tYGvJFXREwLxdEU5z3/Cdv/hTV2kmdz1I/JKcEB2cmgqLV1jBpIl\nMzebwl7Dwtxc2VYIDgrij3bU2ekRw1s6dV+RlJdbEX+j5/pxi/sh67szP0JZ4Lfk\n87Pk1pmUksqKc6ybU+KCqHmTvlKu1fMrAj4DVG2qLMKl3nVXLnYCm8J7jh5CNXZe\n/Qptee/KaT86bP4POQ6w8fKwF2EMT0HrvL5tUoHt6cwH8Hn6pHmrYRI8dbaG9PH2\nwtOvgzRzCalkCh0hAJDrYehnmDCMTFxDC0y8O3l5Ngz/0vhXEsiMUoEk4J9ZbkuZ\nyjLl8DSeA8tcCh5XmQ99QMrhdtPCcak+LmbmbEVPWV91FYowcvTTegC9qaJvE5HK\nT5X/JkJtw2ZzC/P0CJuAnfLD+mDBayb71/44PqlzOnuPB/6qqldktw4utH0Yjmdx\nkri2neh2tJswe/d8pm2g6RJXquxgmszIBh7O6AZ38hjQVVAiQbxuUviB8K4Q7w3B\nbTEB2xM7/4uW/QJbPShAA9pd6iMVm8dWMqXJ3zLg+P+qUGQbI9XKx2Ramsy91Xk6\no6hYZncsAZoVO6HnCoH7WhBozoyFKiZlA9WqqMGL83lU\n-----END ENCRYPTED PRIVATE KEY-----\n" +#define PASSPHRASE "abcd" +// Add more configuration variables as needed + +#endif // CONFIG_H diff --git a/libs/libdecrypt/src/decryption.cpp b/libs/libdecrypt/src/decryption.cpp new file mode 100644 index 00000000000..2a745cfd5bf --- /dev/null +++ b/libs/libdecrypt/src/decryption.cpp @@ -0,0 +1,185 @@ +#include "decryption.h" + +#ifdef PASS_PHRASE +std::string passphrase = PASS_PHRASE; +#else +std::string passphrase = ""; // Set your PEM pass phrase here +#endif + +/** + * @brief Constructs a Decryption object with the specified encrypted file. + * + * @param encryptedFile The path to the encrypted file. + */ +Decryption::Decryption(const std::string& encryptedFile) + : encryptedFile_(encryptedFile) { + decryptFile(); +} + +/** + * @brief Decrypts the contents of the encrypted file. + * + * This function performs the decryption process, including loading the private key, + * retrieving the encrypted data and session key from the XML file, decrypting the + * session key, and decrypting the XML string. + */ +void Decryption::decryptFile() { +#ifdef PRIVATE_KEY + const char* privateKeyString = AY_OBFUSCATE(PRIVATE_KEY); +#else + const char* privateKeyString = AY_OBFUSCATE( + "-----BEGIN RSA PRIVATE KEY-----\n" + "dummykey\n" + "-----END RSA PRIVATE KEY-----\n"); // Replace with your private key string +#endif + RSA* privateKey = loadPrivateKey(privateKeyString); + if (!privateKey) { + return; + } + + // Load encrypted data and session key from XML file + pugi::xml_document encryptedDocLoaded; + pugi::xml_parse_result result = encryptedDocLoaded.load_file(encryptedFile_.c_str()); + if (!result) { + std::cerr << "XML parse error: " << result.description() << std::endl; + RSA_free(privateKey); + return; + } + + pugi::xml_node root = encryptedDocLoaded.child("EncryptedData"); + std::string base64EncryptedSessionKeyLoaded = root.child_value("SessionKey"); + std::string base64EncryptedLoaded = root.child_value("Data"); + + // Base64 decode encrypted session key and data + std::string encryptedSessionKeyLoaded = base64_decode(base64EncryptedSessionKeyLoaded); + std::string encryptedLoaded = base64_decode(base64EncryptedLoaded); + + // Decrypt session key + std::string decryptedSessionKey = decryptSessionKey(encryptedSessionKeyLoaded, privateKey); + + // Decrypt XML string + std::string decrypted = decrypt(encryptedLoaded, privateKey); + + // Write the decrypted data to a file + // std::ofstream decryptedFile("decrypted.xml"); + // decryptedFile << decrypted; + // decryptedFile.close(); + + decryptedContent_ = decrypted; + RSA_free(privateKey); +} + +/** + * @brief Retrieves the decrypted content. + * + * @return The decrypted content as a string. + */ +std::string Decryption::getDecryptedContent() const { + return decryptedContent_; +} + +/** + * @brief Decrypts the given ciphertext using the provided RSA key. + * + * @param ciphertext The ciphertext to decrypt. + * @param key The RSA key for decryption. + * @return The decrypted plaintext. + */ +std::string Decryption::decrypt(const std::string& ciphertext, RSA* key) { + int rsaLen = RSA_size(key); + int len = ciphertext.size(); + std::string plaintext; + + for (int i = 0; i < len; i += rsaLen) { + std::vector buffer(rsaLen); + std::string substr = ciphertext.substr(i, rsaLen); + + int result = RSA_private_decrypt(substr.size(), reinterpret_cast(substr.data()), buffer.data(), key, RSA_PKCS1_OAEP_PADDING); + if (result == -1) { + std::cerr << "Decryption error: " << ERR_error_string(ERR_get_error(), NULL) << std::endl; + return ""; + } + + plaintext.append(reinterpret_cast(buffer.data()), result); + } + + return plaintext; +} + +/** + * @brief Decodes the given base64-encoded string. + * + * @param input The base64-encoded input string. + * @return The decoded output string. + */ +std::string Decryption::base64_decode(const std::string& input) { + BIO* b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + BIO* bmem = BIO_new_mem_buf(input.data(), input.size()); + b64 = BIO_push(b64, bmem); + + std::string output; + output.resize(input.size()); + + int decoded_size = BIO_read(b64, &output[0], input.size()); + output.resize(decoded_size); + BIO_free_all(b64); + + return output; +} + +/** + * @brief Loads the private key from the given PEM string. + * + * @param privateKeyString The PEM string representing the private key. + * @return The loaded RSA private key. + */ +RSA* Decryption::loadPrivateKey(const std::string& privateKeyString) { + RSA* key = nullptr; + BIO* privateKeyBio = BIO_new_mem_buf(privateKeyString.data(), privateKeyString.size()); + + if (!privateKeyBio) { + std::cerr << "Error creating BIO for private key" << std::endl; + return nullptr; + } + + if (!PEM_read_bio_RSAPrivateKey(privateKeyBio, &key, NULL, (void*)passphrase.c_str())) { + std::cerr << "Error reading private key" << std::endl; + BIO_free(privateKeyBio); + return nullptr; + } + + BIO_free(privateKeyBio); + return key; +} + +/** + * @brief Decrypts the given encrypted session key using the provided RSA key. + * + * @param encryptedSessionKey The encrypted session key. + * @param key The RSA key for decryption. + * @return The decrypted session key. + */ +std::string Decryption::decryptSessionKey(const std::string& encryptedSessionKey, RSA* key) { + std::vector decryptedSessionKey(RSA_size(key)); + if (RSA_private_decrypt(encryptedSessionKey.size(), reinterpret_cast(encryptedSessionKey.data()), decryptedSessionKey.data(), key, RSA_PKCS1_OAEP_PADDING) == -1) { + std::cerr << "Session key decryption error: " << ERR_error_string(ERR_get_error(), NULL) << std::endl; + return ""; + } + + return std::string(reinterpret_cast(decryptedSessionKey.data()), decryptedSessionKey.size()); +} + +// int main(int argc, char* argv[]) { +// if (argc < 2) { +// std::cerr << "Usage: " << argv[0] << " \n"; +// return -1; +// } + +// std::string encryptedFile = argv[1]; + +// Decryption decryption(encryptedFile); + +// return 0; +// } diff --git a/libs/libdecrypt/src/decryption.h b/libs/libdecrypt/src/decryption.h new file mode 100644 index 00000000000..52d3023dd93 --- /dev/null +++ b/libs/libdecrypt/src/decryption.h @@ -0,0 +1,86 @@ +#ifndef DECRYPTION_H +#define DECRYPTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pugixml.hpp" +#include "obfuscate.h" + +/** + * @brief The Decryption class for decrypting encrypted files. + */ +class Decryption +{ +public: + /** + * @brief Constructs a Decryption object with the specified encrypted file. + * + * @param encryptedFile The path to the encrypted file. + */ + Decryption(const std::string &encryptedFile); + + /** + * @brief Decrypts the contents of the encrypted file. + * + * This function performs the decryption process, including loading the private key, + * retrieving the encrypted data and session key from the XML file, decrypting the + * session key, and decrypting the XML string. + */ + void decryptFile(); + + /** + * @brief Retrieves the decrypted content. + * + * @return The decrypted content as a string. + */ + std::string getDecryptedContent() const; + +private: + std::string encryptedFile_; /**< The path to the encrypted file. */ + std::string decryptedContent_; /**< The decrypted content of the file. */ + + /** + * @brief Decrypts the given ciphertext using the provided RSA key. + * + * @param ciphertext The ciphertext to decrypt. + * @param key The RSA key for decryption. + * @return The decrypted plaintext. + */ + static std::string decrypt(const std::string &ciphertext, RSA *key); + + /** + * @brief Decodes the given base64-encoded string. + * + * @param input The base64-encoded input string. + * @return The decoded output string. + */ + static std::string base64_decode(const std::string &input); + + /** + * @brief Loads the private key from the given PEM string. + * + * @param privateKeyString The PEM string representing the private key. + * @return The loaded RSA private key. + */ + static RSA *loadPrivateKey(const std::string &privateKeyString); + + /** + * @brief Decrypts the given encrypted session key using the provided RSA key. + * + * @param encryptedSessionKey The encrypted session key. + * @param key The RSA key for decryption. + * @return The decrypted session key. + */ + static std::string decryptSessionKey(const std::string &encryptedSessionKey, RSA *key); +}; + +#endif // DECRYPTION_H diff --git a/libs/libdecrypt/src/obfuscate.h b/libs/libdecrypt/src/obfuscate.h new file mode 100644 index 00000000000..5450eb44f01 --- /dev/null +++ b/libs/libdecrypt/src/obfuscate.h @@ -0,0 +1,185 @@ +#ifndef OBFUSCATE_H +#define OBFUSCATE_H + +#ifdef _MSC_VER + #define AY_CAT(X,Y) AY_CAT2(X,Y) + #define AY_CAT2(X,Y) X##Y + #define AY_LINE int(AY_CAT(__LINE__,U)) +#else + #define AY_LINE __LINE__ +#endif + +#ifndef AY_OBFUSCATE_DEFAULT_KEY + // The default 64 bit key to obfuscate strings with. + // This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before + // including obfuscate.h + #define AY_OBFUSCATE_DEFAULT_KEY ay::generate_key(AY_LINE) +#endif + +namespace ay +{ + using size_type = unsigned long long; + using key_type = unsigned long long; + + // Generate a pseudo-random key that spans all 8 bytes + constexpr key_type generate_key(key_type seed) + { + // Use the MurmurHash3 64-bit finalizer to hash our seed + key_type key = seed; + key ^= (key >> 33); + key *= 0xff51afd7ed558ccd; + key ^= (key >> 33); + key *= 0xc4ceb9fe1a85ec53; + key ^= (key >> 33); + + // Make sure that a bit in each byte is set + key |= 0x0101010101010101ull; + + return key; + } + + // Obfuscates or deobfuscates data with key + constexpr void cipher(char* data, size_type size, key_type key) + { + // Obfuscate with a simple XOR cipher based on key + for (size_type i = 0; i < size; i++) + { + data[i] ^= char(key >> ((i % 8) * 8)); + } + } + + // Obfuscates a string at compile time + template + class obfuscator + { + public: + // Obfuscates the string 'data' on construction + constexpr obfuscator(const char* data) + { + // Copy data + for (size_type i = 0; i < N; i++) + { + m_data[i] = data[i]; + } + + // On construction each of the characters in the string is + // obfuscated with an XOR cipher based on key + cipher(m_data, N, KEY); + } + + constexpr const char* data() const + { + return &m_data[0]; + } + + constexpr size_type size() const + { + return N; + } + + constexpr key_type key() const + { + return KEY; + } + + private: + + char m_data[N]{}; + }; + + // Handles decryption and re-encryption of an encrypted string at runtime + template + class obfuscated_data + { + public: + obfuscated_data(const obfuscator& obfuscator) + { + // Copy obfuscated data + for (size_type i = 0; i < N; i++) + { + m_data[i] = obfuscator.data()[i]; + } + } + + ~obfuscated_data() + { + // Zero m_data to remove it from memory + for (size_type i = 0; i < N; i++) + { + m_data[i] = 0; + } + } + + // Returns a pointer to the plain text string, decrypting it if + // necessary + operator char*() + { + decrypt(); + return m_data; + } + + // Manually decrypt the string + void decrypt() + { + if (m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = false; + } + } + + // Manually re-encrypt the string + void encrypt() + { + if (!m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = true; + } + } + + // Returns true if this string is currently encrypted, false otherwise. + bool is_encrypted() const + { + return m_encrypted; + } + + private: + + // Local storage for the string. Call is_encrypted() to check whether or + // not the string is currently obfuscated. + char m_data[N]; + + // Whether data is currently encrypted + bool m_encrypted{ true }; + }; + + // This function exists purely to extract the number of elements 'N' in the + // array 'data' + template + constexpr auto make_obfuscator(const char(&data)[N]) + { + return obfuscator(data); + } +} + +// Obfuscates the string 'data' at compile-time and returns a reference to a +// ay::obfuscated_data object with global lifetime that has functions for +// decrypting the string and is also implicitly convertable to a char* +#define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY) + +// Obfuscates the string 'data' with 'key' at compile-time and returns a +// reference to a ay::obfuscated_data object with global lifetime that has +// functions for decrypting the string and is also implicitly convertable to a +// char* +#define AY_OBFUSCATE_KEY(data, key) \ + []() -> ay::obfuscated_data& { \ + static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ + static_assert((key) >= (1ull << 56), " must span all 8 bytes"); \ + constexpr auto n = sizeof(data)/sizeof(data[0]); \ + constexpr auto obfuscator = ay::make_obfuscator(data); \ + static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ + return obfuscated_data; \ + }() + +#endif diff --git a/libs/libencrypt/CMakeLists.txt b/libs/libencrypt/CMakeLists.txt new file mode 100644 index 00000000000..23a589415ac --- /dev/null +++ b/libs/libencrypt/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.16) +project(libencrypt) + +find_package(PkgConfig REQUIRED) +pkg_search_module(OPENSSL REQUIRED openssl) + +file(READ "config.txt" PASSPHRASE_CONTENTS) +string(REPLACE "\n" "\\n" PASSPHRASE_CONTENTS "${PASSPHRASE_CONTENTS}") +set(PASS_PHRASE "${PASSPHRASE_CONTENTS}") +add_compile_definitions(PASS_PHRASE="${PASS_PHRASE}") + +if(OPENSSL_FOUND) + include_directories(${OPENSSL_INCLUDE_DIRS}) + message(STATUS "Using OpenSSL ${OPENSSL_VERSION}") +else() + message(FATAL_ERROR "OpenSSL not found") +endif() + +# Source files for the library +file(GLOB_RECURSE LIB_SOURCES src/*.cpp ) +file(GLOB_RECURSE LIB_HEADERS src/*.hpp src/*.h) +set(LIB_INCLUDE_DIRS "") +foreach(_headerFile ${LIB_HEADERS}) + get_filename_component(_dir ${_headerFile} PATH) + list(APPEND LIB_INCLUDE_DIRS ${_dir}) +endforeach() +list(REMOVE_DUPLICATES LIB_INCLUDE_DIRS) + + + +# Add executable +add_executable(encrypt main.cpp ${LIB_HEADERS} ${LIB_SOURCES} ) + +target_include_directories(encrypt PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src +) +target_link_libraries(encrypt PRIVATE ${OPENSSL_LIBRARIES} libpugixml) + +#install(TARGETS libencrypt DESTINATION bin) +#install(FILES ${LIB_HEADERS} DESTINATION include/libencrypt) diff --git a/libs/libencrypt/README.md b/libs/libencrypt/README.md new file mode 100644 index 00000000000..e61004cffc1 --- /dev/null +++ b/libs/libencrypt/README.md @@ -0,0 +1,71 @@ + # libencrypt (XML Encryption) + +libencrypt is a C++ library that provides XML encryption functionality using the RSA encryption algorithm. It allows you to encrypt XML data and files using a public key, perform encryption-related operations, and save the encrypted data in an XML format. + +## Prerequisites + +Before using the libencrypt library, make sure you have the following prerequisites installed: + +- C++ compiler with C++17 support +- OpenSSL library (version 1.1 or higher) + +## Installation + +To use the libencrypt library in your project, follow these steps: + +1. Clone the libencrypt repository or download the source code. + +2. Configure the library by replacing the contents of the `public_key.pem` file with your own RSA public key. Also, update the `config.txt` file with your desired passphrase. + +3. Build the library using CMake: + + ```bash + cmake . + make + +## Usage + +To use the libencrypt library in your code, follow these steps: + +1. Include the `encryption.h` header in your source file. + +2. Use the provided functions to perform XML encryption operations: + + - `loadPublicKey`: Loads a public key from a file. + - `encryptSessionKey`: Encrypts a session key using the provided public key. + - `encrypt`: Encrypts XML data using the provided public key. + - `base64_encode`: Base64 encodes a string. + - `encryptFile`: Encrypts an XML file using the provided public key. + +3. Customize the encryption process as needed based on your XML structure and encryption requirements. + +## Example + +Here's an example of how to use the libencrypt library to encrypt an XML file: + +```cpp +#include "encryption.h" + +int main() { + std::string filePath = "data.xml"; + + // Encrypt the XML file using the provided public key + bool success = Encryption::encryptFile(filePath); + + if (success) { + std::cout << "XML file encryption successful!" << std::endl; + } else { + std::cerr << "XML file encryption failed." << std::endl; + } + + return 0; + } + + +##License + +This project is licensed under the MIT License + +##Contributing + +Contributions are welcome! If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request. We appreciate your contributions to make this project better. \ No newline at end of file diff --git a/libs/libencrypt/config.txt b/libs/libencrypt/config.txt new file mode 100644 index 00000000000..85df50785d6 --- /dev/null +++ b/libs/libencrypt/config.txt @@ -0,0 +1 @@ +abcd \ No newline at end of file diff --git a/libs/libencrypt/main.cpp b/libs/libencrypt/main.cpp new file mode 100644 index 00000000000..9e798b00613 --- /dev/null +++ b/libs/libencrypt/main.cpp @@ -0,0 +1,22 @@ +#include +#include "encryption.h" + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return -1; + } + + + std::string publicKeyFile = argv[1]; + std::string filePath = argv[2]; + + if (Encryption::encryptFile(publicKeyFile,filePath)) { + std::cout << "Encryption completed successfully." << std::endl; + return 0; + } else { + std::cerr << "Encryption failed." << std::endl; + return -1; + } +} + diff --git a/libs/libencrypt/public_key.pem b/libs/libencrypt/public_key.pem new file mode 100644 index 00000000000..a166d5329f9 --- /dev/null +++ b/libs/libencrypt/public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqZGiAzN24Hao44eCxVgv +EBxsR8OAI7hNp7uqzPF85QNafQKCJqeGKtY7NM/w1vkaxm0uHTBCMsjdi48v0Eof +m18v3n9OBczDeTJVjwGhRz8ePurp68gLmXLBK2hFgEZUY4+08cb+K63i+BqRP7iJ +wP5iVofHY9HDpIHk+jwFSTWIArgR2+4RM95qF3i+/6zl+4uIN6oI9q9v/TqmiLTY +bzIynGeDpYhh0kM33nfxBz9xKz78kGS885HMT0ZoGrFiJmJ8AkqsNUSWF0DinA7b +MHCBrurqhmBsWFa3Rk0V74F7ofQFIK7jD/tNpXVhEqeqr0gHXzJ27xNILJ8b+rGF +LwIDAQAB +-----END PUBLIC KEY----- diff --git a/libs/libencrypt/src/encryption.cpp b/libs/libencrypt/src/encryption.cpp new file mode 100644 index 00000000000..07bdae5c220 --- /dev/null +++ b/libs/libencrypt/src/encryption.cpp @@ -0,0 +1,177 @@ +#include "encryption.h" +#ifdef PASSPHRASE +std::string passphrase_enc = PASSPHRASE; +#else +std::string passphrase_enc = ""; // Set your PEM pass phrase here +#endif +/** + * @brief Loads a public key from a file. + * + * @param filename The name of the file containing the public key. + * @return RSA* A pointer to the loaded public key. + * Returns nullptr if the key file cannot be opened or there is an error reading the key. + */ +RSA* Encryption::loadPublicKey(const std::string& filename) { + + RSA* key = nullptr; + FILE* keyFile = fopen(filename.c_str(), "r"); + if (!keyFile) { + std::cerr << "Unable to open key file: " << filename << std::endl; + return nullptr; + } + + if (!PEM_read_RSA_PUBKEY(keyFile, &key, NULL, (void*)passphrase_enc.c_str())) { + std::cerr << "Error reading public key from file: " << filename << std::endl; + fclose(keyFile); + return nullptr; + } + + fclose(keyFile); + return key; +} + +/** + * @brief Encrypts a session key using the provided public key. + * + * @param key The public key used for encryption. + * @return std::string The encrypted session key. + * Returns an empty string if there is an error generating or encrypting the session key. + */ +std::string Encryption::encryptSessionKey(RSA* key) { + unsigned char sessionKey[128]; + if (!RAND_bytes(sessionKey, sizeof(sessionKey))) { + std::cerr << "Error generating session key" << std::endl; + return ""; + } + + std::vector encryptedSessionKey(RSA_size(key)); + if (RSA_public_encrypt(sizeof(sessionKey), sessionKey, encryptedSessionKey.data(), key, RSA_PKCS1_OAEP_PADDING) == -1) { + std::cerr << "Session key encryption error: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return ""; + } + + return std::string(reinterpret_cast(encryptedSessionKey.data()), encryptedSessionKey.size()); +} + +/** + * @brief Encrypts the given plaintext using the provided public key. + * + * @param plaintext The plaintext to be encrypted. + * @param key The public key used for encryption. + * @return std::string The encrypted ciphertext. + * Returns an empty string if there is an error during encryption. + */ +std::string Encryption::encrypt(const std::string& plaintext, RSA* key) { + int rsaLen = RSA_size(key); + int blockLen = rsaLen - 42; // PKCS1_OAEP_PADDING reduces max data length + int len = plaintext.size(); + std::string ciphertext; + + for (int i = 0; i < len; i += blockLen) { + std::vector buffer(rsaLen); + std::string substr = plaintext.substr(i, blockLen); + + if (RSA_public_encrypt(substr.size(), reinterpret_cast(substr.data()), buffer.data(), key, RSA_PKCS1_OAEP_PADDING) == -1) { + std::cerr << "Encryption error: " << ERR_error_string(ERR_get_error(), nullptr) << std::endl; + return ""; + } + + ciphertext.append(reinterpret_cast(buffer.data()), rsaLen); + } + + return ciphertext; +} + +/** + * @brief Base64 encodes the given input string. + * + * @param input The input string to be encoded. + * @return std::string The base64-encoded string. + */ +std::string Encryption::base64_encode(const std::string& input) { + BIO* b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + BIO* bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + + BIO_write(b64, input.data(), input.size()); + BIO_flush(b64); + + BUF_MEM* bptr; + BIO_get_mem_ptr(b64, &bptr); + + std::string output(bptr->data, bptr->length); + BIO_free_all(b64); + + return output; +} + +/** + * @brief Encrypts a file using the provided public key. + * + * @param filePath The path to the file to be encrypted. + * @param publicKeyFile The path to the file containing the public key. + * @return bool True if the file encryption is successful, false otherwise. + */ +bool Encryption::encryptFile(const std::string& publicKeyFile, std::string& filePath ) { + + // Load public key + RSA* publicKey = loadPublicKey(publicKeyFile); + if (!publicKey) { + return false; + } + + // Read file contents + std::ifstream file(filePath, std::ios::binary); + if (!file) { + std::cerr << "Unable to open file: " << filePath << std::endl; + RSA_free(publicKey); + return false; + } + + std::string plaintext((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + file.close(); + + // Encrypt session key + std::string encryptedSessionKey = encryptSessionKey(publicKey); + if (encryptedSessionKey.empty()) { + RSA_free(publicKey); + return false; + } + + // Base64 encode session key + std::string base64EncryptedSessionKey = base64_encode(encryptedSessionKey); + + // Encrypt file contents + std::string encrypted = encrypt(plaintext, publicKey); + RSA_free(publicKey); + + // Base64 encode encrypted data + std::string base64Encrypted = base64_encode(encrypted); + + // Create an XML document for the encrypted data and session key + pugi::xml_document encryptedDoc; + auto root = encryptedDoc.append_child("EncryptedData"); + auto sessionKeyNode = root.append_child("SessionKey"); + sessionKeyNode.append_child(pugi::node_pcdata).set_value(base64EncryptedSessionKey.c_str()); + auto dataNode = root.append_child("Data"); + dataNode.append_child(pugi::node_pcdata).set_value(base64Encrypted.c_str()); + + + size_t lastDotPos = filePath.find_last_of('.'); + + if (lastDotPos != std::string::npos && filePath.substr(lastDotPos) == ".xml") { + // Remove the .xml extension + filePath.erase(lastDotPos); + // Append the .xmle extension + filePath.append(".xmle"); + } + // Save the encrypted data to a new file + std::string encryptedFilePath = filePath; + encryptedDoc.save_file(encryptedFilePath.c_str(), " "); + + std::cout << "File encrypted successfully. Encrypted file saved as: " << encryptedFilePath << std::endl; + + return true; +} diff --git a/libs/libencrypt/src/encryption.h b/libs/libencrypt/src/encryption.h new file mode 100644 index 00000000000..daedcdbcba2 --- /dev/null +++ b/libs/libencrypt/src/encryption.h @@ -0,0 +1,70 @@ +#ifndef ENCRYPTION_H +#define ENCRYPTION_H + +#include +#include +#include +#include +#include +#include +#include "pugixml.hpp" +#include +#include +#include +#include +#include +#include "obfuscate.h" +//#include "config.h" +/** + * @class Encryption + * @brief Provides encryption and decryption functionality using RSA encryption algorithm. + */ +class Encryption { +public: + /** + * @brief Loads a public key from a file. + * + * @param filename The name of the file containing the public key. + * @return RSA* A pointer to the loaded public key. + * Returns nullptr if the key file cannot be opened or there is an error reading the key. + */ + static RSA *loadPublicKey(const std::string &filename); + + /** + * @brief Encrypts a session key using the provided public key. + * + * @param key The public key used for encryption. + * @return std::string The encrypted session key. + * Returns an empty string if there is an error generating or encrypting the session key. + */ + static std::string encryptSessionKey(RSA *key); + + /** + * @brief Encrypts the given plaintext using the provided public key. + * + * @param plaintext The plaintext to be encrypted. + * @param key The public key used for encryption. + * @return std::string The encrypted ciphertext. + * Returns an empty string if there is an error during encryption. + */ + static std::string encrypt(const std::string &plaintext, RSA *key); + + /** + * @brief Base64 encodes the given input string. + * + * @param input The input string to be encoded. + * @return std::string The base64-encoded string. + */ + static std::string base64_encode(const std::string &input); + + /** + * @brief Encrypts a file using the provided public key. + * + * @param filePath The path to the file to be encrypted. + * @param publicKeyFile The path to the file containing the public key. + * @return bool True if the file encryption is successful, false otherwise. + */ + static bool encryptFile(const std::string& publicKeyFile, std::string &filePath); +}; + +#endif // ENCRYPTION_H diff --git a/libs/libencrypt/src/obfuscate.h b/libs/libencrypt/src/obfuscate.h new file mode 100644 index 00000000000..5450eb44f01 --- /dev/null +++ b/libs/libencrypt/src/obfuscate.h @@ -0,0 +1,185 @@ +#ifndef OBFUSCATE_H +#define OBFUSCATE_H + +#ifdef _MSC_VER + #define AY_CAT(X,Y) AY_CAT2(X,Y) + #define AY_CAT2(X,Y) X##Y + #define AY_LINE int(AY_CAT(__LINE__,U)) +#else + #define AY_LINE __LINE__ +#endif + +#ifndef AY_OBFUSCATE_DEFAULT_KEY + // The default 64 bit key to obfuscate strings with. + // This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before + // including obfuscate.h + #define AY_OBFUSCATE_DEFAULT_KEY ay::generate_key(AY_LINE) +#endif + +namespace ay +{ + using size_type = unsigned long long; + using key_type = unsigned long long; + + // Generate a pseudo-random key that spans all 8 bytes + constexpr key_type generate_key(key_type seed) + { + // Use the MurmurHash3 64-bit finalizer to hash our seed + key_type key = seed; + key ^= (key >> 33); + key *= 0xff51afd7ed558ccd; + key ^= (key >> 33); + key *= 0xc4ceb9fe1a85ec53; + key ^= (key >> 33); + + // Make sure that a bit in each byte is set + key |= 0x0101010101010101ull; + + return key; + } + + // Obfuscates or deobfuscates data with key + constexpr void cipher(char* data, size_type size, key_type key) + { + // Obfuscate with a simple XOR cipher based on key + for (size_type i = 0; i < size; i++) + { + data[i] ^= char(key >> ((i % 8) * 8)); + } + } + + // Obfuscates a string at compile time + template + class obfuscator + { + public: + // Obfuscates the string 'data' on construction + constexpr obfuscator(const char* data) + { + // Copy data + for (size_type i = 0; i < N; i++) + { + m_data[i] = data[i]; + } + + // On construction each of the characters in the string is + // obfuscated with an XOR cipher based on key + cipher(m_data, N, KEY); + } + + constexpr const char* data() const + { + return &m_data[0]; + } + + constexpr size_type size() const + { + return N; + } + + constexpr key_type key() const + { + return KEY; + } + + private: + + char m_data[N]{}; + }; + + // Handles decryption and re-encryption of an encrypted string at runtime + template + class obfuscated_data + { + public: + obfuscated_data(const obfuscator& obfuscator) + { + // Copy obfuscated data + for (size_type i = 0; i < N; i++) + { + m_data[i] = obfuscator.data()[i]; + } + } + + ~obfuscated_data() + { + // Zero m_data to remove it from memory + for (size_type i = 0; i < N; i++) + { + m_data[i] = 0; + } + } + + // Returns a pointer to the plain text string, decrypting it if + // necessary + operator char*() + { + decrypt(); + return m_data; + } + + // Manually decrypt the string + void decrypt() + { + if (m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = false; + } + } + + // Manually re-encrypt the string + void encrypt() + { + if (!m_encrypted) + { + cipher(m_data, N, KEY); + m_encrypted = true; + } + } + + // Returns true if this string is currently encrypted, false otherwise. + bool is_encrypted() const + { + return m_encrypted; + } + + private: + + // Local storage for the string. Call is_encrypted() to check whether or + // not the string is currently obfuscated. + char m_data[N]; + + // Whether data is currently encrypted + bool m_encrypted{ true }; + }; + + // This function exists purely to extract the number of elements 'N' in the + // array 'data' + template + constexpr auto make_obfuscator(const char(&data)[N]) + { + return obfuscator(data); + } +} + +// Obfuscates the string 'data' at compile-time and returns a reference to a +// ay::obfuscated_data object with global lifetime that has functions for +// decrypting the string and is also implicitly convertable to a char* +#define AY_OBFUSCATE(data) AY_OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY) + +// Obfuscates the string 'data' with 'key' at compile-time and returns a +// reference to a ay::obfuscated_data object with global lifetime that has +// functions for decrypting the string and is also implicitly convertable to a +// char* +#define AY_OBFUSCATE_KEY(data, key) \ + []() -> ay::obfuscated_data& { \ + static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ + static_assert((key) >= (1ull << 56), " must span all 8 bytes"); \ + constexpr auto n = sizeof(data)/sizeof(data[0]); \ + constexpr auto obfuscator = ay::make_obfuscator(data); \ + static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ + return obfuscated_data; \ + }() + +#endif diff --git a/libs/libpugiutil/CMakeLists.txt b/libs/libpugiutil/CMakeLists.txt index b7c2640c549..183afc81dad 100644 --- a/libs/libpugiutil/CMakeLists.txt +++ b/libs/libpugiutil/CMakeLists.txt @@ -17,7 +17,8 @@ target_include_directories(libpugiutil PUBLIC ${LIB_INCLUDE_DIRS}) set_target_properties(libpugiutil PROPERTIES PREFIX "") #Avoid extra 'lib' prefix target_link_libraries(libpugiutil - libpugixml) + libpugixml + libdecrypt) install(TARGETS libpugiutil DESTINATION bin) install(FILES ${LIB_HEADERS} DESTINATION include/libpugiutil) diff --git a/libs/libpugiutil/src/pugixml_loc.cpp b/libs/libpugiutil/src/pugixml_loc.cpp index b773b410b53..5a86d3f3409 100644 --- a/libs/libpugiutil/src/pugixml_loc.cpp +++ b/libs/libpugiutil/src/pugixml_loc.cpp @@ -46,4 +46,16 @@ void loc_data::build_loc_data() { fclose(f); } +void loc_data::build_loc_data_from_string(char* filename_val, size_t buffersize) { + std::ptrdiff_t offset = 0; + char buffer[1024]; + std::size_t size; + for (std::size_t i = 0; i < buffersize; ++i) { + if (filename_val[i] == '\0') + break; + else if (filename_val[i] == '\n') { + offsets_.push_back(i); + } + } +} } // namespace pugiutil diff --git a/libs/libpugiutil/src/pugixml_loc.hpp b/libs/libpugiutil/src/pugixml_loc.hpp index 0f597a593b1..b8e11fded7d 100644 --- a/libs/libpugiutil/src/pugixml_loc.hpp +++ b/libs/libpugiutil/src/pugixml_loc.hpp @@ -7,7 +7,8 @@ #include #include "pugixml.hpp" - +#include "decryption.h" +#include namespace pugiutil { //pugi offset to line/col data based on: https://stackoverflow.com/questions/21003471/convert-pugixmls-result-offset-to-column-line @@ -20,6 +21,10 @@ class loc_data { build_loc_data(); } + loc_data(char* filename_val, size_t buffersize) { + build_loc_data_from_string(filename_val, buffersize); + } + //The filename this location data is for const std::string& filename() const { return filename_; } const char* filename_c_str() const { return filename_.c_str(); } @@ -42,7 +47,7 @@ class loc_data { private: void build_loc_data(); - + void build_loc_data_from_string(char* filename_val, size_t buffersize); std::string filename_; std::vector offsets_; }; diff --git a/libs/libpugiutil/src/pugixml_util.cpp b/libs/libpugiutil/src/pugixml_util.cpp index d4d2a398246..8002ce620ff 100644 --- a/libs/libpugiutil/src/pugixml_util.cpp +++ b/libs/libpugiutil/src/pugixml_util.cpp @@ -8,21 +8,43 @@ namespace pugiutil { //Returns loc_data look-up for xml node line numbers loc_data load_xml(pugi::xml_document& doc, //Document object to be loaded with file contents const std::string filename) { //Filename to load from - auto location_data = loc_data(filename); - - auto load_result = doc.load_file(filename.c_str()); - if (!load_result) { - std::string msg = load_result.description(); - auto line = location_data.line(load_result.offset); - auto col = location_data.col(load_result.offset); - throw XmlError("Unable to load XML file '" + filename + "', " + msg - + " (line: " + std::to_string(line) + " col: " + std::to_string(col) + ")", - filename.c_str(), line); + //store the position of last '.' in the file name + int position = filename.find_last_of("."); + //store the characters after the '.' from the file_name string + std::string result = filename.substr(position); + if (result == ".xmle") { + Decryption E1(filename); + std::string fn_file = E1.getDecryptedContent(); + size_t buffersize = fn_file.size(); + char* final = new char[buffersize]; + strcpy(final, fn_file.c_str()); + auto location_data = loc_data(final, buffersize); + auto load_result = doc.load_buffer(final, buffersize); + if (!load_result) { + std::string msg = load_result.description(); + auto line = location_data.line(load_result.offset); + auto col = location_data.col(load_result.offset); + throw XmlError("Unable to load XML file '" + filename + "', " + msg + + " (line: " + std::to_string(line) + " col: " + std::to_string(col) + ")", + filename.c_str(), line); + } + delete (final); + return location_data; + } else { + //auto location_data = loc_data(end_result_fname); + auto location_data = loc_data(filename); + auto load_result = doc.load_file(filename.c_str()); + if (!load_result) { + std::string msg = load_result.description(); + auto line = location_data.line(load_result.offset); + auto col = location_data.col(load_result.offset); + throw XmlError("Unable to load XML file '" + filename + "', " + msg + + " (line: " + std::to_string(line) + " col: " + std::to_string(col) + ")", + filename.c_str(), line); + } + return location_data; } - - return location_data; } - //Gets the first child element of the given name and returns it. // // node - The parent xml node diff --git a/libs/librrgraph/src/base/get_parallel_segs.cpp b/libs/librrgraph/src/base/get_parallel_segs.cpp index d20b3066992..adc3bce4f64 100644 --- a/libs/librrgraph/src/base/get_parallel_segs.cpp +++ b/libs/librrgraph/src/base/get_parallel_segs.cpp @@ -4,14 +4,17 @@ *Segments that have BOTH_AXIS attribute value are always included in the returned vector.*/ std::vector get_parallel_segs(const std::vector& segment_inf, t_unified_to_parallel_seg_index& seg_index_map, - enum e_parallel_axis parallel_axis) { + enum e_parallel_axis parallel_axis, + bool keep_original_index) { std::vector result; for (size_t i = 0; i < segment_inf.size(); ++i) { if (segment_inf[i].parallel_axis == parallel_axis || segment_inf[i].parallel_axis == BOTH_AXIS) { result.push_back(segment_inf[i]); - result[result.size() - 1].seg_index = i; + if (!keep_original_index) { + result[result.size() - 1].seg_index = i; + } seg_index_map.insert(std::make_pair(i, std::make_pair(result.size() - 1, parallel_axis))); } } return result; -} \ No newline at end of file +} diff --git a/libs/librrgraph/src/base/get_parallel_segs.h b/libs/librrgraph/src/base/get_parallel_segs.h index c88b2c88701..70539863dda 100644 --- a/libs/librrgraph/src/base/get_parallel_segs.h +++ b/libs/librrgraph/src/base/get_parallel_segs.h @@ -6,6 +6,7 @@ std::vector get_parallel_segs(const std::vector& segment_inf, t_unified_to_parallel_seg_index& seg_index_map, - enum e_parallel_axis parallel_axis); + enum e_parallel_axis parallel_axis, + bool keep_original_index = false); -#endif \ No newline at end of file +#endif diff --git a/libs/librrgraph/src/base/rr_graph_builder.cpp b/libs/librrgraph/src/base/rr_graph_builder.cpp index ca73a5a3811..ec7d66cab18 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.cpp +++ b/libs/librrgraph/src/base/rr_graph_builder.cpp @@ -1,3 +1,4 @@ +#include "vtr_assert.h" #include "vtr_log.h" #include "rr_graph_builder.h" #include "vtr_time.h" @@ -7,7 +8,10 @@ //#include "globals.h" -RRGraphBuilder::RRGraphBuilder() {} +RRGraphBuilder::RRGraphBuilder() { + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; +} t_rr_graph_storage& RRGraphBuilder::rr_nodes() { return node_storage_; @@ -25,6 +29,14 @@ MetadataStorage>& RRGraphBuilder::rr_edge_metadata() return rr_edge_metadata_; } +vtr::vector>& RRGraphBuilder::node_in_edge_storage() { + return node_in_edges_; +} + +vtr::vector>& RRGraphBuilder::node_ptc_storage() { + return node_ptc_nums_; +} + void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { t_rr_type node_type = node_storage_.node_type(node); short node_ptc_num = node_storage_.node_ptc_num(node); @@ -60,6 +72,32 @@ void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { } } +RRNodeId RRGraphBuilder::create_node(int layer, int x, int y, t_rr_type type, int ptc, e_side side) { + e_side node_side = SIDES[0]; + /* Only OPIN and IPIN nodes have sides, otherwise force to use a default side */ + if (OPIN == type || IPIN == type) { + node_side = side; + } + node_storage_.emplace_back(); + node_ptc_nums_.emplace_back(); + RRNodeId new_node = RRNodeId(node_storage_.size() - 1); + node_storage_.set_node_layer(new_node, layer); + node_storage_.set_node_type(new_node, type); + node_storage_.set_node_coordinates(new_node, x, y, x, y); + node_storage_.set_node_ptc_num(new_node, ptc); + if (OPIN == type || IPIN == type) { + node_storage_.add_node_side(new_node, node_side); + } + /* Special for CHANX, being consistent with the rule in find_node() */ + if (CHANX == type) { + node_lookup_.add_node(new_node, layer, y, x, type, ptc, node_side); + } else { + node_lookup_.add_node(new_node, layer, x, y, type, ptc, node_side); + } + + return new_node; +} + void RRGraphBuilder::init_edge_remap(bool val) { node_storage_.init_edge_remap(val); } @@ -71,10 +109,15 @@ void RRGraphBuilder::clear_temp_storage() { void RRGraphBuilder::clear() { node_lookup_.clear(); node_storage_.clear(); + node_in_edges_.clear(); + node_ptc_nums_.clear(); rr_node_metadata_.clear(); rr_edge_metadata_.clear(); rr_segments_.clear(); rr_switch_inf_.clear(); + edges_to_build_.clear(); + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; } void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_nodes_algorithm, @@ -146,3 +189,96 @@ void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_ std::get<2>(edge)); }); } + +void RRGraphBuilder::create_edge(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped) { + edges_to_build_.emplace_back(src, dest, size_t(edge_switch), remapped); + is_edge_dirty_ = true; /* Adding a new edge revokes the flag */ + is_incoming_edge_dirty_ = true; +} + +void RRGraphBuilder::build_edges(const bool& uniquify) { + if (uniquify) { + std::sort(edges_to_build_.begin(), edges_to_build_.end()); + edges_to_build_.erase(std::unique(edges_to_build_.begin(), edges_to_build_.end()), edges_to_build_.end()); + } + alloc_and_load_edges(&edges_to_build_); + edges_to_build_.clear(); + is_edge_dirty_ = false; +} + +void RRGraphBuilder::build_in_edges() { + VTR_ASSERT(validate()); + node_in_edges_.clear(); + node_in_edges_.resize(node_storage_.size()); + + for (RRNodeId src_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + for (auto iedge : node_storage_.edges(src_node)) { + VTR_ASSERT(src_node == node_storage_.edge_source_node(node_storage_.edge_id(src_node, iedge))); + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, iedge)); + node_in_edges_[des_node].push_back(node_storage_.edge_id(src_node, iedge)); + } + } + is_incoming_edge_dirty_ = false; +} + +std::vector RRGraphBuilder::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (is_incoming_edge_dirty_) { + VTR_LOG_ERROR("Incoming edges are not built yet in routing resource graph. Please call build_in_edges()."); + return std::vector(); + } + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +void RRGraphBuilder::add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id) { + VTR_ASSERT(size_t(node) < node_storage_.size()); + VTR_ASSERT(size_t(node) < node_ptc_nums_.size()); + VTR_ASSERT_MSG(node_storage_.node_type(node) == CHANX || node_storage_.node_type(node) == CHANY, "Track number valid only for CHANX/CHANY RR nodes"); + + size_t node_length = std::abs(node_storage_.node_xhigh(node) - node_storage_.node_xlow(node)) + + std::abs(node_storage_.node_yhigh(node) - node_storage_.node_ylow(node)); + if (node_length + 1 != node_ptc_nums_[node].size()) { + node_ptc_nums_[node].resize(node_length + 1); + } + + size_t offset = node_offset.x() - node_storage_.node_xlow(node) + node_offset.y() - node_storage_.node_ylow(node); + VTR_ASSERT(offset < node_ptc_nums_[node].size()); + + node_ptc_nums_[node][offset] = track_id; +} + +void RRGraphBuilder::add_track_node_to_lookup(RRNodeId node) { + VTR_ASSERT_MSG(node_storage_.node_type(node) == CHANX || node_storage_.node_type(node) == CHANY, "Update track node look-up is only valid to CHANX/CHANY nodes"); + + /* Compute the track id based on the (x, y) coordinate */ + size_t x_start = std::min(node_storage_.node_xlow(node), node_storage_.node_xhigh(node)); + size_t y_start = std::min(node_storage_.node_ylow(node), node_storage_.node_yhigh(node)); + std::vector node_x(std::abs(node_storage_.node_xlow(node) - node_storage_.node_xhigh(node)) + 1); + std::vector node_y(std::abs(node_storage_.node_ylow(node) - node_storage_.node_yhigh(node)) + 1); + + std::iota(node_x.begin(), node_x.end(), x_start); + std::iota(node_y.begin(), node_y.end(), y_start); + + VTR_ASSERT(size_t(std::max(node_storage_.node_xlow(node), node_storage_.node_xhigh(node))) == node_x.back()); + VTR_ASSERT(size_t(std::max(node_storage_.node_ylow(node), node_storage_.node_yhigh(node))) == node_y.back()); + + for (const size_t& x : node_x) { + for (const size_t& y : node_y) { + size_t ptc = node_storage_.node_ptc_num(node); + /* Routing channel nodes may have different ptc num + * Find the track ids using the x/y offset + * FIXME: Special case on assigning CHANX (x,y) should be changed to a natural way! + */ + if (CHANX == node_storage_.node_type(node)) { + ptc = node_ptc_nums_[node][x - node_storage_.node_xlow(node)]; + node_lookup_.add_node(node, node_storage_.node_layer(node), y, x, CHANX, ptc); + } else if (CHANY == node_storage_.node_type(node)) { + ptc = node_ptc_nums_[node][y - node_storage_.node_ylow(node)]; + node_lookup_.add_node(node, node_storage_.node_layer(node), x, y, CHANY, ptc); + } + } + } +} diff --git a/libs/librrgraph/src/base/rr_graph_builder.h b/libs/librrgraph/src/base/rr_graph_builder.h index cf82d64a24a..e36b70449e0 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.h +++ b/libs/librrgraph/src/base/rr_graph_builder.h @@ -16,6 +16,7 @@ #include "rr_graph_storage.h" #include "rr_spatial_lookup.h" #include "metadata_storage.h" +#include "rr_edge.h" class RRGraphBuilder { /* -- Constructors -- */ @@ -44,6 +45,10 @@ class RRGraphBuilder { MetadataStorage& rr_node_metadata(); /** @brief Return a writable object for the meta data on the edge */ MetadataStorage>& rr_edge_metadata(); + /** @brief Return a writable object fo the incoming edge storage */ + vtr::vector>& node_in_edge_storage(); + /** @brief Return a writable object of the node ptc storage (for tileable routing resource graph) */ + vtr::vector>& node_ptc_storage(); /** @brief Return the size for rr_node_metadata */ inline size_t rr_node_metadata_size() const { @@ -114,6 +119,10 @@ class RRGraphBuilder { inline void set_node_type(RRNodeId id, t_rr_type type) { node_storage_.set_node_type(id, type); } + /** @brief Create a new rr_node in the node storage and register it to the node look-up. + * Return a valid node id if succeed. Otherwise, return an invalid id. + */ + RRNodeId create_node(int layer, int x, int y, t_rr_type type, int ptc, e_side side = NUM_SIDES); /** * @brief Add an existing rr_node in the node storage to the node look-up * @@ -188,11 +197,6 @@ class RRGraphBuilder { node_storage_.set_node_ptc_num(id, new_ptc_num); } - /** @brief set the layer number at which RRNodeId is located at */ - inline void set_node_layer(RRNodeId id, int layer){ - node_storage_.set_node_layer(id, layer); - } - /** @brief set_node_pin_num() is designed for logic blocks, which are IPIN and OPIN nodes */ inline void set_node_pin_num(RRNodeId id, int new_pin_num) { node_storage_.set_node_pin_num(id, new_pin_num); @@ -203,7 +207,16 @@ class RRGraphBuilder { node_storage_.set_node_track_num(id, new_track_num); } - /** @brief set_ node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ + /** @brief Add a track id for a given node base on the offset in coordinate, applicable only to CHANX and CHANY nodes. + * This API is used by tileable routing resource graph generator, which requires each routing track has a different + * track id depending their location in FPGA fabric. + */ + void add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id); + + /** @brief Update the node_lookup for a track node. This is applicable to tileable routing graph */ + void add_track_node_to_lookup(RRNodeId node); + + /** @brief set_node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ inline void set_node_class_num(RRNodeId id, int new_class_num) { node_storage_.set_node_class_num(id, new_class_num); } @@ -213,6 +226,24 @@ class RRGraphBuilder { node_storage_.set_node_direction(id, new_direction); } + /** @brief Add a new edge to the cache of edges to be built + * .. note:: This will not add an edge to storage! You need to call build_edges() after all the edges are cached! */ + void create_edge(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped); + + /** @brief Allocate and build actual edges in storage. + * Once called, the cached edges will be uniquified and added to routing resource nodes, + * while the cache will be empty once build-up is accomplished */ + void build_edges(const bool& uniquify = true); + + /** @brief Allocate and build incoming edges for each node. + * By default, no incoming edges are kept in storage, to be memory efficient */ + void build_in_edges(); + + /** @brief Return incoming edges for a given routing resource node + * Require build_in_edges() to be called first + */ + std::vector node_in_edges(RRNodeId node) const; + /** @brief Reserve the lists of edges to be memory efficient. * This function is mainly used to reserve memory space inside RRGraph, * when adding a large number of edges in order to avoid memory fragements */ @@ -277,6 +308,9 @@ class RRGraphBuilder { return node_storage_.count_rr_switches(arch_switch_inf, arch_switch_fanins); } + /* Unlock storage; required to modify an routing resource graph after edge is read */ + inline void unlock_storage() { node_storage_.edges_read_ = false; node_storage_.partitioned_ = false; node_storage_.clear_node_first_edge();} + /** @brief Reserve the lists of nodes, edges, switches etc. to be memory efficient. * This function is mainly used to reserve memory space inside RRGraph, * when adding a large number of nodes/edge/switches/segments, @@ -300,13 +334,13 @@ class RRGraphBuilder { rr_switch_inf_.resize(size); } - /** @brief Validate that edge data is partitioned correctly + /** @brief Validate that edge data is partitioned correctly. Also there are no edges left to be built! * @note This function is used to validate the correctness of the routing resource graph in terms * of graph attributes. Strongly recommend to call it when you finish the building a routing resource * graph. If you need more advance checks, which are related to architecture features, you should * consider to use the check_rr_graph() function or build your own check_rr_graph() function. */ inline bool validate() const { - return node_storage_.validate(rr_switch_inf_); + return node_storage_.validate(rr_switch_inf_) && edges_to_build_.empty(); } /** @brief Sorts edge data such that configurable edges appears before @@ -353,6 +387,14 @@ class RRGraphBuilder { /* Fast look-up for rr nodes */ RRSpatialLookup node_lookup_; + /* A cache for edge-related information, required to build edges for routing resource nodes. + * It is used when building a routing resource graph by considering memory efficiency. + * It will be clear up after calling build_edges(). + * + * .. warning:: This is a temporary data which is used to collect edges to be built for nodes + */ + t_rr_edge_info_set edges_to_build_; + /** Wire segment types in RR graph * - Each rr_segment contains the detailed information of a routing track, which is denoted by a node in CHANX or CHANY type. * - We use a fly-weight data structure here, in the same philosophy as the rr_indexed_data. See detailed explanation in the t_segment_inf data structure @@ -366,6 +408,29 @@ class RRGraphBuilder { /* Detailed information about the switches, which are used in the RRGraph */ vtr::vector rr_switch_inf_; + /** A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. + * By default, it is empty! Call build_in_edges() to construct it!!! */ + vtr::vector> node_in_edges_; + + /* Extra ptc number for each routing resource node. This is required by tileable routing resource graph. + * In a tileable routing architecture, routing tracks, e.g., CHANX and CHANY, follows a staggered organization. + * Hence, a routing track may appear in different routing channels, representing different ptc/track id. + * Here is an illustrative example of a X-direction routing track (CHANX) in INC direction, which is organized in staggered way. + * + * Coord(x,y) (1,0) (2,0) (3,0) (4,0) Another track (node) + * ptc=0 ------> ------> + * \ / + * ptc=1 ------> / + * \ / + * ptc=2 ------> / + * \ / + * ptc=3 -------> + * ^ ^ + * | | + * starting point ending point + */ + vtr::vector> node_ptc_nums_; + /** .. warning:: The Metadata should stay as an independent data structure than rest of the internal data, * e.g., node_lookup! */ /* Metadata is an extra data on rr-nodes and edges, respectively, that is not used by vpr @@ -390,6 +455,11 @@ class RRGraphBuilder { * value: map of */ MetadataStorage> rr_edge_metadata_; + + /** @brief a flag to mark the status of edge storage + * dirty means that the edge storage is not complete, should call related APIs to build */ + bool is_edge_dirty_; + bool is_incoming_edge_dirty_; }; #endif diff --git a/libs/librrgraph/src/base/rr_graph_fwd.h b/libs/librrgraph/src/base/rr_graph_fwd.h index 41d0b8f3d58..2f685e4c15a 100644 --- a/libs/librrgraph/src/base/rr_graph_fwd.h +++ b/libs/librrgraph/src/base/rr_graph_fwd.h @@ -1,8 +1,7 @@ -#ifndef RR_GRAPH_OBJ_FWD_H -#define RR_GRAPH_OBJ_FWD_H +#ifndef RR_GRAPH_FWD_H +#define RR_GRAPH_FWD_H #include - #include "vtr_strong_id.h" /*************************************************************** @@ -14,8 +13,6 @@ //Forward declaration class t_rr_graph_storage; -class RRGraph; - struct rr_node_id_tag; struct rr_edge_id_tag; struct rr_indexed_data_id_tag; diff --git a/libs/librrgraph/src/base/rr_graph_storage.cpp b/libs/librrgraph/src/base/rr_graph_storage.cpp index ef85d779a4c..2930c29d5a6 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.cpp +++ b/libs/librrgraph/src/base/rr_graph_storage.cpp @@ -380,7 +380,10 @@ void t_rr_graph_storage::assign_first_edges() { bool t_rr_graph_storage::verify_first_edges() const { size_t num_edges = edge_src_node_.size(); - VTR_ASSERT(node_first_edge_[RRNodeId(node_storage_.size())] == RREdgeId(num_edges)); + if (node_first_edge_[RRNodeId(node_storage_.size())] != RREdgeId(num_edges)) { + VTR_LOG("node first edge is '%lu' while expected edge id is '%lu'\n", size_t(node_first_edge_[RRNodeId(node_storage_.size())]), num_edges); + VTR_ASSERT(node_first_edge_[RRNodeId(node_storage_.size())] == RREdgeId(num_edges)); + } // Each edge should belong with the edge range defined by // [node_first_edge_[src_node], node_first_edge_[src_node+1]). @@ -558,6 +561,11 @@ t_edge_size t_rr_graph_storage::num_non_configurable_edges(RRNodeId node, const return num_edges(node) - num_configurable_edges(node, rr_switches); } +bool t_rr_graph_storage::edge_is_configurable(RREdgeId edge, const vtr::vector& rr_switches) const { + auto iswitch = edge_switch(edge); + return rr_switches[RRSwitchId(iswitch)].configurable(); +} + bool t_rr_graph_storage::edge_is_configurable(RRNodeId id, t_edge_size iedge, const vtr::vector& rr_switches) const { auto iswitch = edge_switch(id, iedge); return rr_switches[RRSwitchId(iswitch)].configurable(); @@ -627,21 +635,21 @@ void t_rr_graph_storage::set_node_ptc_num(RRNodeId id, int new_ptc_num) { } void t_rr_graph_storage::set_node_pin_num(RRNodeId id, int new_pin_num) { if (node_type(id) != IPIN && node_type(id) != OPIN) { - VTR_LOG_ERROR("Attempted to set RR node 'pin_num' for non-IPIN/OPIN type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'pin_num' for non-IPIN/OPIN type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.pin_num = new_pin_num; } void t_rr_graph_storage::set_node_track_num(RRNodeId id, int new_track_num) { if (node_type(id) != CHANX && node_type(id) != CHANY) { - VTR_LOG_ERROR("Attempted to set RR node 'track_num' for non-CHANX/CHANY type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'track_num' for non-CHANX/CHANY type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.track_num = new_track_num; } void t_rr_graph_storage::set_node_class_num(RRNodeId id, int new_class_num) { if (node_type(id) != SOURCE && node_type(id) != SINK) { - VTR_LOG_ERROR("Attempted to set RR node 'class_num' for non-SOURCE/SINK type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'class_num' for non-SOURCE/SINK type '%s'\n", node_type_string(id)); } node_ptc_[id].ptc_.class_num = new_class_num; } @@ -656,7 +664,7 @@ static int get_node_pin_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != IPIN && node_type != OPIN) { - VTR_LOG_ERROR("Attempted to access RR node 'pin_num' for non-IPIN/OPIN type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'pin_num' for non-IPIN/OPIN type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.pin_num; } @@ -667,7 +675,7 @@ static int get_node_track_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != CHANX && node_type != CHANY) { - VTR_LOG_ERROR("Attempted to access RR node 'track_num' for non-CHANX/CHANY type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'track_num' for non-CHANX/CHANY type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.track_num; } @@ -678,7 +686,7 @@ static int get_node_class_num( RRNodeId id) { auto node_type = node_storage[id].type_; if (node_type != SOURCE && node_type != SINK) { - VTR_LOG_ERROR("Attempted to access RR node 'class_num' for non-SOURCE/SINK type '%s'", rr_node_typename[node_type]); + VTR_LOG_ERROR("Attempted to access RR node 'class_num' for non-SOURCE/SINK type '%s'\n", rr_node_typename[node_type]); } return node_ptc[id].ptc_.class_num; } @@ -728,7 +736,7 @@ void t_rr_graph_storage::set_node_coordinates(RRNodeId id, short x1, short y1, s void t_rr_graph_storage::set_node_cost_index(RRNodeId id, RRIndexedDataId new_cost_index) { auto& node = node_storage_[id]; if ((size_t)new_cost_index >= std::numeric_limits::max()) { - VTR_LOG_ERROR("Attempted to set cost_index_ %zu above cost_index storage max value.", + VTR_LOG_ERROR("Attempted to set cost_index_ %zu above cost_index storage max value.\n", new_cost_index); } node.cost_index_ = (size_t)new_cost_index; @@ -745,19 +753,19 @@ void t_rr_graph_storage::set_node_capacity(RRNodeId id, short new_capacity) { void t_rr_graph_storage::set_node_direction(RRNodeId id, Direction new_direction) { if (node_type(id) != CHANX && node_type(id) != CHANY) { - VTR_LOG_ERROR("Attempted to set RR node 'direction' for non-channel type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'direction' for non-channel type '%s'\n", node_type_string(id)); } node_storage_[id].dir_side_.direction = new_direction; } void t_rr_graph_storage::add_node_side(RRNodeId id, e_side new_side) { if (node_type(id) != IPIN && node_type(id) != OPIN) { - VTR_LOG_ERROR("Attempted to set RR node 'side' for non-channel type '%s'", node_type_string(id)); + VTR_LOG_ERROR("Attempted to set RR node 'side' for non-channel type '%s'\n", node_type_string(id)); } std::bitset side_bits = node_storage_[id].dir_side_.sides; side_bits[size_t(new_side)] = true; if (side_bits.to_ulong() > CHAR_MAX) { - VTR_LOG_ERROR("Invalid side '%s' to be added to rr node %u", SIDE_STRING[new_side], size_t(id)); + VTR_LOG_ERROR("Invalid side '%s' to be added to rr node %u\n", SIDE_STRING[new_side], size_t(id)); } node_storage_[id].dir_side_.sides = static_cast(side_bits.to_ulong()); } diff --git a/libs/librrgraph/src/base/rr_graph_storage.h b/libs/librrgraph/src/base/rr_graph_storage.h index 09d80264645..60cafacf1cd 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.h +++ b/libs/librrgraph/src/base/rr_graph_storage.h @@ -63,13 +63,13 @@ struct alignas(16) t_rr_node_data { t_rr_type type_ = NUM_RR_TYPES; /* The character is a hex number which is a 4-bit truth table for node sides - * The 4-bits in serial represent 4 sides on which a node could appear - * It follows a fixed sequence, which is (LEFT, BOTTOM, RIGHT, TOP) whose indices are (3, 2, 1, 0) + * The 4-bits in serial represent 4 sides on which a node could appear + * It follows a fixed sequence, which is (LEFT, BOTTOM, RIGHT, TOP) whose indices are (3, 2, 1, 0) * - When a node appears on a given side, it is set to "1" * - When a node does not appear on a given side, it is set to "0" * For example, - * - '1' means '0001' in hex number, which means the node appears on TOP - * - 'A' means '1100' in hex number, which means the node appears on LEFT and BOTTOM sides, + * - '1' means '0001' in hex number, which means the node appears on TOP + * - 'A' means '1100' in hex number, which means the node appears on LEFT and BOTTOM sides, */ union { Direction direction; //Valid only for CHANX/CHANY @@ -261,6 +261,7 @@ class t_rr_graph_storage { * - num_non_configurable_edges(RRNodeId) * - edge_id(RRNodeId, t_edge_size) * - edge_sink_node(RRNodeId, t_edge_size) + * - edge_source_node(RRNodeId, t_edge_size) * - edge_switch(RRNodeId, t_edge_size) * * Only call these methods after partition_edges has been invoked. */ @@ -278,6 +279,7 @@ class t_rr_graph_storage { t_edge_size num_edges(const RRNodeId& id) const { return size_t(last_edge(id)) - size_t(first_edge(id)); } + bool edge_is_configurable(RREdgeId edge, const vtr::vector& rr_switches) const; bool edge_is_configurable(RRNodeId id, t_edge_size iedge, const vtr::vector& rr_switches) const; t_edge_size num_configurable_edges(RRNodeId node, const vtr::vector& rr_switches) const; t_edge_size num_non_configurable_edges(RRNodeId node, const vtr::vector& rr_switches) const; @@ -325,6 +327,11 @@ class t_rr_graph_storage { return edge_dest_node_[edge]; } + // Get the source node for the specified edge. + RRNodeId edge_source_node(const RREdgeId& edge) const { + return edge_src_node_[edge]; + } + // Call the `apply` function with the edge id, source, and sink nodes of every edge. void for_each_edge(std::function apply) const { for (size_t i = 0; i < edge_dest_node_.size(); i++) { @@ -341,6 +348,11 @@ class t_rr_graph_storage { return edge_sink_node(edge_id(id, iedge)); } + // Get the source node for the iedge'th edge from specified RRNodeId. + RRNodeId edge_source_node(const RRNodeId& id, t_edge_size iedge) const { + return edge_source_node(edge_id(id, iedge)); + } + // Get the switch used for the specified edge. short edge_switch(const RREdgeId& edge) const { return edge_switch_[edge]; @@ -654,6 +666,7 @@ class t_rr_graph_storage { return side_tt[size_t(side)]; } + public: inline void clear_node_first_edge() { node_first_edge_.clear(); } diff --git a/libs/librrgraph/src/base/rr_graph_utils.h b/libs/librrgraph/src/base/rr_graph_utils.h index 0725bcd0cf9..1aa58acc0b4 100644 --- a/libs/librrgraph/src/base/rr_graph_utils.h +++ b/libs/librrgraph/src/base/rr_graph_utils.h @@ -5,8 +5,8 @@ * the function declaration */ #include -#include "rr_graph_fwd.h" #include "rr_node_types.h" +#include "rr_graph_obj.h" #include "rr_graph_view.h" struct t_pin_chain_node { @@ -48,4 +48,4 @@ vtr::vector> get_fan_in_list(const RRGraphView& int seg_index_of_cblock(const RRGraphView& rr_graph, t_rr_type from_rr_type, int to_node); int seg_index_of_sblock(const RRGraphView& rr_graph, int from_node, int to_node); -#endif \ No newline at end of file +#endif diff --git a/libs/librrgraph/src/base/rr_graph_view.cpp b/libs/librrgraph/src/base/rr_graph_view.cpp index 0ddc5445d42..225af722e5b 100644 --- a/libs/librrgraph/src/base/rr_graph_view.cpp +++ b/libs/librrgraph/src/base/rr_graph_view.cpp @@ -1,3 +1,4 @@ +#include #include "rr_graph_view.h" #include "rr_node.h" #include "physical_types.h" @@ -9,7 +10,9 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, const vtr::vector& rr_indexed_data, const std::vector& rr_rc_data, const vtr::vector& rr_segments, - const vtr::vector& rr_switch_inf) + const vtr::vector& rr_switch_inf, + const vtr::vector>& node_in_edges, + const vtr::vector>& node_ptc_nums) : node_storage_(node_storage) , node_lookup_(node_lookup) , rr_node_metadata_(rr_node_metadata) @@ -17,5 +20,117 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, , rr_indexed_data_(rr_indexed_data) , rr_rc_data_(rr_rc_data) , rr_segments_(rr_segments) - , rr_switch_inf_(rr_switch_inf) { + , rr_switch_inf_(rr_switch_inf) + , node_in_edges_(node_in_edges) + , node_ptc_nums_(node_ptc_nums) { } + +std::vector RRGraphView::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +std::vector RRGraphView::node_configurable_in_edges(RRNodeId node) const { + /* Note: This is not efficient in runtime, should sort edges by configurability when allocating the array! */ + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (rr_switch_inf_[edge_switch(edge)].configurable()) { + ret_edges.push_back(edge); + } + } + return ret_edges; +} + +std::vector RRGraphView::node_non_configurable_in_edges(RRNodeId node) const { + /* Note: This is not efficient in runtime, should sort edges by configurability when allocating the array! */ + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (!rr_switch_inf_[edge_switch(edge)].configurable()) { + ret_edges.push_back(edge); + } + } + return ret_edges; +} + +std::vector RRGraphView::find_edges(const RRNodeId& src_node, const RRNodeId& des_node) const { + std::vector edge_list; + for (auto iedge : node_out_edges(src_node)) { + if (edge_sink_node(RREdgeId(iedge)) == des_node) { + edge_list.push_back(RREdgeId(iedge)); + } + } + return edge_list; +} + +RRSegmentId RRGraphView::node_segment(RRNodeId node) const { + RRIndexedDataId cost_index = node_cost_index(node); + return RRSegmentId(rr_indexed_data_[cost_index].seg_index); +} + +size_t RRGraphView::in_edges_count() const { + size_t edge_count = 0; + for (auto edge_list : node_in_edges_) { + edge_count += edge_list.size(); + } + return edge_count; +} + +bool RRGraphView::validate_in_edges() const { + size_t num_err = 0; + /* For each edge, validate that + * - The source node is in the fan-in edge list of the destination node + * - The sink node is in the fan-out edge list of the source node + */ + for (RRNodeId curr_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + /* curr_node ---> des_node + * <-?- check if the incoming edge is correct or not + */ + for (auto iedge : node_storage_.edges(curr_node)) { + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(curr_node, iedge)); + std::vector des_fanin_nodes; + for (auto next_edge : node_in_edges(des_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_source_node(next_edge); + des_fanin_nodes.push_back(prev_edge_des_node); + } + if (des_fanin_nodes.end() == std::find(des_fanin_nodes.begin(), des_fanin_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-in edges of Node '%s', while does drive it in its fan-out list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(des_node).c_str()); + num_err++; + } + } + /* src_node -?-> curr_node + * <--- check if the fan-out edge is correct or not + */ + for (auto iedge : node_in_edges(curr_node)) { + RRNodeId src_node = node_storage_.edge_source_node(iedge); + std::vector src_fanout_nodes; + for (auto prev_edge : node_storage_.edges(src_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, prev_edge)); + src_fanout_nodes.push_back(prev_edge_des_node); + } + if (src_fanout_nodes.end() == std::find(src_fanout_nodes.begin(), src_fanout_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-out edges of Node '%s', while does exist in its fan-in list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(src_node).c_str()); + num_err++; + } + } + } + if (num_err) { + VTR_LOG_ERROR("Found %ld errors when validating incoming edges for routing resource graph\n", num_err); + return false; + } + return true; +} + + diff --git a/libs/librrgraph/src/base/rr_graph_view.h b/libs/librrgraph/src/base/rr_graph_view.h index 3d808b23c71..92fac9f0a6c 100644 --- a/libs/librrgraph/src/base/rr_graph_view.h +++ b/libs/librrgraph/src/base/rr_graph_view.h @@ -25,9 +25,9 @@ * - This avoids massive changes for each client on using the APIs * as each frame view provides adhoc APIs for each client * - * TODO: more compact frame views will be created, e.g., + * TODO: more compact frame views will be created, e.g., * - a mini frame view: contains only node and edges, representing the connectivity of the graph - * - a geometry frame view: an extended mini frame view with node-level attributes, + * - a geometry frame view: an extended mini frame view with node-level attributes, * in particular geometry information (type, x, y etc). * */ @@ -42,32 +42,34 @@ class RRGraphView { const vtr::vector& rr_indexed_data, const std::vector& rr_rc_data, const vtr::vector& rr_segments, - const vtr::vector& rr_switch_inf); + const vtr::vector& rr_switch_inf, + const vtr::vector>& node_in_edges, + const vtr::vector>& node_ptc_nums); /* Disable copy constructors and copy assignment operator - * This is to avoid accidental copy because it could be an expensive operation considering that the + * This is to avoid accidental copy because it could be an expensive operation considering that the * memory footprint of the data structure could ~ Gb - * Using the following syntax, we prohibit accidental 'pass-by-value' which can be immediately caught + * Using the following syntax, we prohibit accidental 'pass-by-value' which can be immediately caught * by compiler */ RRGraphView(const RRGraphView&) = delete; void operator=(const RRGraphView&) = delete; /* -- Accessors -- */ - /* TODO: The accessors may be turned into private later if they are replacable by 'questionin' + /* TODO: The accessors may be turned into private later if they are replacable by 'questionin' * kind of accessors */ public: /* Aggregates: create range-based loops for nodes - * To iterate over the nodes in a RRGraph, - * using a range-based loop is suggested. - * ----------------------------------------------------------------- - * Example: iterate over all the nodes - * // Strongly suggest to use a read-only rr_graph object - * const RRGraph& rr_graph; - * for (const RRNodeId& node : rr_graph.nodes()) { - * // Do something with each node - * } + * To iterate over the nodes in a RRGraph, + * using a range-based loop is suggested. + * ----------------------------------------------------------------- + * Example: iterate over all the nodes + * // Strongly suggest to use a read-only rr_graph object + * const RRGraph& rr_graph; + * for (const RRNodeId& node : rr_graph.nodes()) { + * // Do something with each node + * } */ inline vtr::StrongIdRange nodes() const { return vtr::StrongIdRange(RRNodeId(0), RRNodeId(num_nodes())); @@ -193,7 +195,7 @@ class RRGraphView { && (node_xhigh(node) == -1) && (node_yhigh(node) == -1)); } - /** @brief Check if two routing resource nodes are adjacent (must be a CHANX and a CHANY). + /** @brief Check if two routing resource nodes are adjacent (must be a CHANX and a CHANY). * This function is used for error checking; it checks if two nodes are physically adjacent (could be connected) based on their geometry. * It does not check the routing edges to see if they are, in fact, possible to connect in the current routing graph. * This function is inlined for runtime optimization. */ @@ -208,7 +210,7 @@ class RRGraphView { return true; } - /** @brief Check if node is within bounding box. + /** @brief Check if node is within bounding box. * To return true, the RRNode must be completely contained within the specified bounding box, with the edges of the bounding box being inclusive. *This function is inlined for runtime optimization. */ inline bool node_is_inside_bounding_box(RRNodeId node, vtr::Rect bounding_box) const { @@ -305,17 +307,39 @@ class RRGraphView { inline short edge_switch(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_switch(id, iedge); } + inline RRSwitchId edge_switch(RREdgeId edge) const { + return RRSwitchId(node_storage_.edge_switch(edge)); + } + /** @brief Get the source node for the iedge'th edge from specified RRNodeId. + * This method should generally not be used, and instead first_edge and + * last_edge should be used.*/ + inline RRNodeId edge_src_node(RREdgeId edge) const { + return node_storage_.edge_source_node(edge); + } /** @brief Get the destination node for the iedge'th edge from specified RRNodeId. * This method should generally not be used, and instead first_edge and * last_edge should be used.*/ inline RRNodeId edge_sink_node(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_sink_node(id, iedge); } + inline RRNodeId edge_sink_node(RREdgeId edge) const { + return node_storage_.edge_sink_node(edge); + } + + /** @brief Get the source node for the iedge'th edge from specified RRNodeId. + * This method should generally not be used, and instead first_edge and + * last_edge should be used.*/ + inline RRNodeId edge_source_node(RRNodeId id, t_edge_size iedge) const { + return node_storage_.edge_source_node(id, iedge); + } /** @brief Detect if the edge is a configurable edge (controlled by a programmable routing multipler or a tri-state switch). */ inline bool edge_is_configurable(RRNodeId id, t_edge_size iedge) const { return node_storage_.edge_is_configurable(id, iedge, rr_switch_inf_); } + inline bool edge_is_configurable(RREdgeId edge) const { + return node_storage_.edge_is_configurable(edge, rr_switch_inf_); + } /** @brief Get the number of configurable edges. This function is inlined for runtime optimization. */ inline t_edge_size num_configurable_edges(RRNodeId node) const { @@ -327,22 +351,28 @@ class RRGraphView { return node_storage_.num_non_configurable_edges(node, rr_switch_inf_); } - /** @brief A configurable edge represents a programmable switch between routing resources, which could be + /** @brief A configurable edge represents a programmable switch between routing resources, which could be * a multiplexer * a tri-state buffer - * a pass gate + * a pass gate * This API gets ID range for configurable edges. This function is inlined for runtime optimization. */ inline edge_idx_range configurable_edges(RRNodeId node) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(node_storage_.num_edges(node) - num_non_configurable_edges(node))); } + inline edge_idx_range node_configurable_out_edges(RRNodeId node) const { + return configurable_edges(node); + } - /** @brief A non-configurable edge represents a hard-wired connection between routing resources, which could be + /** @brief A non-configurable edge represents a hard-wired connection between routing resources, which could be * a non-configurable buffer that can not be turned off * a short metal connection that can not be turned off * This API gets ID range for non-configurable edges. This function is inlined for runtime optimization. */ inline edge_idx_range non_configurable_edges(RRNodeId node) const { return vtr::make_range(edge_idx_iterator(node_storage_.num_edges(node) - num_non_configurable_edges(node)), edge_idx_iterator(num_edges(node))); } + inline edge_idx_range node_non_configurable_out_edges(RRNodeId node) const { + return non_configurable_edges(node); + } /** @brief Get outgoing edges for a node. * This API is designed to enable range-based loop to walk through the outgoing edges of a node @@ -356,32 +386,38 @@ class RRGraphView { inline edge_idx_range edges(const RRNodeId& id) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); } + inline edge_idx_range node_out_edges(const RRNodeId& id) const { + return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); + } + + /** @brief find the edges between two nodes */ + std::vector find_edges(const RRNodeId& src_node, const RRNodeId& des_node) const; /** @brief Get the number of edges. This function is inlined for runtime optimization. */ inline t_edge_size num_edges(RRNodeId node) const { return node_storage_.num_edges(node); } - /** @brief The ptc_num carries different meanings for different node types - * (true in VPR RRG that is currently supported, may not be true in customized RRG) - * CHANX or CHANY: the track id in routing channels - * OPIN or IPIN: the index of pins in the logic block data structure - * SOURCE and SINK: the class id of a pin (indicating logic equivalence of pins) in the logic block data structure - * @note - * This API is very powerful and developers should not use it unless it is necessary, - * e.g the node type is unknown. If the node type is known, the more specific routines, `node_pin_num()`, + /** @brief The ptc_num carries different meanings for different node types + * (true in VPR RRG that is currently supported, may not be true in customized RRG) + * CHANX or CHANY: the track id in routing channels + * OPIN or IPIN: the index of pins in the logic block data structure + * SOURCE and SINK: the class id of a pin (indicating logic equivalence of pins) in the logic block data structure + * @note + * This API is very powerful and developers should not use it unless it is necessary, + * e.g the node type is unknown. If the node type is known, the more specific routines, `node_pin_num()`, * `node_track_num()`and `node_class_num()`, for different types of nodes should be used.*/ inline int node_ptc_num(RRNodeId node) const { return node_storage_.node_ptc_num(node); } - /** @brief Get the pin num of a routing resource node. This is designed for logic blocks, + /** @brief Get the pin num of a routing resource node. This is designed for logic blocks, * which are IPIN and OPIN nodes. This function is inlined for runtime optimization. */ inline int node_pin_num(RRNodeId node) const { return node_storage_.node_pin_num(node); } - /** @brief Get the track num of a routing resource node. This is designed for routing tracks, + /** @brief Get the track num of a routing resource node. This is designed for routing tracks, * which are CHANX and CHANY nodes. This function is inlined for runtime optimization. */ inline int node_track_num(RRNodeId node) const { return node_storage_.node_track_num(node); @@ -397,6 +433,17 @@ class RRGraphView { RRIndexedDataId node_cost_index(RRNodeId node) const { return node_storage_.node_cost_index(node); } + + /** @brief Get the segment id which a routing resource node represents. Only applicable to nodes whose type is CHANX or CHANY */ + RRSegmentId node_segment(RRNodeId node) const; + + /** @brief Return incoming edges for a given routing resource node + * Require build_in_edges() to be called first + */ + std::vector node_in_edges(RRNodeId node) const; + std::vector node_configurable_in_edges(RRNodeId node) const; + std::vector node_non_configurable_in_edges(RRNodeId node) const; + /** @brief Return detailed routing segment information with a given id* @note The routing segments here may not be exactly same as those defined in architecture file. They have been * adapted to fit the context of routing resource graphs. */ @@ -460,7 +507,7 @@ class RRGraphView { } public: /* Validators */ - /** brief Validate that edge data is partitioned correctly + /** @brief Validate that edge data is partitioned correctly * @note This function is used to validate the correctness of the routing resource graph in terms * of graph attributes. Strongly recommend to call it when you finish the building a routing resource * graph. If you need more advance checks, which are related to architecture features, you should @@ -469,6 +516,21 @@ class RRGraphView { return node_storage_.validate_node(node_id, rr_switch_inf_); } + /** @brief Check if the node id is a valid one in storage */ + inline bool valid_node(RRNodeId node_id) const { + return size_t(node_id) < node_storage_.size(); + } + + /** @brief Check if the switch is a valid one in storage */ + inline bool valid_switch(RRSwitchId switch_id) const { + return (size_t(switch_id) < rr_switch_inf_.size()); + } + + /** @brief Validate if all the fan-in edge lists are valid */ + bool validate_in_edges() const; + /** @brief Count the number of incoming edges for all the nodes */ + size_t in_edges_count() const; + /* -- Internal data storage -- */ /* Note: only read-only object or data structures are allowed!!! */ private: @@ -503,13 +565,20 @@ class RRGraphView { /* rr_indexed_data_ and rr_segments_ are needed to lookup the segment information in node_coordinate_to_string() */ const vtr::vector& rr_indexed_data_; - /* RC data for nodes. This is a flyweight data */ + /* RC data for nodes. This is a flyweight data */ const std::vector& rr_rc_data_; /* Segment info for rr nodes */ const vtr::vector& rr_segments_; /* switch info for rr nodes */ const vtr::vector& rr_switch_inf_; + + /** A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. + * By default, it is empty! Call build_in_edges() to construct it!!! */ + const vtr::vector>& node_in_edges_; + + /** A list of extra ptc numbers for each routing resource node. See details in RRGraphBuilder class */ + const vtr::vector>& node_ptc_nums_; }; #endif diff --git a/libs/librrgraph/src/base/rr_spatial_lookup.cpp b/libs/librrgraph/src/base/rr_spatial_lookup.cpp index 5b76b3418af..37b4c314470 100644 --- a/libs/librrgraph/src/base/rr_spatial_lookup.cpp +++ b/libs/librrgraph/src/base/rr_spatial_lookup.cpp @@ -1,4 +1,5 @@ #include "vtr_assert.h" +#include "vtr_log.h" #include "rr_spatial_lookup.h" RRSpatialLookup::RRSpatialLookup() { diff --git a/libs/libvtrcapnproto/CMakeLists.txt b/libs/libvtrcapnproto/CMakeLists.txt index cd97b0ec4f1..0f66a10856a 100644 --- a/libs/libvtrcapnproto/CMakeLists.txt +++ b/libs/libvtrcapnproto/CMakeLists.txt @@ -32,7 +32,7 @@ capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS ) if (VPR_ENABLE_INTERCHANGE) - set(IC_DIR ${CMAKE_SOURCE_DIR}/libs/EXTERNAL/libinterchange/interchange) + set(IC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../EXTERNAL/libinterchange/interchange) set(CAPNPC_SRC_PREFIX ${IC_DIR}) find_program(WGET wget REQUIRED) diff --git a/libs/libvtrutil/CMakeLists.txt b/libs/libvtrutil/CMakeLists.txt index 12217151ed2..73240389622 100644 --- a/libs/libvtrutil/CMakeLists.txt +++ b/libs/libvtrutil/CMakeLists.txt @@ -61,24 +61,28 @@ endif() # 2) The custom command depends on the touched version input file and generates the processed # version file, with updated values. The custom command uses the configure_version.cmake # script to generate the up-to-date vtr_version.cpp -add_custom_target(version ALL - COMMAND ${CMAKE_COMMAND} -E touch ${VTR_VERSION_FILE_IN}) - -add_custom_command(OUTPUT ${VTR_VERSION_FILE_OUT} - COMMAND ${CMAKE_COMMAND} - -D IN_FILE=${VTR_VERSION_FILE_IN} - -D OUT_FILE=${VTR_VERSION_FILE_OUT} - -D VTR_VERSION_MAJOR=${VTR_VERSION_MAJOR} - -D VTR_VERSION_MINOR=${VTR_VERSION_MINOR} - -D VTR_VERSION_PATCH=${VTR_VERSION_PATCH} - -D VTR_VERSION_PRERELEASE=${VTR_VERSION_PRERELEASE} - -D VTR_COMPILER_INFO=${VTR_COMPILER_INFO} - -D VTR_BUILD_INFO=${VTR_BUILD_INFO} - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/configure_version.cmake - MAIN_DEPENDENCY ${VTR_VERSION_FILE_IN} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - VERBATIM) - +if (VTR_ENABLE_VERSION) + add_custom_target(version ALL + COMMAND ${CMAKE_COMMAND} -E touch ${VTR_VERSION_FILE_IN}) + + add_custom_command(OUTPUT ${VTR_VERSION_FILE_OUT} + COMMAND ${CMAKE_COMMAND} + -D IN_FILE=${VTR_VERSION_FILE_IN} + -D OUT_FILE=${VTR_VERSION_FILE_OUT} + -D VTR_VERSION_MAJOR=${VTR_VERSION_MAJOR} + -D VTR_VERSION_MINOR=${VTR_VERSION_MINOR} + -D VTR_VERSION_PATCH=${VTR_VERSION_PATCH} + -D VTR_VERSION_PRERELEASE=${VTR_VERSION_PRERELEASE} + -D VTR_COMPILER_INFO=${VTR_COMPILER_INFO} + -D VTR_BUILD_INFO=${VTR_BUILD_INFO} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/configure_version.cmake + MAIN_DEPENDENCY ${VTR_VERSION_FILE_IN} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM) +else() +# Just copy the input file to output file with version number + configure_file(${VTR_VERSION_FILE_IN} ${VTR_VERSION_FILE_OUT}) +endif() # # Source files and library @@ -98,7 +102,9 @@ target_include_directories(libvtrutil PUBLIC ${LIB_INCLUDE_DIRS}) set_target_properties(libvtrutil PROPERTIES PREFIX "") #Avoid extra 'lib' prefix #Ensure version is always up to date by requiring version to be run first -add_dependencies(libvtrutil version) +if (VTR_ENABLE_VERSION) + add_dependencies(libvtrutil version) +endif() #Specify link-time dependancies target_link_libraries(libvtrutil diff --git a/libs/libvtrutil/src/vtr_geometry.h b/libs/libvtrutil/src/vtr_geometry.h index 3685c308653..1e11f87e1d7 100644 --- a/libs/libvtrutil/src/vtr_geometry.h +++ b/libs/libvtrutil/src/vtr_geometry.h @@ -61,6 +61,9 @@ bool operator!=(const RectUnion& lhs, const RectUnion& rhs); template class Point { public: //Constructors + // below is to create a no argument constructor for libopenfpga/libopenfpgautil/src/openfpga_pb_parser.cpp + // need to figure out a better solution to avoid change this in libs from vtr + Point(); Point(T x_val, T y_val) noexcept; public: //Accessors diff --git a/libs/libvtrutil/src/vtr_geometry.tpp b/libs/libvtrutil/src/vtr_geometry.tpp index 2010700fc50..c3e25dcf8a9 100644 --- a/libs/libvtrutil/src/vtr_geometry.tpp +++ b/libs/libvtrutil/src/vtr_geometry.tpp @@ -3,6 +3,11 @@ namespace vtr { * Point */ +template +Point::Point() { + //pass +} + template Point::Point(T x_val, T y_val) noexcept : x_(x_val) diff --git a/libs/libvtrutil/src/vtr_logic.h b/libs/libvtrutil/src/vtr_logic.h index 30d44c4a6b9..b43ee49291e 100644 --- a/libs/libvtrutil/src/vtr_logic.h +++ b/libs/libvtrutil/src/vtr_logic.h @@ -16,6 +16,8 @@ constexpr int FALSE = 0; constexpr int TRUE = 1; +#include + namespace vtr { /** @@ -25,9 +27,12 @@ enum class LogicValue { FALSE = 0, TRUE = 1, DONT_CARE = 2, - UNKOWN = 3 + UNKOWN = 3, + NUM_LOGIC_VALUE_TYPES }; +constexpr std::array LOGIC_VALUE_STRING = {{"false", "true", "don't care", "unknown"}}; + } // namespace vtr #endif diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 18306db87a7..ffecd4aa9f6 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -38,6 +38,8 @@ static void SetupAnnealSched(const t_options& Options, static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts); static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts); +static void SetupServerOpts(const t_options& Options, + t_server_opts* ServerOpts); static void SetupRoutingArch(const t_arch& Arch, t_det_routing_arch* RoutingArch); static void SetupTiming(const t_options& Options, const bool TimingEnabled, t_timing_inf* Timing); static void SetupSwitches(const t_arch& Arch, @@ -99,6 +101,7 @@ void SetupVPR(const t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraphs, std::vector& Segments, @@ -144,6 +147,7 @@ void SetupVPR(const t_options* Options, SetupAnalysisOpts(*Options, *AnalysisOpts); SetupPowerOpts(*Options, PowerOpts, Arch); SetupNocOpts(*Options, NocOpts); + SetupServerOpts(*Options, ServerOpts); if (readArchFile == true) { vtr::ScopedStartFinishTimer t("Loading Architecture Description"); @@ -395,9 +399,11 @@ static void SetupSwitches(const t_arch& Arch, static void SetupRoutingArch(const t_arch& Arch, t_det_routing_arch* RoutingArch) { RoutingArch->switch_block_type = Arch.SBType; + RoutingArch->switch_block_subtype = Arch.SBSubType; RoutingArch->R_minW_nmos = Arch.R_minW_nmos; RoutingArch->R_minW_pmos = Arch.R_minW_pmos; RoutingArch->Fs = Arch.Fs; + RoutingArch->subFs = Arch.subFs; RoutingArch->directionality = BI_DIRECTIONAL; if (Arch.Segments.size()) { RoutingArch->directionality = Arch.Segments[0].directionality; @@ -405,6 +411,11 @@ static void SetupRoutingArch(const t_arch& Arch, /* copy over the switch block information */ RoutingArch->switchblocks = Arch.switchblocks; + + /* Copy the tileable routing setting */ + RoutingArch->tileable = Arch.tileable; + RoutingArch->shrink_boundary = Arch.shrink_boundary; + RoutingArch->through_channel = Arch.through_channel; } static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) { @@ -432,6 +443,11 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->router_algorithm = Options.RouterAlgorithm; RouterOpts->fixed_channel_width = Options.RouteChanWidth; RouterOpts->min_channel_width_hint = Options.min_route_chan_width_hint; + + //TODO document these? + RouterOpts->trim_empty_channels = false; /* DEFAULT */ + RouterOpts->trim_obs_channels = false; /* DEFAULT */ + RouterOpts->read_rr_edge_metadata = Options.read_rr_edge_metadata; RouterOpts->reorder_rr_graph_nodes_algorithm = Options.reorder_rr_graph_nodes_algorithm; RouterOpts->reorder_rr_graph_nodes_threshold = Options.reorder_rr_graph_nodes_threshold; @@ -697,6 +713,8 @@ static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysi analysis_opts.timing_update_type = Options.timing_update_type; analysis_opts.write_timing_summary = Options.write_timing_summary; + + analysis_opts.skip_sync_clustering_and_routing_results = Options.skip_sync_clustering_and_routing_results; } static void SetupPowerOpts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch) { @@ -736,6 +754,11 @@ static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts) { return; } +static void SetupServerOpts(const t_options& Options, t_server_opts* ServerOpts) { + ServerOpts->is_server_mode_enabled = Options.is_server_mode_enabled; + ServerOpts->port_num = Options.server_port_num; +} + static void find_ipin_cblock_switch_index(const t_arch& Arch, int& wire_to_arch_ipin_switch, int& wire_to_arch_ipin_switch_between_dice) { for (auto cb_switch_name_index = 0; cb_switch_name_index < (int)Arch.ipin_cblock_switch_name.size(); cb_switch_name_index++) { int ipin_cblock_switch_index = UNDEFINED; diff --git a/vpr/src/base/SetupVPR.h b/vpr/src/base/SetupVPR.h index 7f7bb7105ea..97499eb8614 100644 --- a/vpr/src/base/SetupVPR.h +++ b/vpr/src/base/SetupVPR.h @@ -20,6 +20,7 @@ void SetupVPR(const t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraphs, std::vector& Segments, diff --git a/vpr/src/base/ShowSetup.cpp b/vpr/src/base/ShowSetup.cpp index 61f1bf772c3..189e90fb890 100644 --- a/vpr/src/base/ShowSetup.cpp +++ b/vpr/src/base/ShowSetup.cpp @@ -319,6 +319,8 @@ static void ShowRouterOpts(const t_router_opts& RouterOpts) { VPR_FATAL_ERROR(VPR_ERROR_UNKNOWN, "Unknown check_route value\n"); } + VTR_LOG("RouterOpts.trim_empty_chan: %s\n", (RouterOpts.trim_empty_channels ? "true" : "false")); + VTR_LOG("RouterOpts.trim_obs_chan: %s\n", (RouterOpts.trim_obs_channels ? "true" : "false")); VTR_LOG("RouterOpts.acc_fac: %f\n", RouterOpts.acc_fac); VTR_LOG("RouterOpts.bb_factor: %d\n", RouterOpts.bb_factor); VTR_LOG("RouterOpts.bend_cost: %f\n", RouterOpts.bend_cost); diff --git a/vpr/src/base/gen/vpr_constraints_uxsdcxx.h b/vpr/src/base/gen/vpr_constraints_uxsdcxx.h index 3248c338281..166a5629425 100644 --- a/vpr/src/base/gen/vpr_constraints_uxsdcxx.h +++ b/vpr/src/base/gen/vpr_constraints_uxsdcxx.h @@ -4,9 +4,9 @@ * https://github.com/duck2/uxsdcxx * Modify only if your build process doesn't involve regenerating this file. * - * Cmdline: uxsdcxx.py vpr_constraints.xsd - * Input file: /home/khalid88/Documents/uxsdcxx/vpr_constraints.xsd - * md5sum of input file: 6b6011a6e6446347b234da82e517422e + * Cmdline: uxsdcxx.py /home/tao/works/dev/clock/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * Input file: /home/tao/works/dev/clock/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * md5sum of input file: 2d6f442d8044f76e8f1b1d276b7358da */ #include @@ -25,8 +25,6 @@ #include "pugixml.hpp" #include "vpr_constraints_uxsdcxx_interface.h" -#include "region.h" - /* All uxsdcxx functions and structs live in this namespace. */ namespace uxsd { @@ -52,6 +50,10 @@ inline void load_partition(const pugi::xml_node& root, T& out, Context& context, template inline void load_partition_list(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); template +inline void load_set_global_signal(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); +template +inline void load_global_route_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); +template inline void load_vpr_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug); /* Declarations for internal write functions for the complex types. */ @@ -60,6 +62,8 @@ inline void write_partition(T& in, std::ostream& os, const void* data, void* ite template inline void write_partition_list(T& in, std::ostream& os, const void* data, void* iter); template +inline void write_global_route_constraints(T& in, std::ostream& os, const void* data, void* iter); +template inline void write_vpr_constraints(T& in, std::ostream& os, const void* data, void* iter); /* Load function for the root element. */ @@ -142,8 +146,17 @@ constexpr const char* atok_lookup_t_partition[] = {"name"}; enum class gtok_t_partition_list { PARTITION }; constexpr const char* gtok_lookup_t_partition_list[] = {"partition"}; -enum class gtok_t_vpr_constraints { PARTITION_LIST }; -constexpr const char* gtok_lookup_t_vpr_constraints[] = {"partition_list"}; + +enum class atok_t_set_global_signal { NAME, + ROUTE_MODEL, + TYPE }; +constexpr const char* atok_lookup_t_set_global_signal[] = {"name", "route_model", "type"}; + +enum class gtok_t_global_route_constraints { SET_GLOBAL_SIGNAL }; +constexpr const char* gtok_lookup_t_global_route_constraints[] = {"set_global_signal"}; +enum class gtok_t_vpr_constraints { PARTITION_LIST, + GLOBAL_ROUTE_CONSTRAINTS }; +constexpr const char* gtok_lookup_t_vpr_constraints[] = {"partition_list", "global_route_constraints"}; enum class atok_t_vpr_constraints { TOOL_NAME }; constexpr const char* atok_lookup_t_vpr_constraints[] = {"tool_name"}; @@ -348,6 +361,84 @@ inline gtok_t_partition_list lex_node_t_partition_list(const char* in, const std noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); } +inline atok_t_set_global_signal lex_attr_t_set_global_signal(const char* in, const std::function* report_error) { + unsigned int len = strlen(in); + switch (len) { + case 4: + switch (*((triehash_uu32*)&in[0])) { + case onechar('n', 0, 32) | onechar('a', 8, 32) | onechar('m', 16, 32) | onechar('e', 24, 32): + return atok_t_set_global_signal::NAME; + break; + case onechar('t', 0, 32) | onechar('y', 8, 32) | onechar('p', 16, 32) | onechar('e', 24, 32): + return atok_t_set_global_signal::TYPE; + break; + default: + break; + } + break; + case 11: + switch (*((triehash_uu64*)&in[0])) { + case onechar('r', 0, 64) | onechar('o', 8, 64) | onechar('u', 16, 64) | onechar('t', 24, 64) | onechar('e', 32, 64) | onechar('_', 40, 64) | onechar('m', 48, 64) | onechar('o', 56, 64): + switch (in[8]) { + case onechar('d', 0, 8): + switch (in[9]) { + case onechar('e', 0, 8): + switch (in[10]) { + case onechar('l', 0, 8): + return atok_t_set_global_signal::ROUTE_MODEL; + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + noreturn_report(report_error, ("Found unrecognized attribute " + std::string(in) + " of .").c_str()); +} + +inline gtok_t_global_route_constraints lex_node_t_global_route_constraints(const char* in, const std::function* report_error) { + unsigned int len = strlen(in); + switch (len) { + case 17: + switch (*((triehash_uu64*)&in[0])) { + case onechar('s', 0, 64) | onechar('e', 8, 64) | onechar('t', 16, 64) | onechar('_', 24, 64) | onechar('g', 32, 64) | onechar('l', 40, 64) | onechar('o', 48, 64) | onechar('b', 56, 64): + switch (*((triehash_uu64*)&in[8])) { + case onechar('a', 0, 64) | onechar('l', 8, 64) | onechar('_', 16, 64) | onechar('s', 24, 64) | onechar('i', 32, 64) | onechar('g', 40, 64) | onechar('n', 48, 64) | onechar('a', 56, 64): + switch (in[16]) { + case onechar('l', 0, 8): + return gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL; + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + noreturn_report(report_error, ("Found unrecognized child " + std::string(in) + " of .").c_str()); +} + inline gtok_t_vpr_constraints lex_node_t_vpr_constraints(const char* in, const std::function* report_error) { unsigned int len = strlen(in); switch (len) { @@ -378,6 +469,27 @@ inline gtok_t_vpr_constraints lex_node_t_vpr_constraints(const char* in, const s break; } break; + case 24: + switch (*((triehash_uu64*)&in[0])) { + case onechar('g', 0, 64) | onechar('l', 8, 64) | onechar('o', 16, 64) | onechar('b', 24, 64) | onechar('a', 32, 64) | onechar('l', 40, 64) | onechar('_', 48, 64) | onechar('r', 56, 64): + switch (*((triehash_uu64*)&in[8])) { + case onechar('o', 0, 64) | onechar('u', 8, 64) | onechar('t', 16, 64) | onechar('e', 24, 64) | onechar('_', 32, 64) | onechar('c', 40, 64) | onechar('o', 48, 64) | onechar('n', 56, 64): + switch (*((triehash_uu64*)&in[16])) { + case onechar('s', 0, 64) | onechar('t', 8, 64) | onechar('r', 16, 64) | onechar('a', 24, 64) | onechar('i', 32, 64) | onechar('n', 40, 64) | onechar('t', 48, 64) | onechar('s', 56, 64): + return gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS; + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } + break; default: break; } @@ -412,12 +524,6 @@ inline atok_t_vpr_constraints lex_attr_t_vpr_constraints(const char* in, const s */ [[noreturn]] inline void dfa_error(const char* wrong, const int* states, const char* const* lookup, int len, const std::function* report_error); -/** - * Internal error function for xs:all validators. - */ -template -[[noreturn]] inline void all_error(std::bitset gstate, const char* const* lookup, const std::function* report_error); - /** * Internal error function for attribute validators. */ @@ -427,8 +533,6 @@ template /* Internal loading functions, which validate and load a PugiXML DOM tree into memory. */ inline int load_int(const char* in, const std::function* report_error) { int out; - // global variable, must set to 0 before using it to avoid changed by other errors - errno = 0; out = std::strtol(in, NULL, 10); if (errno != 0) noreturn_report(report_error, ("Invalid value `" + std::string(in) + "` when loading into a int.").c_str()); @@ -674,6 +778,102 @@ inline void load_partition_list(const pugi::xml_node& root, T& out, Context& con if (state != 0) dfa_error("end of input", gstate_t_partition_list[state], gtok_lookup_t_partition_list, 1, report_error); } +template +inline void load_set_global_signal(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + for (pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()) { + atok_t_set_global_signal in = lex_attr_t_set_global_signal(attr.name(), report_error); + switch (in) { + case atok_t_set_global_signal::NAME: + out.set_set_global_signal_name(attr.value(), context); + break; + case atok_t_set_global_signal::ROUTE_MODEL: + out.set_set_global_signal_route_model(attr.value(), context); + break; + case atok_t_set_global_signal::TYPE: + out.set_set_global_signal_type(attr.value(), context); + break; + default: + break; /* Not possible. */ + } + } + + if (root.first_child().type() == pugi::node_element) + noreturn_report(report_error, "Unexpected child element in ."); +} + +constexpr int NUM_T_GLOBAL_ROUTE_CONSTRAINTS_STATES = 2; +constexpr const int NUM_T_GLOBAL_ROUTE_CONSTRAINTS_INPUTS = 1; +constexpr int gstate_t_global_route_constraints[NUM_T_GLOBAL_ROUTE_CONSTRAINTS_STATES][NUM_T_GLOBAL_ROUTE_CONSTRAINTS_INPUTS] = { + {0}, + {0}, +}; +template +inline void load_global_route_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { + (void)root; + (void)out; + (void)context; + (void)report_error; + // Update current file offset in case an error is encountered. + *offset_debug = root.offset_debug(); + + if (root.first_attribute()) + noreturn_report(report_error, "Unexpected attribute in ."); + + // Preallocate arrays by counting child nodes (if any) + size_t set_global_signal_count = 0; + { + int next, state = 1; + for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_global_route_constraints in = lex_node_t_global_route_constraints(node.name(), report_error); + next = gstate_t_global_route_constraints[state][(int)in]; + if (next == -1) + dfa_error(gtok_lookup_t_global_route_constraints[(int)in], gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); + state = next; + switch (in) { + case gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL: + set_global_signal_count += 1; + break; + default: + break; /* Not possible. */ + } + } + + out.preallocate_global_route_constraints_set_global_signal(context, set_global_signal_count); + } + int next, state = 1; + for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_global_route_constraints in = lex_node_t_global_route_constraints(node.name(), report_error); + next = gstate_t_global_route_constraints[state][(int)in]; + if (next == -1) + dfa_error(gtok_lookup_t_global_route_constraints[(int)in], gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); + state = next; + switch (in) { + case gtok_t_global_route_constraints::SET_GLOBAL_SIGNAL: { + auto child_context = out.add_global_route_constraints_set_global_signal(context); + load_set_global_signal(node, out, child_context, report_error, offset_debug); + out.finish_global_route_constraints_set_global_signal(child_context); + } break; + default: + break; /* Not possible. */ + } + } + if (state != 0) dfa_error("end of input", gstate_t_global_route_constraints[state], gtok_lookup_t_global_route_constraints, 1, report_error); +} + +constexpr int NUM_T_VPR_CONSTRAINTS_STATES = 1; +constexpr const int NUM_T_VPR_CONSTRAINTS_INPUTS = 2; +constexpr int gstate_t_vpr_constraints[NUM_T_VPR_CONSTRAINTS_STATES][NUM_T_VPR_CONSTRAINTS_INPUTS] = { + {0, 0}, +}; template inline void load_vpr_constraints(const pugi::xml_node& root, T& out, Context& context, const std::function* report_error, ptrdiff_t* offset_debug) { (void)root; @@ -694,26 +894,57 @@ inline void load_vpr_constraints(const pugi::xml_node& root, T& out, Context& co } } - std::bitset<1> gstate = 0; + // Preallocate arrays by counting child nodes (if any) + size_t partition_list_count = 0; + size_t global_route_constraints_count = 0; + { + int next, state = 0; + for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { + *offset_debug = node.offset_debug(); + gtok_t_vpr_constraints in = lex_node_t_vpr_constraints(node.name(), report_error); + next = gstate_t_vpr_constraints[state][(int)in]; + if (next == -1) + dfa_error(gtok_lookup_t_vpr_constraints[(int)in], gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 2, report_error); + state = next; + switch (in) { + case gtok_t_vpr_constraints::PARTITION_LIST: + partition_list_count += 1; + break; + case gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS: + global_route_constraints_count += 1; + break; + default: + break; /* Not possible. */ + } + } + + out.preallocate_vpr_constraints_partition_list(context, partition_list_count); + out.preallocate_vpr_constraints_global_route_constraints(context, global_route_constraints_count); + } + int next, state = 0; for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) { *offset_debug = node.offset_debug(); gtok_t_vpr_constraints in = lex_node_t_vpr_constraints(node.name(), report_error); - if (gstate[(int)in] == 0) - gstate[(int)in] = 1; - else - noreturn_report(report_error, ("Duplicate element " + std::string(node.name()) + " in .").c_str()); + next = gstate_t_vpr_constraints[state][(int)in]; + if (next == -1) + dfa_error(gtok_lookup_t_vpr_constraints[(int)in], gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 2, report_error); + state = next; switch (in) { case gtok_t_vpr_constraints::PARTITION_LIST: { - auto child_context = out.init_vpr_constraints_partition_list(context); + auto child_context = out.add_vpr_constraints_partition_list(context); load_partition_list(node, out, child_context, report_error, offset_debug); out.finish_vpr_constraints_partition_list(child_context); } break; + case gtok_t_vpr_constraints::GLOBAL_ROUTE_CONSTRAINTS: { + auto child_context = out.add_vpr_constraints_global_route_constraints(context); + load_global_route_constraints(node, out, child_context, report_error, offset_debug); + out.finish_vpr_constraints_global_route_constraints(child_context); + } break; default: break; /* Not possible. */ } } - std::bitset<1> test_gstate = gstate | std::bitset<1>(0b0); - if (!test_gstate.all()) all_error(test_gstate, gtok_lookup_t_vpr_constraints, report_error); + if (state != 0) dfa_error("end of input", gstate_t_vpr_constraints[state], gtok_lookup_t_vpr_constraints, 2, report_error); } /* Internal writing functions, which uxsdcxx uses to write out a class. */ @@ -734,7 +965,7 @@ inline void write_partition(T& in, std::ostream& os, Context& context) { for (size_t i = 0, n = in.num_partition_add_region(context); i < n; i++) { auto child_context = in.get_partition_add_region(i, context); os << " +inline void write_global_route_constraints(T& in, std::ostream& os, Context& context) { + (void)in; + (void)os; + (void)context; + { + for (size_t i = 0, n = in.num_global_route_constraints_set_global_signal(context); i < n; i++) { + auto child_context = in.get_global_route_constraints_set_global_signal(i, context); + os << "\n"; + } + } +} + template inline void write_vpr_constraints(T& in, std::ostream& os, Context& context) { (void)in; (void)os; (void)context; { - auto child_context = in.get_vpr_constraints_partition_list(context); - os << "\n"; - write_partition_list(in, os, child_context); - os << "\n"; + for (size_t i = 0, n = in.num_vpr_constraints_partition_list(context); i < n; i++) { + auto child_context = in.get_vpr_constraints_partition_list(i, context); + os << "\n"; + write_partition_list(in, os, child_context); + os << "\n"; + } + } + { + for (size_t i = 0, n = in.num_vpr_constraints_global_route_constraints(context); i < n; i++) { + auto child_context = in.get_vpr_constraints_global_route_constraints(i, context); + os << "\n"; + write_global_route_constraints(in, os, child_context); + os << "\n"; + } } } @@ -788,20 +1046,6 @@ inline void dfa_error(const char* wrong, const int* states, const char* const* l noreturn_report(report_error, ("Expected " + expected_or + ", found " + std::string(wrong)).c_str()); } -template -inline void all_error(std::bitset gstate, const char* const* lookup, const std::function* report_error) { - std::vector missing; - for (unsigned int i = 0; i < N; i++) { - if (gstate[i] == 0) missing.push_back(lookup[i]); - } - - std::string missing_and = missing[0]; - for (unsigned int i = 1; i < missing.size(); i++) - missing_and += std::string(", ") + missing[i]; - - noreturn_report(report_error, ("Didn't find required elements " + missing_and + ".").c_str()); -} - template inline void attr_error(std::bitset astate, const char* const* lookup, const std::function* report_error) { std::vector missing; @@ -820,7 +1064,7 @@ inline void get_line_number(const char* filename, std::ptrdiff_t target_offset, std::unique_ptr f(fopen(filename, "rb"), fclose); if (!f) { - throw std::runtime_error(std::string("Failed to open file") + filename); + throw std::runtime_error(std::string("Failed to open file ") + filename); } int current_line = 1; diff --git a/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h b/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h index 8939778861e..a3ec52085fe 100644 --- a/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h +++ b/vpr/src/base/gen/vpr_constraints_uxsdcxx_interface.h @@ -4,9 +4,9 @@ * https://github.com/duck2/uxsdcxx * Modify only if your build process doesn't involve regenerating this file. * - * Cmdline: uxsdcxx.py vpr_constraints.xsd - * Input file: /home/khalid88/Documents/uxsdcxx/vpr_constraints.xsd - * md5sum of input file: 6b6011a6e6446347b234da82e517422e + * Cmdline: uxsdcxx.py /home/tao/works/dev/clock/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * Input file: /home/tao/works/dev/clock/vtr-verilog-to-routing/vpr/src/base/vpr_constraints.xsd + * md5sum of input file: 2d6f442d8044f76e8f1b1d276b7358da */ #include @@ -24,11 +24,15 @@ struct DefaultVprConstraintsContextTypes { using AddRegionReadContext = void*; using PartitionReadContext = void*; using PartitionListReadContext = void*; + using SetGlobalSignalReadContext = void*; + using GlobalRouteConstraintsReadContext = void*; using VprConstraintsReadContext = void*; using AddAtomWriteContext = void*; using AddRegionWriteContext = void*; using PartitionWriteContext = void*; using PartitionListWriteContext = void*; + using SetGlobalSignalWriteContext = void*; + using GlobalRouteConstraintsWriteContext = void*; using VprConstraintsWriteContext = void*; }; @@ -93,7 +97,7 @@ class VprConstraintsBase { /** Generated for complex type "partition_list": * * - * + * * * */ @@ -103,19 +107,54 @@ class VprConstraintsBase { virtual inline size_t num_partition_list_partition(typename ContextTypes::PartitionListReadContext& ctx) = 0; virtual inline typename ContextTypes::PartitionReadContext get_partition_list_partition(int n, typename ContextTypes::PartitionListReadContext& ctx) = 0; + /** Generated for complex type "set_global_signal": + * + * + * + * + * + */ + virtual inline const char* get_set_global_signal_name(typename ContextTypes::SetGlobalSignalReadContext& ctx) = 0; + virtual inline void set_set_global_signal_name(const char* name, typename ContextTypes::SetGlobalSignalWriteContext& ctx) = 0; + virtual inline const char* get_set_global_signal_route_model(typename ContextTypes::SetGlobalSignalReadContext& ctx) = 0; + virtual inline void set_set_global_signal_route_model(const char* route_model, typename ContextTypes::SetGlobalSignalWriteContext& ctx) = 0; + virtual inline const char* get_set_global_signal_type(typename ContextTypes::SetGlobalSignalReadContext& ctx) = 0; + virtual inline void set_set_global_signal_type(const char* type, typename ContextTypes::SetGlobalSignalWriteContext& ctx) = 0; + + /** Generated for complex type "global_route_constraints": + * + * + * + * + * + */ + virtual inline void preallocate_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsWriteContext& ctx, size_t size) = 0; + virtual inline typename ContextTypes::SetGlobalSignalWriteContext add_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsWriteContext& ctx) = 0; + virtual inline void finish_global_route_constraints_set_global_signal(typename ContextTypes::SetGlobalSignalWriteContext& ctx) = 0; + virtual inline size_t num_global_route_constraints_set_global_signal(typename ContextTypes::GlobalRouteConstraintsReadContext& ctx) = 0; + virtual inline typename ContextTypes::SetGlobalSignalReadContext get_global_route_constraints_set_global_signal(int n, typename ContextTypes::GlobalRouteConstraintsReadContext& ctx) = 0; + /** Generated for complex type "vpr_constraints": * - * + * * - * + * + * * * */ virtual inline const char* get_vpr_constraints_tool_name(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; virtual inline void set_vpr_constraints_tool_name(const char* tool_name, typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; - virtual inline typename ContextTypes::PartitionListWriteContext init_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; + virtual inline void preallocate_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext& ctx, size_t size) = 0; + virtual inline typename ContextTypes::PartitionListWriteContext add_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; virtual inline void finish_vpr_constraints_partition_list(typename ContextTypes::PartitionListWriteContext& ctx) = 0; - virtual inline typename ContextTypes::PartitionListReadContext get_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; + virtual inline size_t num_vpr_constraints_partition_list(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; + virtual inline typename ContextTypes::PartitionListReadContext get_vpr_constraints_partition_list(int n, typename ContextTypes::VprConstraintsReadContext& ctx) = 0; + virtual inline void preallocate_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsWriteContext& ctx, size_t size) = 0; + virtual inline typename ContextTypes::GlobalRouteConstraintsWriteContext add_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsWriteContext& ctx) = 0; + virtual inline void finish_vpr_constraints_global_route_constraints(typename ContextTypes::GlobalRouteConstraintsWriteContext& ctx) = 0; + virtual inline size_t num_vpr_constraints_global_route_constraints(typename ContextTypes::VprConstraintsReadContext& ctx) = 0; + virtual inline typename ContextTypes::GlobalRouteConstraintsReadContext get_vpr_constraints_global_route_constraints(int n, typename ContextTypes::VprConstraintsReadContext& ctx) = 0; }; } /* namespace uxsd */ diff --git a/vpr/src/base/place_and_route.cpp b/vpr/src/base/place_and_route.cpp index c34cd9cfbae..b4cd431ce7d 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -96,6 +96,10 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } diff --git a/vpr/src/base/read_blif.cpp b/vpr/src/base/read_blif.cpp index 2425a18d239..326216896f4 100644 --- a/vpr/src/base/read_blif.cpp +++ b/vpr/src/base/read_blif.cpp @@ -719,18 +719,15 @@ bool is_binary_param(const std::string& param) { } bool is_real_param(const std::string& param) { - const std::string chars = "012345678."; - /* Must be non-empty */ if (param.empty()) { return false; } - /* The string mustn't contain any other chars that the expected ones */ - for (size_t i = 0; i < param.length(); ++i) { - if (chars.find(param[i]) == std::string::npos) { - return false; - } + /* The string must match the regular expression */ + static const std::regex real_number_expr("[+-]?([0-9]*\\.[0-9]+)|([0-9]+\\.[0-9]*)"); + if (!std::regex_match(param, real_number_expr)) { + return false; } /* This is a real number param */ diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 35fd191c488..36308fe3720 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -1280,6 +1280,16 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .action(argparse::Action::STORE_TRUE) .default_value("off"); + stage_grp.add_argument(args.is_server_mode_enabled, "--server") + .help("Run in server mode") + .action(argparse::Action::STORE_TRUE) + .default_value("off"); + + stage_grp.add_argument(args.server_port_num, "--port") + .help("Server port number") + .default_value("60555") + .show_in(argparse::ShowIn::HELP_ONLY); + stage_grp.epilog( "If none of the stage options are specified, all stages are run.\n" "Analysis is always run after routing, unless the implementation\n" @@ -2677,6 +2687,13 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .help("Writes implemented design final timing summary to the specified JSON, XML or TXT file.") .show_in(argparse::ShowIn::HELP_ONLY); + analysis_grp.add_argument(args.skip_sync_clustering_and_routing_results, "--skip_sync_clustering_and_routing_results") + .help( + "Select to skip the synchronization on clustering results based on routing optimization results." + "Note that when this sync-up is disabled, clustering results may be wrong (leading to incorrect bitstreams)!") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + auto& power_grp = parser.add_argument_group("power analysis options"); power_grp.add_argument(args.do_power, "--power") diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index 90f82ac07fb..fc4992c9261 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -74,6 +74,10 @@ struct t_options { argparse::ArgValue allow_dangling_combinational_nodes; argparse::ArgValue terminate_if_timing_fails; + /* Server options */ + argparse::ArgValue is_server_mode_enabled; + argparse::ArgValue server_port_num; + /* Atom netlist options */ argparse::ArgValue absorb_buffer_luts; argparse::ArgValue const_gen_inference; @@ -233,6 +237,7 @@ struct t_options { argparse::ArgValue post_synth_netlist_unconn_input_handling; argparse::ArgValue post_synth_netlist_unconn_output_handling; argparse::ArgValue write_timing_summary; + argparse::ArgValue skip_sync_clustering_and_routing_results; }; argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& args); diff --git a/vpr/src/base/route_constraint.cpp b/vpr/src/base/route_constraint.cpp new file mode 100644 index 00000000000..4ed76deb108 --- /dev/null +++ b/vpr/src/base/route_constraint.cpp @@ -0,0 +1,43 @@ +#include "route_constraint.h" + +RouteConstraint::RouteConstraint() { + net_name_ = std::string(""); + net_type_ = std::string(""); + route_method_ = std::string(""); + is_valid_ = false; +} + +void RouteConstraint::set_net_name(std::string name) { + net_name_ = name; + return; +} + +std::string RouteConstraint::net_name() const { + return net_name_; +} + +void RouteConstraint::set_net_type(std::string type) { + net_type_ = type; + return; +} + +std::string RouteConstraint::net_type() const { + return net_type_; +} + +void RouteConstraint::set_route_model(std::string route_method) { + route_method_ = route_method; + return; +} + +std::string RouteConstraint::route_model() const { + return route_method_; +} + +void RouteConstraint::set_is_valid(bool value) { + is_valid_ = value; +} + +bool RouteConstraint::is_valid() const { + return is_valid_; +} diff --git a/vpr/src/base/route_constraint.h b/vpr/src/base/route_constraint.h new file mode 100644 index 00000000000..58b2839d060 --- /dev/null +++ b/vpr/src/base/route_constraint.h @@ -0,0 +1,65 @@ +#ifndef ROUTE_CONSTRAINT_H +#define ROUTE_CONSTRAINT_H + +#include "vpr_types.h" + +/** + * @file + * @brief This file defines the RouteConstraint class. + */ + +class RouteConstraint { + public: + /** + * @brief Constructor for the RouteConstraint class, sets member variables to invalid values + */ + RouteConstraint(); + + /** + * @brief get net name + */ + std::string net_name() const; + + /** + * @brief set net name + */ + void set_net_name(std::string); + + /** + * @brief get net type + */ + std::string net_type() const; + + /** + * @brief set net type + */ + void set_net_type(std::string); + + /** + * @brief get route model + */ + std::string route_model() const; + + /** + * @brief set route model + */ + void set_route_model(std::string); + + /** + * @brief set is valid + */ + void set_is_valid(bool); + + /** + * @brief get is valid + */ + bool is_valid() const; + + private: + std::string net_name_; + std::string net_type_; + std::string route_method_; + bool is_valid_; +}; + +#endif /* ROUTE_CONSTRAINT_H */ diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index c87d2bec7fc..c9f3ef62966 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -290,6 +290,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a &vpr_setup->RouterOpts, &vpr_setup->AnalysisOpts, &vpr_setup->NocOpts, + &vpr_setup->ServerOpts, &vpr_setup->RoutingArch, &vpr_setup->PackerRRGraph, vpr_setup->Segments, @@ -346,10 +347,15 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a } } - //Initialize vpr floorplanning constraints + //Initialize vpr floorplanning and routing constraints auto& filename_opts = vpr_setup->FileNameOpts; if (!filename_opts.read_vpr_constraints_file.empty()) { - load_vpr_constraints_file(filename_opts.read_vpr_constraints_file.c_str()); + load_vpr_constraints_files(filename_opts.read_vpr_constraints_file.c_str()); + + // give a notificaiton on routing constraints overiding clock modeling + if (g_vpr_ctx.routing().constraints.get_route_constraint_num() && options->clock_modeling.provenance() == argparse::Provenance::SPECIFIED) { + VTR_LOG_WARN("Route constraint(s) detected and will override clock modeling setting.\n"); + } } fflush(stdout); @@ -399,6 +405,9 @@ bool vpr_flow(t_vpr_setup& vpr_setup, t_arch& arch) { vpr_analysis_flow(router_net_list, vpr_setup, arch, route_status, is_flat); } + // write out constratins + write_vpr_constraints(vpr_setup); + //close the graphics vpr_close_graphics(vpr_setup); @@ -785,6 +794,11 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, //Assume successful route_status = RouteStatus(true, -1); } else { //Do or load + + // apply route constraints + // use mutable routing context here to apply change on the constraints when binding the constraint to real net + apply_route_constraints(g_vpr_ctx.mutable_routing().constraints); + int chan_width = router_opts.fixed_channel_width; NetPinsMatrix net_delay; @@ -1007,6 +1021,9 @@ void vpr_create_rr_graph(t_vpr_setup& vpr_setup, const t_arch& arch, int chan_wi } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + if ((UNI_DIRECTIONAL == det_routing_arch->directionality) && (true == det_routing_arch->tileable)) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } } t_chan_width chan_width = init_chan(chan_width_fac, arch.Chans, graph_directionality); @@ -1035,7 +1052,7 @@ void vpr_init_graphics(const t_vpr_setup& vpr_setup, const t_arch& arch, bool is /* Startup X graphics */ init_graphics_state(vpr_setup.ShowGraphics, vpr_setup.GraphPause, vpr_setup.RouterOpts.route_type, vpr_setup.SaveGraphics, - vpr_setup.GraphicsCommands, is_flat); + vpr_setup.GraphicsCommands, is_flat, vpr_setup.ServerOpts.is_server_mode_enabled, vpr_setup.ServerOpts.port_num); if (vpr_setup.ShowGraphics || vpr_setup.SaveGraphics || !vpr_setup.GraphicsCommands.empty()) alloc_draw_structs(&arch); } @@ -1250,6 +1267,7 @@ void vpr_setup_vpr(t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraph, std::vector& Segments, @@ -1274,6 +1292,7 @@ void vpr_setup_vpr(t_options* Options, RouterOpts, AnalysisOpts, NocOpts, + ServerOpts, RoutingArch, PackerRRGraph, Segments, @@ -1329,7 +1348,8 @@ bool vpr_analysis_flow(const Netlist<>& net_list, VTR_LOG("*****************************************************************************************\n"); } - /* If routing is successful, apply post-routing annotations + /* If routing is successful and users do not force to skip the sync-up, + * - apply post-routing annotations * - apply logic block pin fix-up * * Note: @@ -1338,20 +1358,24 @@ bool vpr_analysis_flow(const Netlist<>& net_list, */ if (!is_flat) { if (route_status.success()) { - sync_netlists_to_routing(net_list, - g_vpr_ctx.device(), - g_vpr_ctx.mutable_atom(), - g_vpr_ctx.atom().lookup, - g_vpr_ctx.mutable_clustering(), - g_vpr_ctx.placement(), - g_vpr_ctx.routing(), - vpr_setup.PackerOpts.pack_verbosity > 2, - is_flat); - - std::string post_routing_packing_output_file_name = vpr_setup.PackerOpts.output_file + ".post_routing"; - write_packing_results_to_xml(vpr_setup.PackerOpts.global_clocks, - Arch.architecture_id, - post_routing_packing_output_file_name.c_str()); + if (!analysis_opts.skip_sync_clustering_and_routing_results) { + sync_netlists_to_routing(net_list, + g_vpr_ctx.device(), + g_vpr_ctx.mutable_atom(), + g_vpr_ctx.atom().lookup, + g_vpr_ctx.mutable_clustering(), + g_vpr_ctx.placement(), + g_vpr_ctx.routing(), + vpr_setup.PackerOpts.pack_verbosity > 2, + is_flat); + + std::string post_routing_packing_output_file_name = vpr_setup.PackerOpts.output_file + ".post_routing"; + write_packing_results_to_xml(vpr_setup.PackerOpts.global_clocks, + Arch.architecture_id, + post_routing_packing_output_file_name.c_str()); + } else { + VTR_LOG_WARN("Sychronization between packing and routing results is not applied due to users select to skip it\n"); + } } else { VTR_LOG_WARN("Sychronization between packing and routing results is not applied due to illegal circuit implementation\n"); } diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index 15509be1115..fbdd0f052f8 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -177,6 +177,7 @@ void vpr_setup_vpr(t_options* Options, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, t_noc_opts* NocOpts, + t_server_opts* ServerOpts, t_det_routing_arch* RoutingArch, std::vector** PackerRRGraph, std::vector& Segments, diff --git a/vpr/src/base/vpr_constraints.cpp b/vpr/src/base/vpr_constraints.cpp index 95c7e7b7358..64dcfd310c8 100644 --- a/vpr/src/base/vpr_constraints.cpp +++ b/vpr/src/base/vpr_constraints.cpp @@ -1,5 +1,7 @@ #include "vpr_constraints.h" #include "partition.h" +#include "route_constraint.h" +#include void VprConstraints::add_constrained_atom(const AtomBlockId blk_id, const PartitionId part_id) { auto got = constrained_atoms.find(blk_id); @@ -58,6 +60,68 @@ PartitionRegion VprConstraints::get_partition_pr(PartitionId part_id) { return pr; } +void VprConstraints::add_route_constraint(RouteConstraint rc) { + route_constraints_.insert({rc.net_name(), rc}); + return; +} + +const RouteConstraint VprConstraints::get_route_constraint_by_net_name(std::string net_name) { + RouteConstraint rc; + auto const& rc_itr = route_constraints_.find(net_name); + if (rc_itr == route_constraints_.end()) { + // try regexp + bool found_thru_regex = false; + for (auto constraint : route_constraints_) { + if (std::regex_match(net_name, std::regex(constraint.first))) { + rc = constraint.second; + + // mark as invalid so write constraint function will not write constraint + // of regexpr name + // instead a matched constraint is inserted in + constraint.second.set_is_valid(false); + rc.set_net_name(net_name); + rc.set_is_valid(true); + route_constraints_.insert({net_name, rc}); + + found_thru_regex = true; + break; + } + } + if (!found_thru_regex) { + rc.set_net_name("INVALID"); + rc.set_net_type("INVALID"); + rc.set_route_model("INVALID"); + rc.set_is_valid(false); + } + } else { + rc = rc_itr->second; + } + return rc; +} + +const RouteConstraint VprConstraints::get_route_constraint_by_idx(std::size_t idx) const { + RouteConstraint rc; + if ((route_constraints_.size() == 0) || (idx > route_constraints_.size() - 1)) { + rc.set_net_name("INVALID"); + rc.set_net_type("INVALID"); + rc.set_route_model("INVALID"); + rc.set_is_valid(false); + } else { + std::size_t i = 0; + for (auto const& rc_itr : route_constraints_) { + if (i == idx) { + rc = rc_itr.second; + break; + } + } + } + return rc; +} + +int VprConstraints::get_route_constraint_num(void) const { + return route_constraints_.size(); +} + void print_constraints(FILE* fp, VprConstraints constraints) { Partition temp_part; std::vector atoms; diff --git a/vpr/src/base/vpr_constraints.h b/vpr/src/base/vpr_constraints.h index fd3f64842a4..f9343267a9e 100644 --- a/vpr/src/base/vpr_constraints.h +++ b/vpr/src/base/vpr_constraints.h @@ -5,6 +5,7 @@ #include "vpr_utils.h" #include "partition.h" #include "partition_region.h" +#include "route_constraint.h" /** * @file @@ -87,6 +88,34 @@ class VprConstraints { */ PartitionRegion get_partition_pr(PartitionId part_id); + /** + * @brief add route constraint + * + * @param net_name the route constraint + */ + void add_route_constraint(RouteConstraint rc); + + /** + * @brief returns route constraint by index + * + * @param index the constraint index + */ + const RouteConstraint get_route_constraint_by_idx(std::size_t index) const; + + /** + * @brief returns route constraint of a specific net + * + * @param net_name the net name + */ + const RouteConstraint get_route_constraint_by_net_name(std::string net_name); + + /** + * @brief returns number of route constraints + * + * @param void + */ + int get_route_constraint_num(void) const; + private: /** * Store all constrained atoms @@ -97,6 +126,11 @@ class VprConstraints { * Store all partitions */ vtr::vector partitions; + + /** + * store all route constraints + */ + std::unordered_map route_constraints_; }; ///@brief used to print floorplanning constraints data from a VprConstraints object diff --git a/vpr/src/base/vpr_constraints.xsd b/vpr/src/base/vpr_constraints.xsd index 406e2abcda4..2ed93b77347 100644 --- a/vpr/src/base/vpr_constraints.xsd +++ b/vpr/src/base/vpr_constraints.xsd @@ -59,16 +59,36 @@ + + + + + + + + + + + + + + + - + - - + + diff --git a/vpr/src/base/vpr_constraints_reader.cpp b/vpr/src/base/vpr_constraints_reader.cpp index 8e69b7b42b4..1140e5269c6 100644 --- a/vpr/src/base/vpr_constraints_reader.cpp +++ b/vpr/src/base/vpr_constraints_reader.cpp @@ -10,31 +10,60 @@ #include #include "vpr_constraints_reader.h" +#include "vtr_token.h" -void load_vpr_constraints_file(const char* read_vpr_constraints_name) { - vtr::ScopedStartFinishTimer timer("Loading VPR constraints file"); +void load_vpr_constraints_files(const char* read_vpr_constraints_name) { + vtr::ScopedStartFinishTimer timer("Loading VPR constraints file(s)"); VprConstraintsSerializer reader; - if (vtr::check_file_name_extension(read_vpr_constraints_name, ".xml")) { - try { - std::ifstream file(read_vpr_constraints_name); - void* context; - uxsd::load_vpr_constraints_xml(reader, context, read_vpr_constraints_name, file); - } catch (pugiutil::XmlError& e) { - vpr_throw(VPR_ERROR_ROUTE, read_vpr_constraints_name, e.line(), "%s", e.what()); + // file name from arguments could be a serial of files, seperated by colon ":" + // at this point, caller has already checked that the required constraint file name is not emtpy + int num_tokens = 0, num_file_read = 0; + bool found_file = false; + t_token* tokens = GetTokensFromString(read_vpr_constraints_name, &num_tokens); + std::string in_tokens(""); + for (int i = 0; i < num_tokens; i++) { + if ((tokens[i].type == TOKEN_COLON)) { // end of one input file + found_file = true; + } else if (i == num_tokens - 1) { // end of inputs, append token anyway + in_tokens += std::string(tokens[i].data); + found_file = true; + } else { + in_tokens += std::string(tokens[i].data); + } + if (found_file) { + const char* file_name = in_tokens.c_str(); + if (vtr::check_file_name_extension(file_name, ".xml")) { + try { + std::ifstream file(file_name); + void* context; + uxsd::load_vpr_constraints_xml(reader, context, file_name, file); + } catch (pugiutil::XmlError& e) { + vpr_throw(VPR_ERROR_ROUTE, file_name, e.line(), "%s", e.what()); + } + } else { + VTR_LOG_WARN( + "VPR constraints file '%s' may be in incorrect format. " + "Expecting .xml format. Not reading file.\n", + file_name); + } + in_tokens.clear(); + num_file_read++; + found_file = false; } - } else { - VTR_LOG_WARN( - "VPR constraints file '%s' may be in incorrect format. " - "Expecting .xml format. Not reading file.\n", - read_vpr_constraints_name); } + VTR_LOG("Read in '%d' constraint file(s) successfully.\n", num_file_read); + freeTokens(tokens, num_tokens); //Update the floorplanning constraints in the floorplanning constraints context auto& floorplanning_ctx = g_vpr_ctx.mutable_floorplanning(); floorplanning_ctx.constraints = reader.constraints_; + // update vpr constraints for routing + auto& routing_ctx = g_vpr_ctx.mutable_routing(); + routing_ctx.constraints = reader.constraints_; + VprConstraints ctx_constraints = floorplanning_ctx.constraints; if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_VPR_CONSTRAINTS)) { diff --git a/vpr/src/base/vpr_constraints_reader.h b/vpr/src/base/vpr_constraints_reader.h index 95fcc988607..60aefc25b5b 100644 --- a/vpr/src/base/vpr_constraints_reader.h +++ b/vpr/src/base/vpr_constraints_reader.h @@ -5,6 +5,6 @@ #ifndef VPR_CONSTRAINTS_READER_H_ #define VPR_CONSTRAINTS_READER_H_ -void load_vpr_constraints_file(const char* read_vpr_constraints_name); +void load_vpr_constraints_files(const char* read_vpr_constraints_name); #endif /* VPR_CONSTRAINTS_READER_H_ */ diff --git a/vpr/src/base/vpr_constraints_serializer.h b/vpr/src/base/vpr_constraints_serializer.h index 5405eb0e21a..13ceca72896 100644 --- a/vpr/src/base/vpr_constraints_serializer.h +++ b/vpr/src/base/vpr_constraints_serializer.h @@ -2,6 +2,7 @@ #define VPR_CONSTRAINTS_SERIALIZER_H_ #include "region.h" +#include "route_constraint.h" #include "vpr_constraints.h" #include "partition.h" #include "partition_region.h" @@ -69,11 +70,15 @@ struct VprConstraintsContextTypes : public uxsd::DefaultVprConstraintsContextTyp using AddRegionReadContext = Region; using PartitionReadContext = partition_info; using PartitionListReadContext = void*; + using SetGlobalSignalReadContext = RouteConstraint; + using GlobalRouteConstraintsReadContext = void*; using VprConstraintsReadContext = void*; using AddAtomWriteContext = void*; using AddRegionWriteContext = void*; using PartitionWriteContext = void*; using PartitionListWriteContext = void*; + using SetGlobalSignalWriteContext = void*; + using GlobalRouteConstraintsWriteContext = void*; using VprConstraintsWriteContext = void*; }; @@ -305,11 +310,65 @@ class VprConstraintsSerializer final : public uxsd::VprConstraintsBase + * + * + * + * + */ + virtual inline const char* get_set_global_signal_name(RouteConstraint& rc) final { + temp_name_string_ = rc.net_name(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_name(const char* name, void*& /*ctx*/) final { + std::string net_name = std::string(name); + loaded_route_constraint.set_net_name(net_name); + return; + } + virtual inline const char* get_set_global_signal_route_model(RouteConstraint& rc) final { + temp_name_string_ = rc.route_model(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_route_model(const char* route_model, void*& /*ctx*/) final { + loaded_route_constraint.set_route_model(std::string(route_model)); + loaded_route_constraint.set_is_valid(true); + } + virtual inline const char* get_set_global_signal_type(RouteConstraint& rc) final { + temp_name_string_ = rc.net_type(); + return temp_name_string_.c_str(); + } + virtual inline void set_set_global_signal_type(const char* type, void*& /*ctx*/) final { + loaded_route_constraint.set_net_type(std::string(type)); + } + + /** Generated for complex type "global_route_constraints": + * + * + * + * + * + */ + virtual inline void preallocate_global_route_constraints_set_global_signal(void*& /*ctx*/, size_t /*size*/) final {} + virtual inline void* add_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + return nullptr; + } + virtual inline void finish_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + constraints_.add_route_constraint(loaded_route_constraint); + } + virtual inline size_t num_global_route_constraints_set_global_signal(void*& /*ctx*/) final { + return constraints_.get_route_constraint_num(); + } + virtual inline RouteConstraint get_global_route_constraints_set_global_signal(int n, void*& /*ctx*/) final { + return constraints_.get_route_constraint_by_idx((std::size_t)n); + } + /** Generated for complex type "vpr_constraints": * - * + * * - * + * + * * * */ @@ -317,30 +376,64 @@ class VprConstraintsSerializer final : public uxsd::VprConstraintsBase::max_digits10); + void* context; + uxsd::write_vpr_constraints_xml(writer, context, fp); + } else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Unknown extension on output %s", + file_name.c_str()); + } + } + return; +} + void write_vpr_floorplan_constraints(const char* file_name, int expand, bool subtile, int horizontal_partitions, int vertical_partitions) { VprConstraints constraints; diff --git a/vpr/src/base/vpr_constraints_writer.h b/vpr/src/base/vpr_constraints_writer.h index 955542be637..131c842328a 100644 --- a/vpr/src/base/vpr_constraints_writer.h +++ b/vpr/src/base/vpr_constraints_writer.h @@ -25,6 +25,14 @@ #ifndef VPR_SRC_BASE_VPR_CONSTRAINTS_WRITER_H_ #define VPR_SRC_BASE_VPR_CONSTRAINTS_WRITER_H_ +/** + * @brief Write out vpr constratins to an XML file based on current placement and + * route constraint settings + * + * @param vpr_setup VPR setup information + */ +void write_vpr_constraints(t_vpr_setup& vpr_setup); + /** * @brief Write out floorplan constraints to an XML file based on current placement * diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 9e1c9241346..fd54a2219da 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -32,6 +32,9 @@ #include "noc_storage.h" #include "noc_traffic_flows.h" #include "noc_routing.h" +#include "gateio.h" +#include "taskresolver.h" +#include "tatum/report/TimingPath.hpp" /** * @brief A Context is collection of state relating to a particular part of VPR @@ -208,7 +211,13 @@ struct DeviceContext : public Context { /* A read-only view of routing resource graph to be the ONLY database * for client functions: GUI, placer, router, timing analyzer etc. */ - RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch()}; + RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch(), rr_graph_builder.node_in_edge_storage(), rr_graph_builder.node_ptc_storage()}; + + /* Track ids for each rr_node in the rr_graph. + * This is used by drawer for tileable routing resource graph + */ + std::map> rr_node_track_ids; + std::vector arch_switch_inf; // [0..(num_arch_switches-1)] std::map all_sw_inf; @@ -472,6 +481,11 @@ struct RoutingContext : public Context { vtr::Cache>, RouterLookahead> cached_router_lookahead_; + + /** + * @brief Routing constraints, read only + */ + VprConstraints constraints; }; /** @@ -545,6 +559,69 @@ struct NocContext : public Context { NocRouting* noc_flows_router; }; +/** + * @brief State relating to server mode + * + * This should contain only data structures that + * related to server state. + */ +class ServerContext : public Context { + public: + const server::GateIO& gateIO() const { return gate_io_; } + server::GateIO& mutable_gateIO() { return gate_io_; } + + const server::TaskResolver& task_resolver() const { return task_resolver_; } + server::TaskResolver& mutable_task_resolver() { return task_resolver_; } + + void set_crit_paths(const std::vector& crit_paths) { crit_paths_ = crit_paths; } + const std::vector& crit_paths() const { return crit_paths_; } + + void set_critical_path_num(int critical_path_num) { critical_path_num_ = critical_path_num; } + int critical_path_num() const { return critical_path_num_; } + + void set_path_type(const std::string& path_type) { path_type_ = path_type; } + const std::string& path_type() const { return path_type_; } + + void set_crit_path_index(int crit_path_index) { crit_path_index_ = crit_path_index; } + int crit_path_index() const { return crit_path_index_; } + + private: + server::GateIO gate_io_; + server::TaskResolver task_resolver_; + + /** + * @brief Stores the critical path items. + * + * This value is used when rendering the critical path by the selected index. + * Once calculated upon request, it provides the value for a specific critical path + * to be rendered upon user request. + */ + std::vector crit_paths_; + + /** + * @brief Stores the number of critical paths items. + * + * This value is used to generate a critical path report with a certain number of items, + * which will be sent back to the client upon request. + */ + int critical_path_num_ = 1; + + /** + * @brief Stores the critical path type. + * + * This value is used to generate a specific type of critical path report and send + * it back to the client upon request. + */ + std::string path_type_ = "setup"; + + /** + * @brief Stores the last selected critical path index. + * + * This value is used to render the selected critical path upon client request. + */ + int crit_path_index_ = 0; +}; + /** * @brief This object encapsulates VPR's state. * @@ -628,6 +705,9 @@ class VprContext : public Context { const PackingMultithreadingContext& packing_multithreading() const { return packing_multithreading_; } PackingMultithreadingContext& mutable_packing_multithreading() { return packing_multithreading_; } + const ServerContext& server() const { return server_; } + ServerContext& mutable_server() { return server_; } + private: DeviceContext device_; @@ -644,6 +724,8 @@ class VprContext : public Context { FloorplanningContext constraints_; NocContext noc_; + ServerContext server_; + PackingMultithreadingContext packing_multithreading_; }; diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 5d03e194f8a..158ae4b7fce 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1338,6 +1338,8 @@ struct t_router_opts { enum e_route_type route_type; int fixed_channel_width; int min_channel_width_hint; /// switchblocks; + /* Xifan Tang: subtype of switch blocks. + * Sub type and Fs are applied to pass tracks + */ + int subFs; + enum e_switch_block_type switch_block_subtype; + + /* Xifan Tang: tileable routing */ + bool tileable; + bool shrink_boundary; + bool through_channel; + short global_route_switch; short delayless_switch; int wire_to_arch_ipin_switch; @@ -1762,6 +1776,12 @@ struct t_TokenPair { struct t_lb_type_rr_node; /* Defined in pack_types.h */ +/// @brief Stores settings for VPR server mode +struct t_server_opts { + bool is_server_mode_enabled = false; + int port_num = -1; +}; + ///@brief Store settings for VPR struct t_vpr_setup { bool TimingEnabled; ///* PackerRRGraph; std::vector Segments; ///graphics_commands = graphics_commands; draw_state->is_flat = is_flat; + if (enable_server) { + /* Set up a server and its callback to be triggered at 100ms intervals by the timer's timeout event. */ + server::GateIO& gate_io = g_vpr_ctx.mutable_server().mutable_gateIO(); + if (!gate_io.isRunning()) { + gate_io.start(port_num); + g_timeout_add(/*interval_ms*/ 100, server::update, &application); + } + } #else //Suppress unused parameter warnings (void)show_graphics_val; diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index 145f05800aa..9c68fd80ac5 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -56,7 +56,9 @@ void init_graphics_state(bool show_graphics_val, enum e_route_type route_type, bool save_graphics, std::string graphics_commands, - bool is_flat); + bool is_flat, + bool server, + int port_num); /* Allocates the structures needed to draw the placement and routing.*/ void alloc_draw_structs(const t_arch* arch); diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 0eb49a02034..c80f844d4a1 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -932,17 +932,10 @@ void draw_routing_util(ezgl::renderer* g) { draw_state->color_map = std::move(cmap); } -/* Draws the critical path if Crit. Path (in the GUI) is selected. Each stage between primitive - * pins is shown in a different colour. - * User can toggle between two different visualizations: - * a) during placement, critical path only shown as flylines - * b) during routing, critical path is shown by both flylines and routed net connections. +/* Draws the critical path when selected in the GUI or requested by the client in server mode. */ void draw_crit_path(ezgl::renderer* g) { - tatum::TimingPathCollector path_collector; - t_draw_state* draw_state = get_draw_state_vars(); - auto& timing_ctx = g_vpr_ctx.timing(); if (draw_state->show_crit_path == DRAW_NO_CRIT_PATH) { return; @@ -952,11 +945,47 @@ void draw_crit_path(ezgl::renderer* g) { return; //No timing to draw } - //Get the worst timing path - auto paths = path_collector.collect_worst_setup_timing_paths( - *timing_ctx.graph, - *(draw_state->setup_timing_info->setup_analyzer()), 1); - tatum::TimingPath path = paths[0]; + if (g_vpr_ctx.server().gateIO().isRunning()) { + const ServerContext& server_ctx = g_vpr_ctx.server(); // shortcut + const auto& paths = server_ctx.crit_paths(); + + const int index = server_ctx.crit_path_index(); // shortcut + + /* check critical path index bounds and render critical path, + * otherwise, resets selected critical path index with value -1. + */ + if ((index >= 0) && (index < static_cast(paths.size()))) { + tatum::TimingPath path = paths[index]; + draw_concrete_crit_path(path, g); + } else { + if (index != -1) { + g_vpr_ctx.mutable_server().set_crit_path_index(-1); + } + } + } else { + tatum::TimingPathCollector path_collector; + + auto& timing_ctx = g_vpr_ctx.timing(); + + auto paths = path_collector.collect_worst_setup_timing_paths( + *timing_ctx.graph, + *(draw_state->setup_timing_info->setup_analyzer()), 1); + + if (paths.size() > 0) { + auto path = paths[0]; + draw_concrete_crit_path(path, g); + } + } +} + +/* Draws the concrete critical path helper function. + * Each stage between primitive pins is shown in a different colour. + * User can toggle between two different visualizations: + * a) during placement, critical path only shown as flylines + * b) during routing, critical path is shown by both flylines and routed net connections. + */ +void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g) { + t_draw_state* draw_state = get_draw_state_vars(); //Walk through the timing path drawing each edge tatum::NodeId prev_node; diff --git a/vpr/src/draw/draw_basic.h b/vpr/src/draw/draw_basic.h index 4b2fad749c2..31f9342d9ae 100644 --- a/vpr/src/draw/draw_basic.h +++ b/vpr/src/draw/draw_basic.h @@ -78,13 +78,17 @@ void draw_partial_route(const std::vector& rr_nodes_to_draw, * channels, while darker colours (e.g. blue) correspond to lower utilization.*/ void draw_routing_util(ezgl::renderer* g); -/* Draws the critical path if Crit. Path (in the GUI) is selected. Each stage between primitive - * pins is shown in a different colour. +/* Draws the critical path when selected in the GUI or requested by the client in server mode. + */ +void draw_crit_path(ezgl::renderer* g); + +/* Draws the concrete critical path helper function. + * Each stage between primitive pins is shown in a different colour. * User can toggle between two different visualizations: * a) during placement, critical path only shown as flylines * b) during routing, critical path is shown by both flylines and routed net connections. */ -void draw_crit_path(ezgl::renderer* g); +void draw_concrete_crit_path(const tatum::TimingPath& path, ezgl::renderer* g); /* Draws critical path shown as flylines. Takes in start and end coordinates, time delay, & renderer.*/ void draw_flyline_timing_edge(ezgl::point2d start, ezgl::point2d end, float incr_delay, ezgl::renderer* g); diff --git a/vpr/src/route/route_common.cpp b/vpr/src/route/route_common.cpp index 881dbfd46aa..26f44e47afb 100644 --- a/vpr/src/route/route_common.cpp +++ b/vpr/src/route/route_common.cpp @@ -167,6 +167,10 @@ void try_graph(int width_fac, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } @@ -221,6 +225,10 @@ bool try_route(const Netlist<>& net_list, graph_directionality = GRAPH_BIDIR; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch->directionality == UNI_DIRECTIONAL && det_routing_arch->tileable) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index 4e2274c406f..63c35dc8f35 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -217,6 +217,9 @@ void alloc_routing_structs(t_chan_width chan_width, graph_type = GRAPH_GLOBAL; } else { graph_type = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); + if ((UNI_DIRECTIONAL == det_routing_arch->directionality) && (true == det_routing_arch->tileable)) { + graph_type = GRAPH_UNIDIR_TILEABLE; + } } create_rr_graph(graph_type, diff --git a/vpr/src/route/router_lookahead_map.cpp b/vpr/src/route/router_lookahead_map.cpp index 33b181a8be7..0a7004691cb 100644 --- a/vpr/src/route/router_lookahead_map.cpp +++ b/vpr/src/route/router_lookahead_map.cpp @@ -1431,7 +1431,9 @@ static void compute_tile_lookahead(std::unordered_map>& switch_fanin_remap, - const std::map arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch); - static void remap_rr_node_switch_indices(RRGraphBuilder& rr_graph_builder, const t_arch_switch_fanin& switch_fanin); @@ -509,27 +492,9 @@ static void alloc_rr_switch_inf(RRGraphBuilder& rr_graph_builder, t_arch_switch_fanin& arch_switch_fanins, const std::map& arch_sw_map); -static void rr_graph_externals(const std::vector& segment_inf, - const std::vector& segment_inf_x, - const std::vector& segment_inf_y, - int wire_to_rr_ipin_switch, - enum e_base_cost_type base_cost_type); - -static t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, const int delayless_switch); - static t_seg_details* alloc_and_load_global_route_seg_details(const int global_route_switch, int* num_seg_details = nullptr); -static std::vector> alloc_and_load_actual_fc(const std::vector& types, - const int max_pins, - const std::vector& segment_inf, - const int* sets_per_seg_type, - const t_chan_width* nodes_per_chan, - const e_fc_type fc_type, - const enum e_directionality directionality, - bool* Fc_clipped, - bool is_flat); - static RRNodeId pick_best_direct_connect_target_rr_node(const RRGraphView& rr_graph, RRNodeId from_rr, const std::vector& candidate_rr_nodes); @@ -716,26 +681,50 @@ void create_rr_graph(const t_graph_type graph_type, } } else { free_rr_graph(); - build_rr_graph(graph_type, - block_types, - grid, - nodes_per_chan, - det_routing_arch->switch_block_type, - det_routing_arch->Fs, - det_routing_arch->switchblocks, - segment_inf, - det_routing_arch->global_route_switch, - det_routing_arch->wire_to_arch_ipin_switch, - det_routing_arch->wire_to_arch_ipin_switch_between_dice, - det_routing_arch->delayless_switch, - det_routing_arch->R_minW_nmos, - det_routing_arch->R_minW_pmos, - router_opts.base_cost_type, - router_opts.clock_modeling, - directs, num_directs, - &det_routing_arch->wire_to_rr_ipin_switch, - is_flat, - Warnings); + if (GRAPH_UNIDIR_TILEABLE != graph_type) { + build_rr_graph(graph_type, + block_types, + grid, + nodes_per_chan, + det_routing_arch->switch_block_type, + det_routing_arch->Fs, + det_routing_arch->switchblocks, + segment_inf, + det_routing_arch->global_route_switch, + det_routing_arch->wire_to_arch_ipin_switch, + det_routing_arch->wire_to_arch_ipin_switch_between_dice, + det_routing_arch->delayless_switch, + det_routing_arch->R_minW_nmos, + det_routing_arch->R_minW_pmos, + router_opts.base_cost_type, + router_opts.clock_modeling, + directs, num_directs, + &det_routing_arch->wire_to_rr_ipin_switch, + is_flat, + Warnings); + } else { + /* We do not support dedicated network for clocks in tileable rr_graph generation */ + VTR_LOG_WARN("Tileable routing resource graph does not support clock modeling yet! Related options are ignored...\n"); + build_tileable_unidir_rr_graph(block_types, + grid, + nodes_per_chan, + det_routing_arch->switch_block_type, + det_routing_arch->Fs, + det_routing_arch->switch_block_subtype, + det_routing_arch->subFs, + segment_inf, + det_routing_arch->delayless_switch, + det_routing_arch->wire_to_arch_ipin_switch, + det_routing_arch->R_minW_nmos, + det_routing_arch->R_minW_pmos, + router_opts.base_cost_type, + directs, num_directs, + &det_routing_arch->wire_to_rr_ipin_switch, + det_routing_arch->shrink_boundary, /* Shrink to the smallest boundary, no routing wires for empty zone */ + router_opts.trim_obs_channels || det_routing_arch->through_channel, /* Allow/Prohibit through tracks across multi-height and multi-width grids */ + false, /* Do not allow passing tracks to be wired to the same routing channels */ + Warnings); + } } } @@ -751,6 +740,7 @@ void create_rr_graph(const t_graph_type graph_type, is_flat, load_rr_graph); + /* Reorder nodes upon needs in algorithms and router options */ if (router_opts.reorder_rr_graph_nodes_algorithm != DONT_REORDER) { mutable_device_ctx.rr_graph_builder.reorder_nodes(router_opts.reorder_rr_graph_nodes_algorithm, router_opts.reorder_rr_graph_nodes_threshold, @@ -1548,13 +1538,13 @@ void build_tile_rr_graph(RRGraphBuilder& rr_graph_builder, * and count how many different fan-ins exist for each arch switch. * Then we create these rr switches and update the switch indices * of rr_nodes to index into the rr_switch_inf array. */ -static void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, - std::vector>& switch_fanin_remap, - const std::map arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch) { +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch) { /* we will potentially be creating a couple of versions of each arch switch where * each version corresponds to a different fan-in. We will need to fill device_ctx.rr_switch_inf * with this expanded list of switches. @@ -1736,11 +1726,11 @@ static void remap_rr_node_switch_indices(RRGraphBuilder& rr_graph_builder, rr_graph_builder.remap_rr_node_switch_indices(switch_fanin); } -static void rr_graph_externals(const std::vector& segment_inf, - const std::vector& segment_inf_x, - const std::vector& segment_inf_y, - int wire_to_rr_ipin_switch, - enum e_base_cost_type base_cost_type) { +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; const auto& grid = device_ctx.grid; @@ -1838,15 +1828,15 @@ static t_seg_details* alloc_and_load_global_route_seg_details(const int global_r } /* Calculates the number of track connections from each block pin to each segment type */ -static std::vector> alloc_and_load_actual_fc(const std::vector& types, - const int max_pins, - const std::vector& segment_inf, - const int* sets_per_seg_type, - const t_chan_width* nodes_per_chan, - const e_fc_type fc_type, - const enum e_directionality directionality, - bool* Fc_clipped, - bool is_flat) { +std::vector> alloc_and_load_actual_fc(const std::vector& types, + const int max_pins, + const std::vector& segment_inf, + const int* sets_per_seg_type, + const t_chan_width* nodes_per_chan, + const e_fc_type fc_type, + const enum e_directionality directionality, + bool* Fc_clipped, + bool is_flat) { //Initialize Fc of all blocks to zero auto zeros = vtr::Matrix({size_t(max_pins), segment_inf.size()}, 0); std::vector> Fc(types.size(), zeros); @@ -2603,6 +2593,8 @@ void free_rr_graph() { device_ctx.rr_graph_builder.clear(); + device_ctx.rr_node_track_ids.clear(); + device_ctx.rr_indexed_data.clear(); device_ctx.switch_fanin_remap.clear(); @@ -4106,7 +4098,7 @@ static void build_unidir_rr_opins(RRGraphBuilder& rr_graph_builder, * This data structure supplements the the info in the "directs" data structure * TODO: The function that does this parsing in placement is poorly done because it lacks generality on heterogeniety, should replace with this one */ -static t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch) { +t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch) { int i; t_clb_to_clb_directs* clb_to_clb_directs; char *tile_name, *port_name; diff --git a/vpr/src/route/rr_graph.h b/vpr/src/route/rr_graph.h index af06257d98e..14d2bf903b6 100644 --- a/vpr/src/route/rr_graph.h +++ b/vpr/src/route/rr_graph.h @@ -10,6 +10,7 @@ #include "vpr_types.h" #include "rr_graph_type.h" #include "describe_rr_node.h" +#include "clb2clb_directs.h" /* Warnings about the routing graph that can be returned. * This is to avoid output messages during a value sweep */ @@ -47,6 +48,33 @@ void free_rr_graph(); t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_sw_inf, const float R_minW_nmos, const float R_minW_pmos); + +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch); + +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type); + +t_clb_to_clb_directs* alloc_and_load_clb_to_clb_directs(const t_direct_inf* directs, const int num_directs, int delayless_switch); + +std::vector> alloc_and_load_actual_fc(const std::vector& types, + const int max_pins, + const std::vector& segment_inf, + const int* sets_per_seg_type, + const t_chan_width* nodes_per_chan, + const e_fc_type fc_type, + const enum e_directionality directionality, + bool* Fc_clipped, + bool is_flat); + // Sets the spec for the rr_switch based on the arch switch void load_rr_switch_from_arch_switch(RRGraphBuilder& rr_graph_builder, const std::map& arch_sw_inf, diff --git a/vpr/src/server/commconstants.h b/vpr/src/server/commconstants.h new file mode 100644 index 00000000000..3462e345e54 --- /dev/null +++ b/vpr/src/server/commconstants.h @@ -0,0 +1,36 @@ +#ifndef COMMCONSTS_H +#define COMMCONSTS_H + +namespace comm { + +constexpr const char* KEY_JOB_ID = "JOB_ID"; +constexpr const char* KEY_CMD = "CMD"; +constexpr const char* KEY_OPTIONS = "OPTIONS"; +constexpr const char* KEY_DATA = "DATA"; +constexpr const char* KEY_STATUS = "STATUS"; +constexpr const unsigned char TELEGRAM_FRAME_DELIMETER{0x17}; // 0x17 - End of Transmission Block + +constexpr const char* OPTION_PATH_NUM = "path_num"; +constexpr const char* OPTION_PATH_TYPE = "path_type"; +constexpr const char* OPTION_DETAILS_LEVEL = "details_level"; +constexpr const char* OPTION_IS_FLOAT_ROUTING = "is_flat_routing"; +constexpr const char* OPTION_PATH_INDEX = "path_index"; +constexpr const char* OPTION_PATH_ELEMENTS = "path_elements"; +constexpr const char* OPTION_HIGHTLIGHT_MODE = "hight_light_mode"; +constexpr const char* OPTION_DRAW_PATH_CONTOUR = "draw_path_contour"; + +constexpr const char* CRITICAL_PATH_ITEMS_SELECTION_NONE = "none"; + +// please don't change values as they are involved in socket communication +constexpr const char* KEY_SETUP_PATH_LIST = "setup"; +constexpr const char* KEY_HOLD_PATH_LIST = "hold"; +// + +enum CMD { + CMD_GET_PATH_LIST_ID = 0, + CMD_DRAW_PATH_ID +}; + +} // namespace comm + +#endif diff --git a/vpr/src/server/convertutils.cpp b/vpr/src/server/convertutils.cpp new file mode 100644 index 00000000000..691aedca1bf --- /dev/null +++ b/vpr/src/server/convertutils.cpp @@ -0,0 +1,17 @@ +#include "convertutils.h" +#include + +std::optional tryConvertToInt(const std::string& str) { + std::optional result; + + std::istringstream iss(str); + int intValue; + if (iss >> intValue) { + // Check if there are no any characters left in the stream + char remaining; + if (!(iss >> remaining)) { + result = intValue; + } + } + return result; +} diff --git a/vpr/src/server/convertutils.h b/vpr/src/server/convertutils.h new file mode 100644 index 00000000000..0647449c6a8 --- /dev/null +++ b/vpr/src/server/convertutils.h @@ -0,0 +1,9 @@ +#ifndef CONVERTUTILS_H +#define CONVERTUTILS_H + +#include +#include + +std::optional tryConvertToInt(const std::string&); + +#endif // CONVERTUTILS_H diff --git a/vpr/src/server/gateio.cpp b/vpr/src/server/gateio.cpp new file mode 100644 index 00000000000..2c309d3d5e5 --- /dev/null +++ b/vpr/src/server/gateio.cpp @@ -0,0 +1,197 @@ +#include "gateio.h" +#include "telegramparser.h" + +#include +#include +#include +#include +#include +#include + +namespace server { + +GateIO::GateIO() { + m_isRunning.store(false); +} + +GateIO::~GateIO() { + stop(); +} + +void GateIO::start(int portNum) { + if (!m_isRunning.load()) { + m_portNum = portNum; + std::cout << "th=" << std::this_thread::get_id() << " starting server" << std::endl; + m_isRunning.store(true); + m_thread = std::thread(&GateIO::startListening, this); + } +} + +void GateIO::stop() { + if (m_isRunning.load()) { + m_isRunning.store(false); + if (m_thread.joinable()) { + m_thread.join(); + } + } +} + +void GateIO::takeRecievedTasks(std::vector& tasks) { + tasks.clear(); + std::unique_lock lock(m_receivedTasksMutex); + if (m_receivedTasks.size() > 0) { + std::cout << "take " << m_receivedTasks.size() << " num of received tasks" << std::endl; + } + std::swap(tasks, m_receivedTasks); +} + +void GateIO::addSendTasks(const std::vector& tasks) { + std::unique_lock lock(m_sendTasksMutex); + for (const Task& task : tasks) { + std::cout << "addSendTasks id=" << task.jobId() << std::endl; + m_sendTasks.push_back(task); + } +} + +void GateIO::startListening() { + int server_socket; + int client_socket = -1; + struct sockaddr_in address; + int opt = 1; + int addrlen = sizeof(address); + + // Creating socket file descriptor + if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("socket failed"); + exit(EXIT_FAILURE); + } + + // Forcefully attaching socket to the port + if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { + perror("setsockopt"); + exit(EXIT_FAILURE); + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(m_portNum); + + // Bind the socket to the network address and port + if (bind(server_socket, (struct sockaddr*)&address, sizeof(address)) < 0) { + perror("bind failed"); + exit(EXIT_FAILURE); + } else { + std::cout << "th=" << std::this_thread::get_id() << " start listening port: " << m_portNum << std::endl; + } + + // Put the server socket in a passive mode, where it waits for the client to approach the server to make a connection + if (listen(server_socket, 3) < 0) { + perror("listen"); + exit(EXIT_FAILURE); + } + + // Set the server socket to non-blocking mode + if (fcntl(server_socket, F_SETFL, fcntl(server_socket, F_GETFL, 0) | O_NONBLOCK) < 0) { + perror("Error setting socket to non-blocking"); + exit(EXIT_FAILURE); + } + + bool connectionProblemDetected = false; + + // Event loop + while (m_isRunning.load()) { + if (connectionProblemDetected || client_socket < 0) { + int flags = fcntl(client_socket, F_GETFL, 0); + fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); + client_socket = accept(server_socket, (struct sockaddr*)&address, (socklen_t*)&addrlen); + if (client_socket > 0) { + std::cout << "accept client" << std::endl; + } + } + + if (client_socket >= 0) { + connectionProblemDetected = false; + // Handle sending + { + std::unique_lock lock(m_sendTasksMutex); + for (const Task& task : m_sendTasks) { + std::string response = task.toJsonStr(); + response += static_cast(comm::TELEGRAM_FRAME_DELIMETER); + std::cout << "sending" << response << "to client" << std::endl; + send(client_socket, response.c_str(), response.length(), 0); + } + m_sendTasks.clear(); + } + + //////////////////// + // Handle receiving + fd_set readfds; + struct timeval tv; + + // Set a timeout + tv.tv_sec = 1; + tv.tv_usec = 0; + + // Clear the set ahead of time + FD_ZERO(&readfds); + + // Add our descriptor to the set + FD_SET(client_socket, &readfds); + + // Wait until data is available or timeout + int selectResult = select(client_socket + 1, &readfds, NULL, NULL, &tv); + + if (selectResult == -1) { + std::cerr << "Error in select()\n"; + } else if (selectResult == 0) { + //std::cout << "Timeout occurred! No data available.\n"; + } else { + if (FD_ISSET(client_socket, &readfds)) { + // Data is available; proceed with recv + char data[2048]; + std::memset(data, 0, sizeof(data)); + ssize_t bytes_received = recv(client_socket, data, sizeof(data), 0); + if (bytes_received > 0) { + m_telegramBuff.append(comm::ByteArray{data, static_cast(bytes_received)}); + } else if (bytes_received == 0) { + std::cout << "Connection closed\n"; + connectionProblemDetected = true; + } else { + std::cerr << "Error in recv()\n"; + connectionProblemDetected = true; + } + } + } + + auto frames = m_telegramBuff.takeFrames(); + for (const comm::ByteArray& frame : frames) { + // Process received data + std::string message{frame.to_string()}; + std::cout << "Received: " << message << std::endl; + std::optional jobIdOpt = comm::TelegramParser::tryExtractFieldJobId(message); + std::optional cmdOpt = comm::TelegramParser::tryExtractFieldCmd(message); + std::optional optionsOpt = comm::TelegramParser::tryExtractFieldOptions(message); + if (jobIdOpt && cmdOpt && optionsOpt) { + std::cout << "server: jobId=" << jobIdOpt.value() << ", cmd=" << cmdOpt.value() << ", options=" << optionsOpt.value() << std::endl; + std::unique_lock lock(m_receivedTasksMutex); + m_receivedTasks.emplace_back(jobIdOpt.value(), cmdOpt.value(), optionsOpt.value()); + } + } + + if (connectionProblemDetected && (client_socket >= 0)) { + std::cout << "close client connection" << std::endl; + close(client_socket); + m_telegramBuff.clear(); + } + } + } + + if (client_socket >= 0) { + std::cout << "close client socket" << std::endl; + close(client_socket); + } + + close(server_socket); +} + +} // namespace server diff --git a/vpr/src/server/gateio.h b/vpr/src/server/gateio.h new file mode 100644 index 00000000000..fee9591eec8 --- /dev/null +++ b/vpr/src/server/gateio.h @@ -0,0 +1,64 @@ +#ifndef GATEIO_H +#define GATEIO_H + +#include "task.h" +#include "telegrambuffer.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace server { + +/** + * @brief Implements the socket communication layer with the outside world. + * It begins listening on the specified port number for incoming client requests, + * collects and encapsulates them into tasks. + * The incoming tasks are extracted and handled by the top-level logic (TaskResolver). + * Once the tasks are resolved by the TaskResolver, they are returned + * to be sent back to the client as a response. + * + * Note: + * - gateio is not started automatically upon creation; you have to use the 'start' method with the port number. + * - The gateio runs in a separate thread to ensure smooth IO behavior. + * - The socket is initialized in a non-blocking mode to function properly in a multithreaded environment. +*/ +class GateIO { + public: + explicit GateIO(); + ~GateIO(); + + bool isRunning() const { return m_isRunning.load(); } + + void takeRecievedTasks(std::vector&); + void addSendTasks(const std::vector&); + + void start(int portNum); + void stop(); + + private: + int m_portNum = -1; + + std::atomic m_isRunning; // is true when started + + std::thread m_thread; // thread to execute socket IO work + + std::vector m_receivedTasks; // tasks from clinet (requests) + std::mutex m_receivedTasksMutex; + + std::vector m_sendTasks; // task to client (reponses) + std::mutex m_sendTasksMutex; + + comm::TelegramBuffer m_telegramBuff; + + void startListening(); // thread worker function +}; + +} // namespace server + +#endif // GATEIO_H diff --git a/vpr/src/server/gtkcomboboxhelper.cpp b/vpr/src/server/gtkcomboboxhelper.cpp new file mode 100644 index 00000000000..faeec50fc89 --- /dev/null +++ b/vpr/src/server/gtkcomboboxhelper.cpp @@ -0,0 +1,50 @@ +#include "gtkcomboboxhelper.h" +#include + +namespace { + +/** + * @brief Helper function to retrieve the count of items in a GTK combobox. + */ +gint get_items_count(gpointer combo_box) { + GtkComboBoxText* combo = GTK_COMBO_BOX_TEXT(combo_box); + + // Get the model of the combo box + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + + // Get the number of items (indexes) in the combo box + gint count = gtk_tree_model_iter_n_children(model, NULL); + return count; +} + +} // namespace + +/** + * @brief Helper function to retrieve the index of an item by its text. + * Returns -1 if the item with the specified text is absent. + */ +gint get_item_index_by_text(gpointer combo_box, const gchar* target_item) { + gint result_index = -1; + GtkComboBoxText* combo = GTK_COMBO_BOX_TEXT(combo_box); + + // Get the model of the combo box + GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + + gchar* current_item_text = nullptr; + + for (gint index = 0; index < get_items_count(combo_box); ++index) { + GtkTreeIter iter; + + // Check if the index is within bounds + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index)) { + gtk_tree_model_get(model, &iter, 0, ¤t_item_text, -1); + if (g_ascii_strcasecmp(target_item, current_item_text) == 0) { + result_index = index; + break; + } + } + } + + g_free(current_item_text); + return result_index; +} diff --git a/vpr/src/server/gtkcomboboxhelper.h b/vpr/src/server/gtkcomboboxhelper.h new file mode 100644 index 00000000000..24da5c66b7d --- /dev/null +++ b/vpr/src/server/gtkcomboboxhelper.h @@ -0,0 +1,12 @@ +#ifndef GTKCOMBOBOXHELPER_H +#define GTKCOMBOBOXHELPER_H + +#include + +/** + * @brief Helper function to retrieve the index of an item by its text. + * Returns -1 if the item with the specified text is absent. + */ +gint get_item_index_by_text(gpointer combo_box, const gchar* target_item); + +#endif // GTKCOMBOBOXHELPER_H diff --git a/vpr/src/server/pathhelper.cpp b/vpr/src/server/pathhelper.cpp new file mode 100644 index 00000000000..594a1a7067d --- /dev/null +++ b/vpr/src/server/pathhelper.cpp @@ -0,0 +1,95 @@ +#include "pathhelper.h" +#include "globals.h" +#include "vpr_net_pins_matrix.h" +#include "VprTimingGraphResolver.h" +#include "tatum/TimingReporter.hpp" + +#include "draw_types.h" +#include "draw_global.h" +#include "net_delay.h" +#include "concrete_timing_info.h" + +#include "timing_info_fwd.h" +#include "AnalysisDelayCalculator.h" +#include "vpr_types.h" + +#include +#include + +namespace server { + +namespace { + +/** + * @brief helper function to calculate the setup critical path with specified parameters. + */ +CritPathsResult generate_setup_timing_report(const SetupTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { + auto& timing_ctx = g_vpr_ctx.timing(); + auto& atom_ctx = g_vpr_ctx.atom(); + + VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, delay_calc, is_flat); + resolver.set_detail_level(analysis_opts.timing_report_detail); + + tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); + + std::vector paths; + std::stringstream ss; + timing_reporter.report_timing_setup(paths, ss, *timing_info.setup_analyzer(), analysis_opts.timing_report_npaths); + return CritPathsResult{paths, ss.str()}; +} + +/** + * @brief helper function to calculate the hold critical path with specified parameters. + */ +CritPathsResult generate_hold_timing_report(const HoldTimingInfo& timing_info, const AnalysisDelayCalculator& delay_calc, const t_analysis_opts& analysis_opts, bool is_flat) { + auto& timing_ctx = g_vpr_ctx.timing(); + auto& atom_ctx = g_vpr_ctx.atom(); + + VprTimingGraphResolver resolver(atom_ctx.nlist, atom_ctx.lookup, *timing_ctx.graph, delay_calc, is_flat); + resolver.set_detail_level(analysis_opts.timing_report_detail); + + tatum::TimingReporter timing_reporter(resolver, *timing_ctx.graph, *timing_ctx.constraints); + + std::vector paths; + std::stringstream ss; + timing_reporter.report_timing_hold(paths, ss, *timing_info.hold_analyzer(), analysis_opts.timing_report_npaths); + return CritPathsResult{paths, ss.str()}; +} + +} // namespace + +/** + * @brief Unified helper function to calculate the critical path with specified parameters. + */ +CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing) { + // shortcuts + auto& atom_ctx = g_vpr_ctx.atom(); + + //Load the net delays + const Netlist<>& router_net_list = is_flat_routing ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; + const Netlist<>& net_list = router_net_list; + + NetPinsMatrix net_delay = make_net_pins_matrix(net_list); + load_net_delay_from_routing(net_list, + net_delay); + + //Do final timing analysis + auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, is_flat_routing); + + e_timing_update_type timing_update_type = e_timing_update_type::AUTO; // FULL, INCREMENTAL, AUTO + auto timing_info = make_setup_hold_timing_info(analysis_delay_calc, timing_update_type); + timing_info->update(); + + t_analysis_opts analysis_opt; + analysis_opt.timing_report_detail = detailsLevel; + analysis_opt.timing_report_npaths = critPathNum; + + if (type == "setup") { + return generate_setup_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing); + } else if (type == "hold") { + return generate_hold_timing_report(*timing_info, *analysis_delay_calc, analysis_opt, is_flat_routing); + } + return CritPathsResult{std::vector(), ""}; +} + +} // namespace server diff --git a/vpr/src/server/pathhelper.h b/vpr/src/server/pathhelper.h new file mode 100644 index 00000000000..61c86295634 --- /dev/null +++ b/vpr/src/server/pathhelper.h @@ -0,0 +1,31 @@ +#ifndef PATHHELPER_H +#define PATHHELPER_H + +#include +#include +#include + +#include "tatum/report/TimingPath.hpp" +#include "vpr_types.h" + +namespace server { + +/** + * @brief Structure to retain the calculation result of the critical path. + * + * It contains the critical path list and the generated report as a string. +*/ +struct CritPathsResult { + bool isValid() const { return !report.empty(); } + std::vector paths; + std::string report; +}; + +/** + * @brief Unified helper function to calculate the critical path with specified parameters. + */ +CritPathsResult calcCriticalPath(const std::string& type, int critPathNum, e_timing_report_detail detailsLevel, bool is_flat_routing); + +} // namespace server + +#endif // PATHHELPER_H diff --git a/vpr/src/server/serverupdate.cpp b/vpr/src/server/serverupdate.cpp new file mode 100644 index 00000000000..9aab36b2d1e --- /dev/null +++ b/vpr/src/server/serverupdate.cpp @@ -0,0 +1,43 @@ +#include "serverupdate.h" +#include "gateio.h" +#include "taskresolver.h" +#include "globals.h" +#include "ezgl/application.hpp" + +#ifndef NO_GRAPHICS + +namespace server { + +gboolean update(gpointer data) { + bool isRunning = g_vpr_ctx.server().gateIO().isRunning(); + if (isRunning) { + // shortcuts + ezgl::application* app = static_cast(data); + GateIO& gate_io = g_vpr_ctx.mutable_server().mutable_gateIO(); + TaskResolver& task_resolver = g_vpr_ctx.mutable_server().mutable_task_resolver(); + + std::vector tasksBuff; + + gate_io.takeRecievedTasks(tasksBuff); + task_resolver.addTasks(tasksBuff); + + bool process_task = task_resolver.update(app); + + tasksBuff.clear(); + task_resolver.takeFinished(tasksBuff); + + gate_io.addSendTasks(tasksBuff); + + // Call the redraw method of the application if any of task was processed + if (process_task) { + app->refresh_drawing(); + } + } + + // Return TRUE to keep the timer running, or FALSE to stop it + return isRunning; +} + +} // namespace server + +#endif // NO_GRAPHICS \ No newline at end of file diff --git a/vpr/src/server/serverupdate.h b/vpr/src/server/serverupdate.h new file mode 100644 index 00000000000..17301988b45 --- /dev/null +++ b/vpr/src/server/serverupdate.h @@ -0,0 +1,23 @@ +#ifndef SERVERUPDATE_H +#define SERVERUPDATE_H + +#include + +#ifndef NO_GRAPHICS + +namespace server { + +/** + * @brief Main server update callback. + * + * This function is a periodic callback invoked at a fixed interval to manage and handle incoming client requests. + * It acts as the central control point for processing client interactions and orchestrating server-side operations + * within the specified time intervals. + */ +gboolean update(gpointer); + +} // namespace server + +#endif // NO_GRAPHICS + +#endif // SERVERUPDATE_H diff --git a/vpr/src/server/task.h b/vpr/src/server/task.h new file mode 100644 index 00000000000..1576b518c66 --- /dev/null +++ b/vpr/src/server/task.h @@ -0,0 +1,80 @@ +#ifndef TASK_H +#define TASK_H + +#include +#include +#include + +#include "commconstants.h" + +namespace server { + +/** + * @brief Implements the server task. + * + * This structure aids in encapsulating the client request, request result, and result status. + * It generates a JSON data structure to be sent back to the client as a response. + */ +class Task { + public: + Task(int jobId, int cmd, const std::string& options = "") + : m_jobId(jobId) + , m_cmd(cmd) + , m_options(options) {} + + int jobId() const { return m_jobId; } + int cmd() const { return m_cmd; } + const std::string& options() const { return m_options; } + const std::string& result() const { return m_result; } + + std::string info() const { + std::stringstream result; + result << "id=" << std::to_string(m_jobId) << ","; + result << "cmd=" << std::to_string(m_cmd) << ","; + result << "opt=" << m_options; + return result.str(); + } + + bool isFinished() const { return m_isFinished; } + bool hasError() const { return m_hasError; } + + void fail(const std::string& error) { + std::cout << "task " << info() << " finished with error " << error << std::endl; + m_result = error; + m_isFinished = true; + m_hasError = true; + } + void success(const std::string& result = "") { + std::cout << "task " << info() << " finished with success " << result << std::endl; + m_result = result; + m_isFinished = true; + } + + std::string toJsonStr() const { + std::stringstream ss; + ss << "{"; + + ss << "\"" << comm::KEY_JOB_ID << "\":\"" << m_jobId << "\","; + ss << "\"" << comm::KEY_CMD << "\":\"" << m_cmd << "\","; + ss << "\"" << comm::KEY_OPTIONS << "\":\"" << m_options << "\","; + ss << "\"" << comm::KEY_DATA << "\":\"" << m_result << "\","; + int status = m_hasError ? 0 : 1; + ss << "\"" << comm::KEY_STATUS << "\":\"" << status << "\""; + + ss << "}"; + + return ss.str(); + } + + private: + int m_jobId = -1; + int m_cmd = -1; + std::string m_options; + std::string m_result; + bool m_isFinished = false; + bool m_hasError = false; +}; + +} // namespace server + +#endif // TASK_H diff --git a/vpr/src/server/taskresolver.cpp b/vpr/src/server/taskresolver.cpp new file mode 100644 index 00000000000..cf5c98a21b6 --- /dev/null +++ b/vpr/src/server/taskresolver.cpp @@ -0,0 +1,167 @@ +#include "taskresolver.h" + +#include "commconstants.h" +#include "globals.h" +#include "pathhelper.h" +#include "telegramoptions.h" +#include "telegramparser.h" +#include "gtkcomboboxhelper.h" + +#include + +namespace server { + +void TaskResolver::addTask(Task task) { + // pre-process task before adding, where we could quickly detect failure scenario + for (auto& t : m_tasks) { + if (t.cmd() == task.cmd()) { + if (t.options() == task.options()) { + std::string msg = "similar task is already in execution, reject new task: " + t.info() + " and waiting for old task: " + task.info() + " execution"; + task.fail(msg); + } else { + // case when task has same jobId but different options + if (task.jobId() > t.jobId()) { + std::string msg = "old task: " + t.info() + " is overriden by a new task: " + task.info(); + t.fail(msg); + } + } + } + } + + // add task + m_tasks.push_back(std::move(task)); +} + +void TaskResolver::addTasks(const std::vector& tasks) { + for (const Task& task : tasks) { + addTask(task); + } +} + +void TaskResolver::takeFinished(std::vector& result) { + for (auto it = m_tasks.begin(); it != m_tasks.end();) { + Task task = *it; + if (task.isFinished()) { + result.push_back(std::move(task)); + it = m_tasks.erase(it); + } else { + ++it; + } + } +} + +e_timing_report_detail TaskResolver::getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const { + e_timing_report_detail detailesLevel = e_timing_report_detail::NETLIST; + if (pathDetailsLevelStr == "netlist") { + detailesLevel = e_timing_report_detail::NETLIST; + } else if (pathDetailsLevelStr == "aggregated") { + detailesLevel = e_timing_report_detail::AGGREGATED; + } else if (pathDetailsLevelStr == "detailed") { + detailesLevel = e_timing_report_detail::DETAILED_ROUTING; + } else if (pathDetailsLevelStr == "debug") { + detailesLevel = e_timing_report_detail::DEBUG; + } else { + std::cerr << "unhandled option" << pathDetailsLevelStr << std::endl; + } + return detailesLevel; +} + +bool TaskResolver::update(ezgl::application* app) { + bool has_processed_task = false; + for (auto& task : m_tasks) { + if (!task.isFinished()) { + switch (task.cmd()) { + case comm::CMD_GET_PATH_LIST_ID: { + processGetPathListTask(app, task); + has_processed_task = true; + break; + } + case comm::CMD_DRAW_PATH_ID: { + processDrawCriticalPathTask(app, task); + has_processed_task = true; + break; + } + default: + break; + } + } + } + + return has_processed_task; +} + +void TaskResolver::processGetPathListTask(ezgl::application*, Task& task) { + TelegramOptions options{task.options(), {comm::OPTION_PATH_NUM, comm::OPTION_PATH_TYPE, comm::OPTION_DETAILS_LEVEL, comm::OPTION_IS_FLOAT_ROUTING}}; + if (!options.hasErrors()) { + ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut + + server_ctx.set_crit_path_index(-1); // reset selection if path list options has changed + + // read options + const int nCriticalPathNum = options.getInt(comm::OPTION_PATH_NUM, 1); + const std::string pathType = options.getString(comm::OPTION_PATH_TYPE); + const std::string detailsLevel = options.getString(comm::OPTION_DETAILS_LEVEL); + const bool isFlat = options.getBool(comm::OPTION_IS_FLOAT_ROUTING, false); + + // calculate critical path depending on options and store result in server context + CritPathsResult crit_paths_result = calcCriticalPath(pathType, nCriticalPathNum, getDetailsLevelEnum(detailsLevel), isFlat); + + // setup context + server_ctx.set_path_type(pathType); + server_ctx.set_critical_path_num(nCriticalPathNum); + server_ctx.set_crit_paths(crit_paths_result.paths); + + if (crit_paths_result.isValid()) { + std::string msg{crit_paths_result.report}; + task.success(msg); + } else { + std::string msg{"Critical paths report is empty"}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"options errors in get crit path list telegram: " + options.errorsStr()}; + std::cerr << msg << std::endl; + task.fail(msg); + } +} + +void TaskResolver::processDrawCriticalPathTask(ezgl::application* app, Task& task) { + TelegramOptions options{task.options(), {comm::OPTION_PATH_INDEX, comm::OPTION_HIGHTLIGHT_MODE}}; + if (!options.hasErrors()) { + ServerContext& server_ctx = g_vpr_ctx.mutable_server(); // shortcut + + const int pathIndex = options.getInt(comm::OPTION_PATH_INDEX, -1); + const std::string highLightMode = options.getString(comm::OPTION_HIGHTLIGHT_MODE); + + if (pathIndex == -1) { + server_ctx.set_crit_path_index(-1); // clear selection + task.success(); + } else if ((pathIndex >= 0) && (pathIndex < static_cast(server_ctx.crit_paths().size()))) { + // set critical path index for rendering + server_ctx.set_crit_path_index(pathIndex); + + // update gtk UI + GtkComboBox* toggle_crit_path = GTK_COMBO_BOX(app->get_object("ToggleCritPath")); + gint highLightModeIndex = get_item_index_by_text(toggle_crit_path, highLightMode.c_str()); + if (highLightModeIndex != -1) { + gtk_combo_box_set_active(toggle_crit_path, highLightModeIndex); + task.success(); + } else { + std::string msg{"cannot find ToggleCritPath qcombobox index for item " + highLightMode}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"selectedIndex=" + std::to_string(pathIndex) + " is out of range [0-" + std::to_string(static_cast(server_ctx.crit_paths().size()) - 1) + "]"}; + std::cerr << msg << std::endl; + task.fail(msg); + } + } else { + std::string msg{"options errors in highlight crit path telegram: " + options.errorsStr()}; + std::cerr << msg << std::endl; + task.fail(msg); + } +} + +} // namespace server diff --git a/vpr/src/server/taskresolver.h b/vpr/src/server/taskresolver.h new file mode 100644 index 00000000000..50e7e3a5ed6 --- /dev/null +++ b/vpr/src/server/taskresolver.h @@ -0,0 +1,51 @@ +#ifndef TASKRESOLVER_H +#define TASKRESOLVER_H + +#include "task.h" +#include "vpr_types.h" + +#include + +namespace ezgl { +class application; +} + +namespace server { + +/** + * @brief Resolve server task. + * + * Process and resolve server task, store result and status for processed task. +*/ + +class TaskResolver { + public: + TaskResolver() = default; + ~TaskResolver() = default; + + int tasksNum() const { return m_tasks.size(); } + + /* add tasks to process */ + void addTask(Task); + void addTasks(const std::vector&); + + /* process tasks */ + bool update(ezgl::application*); + + /* extract finished tasks */ + void takeFinished(std::vector&); + + const std::vector& tasks() const { return m_tasks; } + + private: + std::vector m_tasks; + + void processGetPathListTask(ezgl::application*, Task&); + void processDrawCriticalPathTask(ezgl::application*, Task&); + + e_timing_report_detail getDetailsLevelEnum(const std::string& pathDetailsLevelStr) const; +}; + +} // namespace server + +#endif // TASKRESOLVER_H diff --git a/vpr/src/server/telegrambuffer.cpp b/vpr/src/server/telegrambuffer.cpp new file mode 100644 index 00000000000..8068beafef3 --- /dev/null +++ b/vpr/src/server/telegrambuffer.cpp @@ -0,0 +1,27 @@ +#include "telegrambuffer.h" +#include "commconstants.h" + +namespace comm { + +void TelegramBuffer::append(const ByteArray& bytes) { + m_rawBuffer.append(bytes); +} + +std::vector TelegramBuffer::takeFrames() { + std::vector result; + ByteArray candidate; + for (unsigned char b : m_rawBuffer) { + if (b == TELEGRAM_FRAME_DELIMETER) { + if (!candidate.empty()) { + result.push_back(candidate); + } + candidate = ByteArray(); + } else { + candidate.append(b); + } + } + std::swap(m_rawBuffer, candidate); + return result; +} + +} // namespace comm diff --git a/vpr/src/server/telegrambuffer.h b/vpr/src/server/telegrambuffer.h new file mode 100644 index 00000000000..9547b7efc8a --- /dev/null +++ b/vpr/src/server/telegrambuffer.h @@ -0,0 +1,69 @@ +#ifndef TELEGRAMBUFFER_H +#define TELEGRAMBUFFER_H + +#include +#include +#include + +namespace comm { + +/** + * @brief ByteArray as a simple wrapper over std::vector +*/ +class ByteArray : public std::vector { + public: + static const std::size_t DEFAULT_SIZE_HINT = 1024; + + ByteArray(const char* data) + : std::vector(reinterpret_cast(data), + reinterpret_cast(data + std::strlen(data))) {} + + ByteArray(const char* data, std::size_t size) + : std::vector(reinterpret_cast(data), + reinterpret_cast(data + size)) {} + ByteArray(std::size_t sizeHint = DEFAULT_SIZE_HINT) { + this->reserve(sizeHint); + } + + void append(const ByteArray& appendix) { + for (unsigned char b : appendix) { + this->push_back(b); + } + } + + void append(unsigned char b) { + this->push_back(b); + } + + std::string to_string() const { + return std::string(reinterpret_cast(this->data()), this->size()); + } +}; + +/** + * @brief Implements Telegram Buffer as a wrapper over BytesArray + * + * It aggregates received bytes and return only well filled frames, separated by telegram delimerer byte. +*/ +class TelegramBuffer { + static const std::size_t DEFAULT_SIZE_HINT = 1024; + + public: + TelegramBuffer(std::size_t sizeHint = DEFAULT_SIZE_HINT) + : m_rawBuffer(sizeHint) {} + ~TelegramBuffer() = default; + + void clear() { m_rawBuffer.clear(); } + + void append(const ByteArray&); + std::vector takeFrames(); + + const ByteArray& data() const { return m_rawBuffer; } + + private: + ByteArray m_rawBuffer; +}; + +} // namespace comm + +#endif // TELEGRAMBUFFER_H diff --git a/vpr/src/server/telegramoptions.h b/vpr/src/server/telegramoptions.h new file mode 100644 index 00000000000..67d9b5e12ec --- /dev/null +++ b/vpr/src/server/telegramoptions.h @@ -0,0 +1,160 @@ +#ifndef TELEGRAMOPTIONS_H +#define TELEGRAMOPTIONS_H + +#include + +#include +#include +#include +#include +#include + +namespace server { + +/** + * @brief Option class Parser + * + * Parse the string of options in the format "TYPE:KEY1:VALUE1;TYPE:KEY2:VALUE2", + * for example "int:path_num:11;string:path_type:debug;int:details_level:3;bool:is_flat_routing:0". + * It provides a simple interface to check value presence and access them. +*/ + +class TelegramOptions { + private: + enum { + INDEX_TYPE = 0, + INDEX_NAME, + INDEX_VALUE, + TOTAL_INDEXES_NUM + }; + + struct Option { + std::string type; + std::string value; + }; + + public: + TelegramOptions(const std::string& data, const std::vector& expectedKeys) { + // parse data string + std::vector options = splitString(data, ';'); + for (const std::string& optionStr : options) { + std::vector fragments = splitString(optionStr, ':'); + if (fragments.size() == TOTAL_INDEXES_NUM) { + std::string name = fragments[INDEX_NAME]; + Option option{fragments[INDEX_TYPE], fragments[INDEX_VALUE]}; + if (isDataTypeSupported(option.type)) { + m_options[name] = option; + } else { + m_errors.emplace_back("bad type for option [" + optionStr + "]"); + } + } else { + m_errors.emplace_back("bad option [" + optionStr + "]"); + } + } + + // check keys presense + checkKeysPresence(expectedKeys); + } + + ~TelegramOptions() {} + + bool hasErrors() const { return !m_errors.empty(); } + + std::map> getMapOfSets(const std::string& name) { + std::map> result; + std::string dataStr = getString(name); + if (!dataStr.empty()) { + std::vector pathes = splitString(dataStr, '|'); + for (const std::string& path : pathes) { + std::vector pathStruct = splitString(path, '#'); + if (pathStruct.size() == 2) { + std::string pathIndexStr = pathStruct[0]; + std::string pathElementIndexesStr = pathStruct[1]; + std::vector pathElementIndexes = splitString(pathElementIndexesStr, ','); + std::set elements; + for (const std::string& pathElementIndex : pathElementIndexes) { + if (std::optional optValue = tryConvertToInt(pathElementIndex.c_str())) { + elements.insert(optValue.value()); + } + } + if (std::optional optPathIndex = tryConvertToInt(pathIndexStr.c_str())) { + result[optPathIndex.value()] = elements; + } + } else { + m_errors.emplace_back("wrong path data structure = " + path); + } + } + } + return result; + } + + std::string getString(const std::string& name) { + std::string result; + if (auto it = m_options.find(name); it != m_options.end()) { + result = it->second.value; + } + return result; + } + + int getInt(const std::string& name, int failValue) { + if (std::optional opt = tryConvertToInt(m_options[name].value)) { + return opt.value(); + } else { + m_errors.emplace_back("cannot get int value for option " + name); + return failValue; + } + } + + bool getBool(const std::string& name, bool failValue) { + if (std::optional opt = tryConvertToInt(m_options[name].value)) { + return opt.value(); + } else { + m_errors.emplace_back("cannot get bool value for option " + name); + return failValue; + } + } + + std::string errorsStr() const { + std::string result; + for (const std::string& error : m_errors) { + result += error + ";"; + } + return result; + } + + private: + std::unordered_map m_options; + std::vector m_errors; + + std::vector splitString(const std::string& input, char delimiter) { + std::vector tokens; + std::istringstream tokenStream(input); + std::string token; + + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + + return tokens; + } + + bool isDataTypeSupported(const std::string& type) { + static std::set supportedTypes{"int", "string", "bool"}; + return supportedTypes.count(type) != 0; + } + + bool checkKeysPresence(const std::vector& keys) { + bool result = true; + for (const std::string& key : keys) { + if (m_options.find(key) == m_options.end()) { + m_errors.emplace_back("cannot find required option " + key); + result = false; + } + } + return result; + } +}; + +} // namespace server + +#endif // TELEGRAMOPTIONS_H diff --git a/vpr/src/server/telegramparser.cpp b/vpr/src/server/telegramparser.cpp new file mode 100644 index 00000000000..57e9c6e0d13 --- /dev/null +++ b/vpr/src/server/telegramparser.cpp @@ -0,0 +1,71 @@ +#include "telegramparser.h" +#include "convertutils.h" +#include "commconstants.h" + +namespace comm { + +std::optional TelegramParser::tryExtractJsonValueStr(const std::string& jsonString, const std::string& key) { + std::optional result; + + // Find the position of the key + size_t keyPos = jsonString.find("\"" + key + "\":"); + + if (keyPos == std::string::npos) { + // Key not found + return result; + } + + // Find the position of the value after the key + size_t valuePosStart = jsonString.find("\"", keyPos + key.length() + std::string("\":\"").size()); + + if (valuePosStart == std::string::npos) { + // Value not found + return result; + } + + // Find the position of the closing quote for the value + size_t valueEnd = jsonString.find("\"", valuePosStart + std::string("\"").size()); + + if (valueEnd == std::string::npos) { + // Closing quote not found + return result; + } + + // Extract the value substring + result = jsonString.substr(valuePosStart + 1, (valueEnd - valuePosStart) - 1); + return result; +} + +std::optional TelegramParser::tryExtractFieldJobId(const std::string& message) { + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_JOB_ID)) { + result = tryConvertToInt(strOpt.value()); + } + return result; +} + +std::optional TelegramParser::tryExtractFieldCmd(const std::string& message) { + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_CMD)) { + result = tryConvertToInt(strOpt.value()); + } + return result; +} + +std::optional TelegramParser::tryExtractFieldOptions(const std::string& message) { + return tryExtractJsonValueStr(message, comm::KEY_OPTIONS); +} + +std::optional TelegramParser::tryExtractFieldData(const std::string& message) { + return tryExtractJsonValueStr(message, comm::KEY_DATA); +} + +std::optional TelegramParser::tryExtractFieldStatus(const std::string& message) { + std::optional result; + if (std::optional strOpt = tryExtractJsonValueStr(message, comm::KEY_STATUS)) { + result = tryConvertToInt(strOpt.value()); + } + return result; +} + +} // namespace comm diff --git a/vpr/src/server/telegramparser.h b/vpr/src/server/telegramparser.h new file mode 100644 index 00000000000..0f1d0114abb --- /dev/null +++ b/vpr/src/server/telegramparser.h @@ -0,0 +1,29 @@ +#ifndef TELEGRAMPARSER_H +#define TELEGRAMPARSER_H + +#include +#include + +namespace comm { + +/** + * @brief Dummy JSON parser using regular expressions. + * + * This module provides helper methods to extract values for a keys as "JOB_ID", "CMD", or "OPTIONS" + * from a JSON schema structured as follows: {JOB_ID:num, CMD:enum, OPTIONS:string}. + */ +class TelegramParser { + public: + static std::optional tryExtractFieldJobId(const std::string& message); + static std::optional tryExtractFieldCmd(const std::string& message); + static std::optional tryExtractFieldOptions(const std::string& message); + static std::optional tryExtractFieldData(const std::string& message); + static std::optional tryExtractFieldStatus(const std::string& message); + + private: + static std::optional tryExtractJsonValueStr(const std::string& jsonString, const std::string& key); +}; + +} // namespace comm + +#endif // TELEGRAMPARSER_H diff --git a/vpr/src/tileable_rr_graph/chan_node_details.cpp b/vpr/src/tileable_rr_graph/chan_node_details.cpp new file mode 100644 index 00000000000..ca8c6915734 --- /dev/null +++ b/vpr/src/tileable_rr_graph/chan_node_details.cpp @@ -0,0 +1,291 @@ +/************************************************************************ + * This file contains member functions for class ChanNodeDetails + ***********************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +#include "chan_node_details.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +ChanNodeDetails::ChanNodeDetails(const ChanNodeDetails& src) { + /* duplicate */ + size_t chan_width = src.get_chan_width(); + this->reserve(chan_width); + for (size_t itrack = 0; itrack < chan_width; ++itrack) { + track_node_ids_.push_back(src.get_track_node_id(itrack)); + track_direction_.push_back(src.get_track_direction(itrack)); + seg_ids_.push_back(src.get_track_segment_id(itrack)); + seg_length_.push_back(src.get_track_segment_length(itrack)); + track_start_.push_back(src.is_track_start(itrack)); + track_end_.push_back(src.is_track_end(itrack)); + } +} + +ChanNodeDetails::ChanNodeDetails() { + this->clear(); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +size_t ChanNodeDetails::get_chan_width() const { + VTR_ASSERT(validate_chan_width()); + return track_node_ids_.size(); +} + +size_t ChanNodeDetails::get_track_node_id(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_node_ids_[track_id]; +} + +/* Return a copy of vector */ +std::vector ChanNodeDetails::get_track_node_ids() const { + std::vector copy; + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + copy.push_back(track_node_ids_[inode]); + } + return copy; +} + +Direction ChanNodeDetails::get_track_direction(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_direction_[track_id]; +} + +size_t ChanNodeDetails::get_track_segment_length(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return seg_length_[track_id]; +} + +size_t ChanNodeDetails::get_track_segment_id(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return seg_ids_[track_id]; +} + +bool ChanNodeDetails::is_track_start(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_start_[track_id]; +} + +bool ChanNodeDetails::is_track_end(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_end_[track_id]; +} + +/* Track_id is the starting point of group (whose is_start should be true) + * This function will try to find the track_ids with the same directionality as track_id and seg_length + * A group size is the number of such nodes between the starting points (include the 1st starting point) + */ +std::vector ChanNodeDetails::get_seg_group(const size_t& track_id) const { + VTR_ASSERT(validate_chan_width()); + VTR_ASSERT(validate_track_id(track_id)); + VTR_ASSERT(is_track_start(track_id)); + + std::vector group; + /* Make sure a clean start */ + group.clear(); + + for (size_t itrack = track_id; itrack < get_chan_width(); ++itrack) { + if ((get_track_direction(itrack) != get_track_direction(track_id)) + || (get_track_segment_id(itrack) != get_track_segment_id(track_id))) { + /* Bypass any nodes in different direction and segment information*/ + continue; + } + if ((false == is_track_start(itrack)) + || ((true == is_track_start(itrack)) && (itrack == track_id))) { + group.push_back(itrack); + continue; + } + /* Stop if this another starting point */ + if (true == is_track_start(itrack)) { + break; + } + } + return group; +} + +/* Get a list of track_ids with the given list of track indices */ +std::vector ChanNodeDetails::get_seg_group_node_id(const std::vector& seg_group) const { + std::vector group; + /* Make sure a clean start */ + group.clear(); + + for (size_t id = 0; id < seg_group.size(); ++id) { + VTR_ASSERT(validate_track_id(seg_group[id])); + group.push_back(get_track_node_id(seg_group[id])); + } + + return group; +} + +/* Get the number of tracks that starts in this routing channel */ +size_t ChanNodeDetails::get_num_starting_tracks(const Direction& track_direction) const { + size_t counter = 0; + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass unmatched track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + if (false == is_track_start(itrack)) { + continue; + } + counter++; + } + return counter; +} + +/* Get the number of tracks that ends in this routing channel */ +size_t ChanNodeDetails::get_num_ending_tracks(const Direction& track_direction) const { + size_t counter = 0; + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass unmatched track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + if (false == is_track_end(itrack)) { + continue; + } + counter++; + } + return counter; +} + +/************************************************************************ + * Mutators + ***********************************************************************/ +/* Reserve the capacitcy of vectors */ +void ChanNodeDetails::reserve(const size_t& chan_width) { + track_node_ids_.reserve(chan_width); + track_direction_.reserve(chan_width); + seg_length_.reserve(chan_width); + seg_ids_.reserve(chan_width); + track_start_.reserve(chan_width); + track_end_.reserve(chan_width); +} + +/* Add a track to the channel */ +void ChanNodeDetails::add_track(const size_t& track_node_id, const Direction& track_direction, const size_t& seg_id, const size_t& seg_length, const size_t& is_start, const size_t& is_end) { + track_node_ids_.push_back(track_node_id); + track_direction_.push_back(track_direction); + seg_ids_.push_back(seg_id); + seg_length_.push_back(seg_length); + track_start_.push_back(is_start); + track_end_.push_back(is_end); +} + +/* Update the node_id of a given track */ +void ChanNodeDetails::set_track_node_id(const size_t& track_index, const size_t& track_node_id) { + VTR_ASSERT(validate_track_id(track_index)); + track_node_ids_[track_index] = track_node_id; +} + +/* Update the node_ids from a vector */ +void ChanNodeDetails::set_track_node_ids(const std::vector& track_node_ids) { + /* the size of vector should match chan_width */ + VTR_ASSERT(get_chan_width() == track_node_ids.size()); + for (size_t inode = 0; inode < track_node_ids.size(); ++inode) { + track_node_ids_[inode] = track_node_ids[inode]; + } +} + +/* Set tracks with a given direction to start */ +void ChanNodeDetails::set_tracks_start(const Direction& track_direction) { + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Bypass non-match tracks */ + if (track_direction != get_track_direction(inode)) { + continue; /* Pass condition*/ + } + track_start_[inode] = true; + } +} + +/* Set tracks with a given direction to end */ +void ChanNodeDetails::set_tracks_end(const Direction& track_direction) { + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Bypass non-match tracks */ + if (track_direction != get_track_direction(inode)) { + continue; /* Pass condition*/ + } + track_end_[inode] = true; + } +} + +/* rotate the track_node_id by an offset */ +void ChanNodeDetails::rotate_track_node_id(const size_t& offset, const Direction& track_direction, const bool& counter_rotate) { + /* Direct return if offset = 0*/ + if (0 == offset) { + return; + } + + /* Rotate the node_ids by groups + * A group begins from a track_start and ends before another track_start + */ + VTR_ASSERT(validate_chan_width()); + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass non-start segment */ + if (false == is_track_start(itrack)) { + continue; + } + /* Bypass segments do not match track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + /* Find the group nodes */ + std::vector track_group = get_seg_group(itrack); + /* Build a vector of the node ids of the tracks */ + std::vector track_group_node_id = get_seg_group_node_id(track_group); + /* adapt offset to the range of track_group_node_id */ + size_t actual_offset = offset % track_group_node_id.size(); + /* Rotate or Counter rotate */ + if (true == counter_rotate) { + std::rotate(track_group_node_id.rbegin(), track_group_node_id.rbegin() + actual_offset, track_group_node_id.rend()); + } else { + std::rotate(track_group_node_id.begin(), track_group_node_id.begin() + actual_offset, track_group_node_id.end()); + } + /* Update the node_ids */ + for (size_t inode = 0; inode < track_group.size(); ++inode) { + track_node_ids_[track_group[inode]] = track_group_node_id[inode]; + } + } + return; +} + +void ChanNodeDetails::clear() { + track_node_ids_.clear(); + track_direction_.clear(); + seg_ids_.clear(); + seg_length_.clear(); + track_start_.clear(); + track_end_.clear(); +} + +/************************************************************************ + * Validators + ***********************************************************************/ +bool ChanNodeDetails::validate_chan_width() const { + size_t chan_width = track_node_ids_.size(); + if ((chan_width == track_direction_.size()) + && (chan_width == seg_ids_.size()) + && (chan_width == seg_length_.size()) + && (chan_width == track_start_.size()) + && (chan_width == track_end_.size())) { + return true; + } + return false; +} + +bool ChanNodeDetails::validate_track_id(const size_t& track_id) const { + if ((track_id < track_node_ids_.size()) + && (track_id < track_direction_.size()) + && (track_id < seg_ids_.size()) + && (track_id < seg_length_.size()) + && (track_id < track_start_.size()) + && (track_id < track_end_.size())) { + return true; + } + return false; +} diff --git a/vpr/src/tileable_rr_graph/chan_node_details.h b/vpr/src/tileable_rr_graph/chan_node_details.h new file mode 100644 index 00000000000..e322b5c6d73 --- /dev/null +++ b/vpr/src/tileable_rr_graph/chan_node_details.h @@ -0,0 +1,73 @@ +#ifndef CHAN_NODE_DETAILS_H +#define CHAN_NODE_DETAILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "vpr_types.h" +#include "rr_node_types.h" +#include "rr_graph_types.h" + +/************************************************************************ + * This file contains a class to model the details of routing node + * in a channel: + * 1. segment information: length, frequency etc. + * 2. starting point of segment + * 3. ending point of segment + * 4. potentail track_id(ptc_num) of each segment + ***********************************************************************/ + +/************************************************************************ + * ChanNodeDetails records segment length, directionality and starting of routing tracks + * +---------------------------------+ + * | Index | Direction | Start Point | + * +---------------------------------+ + * | 0 | --------> | Yes | + * +---------------------------------+ + ***********************************************************************/ + +class ChanNodeDetails { + public: /* Constructor */ + ChanNodeDetails(const ChanNodeDetails&); /* Duplication */ + ChanNodeDetails(); /* Initilization */ + public: /* Accessors */ + size_t get_chan_width() const; + size_t get_track_node_id(const size_t& track_id) const; + std::vector get_track_node_ids() const; + Direction get_track_direction(const size_t& track_id) const; + size_t get_track_segment_length(const size_t& track_id) const; + size_t get_track_segment_id(const size_t& track_id) const; + bool is_track_start(const size_t& track_id) const; + bool is_track_end(const size_t& track_id) const; + std::vector get_seg_group(const size_t& track_id) const; + std::vector get_seg_group_node_id(const std::vector& seg_group) const; + size_t get_num_starting_tracks(const Direction& track_direction) const; + size_t get_num_ending_tracks(const Direction& track_direction) const; + + public: /* Mutators */ + void reserve(const size_t& chan_width); /* Reserve the capacitcy of vectors */ + void add_track(const size_t& track_node_id, const Direction& track_direction, const size_t& seg_id, const size_t& seg_length, const size_t& is_start, const size_t& is_end); + void set_track_node_id(const size_t& track_index, const size_t& track_node_id); + void set_track_node_ids(const std::vector& track_node_ids); + void set_tracks_start(const Direction& track_direction); + void set_tracks_end(const Direction& track_direction); + void rotate_track_node_id(const size_t& offset, + const Direction& track_direction, + const bool& counter_rotate); /* rotate the track_node_id by an offset */ + void clear(); + + private: /* validators */ + bool validate_chan_width() const; + bool validate_track_id(const size_t& track_id) const; + + private: /* Internal data */ + std::vector track_node_ids_; /* indices of each track */ + std::vector track_direction_; /* direction of each track */ + std::vector seg_ids_; /* id of segment of each track */ + std::vector seg_length_; /* Length of each segment */ + std::vector track_start_; /* flag to identify if this is the starting point of the track */ + std::vector track_end_; /* flag to identify if this is the ending point of the track */ +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/clb2clb_directs.h b/vpr/src/tileable_rr_graph/clb2clb_directs.h new file mode 100644 index 00000000000..7cc54bb8813 --- /dev/null +++ b/vpr/src/tileable_rr_graph/clb2clb_directs.h @@ -0,0 +1,16 @@ +#ifndef CLB2CLB_DIRECTS_H +#define CLB2CLB_DIRECTS_H + +#include "physical_types.h" + +struct t_clb_to_clb_directs { + t_physical_tile_type_ptr from_clb_type; + int from_clb_pin_start_index; + int from_clb_pin_end_index; + t_physical_tile_type_ptr to_clb_type; + int to_clb_pin_start_index; + int to_clb_pin_end_index; + int switch_index; //The switch type used by this direct connection +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/device_grid_annotation.cpp b/vpr/src/tileable_rr_graph/device_grid_annotation.cpp new file mode 100644 index 00000000000..e389af027ed --- /dev/null +++ b/vpr/src/tileable_rr_graph/device_grid_annotation.cpp @@ -0,0 +1,94 @@ +#include "device_grid_annotation.h" +#include "vtr_log.h" +#include "vpr_utils.h" + +DeviceGridAnnotation::DeviceGridAnnotation(const DeviceGrid& grid) { + alloc(grid); + init(grid); +} + +void DeviceGridAnnotation::alloc(const DeviceGrid& grid) { + /* Allocate */ + chanx_existence_.resize({grid.width(), grid.height()}, false); + chany_existence_.resize({grid.width(), grid.height()}, false); +} + +void DeviceGridAnnotation::init(const DeviceGrid& grid) { + /* If shrink is not considered, perimeters are the borderlines */ + for (size_t iy = 0; iy < grid.height() - 1; ++iy) { + for (size_t ix = 1; ix < grid.width() - 1; ++ix) { + chanx_existence_[ix][iy] = !is_empty_type(grid.get_physical_type({(int)ix, (int)iy + 1, 0})); + } + } + for (size_t ix = 0; ix < grid.width() - 1; ++ix) { + for (size_t iy = 1; iy < grid.height() - 1; ++iy) { + chany_existence_[ix][iy] = !is_empty_type(grid.get_physical_type({(int)ix, (int)iy, 0})); + } + } +} + +bool DeviceGridAnnotation::is_chanx_exist(const vtr::Point& coord) const { + return chanx_existence_[coord.x()][coord.y()]; +} + +bool DeviceGridAnnotation::is_chany_exist(const vtr::Point& coord) const { + return chany_existence_[coord.x()][coord.y()]; +} + +bool DeviceGridAnnotation::is_chanx_start(const vtr::Point& coord) const { + vtr::Point neighbor_coord = get_neighbor_coord(coord, LEFT); + if (neighbor_coord == coord) { + return true; + } + return chanx_existence_[coord.x()][coord.y()] != chanx_existence_[neighbor_coord.x()][neighbor_coord.y()]; +} + +bool DeviceGridAnnotation::is_chanx_end(const vtr::Point& coord) const { + vtr::Point neighbor_coord = get_neighbor_coord(coord, RIGHT); + if (neighbor_coord == coord) { + return true; + } + return chanx_existence_[coord.x()][coord.y()] != chanx_existence_[neighbor_coord.x()][neighbor_coord.y()]; +} + +bool DeviceGridAnnotation::is_chany_start(const vtr::Point& coord) const { + vtr::Point neighbor_coord = get_neighbor_coord(coord, BOTTOM); + if (neighbor_coord == coord) { + return true; + } + return chany_existence_[coord.x()][coord.y()] != chany_existence_[neighbor_coord.x()][neighbor_coord.y()]; +} + +bool DeviceGridAnnotation::is_chany_end(const vtr::Point& coord) const { + vtr::Point neighbor_coord = get_neighbor_coord(coord, TOP); + if (neighbor_coord == coord) { + return true; + } + return chany_existence_[coord.x()][coord.y()] != chany_existence_[neighbor_coord.x()][neighbor_coord.y()]; +} + +vtr::Point DeviceGridAnnotation::get_neighbor_coord(const vtr::Point& coord, const e_side& side) const { + vtr::Point neighbor_coord(coord.x(), coord.y()); + if (side == LEFT) { + if (coord.x() == 0) { + return coord; + } + neighbor_coord.set_x(coord.x() - 1); + } else if (side == RIGHT) { + if (coord.x() == chanx_existence_.dim_size(0) - 1) { + return coord; + } + neighbor_coord.set_x(coord.x() + 1); + } else if (side == TOP) { + if (coord.y() == chanx_existence_.dim_size(1) - 1) { + return coord; + } + neighbor_coord.set_y(coord.y() + 1); + } else if (side == BOTTOM) { + if (coord.y() == 0) { + return coord; + } + neighbor_coord.set_y(coord.y() - 1); + } + return neighbor_coord; +} diff --git a/vpr/src/tileable_rr_graph/device_grid_annotation.h b/vpr/src/tileable_rr_graph/device_grid_annotation.h new file mode 100644 index 00000000000..377c850909b --- /dev/null +++ b/vpr/src/tileable_rr_graph/device_grid_annotation.h @@ -0,0 +1,42 @@ +#ifndef DEVICE_GRID_ANNOTATION_H +#define DEVICE_GRID_ANNOTATION_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include +#include "vtr_geometry.h" +#include "vtr_ndmatrix.h" +#include "physical_types.h" +#include "device_grid.h" + +/** @brief This is the data structure to provide additional data for the device grid: + * - Border of the device grid (check where the empty types cover the perimeters) + */ +class DeviceGridAnnotation { + public: /* Constructor */ + DeviceGridAnnotation(const DeviceGrid& grid); + + private: /* Private mutators */ + void alloc(const DeviceGrid& grid); + void init(const DeviceGrid& grid); + + public: /* Public accessors */ + /** @brief Check if at a given coordinate, a X-direction routing channel should exist or not */ + bool is_chanx_exist(const vtr::Point& coord) const; + bool is_chanx_start(const vtr::Point& coord) const; + bool is_chanx_end(const vtr::Point& coord) const; + /** @brief Check if at a given coordinate, a Y-direction routing channel should exist or not */ + bool is_chany_exist(const vtr::Point& coord) const; + bool is_chany_start(const vtr::Point& coord) const; + bool is_chany_end(const vtr::Point& coord) const; + + private: /* Private validators */ + vtr::Point get_neighbor_coord(const vtr::Point& coord, const e_side& side) const; + + private: /* Internal data */ + vtr::NdMatrix chanx_existence_; + vtr::NdMatrix chany_existence_; +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.cpp b/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.cpp new file mode 100644 index 00000000000..ea3b52a5440 --- /dev/null +++ b/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.cpp @@ -0,0 +1,201 @@ +/******************************************************************** + * This file includes most utilized functions for the rr_graph + * data structure in the OpenFPGA context + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "openfpga_rr_graph_utils.h" +#include "rr_graph_types.h" +#include "rr_graph_view.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Get the coordinator of a starting point of a routing track + * For routing tracks in INC_DIRECTION + * (xlow, ylow) should be the starting point + * + * For routing tracks in DEC_DIRECTION + * (xhigh, yhigh) should be the starting point + ***********************************************************************/ +vtr::Point get_track_rr_node_start_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((CHANX == rr_graph.node_type(track_rr_node)) + || (CHANY == rr_graph.node_type(track_rr_node))); + + vtr::Point start_coordinator; + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + start_coordinator.set(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + start_coordinator.set(rr_graph.node_xhigh(track_rr_node), rr_graph.node_yhigh(track_rr_node)); + } + + return start_coordinator; +} + +/************************************************************************ + * Get the coordinator of a end point of a routing track + * For routing tracks in INC_DIRECTION + * (xhigh, yhigh) should be the starting point + * + * For routing tracks in DEC_DIRECTION + * (xlow, ylow) should be the starting point + ***********************************************************************/ +vtr::Point get_track_rr_node_end_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((CHANX == rr_graph.node_type(track_rr_node)) + || (CHANY == rr_graph.node_type(track_rr_node))); + + vtr::Point end_coordinator; + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + end_coordinator.set(rr_graph.node_xhigh(track_rr_node), rr_graph.node_yhigh(track_rr_node)); + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + end_coordinator.set(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + } + + return end_coordinator; +} + +/************************************************************************ + * Find the driver switches for a node in the rr_graph + * This function only return unique driver switches + ***********************************************************************/ +std::vector get_rr_graph_driver_switches(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_switches; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + if (driver_switches.end() == std::find(driver_switches.begin(), driver_switches.end(), rr_graph.edge_switch(edge))) { + driver_switches.push_back(rr_graph.edge_switch(edge)); + } + } + + return driver_switches; +} + +/************************************************************************ + * Find the driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Find the configurable driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + /* Bypass non-configurable edges */ + if (false == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Find the configurable driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + /* Bypass configurable edges */ + if (true == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Check if an OPIN of a rr_graph is directly driving an IPIN + * To meet this requirement, the OPIN must: + * - Have only 1 fan-out + * - The only fan-out is an IPIN + ***********************************************************************/ +bool is_opin_direct_connected_ipin(const RRGraphView& rr_graph, + const RRNodeId& node) { + /* We only accept OPIN */ + VTR_ASSERT(OPIN == rr_graph.node_type(node)); + + if (1 != rr_graph.node_out_edges(node).size()) { + return false; + } + + VTR_ASSERT(1 == rr_graph.node_out_edges(node).size()); + for (auto edge : rr_graph.node_out_edges(node)) { + const RRNodeId& sink_node = rr_graph.edge_sink_node(node, edge); + if (IPIN != rr_graph.node_type(sink_node)) { + return false; + } + } + + return true; +} + +/************************************************************************ + * Check if an IPIN of a rr_graph is directly connected to an OPIN + * To meet this requirement, the IPIN must: + * - Have only 1 fan-in + * - The only fan-in is an OPIN + ***********************************************************************/ +bool is_ipin_direct_connected_opin(const RRGraphView& rr_graph, + const RRNodeId& node) { + /* We only accept IPIN */ + VTR_ASSERT(IPIN == rr_graph.node_type(node)); + + if (1 != rr_graph.node_in_edges(node).size()) { + return false; + } + + VTR_ASSERT(1 == rr_graph.node_in_edges(node).size()); + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + if (OPIN != rr_graph.node_type(src_node)) { + return false; + } + } + + return true; +} + +/** @brief Get a side of a given node in a routing resource graph. + * Note that this function expect one valid side to be got. Otherwise, it will fail! */ +e_side get_rr_graph_single_node_side(const RRGraphView& rr_graph, + const RRNodeId& node) { + e_side node_side = NUM_SIDES; + int num_sides = 0; + for (e_side candidate_side : SIDES) { + if (rr_graph.is_node_on_specific_side(node, candidate_side)) { + node_side = candidate_side; + num_sides++; + } + } + VTR_ASSERT(1 == num_sides && node_side != NUM_SIDES); + return node_side; +} diff --git a/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.h b/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.h new file mode 100644 index 00000000000..cba64125615 --- /dev/null +++ b/vpr/src/tileable_rr_graph/openfpga_rr_graph_utils.h @@ -0,0 +1,45 @@ +#ifndef OPENFPGA_RR_GRAPH_UTILS_H +#define OPENFPGA_RR_GRAPH_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from vpr library */ +#include "rr_graph_obj.h" +#include "rr_graph_view.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +vtr::Point get_track_rr_node_start_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node); + +vtr::Point get_track_rr_node_end_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node); + +std::vector get_rr_graph_driver_switches(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +bool is_opin_direct_connected_ipin(const RRGraphView& rr_graph, + const RRNodeId& node); + +bool is_ipin_direct_connected_opin(const RRGraphView& rr_graph, + const RRNodeId& node); + +e_side get_rr_graph_single_node_side(const RRGraphView& rr_graph, + const RRNodeId& node); + +#endif diff --git a/vpr/src/tileable_rr_graph/openfpga_side_manager.cpp b/vpr/src/tileable_rr_graph/openfpga_side_manager.cpp new file mode 100644 index 00000000000..3b1ee1ec404 --- /dev/null +++ b/vpr/src/tileable_rr_graph/openfpga_side_manager.cpp @@ -0,0 +1,170 @@ +/******************************************************************** + * Memeber function for class SideManagerManager + *******************************************************************/ +#include "openfpga_side_manager.h" + +/* Constructors */ +SideManager::SideManager(enum e_side side) { + side_ = side; +} + +SideManager::SideManager() { + side_ = NUM_SIDES; +} + +SideManager::SideManager(size_t side) { + set_side(side); +} + +/* Public Accessors */ +enum e_side SideManager::get_side() const { + return side_; +} + +enum e_side SideManager::get_opposite() const { + switch (side_) { + case TOP: + return BOTTOM; + case RIGHT: + return LEFT; + case BOTTOM: + return TOP; + case LEFT: + return RIGHT; + default: + return NUM_SIDES; + } +} + +enum e_side SideManager::get_rotate_clockwise() const { + switch (side_) { + case TOP: + return RIGHT; + case RIGHT: + return BOTTOM; + case BOTTOM: + return LEFT; + case LEFT: + return TOP; + default: + return NUM_SIDES; + } +} + +enum e_side SideManager::get_rotate_counterclockwise() const { + switch (side_) { + case TOP: + return LEFT; + case RIGHT: + return TOP; + case BOTTOM: + return RIGHT; + case LEFT: + return BOTTOM; + default: + return NUM_SIDES; + } +} + +bool SideManager::validate() const { + if (NUM_SIDES == side_) { + return false; + } + return true; +} + +size_t SideManager::to_size_t() const { + switch (side_) { + case TOP: + return 0; + case RIGHT: + return 1; + case BOTTOM: + return 2; + case LEFT: + return 3; + default: + return 4; + } +} + +/* Convert to char* */ +const char* SideManager::c_str() const { + switch (side_) { + case TOP: + return "top"; + case RIGHT: + return "right"; + case BOTTOM: + return "bottom"; + case LEFT: + return "left"; + default: + return "invalid_side"; + } +} + +/* Convert to char* */ +std::string SideManager::to_string() const { + std::string ret; + switch (side_) { + case TOP: + ret.assign("top"); + break; + case RIGHT: + ret.assign("right"); + break; + case BOTTOM: + ret.assign("bottom"); + break; + case LEFT: + ret.assign("left"); + break; + default: + ret.assign("invalid_side"); + break; + } + + return ret; +} + +/* Public Mutators */ +void SideManager::set_side(size_t side) { + switch (side) { + case 0: + side_ = TOP; + return; + case 1: + side_ = RIGHT; + return; + case 2: + side_ = BOTTOM; + return; + case 3: + side_ = LEFT; + return; + default: + side_ = NUM_SIDES; + return; + } +} + +void SideManager::set_side(enum e_side side) { + side_ = side; + return; +} + +void SideManager::set_opposite() { + side_ = get_opposite(); + return; +} + +void SideManager::rotate_clockwise() { + side_ = get_rotate_clockwise(); + return; +} + +void SideManager::rotate_counterclockwise() { + side_ = get_rotate_counterclockwise(); + return; +} diff --git a/vpr/src/tileable_rr_graph/openfpga_side_manager.h b/vpr/src/tileable_rr_graph/openfpga_side_manager.h new file mode 100644 index 00000000000..a6eb265cd64 --- /dev/null +++ b/vpr/src/tileable_rr_graph/openfpga_side_manager.h @@ -0,0 +1,47 @@ +#ifndef OPENFPGA_SIDE_MANAGER_H +#define OPENFPGA_SIDE_MANAGER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +/* Header files form archfpga library */ +#include "physical_types.h" + +/******************************************************************** + * Define a class for the sides of a physical block in FPGA architecture + * Basically, each block has four sides : + * TOP, RIGHT, BOTTOM, LEFT + * This class aims to provide a easy proctol for manipulating a side + ********************************************************************/ + +class SideManager { + public: /* Constructor */ + SideManager(enum e_side side); + SideManager(); + SideManager(size_t side); + + public: /* Accessors */ + enum e_side get_side() const; + enum e_side get_opposite() const; + enum e_side get_rotate_clockwise() const; + enum e_side get_rotate_counterclockwise() const; + bool validate() const; + size_t to_size_t() const; + const char* c_str() const; + std::string to_string() const; + + public: /* Mutators */ + void set_side(size_t side); + void set_side(enum e_side side); + void set_opposite(); + void rotate_clockwise(); + void rotate_counterclockwise(); + + private: /* internal data */ + enum e_side side_; +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_chan.cpp b/vpr/src/tileable_rr_graph/rr_chan.cpp new file mode 100644 index 00000000000..c6a991ea524 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_chan.cpp @@ -0,0 +1,190 @@ +/************************************************************************ + * Member functions for class RRChan + ***********************************************************************/ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "rr_chan.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +/* default constructor */ +RRChan::RRChan() { + type_ = NUM_RR_TYPES; + nodes_.resize(0); + node_segments_.resize(0); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +t_rr_type RRChan::get_type() const { + return type_; +} + +/* get the number of tracks in this channel */ +size_t RRChan::get_chan_width() const { + return nodes_.size(); +} + +/* get the track_id of a node */ +int RRChan::get_node_track_id(const RRNodeId& node) const { + /* if the given node is NULL, we return an invalid id */ + if (RRNodeId::INVALID() == node) { + return -1; + } + /* check each member and return if we find a match in content */ + std::vector::const_iterator it = std::find(nodes_.begin(), nodes_.end(), node); + if (nodes_.end() == it) { + return -1; + } + return it - nodes_.begin(); +} + +/* get the rr_node with the track_id */ +RRNodeId RRChan::get_node(const size_t& track_num) const { + if (false == valid_node_id(track_num)) { + return RRNodeId::INVALID(); + } + return nodes_[track_num]; +} + +/* get the segment id of a node */ +RRSegmentId RRChan::get_node_segment(const RRNodeId& node) const { + int node_id = get_node_track_id(node); + if (false == valid_node_id(node_id)) { + return RRSegmentId::INVALID(); + } + return get_node_segment(node_id); +} + +/* get the segment id of a node */ +RRSegmentId RRChan::get_node_segment(const size_t& track_num) const { + if (false == valid_node_id(track_num)) { + return RRSegmentId::INVALID(); + } + return node_segments_[track_num]; +} + +/* Get a list of segments used in this routing channel */ +std::vector RRChan::get_segment_ids() const { + std::vector seg_list; + + /* make sure a clean start */ + seg_list.clear(); + + /* Traverse node_segments */ + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + std::vector::iterator it; + /* Try to find the node_segment id in the list */ + it = find(seg_list.begin(), seg_list.end(), node_segments_[inode]); + if (it == seg_list.end()) { + /* Not found, add it to the list */ + seg_list.push_back(node_segments_[inode]); + } + } + + return seg_list; +} + +/* Get a list of nodes whose segment_id is specified */ +std::vector RRChan::get_node_ids_by_segment_ids(const RRSegmentId& seg_id) const { + std::vector node_list; + + /* make sure a clean start */ + node_list.clear(); + + /* Traverse node_segments */ + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Try to find the node_segment id in the list */ + if (seg_id == node_segments_[inode]) { + node_list.push_back(inode); + } + } + + return node_list; +} + +/************************************************************************ + * Mutators + ***********************************************************************/ +void RRChan::set(const RRChan& rr_chan) { + /* Ensure a clean start */ + this->clear(); + /* Assign type of this routing channel */ + this->type_ = rr_chan.get_type(); + /* Copy node and node_segments */ + this->nodes_.resize(rr_chan.get_chan_width()); + this->node_segments_.resize(rr_chan.get_chan_width()); + for (size_t inode = 0; inode < rr_chan.get_chan_width(); ++inode) { + this->nodes_[inode] = rr_chan.get_node(inode); + this->node_segments_[inode] = rr_chan.get_node_segment(inode); + } + return; +} + +/* modify type */ +void RRChan::set_type(const t_rr_type& type) { + VTR_ASSERT(valid_type(type)); + type_ = type; +} + +/* Reserve node list */ +void RRChan::reserve_node(const size_t& node_size) { + nodes_.reserve(node_size); /* reserve to the maximum */ + node_segments_.reserve(node_size); /* reserve to the maximum */ +} + +/* add a node to the array */ +void RRChan::add_node(const RRGraphView& rr_graph, const RRNodeId& node, const RRSegmentId& node_segment) { + /* fill the dedicated element in the vector */ + nodes_.push_back(node); + node_segments_.push_back(node_segment); + + if (NUM_RR_TYPES == type_) { + type_ = rr_graph.node_type(node); + } else { + VTR_ASSERT(type_ == rr_graph.node_type(node)); + } + + VTR_ASSERT(valid_node_type(rr_graph, node)); +} + +/* Clear content */ +void RRChan::clear() { + nodes_.clear(); + node_segments_.clear(); +} + +/************************************************************************ + * Internal validators + ***********************************************************************/ +/* for type, only valid type is CHANX and CHANY */ +bool RRChan::valid_type(const t_rr_type& type) const { + if ((CHANX == type) || (CHANY == type)) { + return true; + } + return false; +} + +/* Check each node, see if the node type is consistent with the type */ +bool RRChan::valid_node_type(const RRGraphView& rr_graph, const RRNodeId& node) const { + valid_type(rr_graph.node_type(node)); + if (NUM_RR_TYPES == type_) { + return true; + } + valid_type(type_); + if (type_ != rr_graph.node_type(node)) { + return false; + } + return true; +} + +/* check if the node id is valid */ +bool RRChan::valid_node_id(const size_t& node_id) const { + if (node_id < nodes_.size()) { + return true; + } + + return false; +} diff --git a/vpr/src/tileable_rr_graph/rr_chan.h b/vpr/src/tileable_rr_graph/rr_chan.h new file mode 100644 index 00000000000..5ba9bc09af5 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_chan.h @@ -0,0 +1,83 @@ +#ifndef RR_CHAN_H +#define RR_CHAN_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from vpr library */ +#include "rr_graph_view.h" + +/******************************************************************** + * RRChan object aim to describe a routing channel in a routing resource graph + * - What are the nodes in the RRGraph object, for each routing track + * - What are routing segments used by each node in the channel + * - What are the directions of each routing channel + * being either X-direction or Y-direction + * + * Note : + * - This is a collection of rr_nodes from the RRGraph Object + * It does not rebuild or contruct any connects between rr_nodes + * It is just an annotation on an existing RRGraph + * ------------- ------ + * | | | | + * | | | Y | + * | CLB | | Chan | + * | | | | + * | | | | + * ------------- ------ + * ------------- + * | X | + * | Channel | + * ------------- + *******************************************************************/ +class RRChan { + public: /* Constructors */ + RRChan(); + + public: /* Accessors */ + t_rr_type get_type() const; + size_t get_chan_width() const; /* get the number of tracks in this channel */ + int get_node_track_id(const RRNodeId& node) const; /* get the track_id of a node */ + RRNodeId get_node(const size_t& track_num) const; /* get the rr_node with the track_id */ + RRSegmentId get_node_segment(const RRNodeId& node) const; + RRSegmentId get_node_segment(const size_t& track_num) const; + std::vector get_segment_ids() const; /* Get a list of segments used in this routing channel */ + std::vector get_node_ids_by_segment_ids(const RRSegmentId& seg_id) const; /* Get a list of segments used in this routing channel */ + public: /* Mutators */ + /* copy */ + void set(const RRChan&); + + /* modify the type of routing channel */ + void set_type(const t_rr_type& type); + + /* reseve a number of nodes to the array */ + void reserve_node(const size_t& node_size); + + /* add a node to the routing channel */ + void add_node(const RRGraphView& rr_graph, const RRNodeId& node, const RRSegmentId& node_segment); + + /* clear the content */ + void clear(); + + private: /* internal functions */ + /* For the type of a routing channel, only valid type is CHANX and CHANY */ + bool valid_type(const t_rr_type& type) const; + + /* Check each node, see if the node type is consistent with the type of routing channel */ + bool valid_node_type(const RRGraphView& rr_graph, const RRNodeId& node) const; + + /* Validate if the track number in the range */ + bool valid_node_id(const size_t& node_id) const; + + private: /* Internal Data */ + t_rr_type type_; /* channel type: CHANX or CHANY */ + std::vector nodes_; /* rr nodes of each track in the channel */ + std::vector node_segments_; /* segment of each track */ +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp new file mode 100644 index 00000000000..a515e55f218 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.cpp @@ -0,0 +1,591 @@ +/************************************************************************ + * This file contains most utilized functions for rr_graph builders + ***********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "vpr_utils.h" + +#include "rr_graph_builder_utils.h" + +/************************************************************************ + * Correct number of routing channel width to be compatible to + * uni-directional routing architecture + ***********************************************************************/ +size_t find_unidir_routing_channel_width(const size_t& chan_width) { + size_t actual_chan_width = chan_width; + /* Correct the chan_width: it should be an even number */ + if (0 != actual_chan_width % 2) { + actual_chan_width++; /* increment it to be even */ + } + VTR_ASSERT(0 == actual_chan_width % 2); + + return actual_chan_width; +} + +/************************************************************************ + * Get the class index of a grid pin + ***********************************************************************/ +int get_grid_pin_class_index(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const int pin_index) { + /* check */ + t_physical_tile_loc tile_loc(x, y, layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + VTR_ASSERT(pin_index < phy_tile_type->num_pins); + return phy_tile_type->pin_class[pin_index]; +} + +/* Deteremine the side of a io grid */ +e_side determine_io_grid_pin_side(const vtr::Point& device_size, + const vtr::Point& grid_coordinate) { + /* TOP side IO of FPGA */ + if (device_size.y() == grid_coordinate.y()) { + return BOTTOM; /* Such I/O has only Bottom side pins */ + } else if (device_size.x() == grid_coordinate.x()) { /* RIGHT side IO of FPGA */ + return LEFT; /* Such I/O has only Left side pins */ + } else if (0 == grid_coordinate.y()) { /* BOTTOM side IO of FPGA */ + return TOP; /* Such I/O has only Top side pins */ + } else if (0 == grid_coordinate.x()) { /* LEFT side IO of FPGA */ + return RIGHT; /* Such I/O has only Right side pins */ + } else if ((grid_coordinate.x() < device_size.x()) && (grid_coordinate.y() < device_size.y())) { + /* I/O grid in the center grid */ + return NUM_SIDES; + } + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid coordinate (%lu, %lu) for I/O Grid whose size is (%lu, %lu)!\n", + grid_coordinate.x(), grid_coordinate.y(), + device_size.x(), device_size.y()); + exit(1); +} + +/* Deteremine the side of a pin of a grid */ +std::vector find_grid_pin_sides(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const size_t& pin_id) { + std::vector pin_sides; + + t_physical_tile_loc tile_loc(x, y, layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + int width_offset = grids.get_width_offset(tile_loc); + int height_offset = grids.get_height_offset(tile_loc); + for (const e_side& side : {TOP, RIGHT, BOTTOM, LEFT}) { + if (true == phy_tile_type->pinloc[width_offset][height_offset][size_t(side)][pin_id]) { + pin_sides.push_back(side); + } + } + + return pin_sides; +} + +/************************************************************************ + * Get a list of pin_index for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +std::vector get_grid_side_pins(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const e_pin_type& pin_type, + const e_side& pin_side, + const int& pin_width, + const int& pin_height) { + std::vector pin_list; + /* Make sure a clear start */ + pin_list.clear(); + + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(x, y, layer)); + for (int ipin = 0; ipin < phy_tile_type->num_pins; ++ipin) { + int class_id = phy_tile_type->pin_class[ipin]; + if ((1 == phy_tile_type->pinloc[pin_width][pin_height][pin_side][ipin]) + && (pin_type == phy_tile_type->class_inf[class_id].type)) { + pin_list.push_back(ipin); + } + } + return pin_list; +} + +/************************************************************************ + * Get the number of pins for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +size_t get_grid_num_pins(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const e_pin_type& pin_type, + const e_side& io_side) { + size_t num_pins = 0; + + /* For IO_TYPE sides */ + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(x, y, layer)); + for (const e_side& side : {TOP, RIGHT, BOTTOM, LEFT}) { + /* skip unwanted sides */ + if ((true == is_io_type(phy_tile_type)) + && (side != io_side) && (NUM_SIDES != io_side)) { + continue; + } + /* Get pin list */ + for (int width = 0; width < phy_tile_type->width; ++width) { + for (int height = 0; height < phy_tile_type->height; ++height) { + std::vector pin_list = get_grid_side_pins(grids, layer, x, y, pin_type, side, width, height); + num_pins += pin_list.size(); + } + } + } + + return num_pins; +} + +/************************************************************************ + * Get the number of pins for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +size_t get_grid_num_classes(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const e_pin_type& pin_type) { + size_t num_classes = 0; + + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(x, y, layer)); + for (size_t iclass = 0; iclass < phy_tile_type->class_inf.size(); ++iclass) { + /* Bypass unmatched pin_type */ + if (pin_type != phy_tile_type->class_inf[iclass].type) { + continue; + } + num_classes++; + } + + return num_classes; +} + +/************************************************************************ + * Idenfity if a X-direction routing channel exist in the fabric + * This could be entirely possible that a routig channel + * is in the middle of a multi-width and multi-height grid + * + * As the chanx always locates on top of a grid with the same coord + * + * +----------+ + * | CHANX | + * | [x][y] | + * +----------+ + * + * +----------+ + * | Grid | height_offset = height - 1 + * | [x][y] | + * +----------+ + * + * +----------+ + * | Grid | height_offset = height - 2 + * | [x][y-1] | + * +----------+ + * If the CHANX is in the middle of a multi-width and multi-height grid + * it should locate at a grid whose height_offset is lower than the its height defined in physical_tile + * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block + ***********************************************************************/ +bool is_chanx_exist(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chanx_coord, + const bool& through_channel) { + if ((1 > chanx_coord.x()) || (chanx_coord.x() > grids.width() - 2)) { + return false; + } + + if (chanx_coord.y() > grids.height() - 2) { + return false; + } + + if (true == through_channel) { + return true; + } + + return (grids.get_height_offset(t_physical_tile_loc(chanx_coord.x(), chanx_coord.y(), layer)) == grids.get_physical_type(t_physical_tile_loc(chanx_coord.x(), chanx_coord.y(), layer))->height - 1); +} + +/************************************************************************ + * Idenfity if a Y-direction routing channel exist in the fabric + * This could be entirely possible that a routig channel + * is in the middle of a multi-width and multi-height grid + * + * As the chany always locates on right of a grid with the same coord + * + * +-----------+ +---------+ +--------+ + * | Grid | | Grid | | CHANY | + * | [x-1][y] | | [x][y] | | [x][y] | + * +-----------+ +---------+ +--------+ + * width_offset width_offset + * = width - 2 = width -1 + * If the CHANY is in the middle of a multi-width and multi-height grid + * it should locate at a grid whose width_offset is lower than the its width defined in physical_tile + * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block + * + * If through channel is allowed, the chany will always exists + * unless it falls out of the grid array + ***********************************************************************/ +bool is_chany_exist(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chany_coord, + const bool& through_channel) { + if (chany_coord.x() > grids.width() - 2) { + return false; + } + + if ((1 > chany_coord.y()) || (chany_coord.y() > grids.height() - 2)) { + return false; + } + + if (true == through_channel) { + return true; + } + + return (grids.get_width_offset(t_physical_tile_loc(chany_coord.x(), chany_coord.y(), layer)) == grids.get_physical_type(t_physical_tile_loc(chany_coord.x(), chany_coord.y(), layer))->width - 1); +} + +/************************************************************************ + * Identify if a X-direction routing channel is at the right side of a + * multi-height grid + * + * +-----------------+ + * | | + * | | +-------------+ + * | Grid | | CHANX | + * | [x-1][y] | | [x][y] | + * | | +-------------+ + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chanx_right_to_multi_height_grid(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chanx_coord, + const bool& through_channel) { + VTR_ASSERT(0 < chanx_coord.x()); + if (1 == chanx_coord.x()) { + /* This is already the LEFT side of FPGA fabric, + * it is the same results as chanx is right to a multi-height grid + */ + return true; + } + + if (false == through_channel) { + /* We check the left neighbor of chanx, if it does not exist, the chanx is left to a multi-height grid */ + vtr::Point left_chanx_coord(chanx_coord.x() - 1, chanx_coord.y()); + if (false == is_chanx_exist(grids, layer, left_chanx_coord)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a X-direction routing channel is at the left side of a + * multi-height grid + * + * +-----------------+ + * | | + * +---------------+ | | + * | CHANX | | Grid | + * | [x][y] | | [x+1][y] | + * +---------------+ | | + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chanx_left_to_multi_height_grid(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chanx_coord, + const bool& through_channel) { + VTR_ASSERT(chanx_coord.x() < grids.width() - 1); + if (grids.width() - 2 == chanx_coord.x()) { + /* This is already the RIGHT side of FPGA fabric, + * it is the same results as chanx is right to a multi-height grid + */ + return true; + } + + if (false == through_channel) { + /* We check the right neighbor of chanx, if it does not exist, the chanx is left to a multi-height grid */ + vtr::Point right_chanx_coord(chanx_coord.x() + 1, chanx_coord.y()); + if (false == is_chanx_exist(grids, layer, right_chanx_coord)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a Y-direction routing channel is at the top side of a + * multi-width grid + * + * +--------+ + * | CHANY | + * | [x][y] | + * +--------+ + * + * +-----------------+ + * | | + * | | + * | Grid | + * | [x-1][y] | + * | | + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chany_top_to_multi_width_grid(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chany_coord, + const bool& through_channel) { + VTR_ASSERT(0 < chany_coord.y()); + if (1 == chany_coord.y()) { + /* This is already the BOTTOM side of FPGA fabric, + * it is the same results as chany is at the top of a multi-width grid + */ + return true; + } + + if (false == through_channel) { + /* We check the bottom neighbor of chany, if it does not exist, the chany is top to a multi-height grid */ + vtr::Point bottom_chany_coord(chany_coord.x(), chany_coord.y() - 1); + if (false == is_chany_exist(grids, layer, bottom_chany_coord)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a Y-direction routing channel is at the bottom side of a + * multi-width grid + * + * +-----------------+ + * | | + * | | + * | Grid | + * | [x][y+1] | + * | | + * | | + * +-----------------+ + * +--------+ + * | CHANY | + * | [x][y] | + * +--------+ + * + ***********************************************************************/ +bool is_chany_bottom_to_multi_width_grid(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chany_coord, + const bool& through_channel) { + VTR_ASSERT(chany_coord.y() < grids.height() - 1); + if (grids.height() - 2 == chany_coord.y()) { + /* This is already the TOP side of FPGA fabric, + * it is the same results as chany is at the bottom of a multi-width grid + */ + return true; + } + + if (false == through_channel) { + /* We check the top neighbor of chany, if it does not exist, the chany is left to a multi-height grid */ + vtr::Point top_chany_coord(chany_coord.x(), chany_coord.y() + 1); + if (false == is_chany_exist(grids, layer, top_chany_coord)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Get the track_id of a routing track w.r.t its coordinator + * In tileable routing architecture, the track_id changes SB by SB. + * Therefore the track_ids are stored in a vector, indexed by the relative coordinator + * based on the starting point of the track + * For routing tracks in INC_DIRECTION + * (xlow, ylow) should be the starting point + * + * (xlow, ylow) (xhigh, yhigh) + * track_id[0] -------------------------------> track_id[xhigh - xlow + yhigh - ylow] + * + * For routing tracks in DEC_DIRECTION + * (xhigh, yhigh) should be the starting point + * + * (xlow, ylow) (xhigh, yhigh) + * track_id[0] <------------------------------- track_id[xhigh - xlow + yhigh - ylow] + * + * + ***********************************************************************/ +short get_rr_node_actual_track_id(const RRGraph& rr_graph, + const RRNodeId& track_rr_node, + const vtr::Point& coord, + const vtr::vector>& tileable_rr_graph_node_track_ids) { + vtr::Point low_coord(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + size_t offset = (int)abs((int)coord.x() - (int)low_coord.x() + (int)coord.y() - (int)low_coord.y()); + return tileable_rr_graph_node_track_ids[track_rr_node][offset]; +} + +/************************************************************************ + * Get the ptc of a routing track in the channel where it ends + * For routing tracks in INC_DIRECTION + * the ptc is the last of track_ids + * + * For routing tracks in DEC_DIRECTION + * the ptc is the first of track_ids + ***********************************************************************/ +short get_track_rr_node_end_track_id(const RRGraph& rr_graph, + const RRNodeId& track_rr_node, + const vtr::vector>& tileable_rr_graph_node_track_ids) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((CHANX == rr_graph.node_type(track_rr_node)) + || (CHANY == rr_graph.node_type(track_rr_node))); + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + return tileable_rr_graph_node_track_ids[track_rr_node].back(); + } + + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + return tileable_rr_graph_node_track_ids[track_rr_node].front(); +} + +/************************************************************************ + * Find the number of nodes in the same class + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_num_nodes(const RRGraph& rr_graph, + const std::vector& node_types) { + short counter = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + counter++; + } + + return counter; +} + +/************************************************************************ + * Find the maximum fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_max_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + short max_fan_in = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + max_fan_in = std::max(rr_graph.node_fan_in(node), max_fan_in); + } + + return max_fan_in; +} + +/************************************************************************ + * Find the minimum fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_min_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + short min_fan_in = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + min_fan_in = std::min(rr_graph.node_fan_in(node), min_fan_in); + } + + return min_fan_in; +} + +/************************************************************************ + * Find the average fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_average_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + /* Get the maximum SB mux size */ + size_t sum = 0; + size_t counter = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + + sum += rr_graph.node_fan_in(node); + counter++; + } + + return sum / counter; +} + +/************************************************************************ + * Print statistics of multiplexers in a routing resource graph + ************************************************************************/ +void print_rr_graph_mux_stats(const RRGraph& rr_graph) { + /* Print MUX size distribution */ + std::vector sb_node_types; + sb_node_types.push_back(CHANX); + sb_node_types.push_back(CHANY); + + /* Print statistics */ + VTR_LOG("------------------------------------------------\n"); + VTR_LOG("Total No. of Switch Block multiplexer size: %d\n", + find_rr_graph_num_nodes(rr_graph, sb_node_types)); + VTR_LOG("Maximum Switch Block multiplexer size: %d\n", + find_rr_graph_max_fan_in(rr_graph, sb_node_types)); + VTR_LOG("Minimum Switch Block multiplexer size: %d\n", + find_rr_graph_min_fan_in(rr_graph, sb_node_types)); + VTR_LOG("Average Switch Block multiplexer size: %lu\n", + find_rr_graph_average_fan_in(rr_graph, sb_node_types)); + VTR_LOG("------------------------------------------------\n"); + + /* Get the maximum CB mux size */ + std::vector cb_node_types(1, IPIN); + + VTR_LOG("------------------------------------------------\n"); + VTR_LOG("Total No. of Connection Block Multiplexer size: %d\n", + find_rr_graph_num_nodes(rr_graph, cb_node_types)); + VTR_LOG("Maximum Connection Block Multiplexer size: %d\n", + find_rr_graph_max_fan_in(rr_graph, cb_node_types)); + VTR_LOG("Minimum Connection Block Multiplexer size: %d\n", + find_rr_graph_min_fan_in(rr_graph, cb_node_types)); + VTR_LOG("Average Connection Block Multiplexer size: %lu\n", + find_rr_graph_average_fan_in(rr_graph, cb_node_types)); + VTR_LOG("------------------------------------------------\n"); +} + +int find_parallel_seg_index(const int abs_index, + const t_unified_to_parallel_seg_index& index_map, + const e_parallel_axis parallel_axis) { + int index = -1; + auto itr_pair = index_map.equal_range(abs_index); + + for (auto itr = itr_pair.first; itr != itr_pair.second; ++itr) { + if (itr->second.second == parallel_axis) { + index = itr->second.first; + } + } + + return index; +} diff --git a/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h new file mode 100644 index 00000000000..f53b350de45 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_builder_utils.h @@ -0,0 +1,119 @@ +#ifndef RR_GRAPH_BUILDER_UTILS_H +#define RR_GRAPH_BUILDER_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "device_grid.h" +#include "rr_graph_obj.h" +#include "rr_graph_type.h" +#include "vtr_geometry.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +size_t find_unidir_routing_channel_width(const size_t& chan_width); + +int get_grid_pin_class_index(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const int pin_index); + +std::vector find_grid_pin_sides(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const size_t& pin_id); + +e_side determine_io_grid_pin_side(const vtr::Point& device_size, + const vtr::Point& grid_coordinate); + +std::vector get_grid_side_pins(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const e_pin_type& pin_type, + const e_side& pin_side, + const int& pin_width, + const int& pin_height); + +size_t get_grid_num_pins(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const e_pin_type& pin_type, + const e_side& io_side); + +size_t get_grid_num_classes(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const e_pin_type& pin_type); + +bool is_chanx_exist(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chanx_coord, + const bool& through_channel = false); + +bool is_chany_exist(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chany_coord, + const bool& through_channel = false); + +bool is_chanx_right_to_multi_height_grid(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chanx_coord, + const bool& through_channel); + +bool is_chanx_left_to_multi_height_grid(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chanx_coord, + const bool& through_channel); + +bool is_chany_top_to_multi_width_grid(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chany_coord, + const bool& through_channel); + +bool is_chany_bottom_to_multi_width_grid(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chany_coord, + const bool& through_channel); + +short get_rr_node_actual_track_id(const RRGraph& rr_graph, + const RRNodeId& track_rr_node, + const vtr::Point& coord, + const vtr::vector>& tileable_rr_graph_node_track_ids); + +vtr::Point get_track_rr_node_start_coordinator(const RRGraph& rr_graph, + const RRNodeId& track_rr_node); + +vtr::Point get_track_rr_node_end_coordinator(const RRGraph& rr_graph, + const RRNodeId& track_rr_node); + +short get_track_rr_node_end_track_id(const RRGraph& rr_graph, + const RRNodeId& track_rr_node, + const vtr::vector>& tileable_rr_graph_node_track_ids); + +short find_rr_graph_num_nodes(const RRGraph& rr_graph, + const std::vector& node_types); + +short find_rr_graph_max_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +short find_rr_graph_min_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +short find_rr_graph_average_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +void print_rr_graph_mux_stats(const RRGraph& rr_graph); + +/* A copy of the function from rr_graph2.cpp; This is keep tilable rr_graph builder self-contained */ +int find_parallel_seg_index(const int abs_index, + const t_unified_to_parallel_seg_index& index_map, + const e_parallel_axis parallel_axis); + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_graph_types.h b/vpr/src/tileable_rr_graph/rr_graph_types.h new file mode 100644 index 00000000000..ca97d7d38a9 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_types.h @@ -0,0 +1,45 @@ +#ifndef RR_GRAPH_TYPES_H +#define RR_GRAPH_TYPES_H + +/******************************************************************** + * Data types required by routing resource graph (RRGraph) definition + *******************************************************************/ + +/******************************************************************** + * Directionality of a routing track (node type CHANX and CHANY) in + * a routing resource graph + *******************************************************************/ +enum e_direction : unsigned char { + INC_DIRECTION = 0, + DEC_DIRECTION = 1, + BI_DIRECTION = 2, + NO_DIRECTION = 3, + NUM_DIRECTIONS +}; + +/* Xifan Tang - string used in describe_rr_node() and write_xml_rr_graph_obj() */ +constexpr std::array DIRECTION_STRING_WRITE_XML = {{"INC_DIR", "DEC_DIR", "BI_DIR", "NO_DIR"}}; + +#if 0 +/* Type of a routing resource node. x-directed channel segment, * + * y-directed channel segment, input pin to a clb to pad, output * + * from a clb or pad (i.e. output pin of a net) and: * + * SOURCE: A dummy node that is a logical output within a block * + * -- i.e., the gate that generates a signal. * + * SINK: A dummy node that is a logical input within a block * + * -- i.e. the gate that needs a signal. */ +typedef enum e_rr_type : unsigned char { + SOURCE = 0, + SINK, + IPIN, + OPIN, + CHANX, + CHANY, + NUM_RR_TYPES +} t_rr_type; +#endif + +// constexpr std::array RR_TYPES = {{SOURCE, SINK, IPIN, OPIN, CHANX, CHANY}}; +// constexpr std::array rr_node_typename{{"SOURCE", "SINK", "IPIN", "OPIN", "CHANX", "CHANY"}}; + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_graph_view_util.cpp b/vpr/src/tileable_rr_graph/rr_graph_view_util.cpp new file mode 100644 index 00000000000..eee1d94b740 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_view_util.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** + * This file include most-utilized functions that manipulate on the + * RRGraph object + ***************************************************************************/ +#include "rr_graph_view_util.h" + +/**************************************************************************** + * Find the switches interconnecting two nodes + * Return a vector of switch ids + ***************************************************************************/ +std::vector find_rr_graph_switches(const RRGraphView& rr_graph, + const RRNodeId& from_node, + const RRNodeId& to_node) { + std::vector switches; + + std::vector edges = rr_graph.find_edges(from_node, to_node); + if (true == edges.empty()) { + /* edge is open, we return an empty vector of switches */ + return switches; + } + + /* Reach here, edge list is not empty, find switch id one by one + * and update the switch list + */ + for (auto edge : edges) { + switches.push_back(rr_graph.edge_switch(edge)); + } + + return switches; +} + +/********************************************************************* + * Like the RRGraph.find_node() but returns all matching nodes, + * rather than just the first. This is particularly useful for getting all instances + * of a specific IPIN/OPIN at a specific grid tile (x,y) location. + **********************************************************************/ +std::vector find_rr_graph_nodes(const RRGraphView& rr_graph, + const size_t& layer, + const int& x, + const int& y, + const t_rr_type& rr_type, + const int& ptc) { + std::vector indices; + + if (rr_type == IPIN || rr_type == OPIN) { + //For pins we need to look at all the sides of the current grid tile + + for (e_side side : SIDES) { + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(layer, x, y, rr_type, ptc, side); + + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + } else { + //Sides do not effect non-pins so there should only be one per ptc + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(layer, x, y, rr_type, ptc); + + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} + +/********************************************************************* + * Find a list of rr nodes in a routing channel at (x,y) + **********************************************************************/ +std::vector find_rr_graph_chan_nodes(const RRGraphView& rr_graph, + const size_t& layer, + const int& x, + const int& y, + const t_rr_type& rr_type) { + std::vector indices; + + VTR_ASSERT(rr_type == CHANX || rr_type == CHANY); + + for (const RRNodeId& rr_node_index : rr_graph.node_lookup().find_channel_nodes(layer, x, y, rr_type)) { + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} + +/********************************************************************* + * Find a list of rr_nodes that locate at a side of a grid + **********************************************************************/ +std::vector find_rr_graph_grid_nodes(const RRGraphView& rr_graph, + const DeviceGrid& device_grid, + const size_t& layer, + const int& x, + const int& y, + const t_rr_type& rr_type, + const e_side& side, + bool include_clock) { + std::vector indices; + + VTR_ASSERT(rr_type == IPIN || rr_type == OPIN); + + /* Ensure that (x, y) is a valid location in grids */ + VTR_ASSERT(size_t(x) <= device_grid.width() && size_t(y) <= device_grid.height()); + + /* Ensure we have a valid side */ + VTR_ASSERT(side != NUM_SIDES); + + /* Find all the pins on the side of the grid */ + t_physical_tile_loc tile_loc(x, y, layer); + int width_offset = device_grid.get_width_offset(tile_loc); + int height_offset = device_grid.get_height_offset(tile_loc); + for (int pin = 0; pin < device_grid.get_physical_type(tile_loc)->num_pins; ++pin) { + /* Skip those pins have been ignored during rr_graph build-up */ + if (true == device_grid.get_physical_type(tile_loc)->is_ignored_pin[pin]) { + /* If specified, force to include all the clock pins */ + if (!include_clock || std::find(device_grid.get_physical_type(tile_loc)->get_clock_pins_indices().begin(), device_grid.get_physical_type(tile_loc)->get_clock_pins_indices().end(), pin) == device_grid.get_physical_type(tile_loc)->get_clock_pins_indices().end()) { + continue; + } + } + if (false == device_grid.get_physical_type(tile_loc)->pinloc[width_offset][height_offset][side][pin]) { + /* Not the pin on this side, we skip */ + continue; + } + + /* Try to find the rr node */ + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(layer, x, y, rr_type, pin, side); + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} diff --git a/vpr/src/tileable_rr_graph/rr_graph_view_util.h b/vpr/src/tileable_rr_graph/rr_graph_view_util.h new file mode 100644 index 00000000000..4ccb1c614e1 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_graph_view_util.h @@ -0,0 +1,38 @@ +#ifndef RR_GRAPH_VIEW_UTIL_H +#define RR_GRAPH_VIEW_UTIL_H + +/* Include header files which include data structures used by + * the function declaration + */ +#include +#include "device_grid.h" +#include "rr_graph_view.h" + +/* Get node-to-node switches in a RRGraph */ +std::vector find_rr_graph_switches(const RRGraphView& rr_graph, + const RRNodeId& from_node, + const RRNodeId& to_node); + +std::vector find_rr_graph_nodes(const RRGraphView& rr_graph, + const size_t& layer, + const int& x, + const int& y, + const t_rr_type& rr_type, + const int& ptc); + +std::vector find_rr_graph_chan_nodes(const RRGraphView& rr_graph, + const size_t& layer, + const int& x, + const int& y, + const t_rr_type& rr_type); + +std::vector find_rr_graph_grid_nodes(const RRGraphView& rr_graph, + const DeviceGrid& device_grid, + const size_t& layer, + const int& x, + const int& y, + const t_rr_type& rr_type, + const e_side& side, + bool include_clock = false); + +#endif diff --git a/vpr/src/tileable_rr_graph/rr_gsb.cpp b/vpr/src/tileable_rr_graph/rr_gsb.cpp new file mode 100644 index 00000000000..f58a7b8dbc3 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_gsb.cpp @@ -0,0 +1,1044 @@ +/************************************************************************ + * Member functions for class RRGSB + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_log.h" +#include "vtr_assert.h" + +#include "openfpga_rr_graph_utils.h" +#include "openfpga_side_manager.h" + +#include "rr_gsb.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +/* Constructor for an empty object */ +RRGSB::RRGSB() { + /* Set a clean start! */ + coordinate_.set(0, 0); + + chan_node_.clear(); + chan_node_direction_.clear(); + chan_node_in_edges_.clear(); + ipin_node_in_edges_.clear(); + + ipin_node_.clear(); + + opin_node_.clear(); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +/* Get the number of sides of this SB */ +size_t RRGSB::get_num_sides() const { + VTR_ASSERT(validate_num_sides()); + return chan_node_direction_.size(); +} + +/* Get the number of routing tracks on a side */ +size_t RRGSB::get_chan_width(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return chan_node_[side_manager.to_size_t()].get_chan_width(); +} + +/* Get the number of routing tracks on a side */ +t_rr_type RRGSB::get_chan_type(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return chan_node_[side_manager.to_size_t()].get_type(); +} + +/* Get the maximum number of routing tracks on all sides */ +size_t RRGSB::get_max_chan_width() const { + size_t max_chan_width = 0; + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + max_chan_width = std::max(max_chan_width, get_chan_width(side_manager.get_side())); + } + return max_chan_width; +} + +const RRChan& RRGSB::chan(const e_side& chan_side) const { + return chan_node_[size_t(chan_side)]; +} + +/* Get the number of routing tracks of a X/Y-direction CB */ +size_t RRGSB::get_cb_chan_width(const t_rr_type& cb_type) const { + return get_chan_width(get_cb_chan_side(cb_type)); +} + +/* Get the sides of ipin_nodes belong to the cb */ +std::vector RRGSB::get_cb_ipin_sides(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + + std::vector ipin_sides; + + /* Make sure a clean start */ + ipin_sides.clear(); + + switch (cb_type) { + case CHANX: + ipin_sides.push_back(TOP); + ipin_sides.push_back(BOTTOM); + break; + case CHANY: + ipin_sides.push_back(RIGHT); + ipin_sides.push_back(LEFT); + break; + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } + + return ipin_sides; +} + +/* Get the direction of a rr_node at a given side and track_id */ +enum PORTS RRGSB::get_chan_node_direction(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_direction_[side_manager.to_size_t()][track_id]; +} + +/* Get a list of segments used in this routing channel */ +std::vector RRGSB::get_chan_segment_ids(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + return chan_node_[side_manager.to_size_t()].get_segment_ids(); +} + +/* Get a list of rr_nodes whose sed_id is specified */ +std::vector RRGSB::get_chan_node_ids_by_segment_ids(const e_side& side, + const RRSegmentId& seg_id) const { + return chan_node_[size_t(side)].get_node_ids_by_segment_ids(seg_id); +} + +/* get a rr_node at a given side and track_id */ +RRNodeId RRGSB::get_chan_node(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_[side_manager.to_size_t()].get_node(track_id); +} + +std::vector RRGSB::get_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + /* The chan node must be an output port for the GSB, we allow users to access input edges*/ + VTR_ASSERT(OUT_PORT == get_chan_node_direction(side, track_id)); + + /* if sorted, we give sorted edges + * if not sorted, we give the empty vector + */ + if (0 == chan_node_in_edges_.size()) { + std::vector unsorted_edges; + + for (const RREdgeId& edge : rr_graph.node_in_edges(get_chan_node(side, track_id))) { + unsorted_edges.push_back(edge); + } + + return unsorted_edges; + } + + return chan_node_in_edges_[side_manager.to_size_t()][track_id]; +} + +std::vector RRGSB::get_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& ipin_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_ipin_node_id(side, ipin_id)); + + /* if sorted, we give sorted edges + * if not sorted, we give the empty vector + */ + if (0 == ipin_node_in_edges_.size()) { + std::vector unsorted_edges; + + for (const RREdgeId& edge : rr_graph.node_in_edges(get_ipin_node(side, ipin_id))) { + unsorted_edges.push_back(edge); + } + + return unsorted_edges; + } + + return ipin_node_in_edges_[side_manager.to_size_t()][ipin_id]; +} + +/* get the segment id of a channel rr_node */ +RRSegmentId RRGSB::get_chan_node_segment(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_[side_manager.to_size_t()].get_node_segment(track_id); +} + +/* Get the number of IPIN rr_nodes on a side */ +size_t RRGSB::get_num_ipin_nodes(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return ipin_node_[side_manager.to_size_t()].size(); +} + +/* get a opin_node at a given side and track_id */ +RRNodeId RRGSB::get_ipin_node(const e_side& side, const size_t& node_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_ipin_node_id(side, node_id)); + + return ipin_node_[side_manager.to_size_t()][node_id]; +} + +/* Get the number of OPIN rr_nodes on a side */ +size_t RRGSB::get_num_opin_nodes(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return opin_node_[side_manager.to_size_t()].size(); +} + +/* get a opin_node at a given side and track_id */ +RRNodeId RRGSB::get_opin_node(const e_side& side, const size_t& node_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_opin_node_id(side, node_id)); + + return opin_node_[side_manager.to_size_t()][node_id]; +} + +/* Get the node index of a routing track of a connection block, return -1 if not found */ +int RRGSB::get_cb_chan_node_index(const t_rr_type& cb_type, const RRNodeId& node) const { + enum e_side chan_side = get_cb_chan_side(cb_type); + return get_chan_node_index(chan_side, node); +} + +/* Get the node index in the array, return -1 if not found */ +int RRGSB::get_chan_node_index(const e_side& node_side, const RRNodeId& node) const { + VTR_ASSERT(validate_side(node_side)); + return chan_node_[size_t(node_side)].get_node_track_id(node); +} + +/* Get the node index in the array, return -1 if not found */ +int RRGSB::get_node_index(const RRGraphView& rr_graph, + const RRNodeId& node, + const e_side& node_side, + const PORTS& node_direction) const { + size_t cnt; + int ret; + + cnt = 0; + ret = -1; + + /* Depending on the type of rr_node, we search different arrays */ + switch (rr_graph.node_type(node)) { + case CHANX: + case CHANY: + for (size_t inode = 0; inode < get_chan_width(node_side); ++inode) { + if ((node == chan_node_[size_t(node_side)].get_node(inode)) + /* Check if direction meets specification */ + && (node_direction == chan_node_direction_[size_t(node_side)][inode])) { + cnt++; + ret = inode; + break; + } + } + break; + case IPIN: + for (size_t inode = 0; inode < get_num_ipin_nodes(node_side); ++inode) { + if (node == ipin_node_[size_t(node_side)][inode]) { + cnt++; + ret = inode; + break; + } + } + break; + case OPIN: + for (size_t inode = 0; inode < get_num_opin_nodes(node_side); ++inode) { + if (node == opin_node_[size_t(node_side)][inode]) { + cnt++; + ret = inode; + break; + } + } + break; + default: + VTR_LOG("Invalid cur_rr_node type! Should be [CHANX|CHANY|IPIN|OPIN]\n"); + exit(1); + } + + VTR_ASSERT((0 == cnt) || (1 == cnt)); + + return ret; /* Return an invalid value: nonthing is found*/ +} + +/* Get the side of a node in this SB */ +void RRGSB::get_node_side_and_index(const RRGraphView& rr_graph, + const RRNodeId& node, + const PORTS& node_direction, + e_side& node_side, + int& node_index) const { + size_t side; + SideManager side_manager; + + /* Count the number of existence of cur_rr_node in cur_sb_info + * It could happen that same cur_rr_node appears on different sides of a SB + * For example, a routing track go vertically across the SB. + * Then its corresponding rr_node appears on both TOP and BOTTOM sides of this SB. + * We need to ensure that the found rr_node has the same direction as user want. + * By specifying the direction of rr_node, There should be only one rr_node can satisfy! + */ + for (side = 0; side < get_num_sides(); ++side) { + side_manager.set_side(side); + node_index = get_node_index(rr_graph, node, side_manager.get_side(), node_direction); + if (-1 != node_index) { + break; + } + } + + if (side == get_num_sides()) { + /* we find nothing, return NUM_SIDES, and a OPEN node (-1) */ + node_side = NUM_SIDES; + VTR_ASSERT(-1 == node_index); + return; + } + + node_side = side_manager.get_side(); + VTR_ASSERT(-1 != node_index); + + return; +} + +/* Check if the node exist in the opposite side of this Switch Block */ +bool RRGSB::is_sb_node_exist_opposite_side(const RRGraphView& rr_graph, + const RRNodeId& node, + const e_side& node_side) const { + SideManager side_manager(node_side); + int index; + + VTR_ASSERT((CHANX == rr_graph.node_type(node)) || (CHANY == rr_graph.node_type(node))); + + /* See if we can find the same src_rr_node in the opposite chan_side + * if there is one, it means a shorted wire across the SB + */ + index = get_node_index(rr_graph, node, side_manager.get_opposite(), IN_PORT); + + return (-1 != index); +} + +/* check if the CB exist in this GSB */ +bool RRGSB::is_cb_exist(const t_rr_type& cb_type) const { + /* if channel width is zero, there is no CB */ + return (0 != get_cb_chan_width(cb_type)); +} + +/* check if the SB exist in this GSB */ +bool RRGSB::is_sb_exist(const RRGraphView& rr_graph) const { + /* Count the number of sides that there are routing wires or opin nodes */ + size_t num_sides_contain_routing_wires = 0; + size_t num_sides_contain_opin_nodes = 0; + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + if (0 != get_chan_width(side_manager.get_side())) { + num_sides_contain_routing_wires++; + } + if (0 != get_num_opin_nodes(side_manager.get_side())) { + num_sides_contain_opin_nodes++; + } + } + /* When there are zero nodes, the sb does not exist */ + if (num_sides_contain_routing_wires == 0 && num_sides_contain_opin_nodes == 0) { + return false; + } + /* If there is only 1 side of nodes and there are 0 opin nodes, and there are no incoming edges, this is also an empty switch block */ + if (num_sides_contain_routing_wires == 1 && num_sides_contain_opin_nodes == 0) { + size_t num_incoming_edges = 0; + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + for (size_t itrack = 0; itrack < get_chan_width(side_manager.get_side()); ++itrack) { + if (OUT_PORT != get_chan_node_direction(side_manager.get_side(), itrack)) { + continue; + } + num_incoming_edges += get_chan_node_in_edges(rr_graph, side_manager.get_side(), itrack).size(); + } + } + return num_incoming_edges ? true : false; + } + + return true; +} + +/************************************************************************ + * Check if the node indicates a passing wire across the Switch Block part of the GSB + * Therefore, we actually do the following check + * Check if a track starts from this GSB or not + * For INC_DIRECTION + * (xlow, ylow) should be same as the GSB side coordinate + * For DEC_DIRECTION + * (xhigh, yhigh) should be same as the GSB side coordinate + ***********************************************************************/ +bool RRGSB::is_sb_node_passing_wire(const RRGraphView& rr_graph, + const e_side& node_side, + const size_t& track_id) const { + /* Get the rr_node */ + RRNodeId track_node = get_chan_node(node_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = get_side_block_coordinate(node_side); + + /* Get the coordinate of where the track starts */ + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* INC_DIRECTION start_track: (xlow, ylow) should be same as the GSB side coordinate */ + /* DEC_DIRECTION start_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + if ((track_start.x() == side_coordinate.x()) + && (track_start.y() == side_coordinate.y()) + && (OUT_PORT == get_chan_node_direction(node_side, track_id))) { + /* Double check: start track should be an OUTPUT PORT of the GSB */ + return false; /* This is a starting point */ + } + + /* Get the coordinate of where the track ends */ + vtr::Point track_end = get_track_rr_node_end_coordinate(rr_graph, track_node); + + /* INC_DIRECTION end_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + /* DEC_DIRECTION end_track: (xlow, ylow) should be same as the GSB side coordinate */ + if ((track_end.x() == side_coordinate.x()) + && (track_end.y() == side_coordinate.y()) + && (IN_PORT == get_chan_node_direction(node_side, track_id))) { + /* Double check: end track should be an INPUT PORT of the GSB */ + return false; /* This is an ending point */ + } + + /* Reach here it means that this will be a passing wire, + * we should be able to find the node on the opposite side of the GSB! + */ + if (true != is_sb_node_exist_opposite_side(rr_graph, track_node, node_side)) { + VTR_LOG("Cannot find a node on the opposite side to GSB[%lu][%lu] track node[%lu] at %s!\nDetailed node information:\n", + get_x(), get_y(), track_id, SIDE_STRING[node_side]); + VTR_LOG("Node type: %s\n", rr_graph.node_type_string(track_node)); + VTR_LOG("Node coordinate: %d\n", rr_graph.node_coordinate_to_string(track_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(track_node)); + } + VTR_ASSERT(true == is_sb_node_exist_opposite_side(rr_graph, track_node, node_side)); + + return true; +} + +/* check if the candidate SB satisfy the basic requirements on being a mirror of the current one */ +/* Idenify mirror Switch blocks + * Check each two switch blocks: + * Number of channel/opin/ipin rr_nodes are same + * If all above are satisfied, the two switch blocks may be mirrors ! + */ +bool RRGSB::is_sb_mirrorable(const RRGraphView& rr_graph, const RRGSB& cand) const { + /* check the numbers of sides */ + if (get_num_sides() != cand.get_num_sides()) { + return false; + } + + /* check the numbers/directionality of channel rr_nodes */ + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + + /* Ensure we have the same channel width on this side */ + if (get_chan_width(side_manager.get_side()) != cand.get_chan_width(side_manager.get_side())) { + return false; + } + + if (((size_t(-1) == get_track_id_first_short_connection(rr_graph, side_manager.get_side())) + && (size_t(-1) != cand.get_track_id_first_short_connection(rr_graph, side_manager.get_side()))) + || ((size_t(-1) != get_track_id_first_short_connection(rr_graph, side_manager.get_side())) + && (size_t(-1) == cand.get_track_id_first_short_connection(rr_graph, side_manager.get_side())))) { + return false; + } + } + + /* check the numbers of opin_rr_nodes */ + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + + if (get_num_opin_nodes(side_manager.get_side()) != cand.get_num_opin_nodes(side_manager.get_side())) { + return false; + } + } + + return true; +} + +/* Public Accessors: Cooridinator conversion */ + +/* get the x coordinate of this GSB */ +size_t RRGSB::get_x() const { + return coordinate_.x(); +} + +/* get the y coordinate of this GSB */ +size_t RRGSB::get_y() const { + return coordinate_.y(); +} + +/* get the x coordinate of this switch block */ +size_t RRGSB::get_sb_x() const { + return coordinate_.x(); +} + +/* get the y coordinate of this switch block */ +size_t RRGSB::get_sb_y() const { + return coordinate_.y(); +} + +/* Get the number of sides of this SB */ +vtr::Point RRGSB::get_sb_coordinate() const { + return coordinate_; +} + +/* get the x coordinate of this X/Y-direction block */ +size_t RRGSB::get_cb_x(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case CHANX: + return get_side_block_coordinate(LEFT).x(); + case CHANY: + return get_side_block_coordinate(TOP).x(); + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +/* get the y coordinate of this X/Y-direction block */ +size_t RRGSB::get_cb_y(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case CHANX: + return get_side_block_coordinate(LEFT).y(); + case CHANY: + return get_side_block_coordinate(TOP).y(); + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +/* Get the coordinate of the X/Y-direction CB */ +vtr::Point RRGSB::get_cb_coordinate(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case CHANX: + return get_side_block_coordinate(LEFT); + case CHANY: + return get_side_block_coordinate(TOP); + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +e_side RRGSB::get_cb_chan_side(const t_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case CHANX: + return LEFT; + case CHANY: + return TOP; + default: + VTR_LOG("Invalid type of connection block!\n"); + exit(1); + } +} + +/* Get the side of routing channel in the GSB according to the side of IPIN */ +e_side RRGSB::get_cb_chan_side(const e_side& ipin_side) const { + switch (ipin_side) { + case TOP: + return LEFT; + case RIGHT: + return TOP; + case BOTTOM: + return LEFT; + case LEFT: + return TOP; + default: + VTR_LOG("Invalid type of ipin_side!\n"); + exit(1); + } +} + +vtr::Point RRGSB::get_side_block_coordinate(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + vtr::Point ret(get_sb_x(), get_sb_y()); + + switch (side_manager.get_side()) { + case TOP: + /* (0 == side) */ + /* 1. Channel Y [x][y+1] inputs */ + ret.set_y(ret.y() + 1); + break; + case RIGHT: + /* 1 == side */ + /* 2. Channel X [x+1][y] inputs */ + ret.set_x(ret.x() + 1); + break; + case BOTTOM: + /* 2 == side */ + /* 3. Channel Y [x][y] inputs */ + break; + case LEFT: + /* 3 == side */ + /* 4. Channel X [x][y] inputs */ + break; + default: + VTR_LOG(" Invalid side!\n"); + exit(1); + } + + return ret; +} + +vtr::Point RRGSB::get_grid_coordinate() const { + vtr::Point ret(get_sb_x(), get_sb_y()); + ret.set_y(ret.y() + 1); + + return ret; +} + +/************************************************************************ + * Public Mutators + ***********************************************************************/ +/* get a copy from a source */ +void RRGSB::set(const RRGSB& src) { + /* Copy coordinate */ + this->set_coordinate(src.get_sb_coordinate().x(), src.get_sb_coordinate().y()); + + /* Initialize sides */ + this->init_num_sides(src.get_num_sides()); + + /* Copy vectors */ + for (size_t side = 0; side < src.get_num_sides(); ++side) { + SideManager side_manager(side); + /* Copy chan_nodes */ + /* skip if there is no channel width */ + if (0 < src.get_chan_width(side_manager.get_side())) { + this->chan_node_[side_manager.get_side()].set(src.chan_node_[side_manager.get_side()]); + /* Copy chan_node_direction_*/ + this->chan_node_direction_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_chan_width(side_manager.get_side()); ++inode) { + this->chan_node_direction_[side_manager.get_side()].push_back(src.get_chan_node_direction(side_manager.get_side(), inode)); + } + } + + /* Copy opin_node and opin_node_grid_side_ */ + this->opin_node_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_num_opin_nodes(side_manager.get_side()); ++inode) { + this->opin_node_[side_manager.get_side()].push_back(src.get_opin_node(side_manager.get_side(), inode)); + } + + /* Copy ipin_node and ipin_node_grid_side_ */ + this->ipin_node_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_num_ipin_nodes(side_manager.get_side()); ++inode) { + this->ipin_node_[side_manager.get_side()].push_back(src.get_ipin_node(side_manager.get_side(), inode)); + } + } +} + +/* Set the coordinate (x,y) for the switch block */ +void RRGSB::set_coordinate(const size_t& x, const size_t& y) { + coordinate_.set(x, y); +} + +/* Allocate the vectors with the given number of sides */ +void RRGSB::init_num_sides(const size_t& num_sides) { + /* Initialize the vectors */ + chan_node_.resize(num_sides); + chan_node_direction_.resize(num_sides); + ipin_node_.resize(num_sides); + opin_node_.resize(num_sides); +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_chan_node(const e_side& node_side, + const RRChan& rr_chan, + const std::vector& rr_chan_dir) { + /* Validate: 1. side is valid, the type of node is valid */ + VTR_ASSERT(validate_side(node_side)); + + /* fill the dedicated element in the vector */ + chan_node_[size_t(node_side)].set(rr_chan); + chan_node_direction_[size_t(node_side)].resize(rr_chan_dir.size()); + for (size_t inode = 0; inode < rr_chan_dir.size(); ++inode) { + chan_node_direction_[size_t(node_side)][inode] = rr_chan_dir[inode]; + } +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_ipin_node(const RRNodeId& node, const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + /* push pack the dedicated element in the vector */ + ipin_node_[size_t(node_side)].push_back(node); +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_opin_node(const RRNodeId& node, const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + /* push pack the dedicated element in the vector */ + opin_node_[size_t(node_side)].push_back(node); +} + +void RRGSB::sort_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& track_id) { + std::map> from_grid_edge_map; + std::map> from_track_edge_map; + + const RRNodeId& chan_node = chan_node_[size_t(chan_side)].get_node(track_id); + + /* Count the edges and ensure every of them has been sorted */ + size_t edge_counter = 0; + + /* For each incoming edge, find the node side and index in this GSB. + * and cache these. Then we will use the data to sort the edge in the + * following sequence: + * 0----------------------------------------------------------------> num_in_edges() + * |<--TOP side-->|<--RIGHT side-->|<--BOTTOM side-->|<--LEFT side-->| + * For each side, the edge will be sorted by the node index starting from 0 + * For each side, the edge from grid pins will be the 1st part + * while the edge from routing tracks will be the 2nd part + */ + for (const RREdgeId& edge : rr_graph.node_in_edges(chan_node)) { + /* We care the source node of this edge, and it should be an input of the GSB!!! */ + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + e_side side = NUM_SIDES; + int index = 0; + get_node_side_and_index(rr_graph, src_node, IN_PORT, side, index); + + /* Must have valid side and index */ + if (NUM_SIDES == side) { + VTR_LOG("GSB[%lu][%lu]:\n", get_x(), get_y()); + VTR_LOG("----------------------------------\n"); + VTR_LOG("SRC node:\n"); + VTR_LOG("Node info: %s\n", rr_graph.node_coordinate_to_string(src_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(src_node)); + VTR_LOG("Fan-out nodes:\n"); + for (const auto& temp_edge : rr_graph.edge_range(src_node)) { + VTR_LOG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_sink_node(temp_edge)).c_str()); + } + VTR_LOG("\n----------------------------------\n"); + VTR_LOG("Channel node:\n"); + VTR_LOG("Node info: %s\n", rr_graph.node_coordinate_to_string(chan_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(chan_node)); + VTR_LOG("Fan-in nodes:\n"); + for (const auto& temp_edge : rr_graph.node_in_edges(chan_node)) { + VTR_LOG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_src_node(temp_edge)).c_str()); + } + } + + VTR_ASSERT(NUM_SIDES != side); + VTR_ASSERT(OPEN != index); + + if (OPIN == rr_graph.node_type(src_node)) { + from_grid_edge_map[side][index] = edge; + } else { + VTR_ASSERT((CHANX == rr_graph.node_type(src_node)) + || (CHANY == rr_graph.node_type(src_node))); + from_track_edge_map[side][index] = edge; + } + + edge_counter++; + } + + /* Store the sorted edge */ + for (size_t side = 0; side < get_num_sides(); ++side) { + /* Edges from grid outputs are the 1st part */ + for (size_t opin_id = 0; opin_id < opin_node_[side].size(); ++opin_id) { + if ((0 < from_grid_edge_map.count(side)) + && (0 < from_grid_edge_map.at(side).count(opin_id))) { + chan_node_in_edges_[size_t(chan_side)][track_id].push_back(from_grid_edge_map[side][opin_id]); + } + } + + /* Edges from routing tracks are the 2nd part */ + for (size_t itrack = 0; itrack < chan_node_[side].get_chan_width(); ++itrack) { + if ((0 < from_track_edge_map.count(side)) + && (0 < from_track_edge_map.at(side).count(itrack))) { + chan_node_in_edges_[size_t(chan_side)][track_id].push_back(from_track_edge_map[side][itrack]); + } + } + } + + VTR_ASSERT(edge_counter == chan_node_in_edges_[size_t(chan_side)][track_id].size()); +} + +void RRGSB::sort_chan_node_in_edges(const RRGraphView& rr_graph) { + /* Allocate here, as sort edge is optional, we do not allocate when adding nodes */ + chan_node_in_edges_.resize(get_num_sides()); + + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + chan_node_in_edges_[side].resize(chan_node_[side].get_chan_width()); + for (size_t track_id = 0; track_id < chan_node_[side].get_chan_width(); ++track_id) { + /* Only sort the output nodes and bypass passing wires */ + if ((OUT_PORT == chan_node_direction_[side][track_id]) + && (false == is_sb_node_passing_wire(rr_graph, side_manager.get_side(), track_id))) { + sort_chan_node_in_edges(rr_graph, side_manager.get_side(), track_id); + } + } + } +} + +void RRGSB::sort_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& ipin_side, + const size_t& ipin_id) { + std::map from_track_edge_map; + + e_side chan_side = get_cb_chan_side(ipin_side); + + const RRNodeId& ipin_node = ipin_node_[size_t(ipin_side)][ipin_id]; + + /* Count the edges and ensure every of them has been sorted */ + size_t edge_counter = 0; + + /* For each incoming edge, find the node side and index in this GSB. + * and cache these. Then we will use the data to sort the edge in the + * following sequence: + * 0----------------------------------------------------------------> num_in_edges() + * |<--TOP side-->|<--RIGHT side-->|<--BOTTOM side-->|<--LEFT side-->| + * For each side, the edge will be sorted by the node index starting from 0 + * For each side, the edge from grid pins will be the 1st part + * while the edge from routing tracks will be the 2nd part + */ + for (const RREdgeId& edge : rr_graph.node_in_edges(ipin_node)) { + /* We care the source node of this edge, and it should be an input of the GSB!!! */ + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + /* The driver routing channel node can be either an input or an output to the GSB. + * Just try to find a qualified one. */ + int index = OPEN; + index = get_node_index(rr_graph, src_node, chan_side, IN_PORT); + if (OPEN == index) { + index = get_node_index(rr_graph, src_node, chan_side, OUT_PORT); + } + + /* Must have valid side and index */ + if (OPEN == index) { + VTR_LOG("GSB[%lu][%lu]:\n", get_x(), get_y()); + VTR_LOG("----------------------------------\n"); + VTR_LOG("SRC node:\n"); + VTR_LOG("Node info: %s\n", rr_graph.node_coordinate_to_string(src_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(src_node)); + VTR_LOG("Fan-out nodes:\n"); + for (const auto& temp_edge : rr_graph.edge_range(src_node)) { + VTR_LOG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_sink_node(temp_edge)).c_str()); + } + VTR_LOG("\n----------------------------------\n"); + VTR_LOG("IPIN node:\n"); + VTR_LOG("Node info: %s\n", rr_graph.node_coordinate_to_string(ipin_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(ipin_node)); + VTR_LOG("Fan-in nodes:\n"); + for (const auto& temp_edge : rr_graph.node_in_edges(ipin_node)) { + VTR_LOG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_src_node(temp_edge)).c_str()); + } + } + + VTR_ASSERT(OPEN != index); + + VTR_ASSERT(CHANX == rr_graph.node_type(src_node) || CHANY == rr_graph.node_type(src_node)); + from_track_edge_map[index] = edge; + edge_counter++; + } + + /* Store the sorted edge */ + for (size_t itrack = 0; itrack < chan_node_[size_t(chan_side)].get_chan_width(); ++itrack) { + if (0 < from_track_edge_map.count(itrack)) { + ipin_node_in_edges_[size_t(ipin_side)][ipin_id].push_back(from_track_edge_map[itrack]); + } + } + + VTR_ASSERT(edge_counter == ipin_node_in_edges_[size_t(ipin_side)][ipin_id].size()); +} + +void RRGSB::sort_ipin_node_in_edges(const RRGraphView& rr_graph) { + /* Allocate here, as sort edge is optional, we do not allocate when adding nodes */ + ipin_node_in_edges_.resize(get_num_sides()); + + for (t_rr_type cb_type : {CHANX, CHANY}) { + for (e_side ipin_side : get_cb_ipin_sides(cb_type)) { + SideManager side_manager(ipin_side); + ipin_node_in_edges_[size_t(ipin_side)].resize(ipin_node_[size_t(ipin_side)].size()); + for (size_t ipin_id = 0; ipin_id < ipin_node_[size_t(ipin_side)].size(); ++ipin_id) { + sort_ipin_node_in_edges(rr_graph, side_manager.get_side(), ipin_id); + } + } + } +} + +/************************************************************************ + * Public Mutators: clean-up functions + ***********************************************************************/ +/* Reset the RRGSB to pristine state */ +void RRGSB::clear() { + /* Clean all the vectors */ + VTR_ASSERT(validate_num_sides()); + /* Clear the inner vector of each matrix */ + for (size_t side = 0; side < get_num_sides(); ++side) { + chan_node_direction_[side].clear(); + chan_node_[side].clear(); + ipin_node_[side].clear(); + opin_node_[side].clear(); + } + chan_node_direction_.clear(); + chan_node_.clear(); + ipin_node_.clear(); + opin_node_.clear(); +} + +/* Clean the chan_width of a side */ +void RRGSB::clear_chan_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + chan_node_[size_t(node_side)].clear(); + chan_node_direction_[size_t(node_side)].clear(); +} + +/* Clean the number of IPINs of a side */ +void RRGSB::clear_ipin_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + ipin_node_[size_t(node_side)].clear(); +} + +/* Clean the number of OPINs of a side */ +void RRGSB::clear_opin_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + opin_node_[size_t(node_side)].clear(); +} + +/* Clean chan/opin/ipin nodes at one side */ +void RRGSB::clear_one_side(const e_side& node_side) { + clear_chan_nodes(node_side); + clear_ipin_nodes(node_side); + clear_opin_nodes(node_side); +} + +/************************************************************************ + * Internal Accessors: identify mirrors + ***********************************************************************/ +size_t RRGSB::get_track_id_first_short_connection(const RRGraphView& rr_graph, const e_side& node_side) const { + VTR_ASSERT(validate_side(node_side)); + + /* Walk through chan_nodes and find the first short connection */ + for (size_t inode = 0; inode < get_chan_width(node_side); ++inode) { + if (true == is_sb_node_passing_wire(rr_graph, node_side, inode)) { + return inode; + } + } + + return size_t(-1); +} + +/************************************************************************ + * Internal validators + ***********************************************************************/ +/* Validate if the number of sides are consistent among internal data arrays ! */ +bool RRGSB::validate_num_sides() const { + size_t num_sides = chan_node_direction_.size(); + + if (num_sides != chan_node_.size()) { + return false; + } + + if (num_sides != ipin_node_.size()) { + return false; + } + + if (num_sides != opin_node_.size()) { + return false; + } + + return true; +} + +/* Check if the side valid in the context: does the switch block have the side? */ +bool RRGSB::validate_side(const e_side& side) const { + return (size_t(side) < get_num_sides()); +} + +/* Check the track_id is valid for chan_node_ and chan_node_direction_ */ +bool RRGSB::validate_track_id(const e_side& side, const size_t& track_id) const { + if (false == validate_side(side)) { + return false; + } + + return ((track_id < chan_node_[size_t(side)].get_chan_width()) + && (track_id < chan_node_direction_[size_t(side)].size())); +} + +/* Check the opin_node_id is valid for opin_node_ and opin_node_grid_side_ */ +bool RRGSB::validate_opin_node_id(const e_side& side, const size_t& node_id) const { + if (false == validate_side(side)) { + return false; + } + return (node_id < opin_node_[size_t(side)].size()); +} + +/* Check the ipin_node_id is valid for opin_node_ and opin_node_grid_side_ */ +bool RRGSB::validate_ipin_node_id(const e_side& side, const size_t& node_id) const { + if (false == validate_side(side)) { + return false; + } + return (node_id < ipin_node_[size_t(side)].size()); +} + +bool RRGSB::validate_cb_type(const t_rr_type& cb_type) const { + return ((CHANX == cb_type) || (CHANY == cb_type)); +} diff --git a/vpr/src/tileable_rr_graph/rr_gsb.h b/vpr/src/tileable_rr_graph/rr_gsb.h new file mode 100644 index 00000000000..7b0e62b2b09 --- /dev/null +++ b/vpr/src/tileable_rr_graph/rr_gsb.h @@ -0,0 +1,260 @@ +#ifndef RR_GSB_H +#define RR_GSB_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +#include "rr_chan.h" +#include "rr_graph_view.h" + +/******************************************************************** + * Object Generic Switch Block + * This block contains + * 1. A switch block + * 2. A X-direction Connection block locates at the left side of the switch block + * 2. A Y-direction Connection block locates at the top side of the switch block + * + * +-------------+ +---------------------------------+ + * | | | Y-direction CB | + * | Grid | | [x][y + 1] | + * | [x][y+1] | +---------------------------------+ + * +-------------+ + * TOP SIDE + * +-------------+ +---------------------------------+ + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | X-direction | | | + * | CB | LEFT SIDE | Switch Block | RIGHT SIDE + * | [x][y] | | [x][y] | + * | | | | + * | | | CHAN_NODES CHAN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * +-------------+ +---------------------------------+ + * BOTTOM SIDE + * num_sides: number of sides of this switch block + * chan_rr_node: a collection of rr_nodes as routing tracks locating at each side of the Switch block <0..num_sides-1><0..chan_width-1> + * chan_rr_node_direction: Indicate if this rr_node is an input or an output of the Switch block <0..num_sides-1><0..chan_width-1> + * ipin_rr_node: a collection of rr_nodes as IPIN of a GRID locating at each side of the Switch block <0..num_sides-1><0..num_ipin_rr_nodes-1> + * ipin_rr_node_grid_side: specify the side of the input pins on which side of a GRID <0..num_sides-1><0..num_ipin_rr_nodes-1> + * opin_rr_node: a collection of rr_nodes as OPIN of a GRID locating at each side of the Switch block <0..num_sides-1><0..num_opin_rr_nodes-1> + * opin_rr_node_grid_side: specify the side of the output pins on which side of a GRID <0..num_sides-1><0..num_opin_rr_nodes-1> + * num_reserved_conf_bits: number of reserved configuration bits this switch block requires (mainly due to RRAM-based multiplexers) + * num_conf_bits: number of configuration bits this switch block requires + *******************************************************************/ +class RRGSB { + public: /* Contructors */ + RRGSB(); /* Default constructor */ + public: /* Accessors */ + /* Get the number of sides of this SB */ + size_t get_num_sides() const; + + /* Get the number of routing tracks on a side */ + size_t get_chan_width(const e_side& side) const; + + /* Get the type of routing tracks on a side */ + t_rr_type get_chan_type(const e_side& side) const; + + /* Get the maximum number of routing tracks on all sides */ + size_t get_max_chan_width() const; + + /* Get the number of routing tracks of a X/Y-direction CB */ + size_t get_cb_chan_width(const t_rr_type& cb_type) const; + + /* Return read-only object of the routing channels with a given side */ + const RRChan& chan(const e_side& chan_side) const; + + /* Get the sides of CB ipins in the array */ + std::vector get_cb_ipin_sides(const t_rr_type& cb_type) const; + + /* Get the direction of a rr_node at a given side and track_id */ + enum PORTS get_chan_node_direction(const e_side& side, const size_t& track_id) const; + + /* Get a list of segments used in this routing channel */ + std::vector get_chan_segment_ids(const e_side& side) const; + + /* Get a list of segments used in this routing channel */ + std::vector get_chan_node_ids_by_segment_ids(const e_side& side, + const RRSegmentId& seg_id) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_chan_node(const e_side& side, const size_t& track_id) const; + + /* get all the sorted incoming edges for a rr_node at a given side and track_id */ + std::vector get_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& track_id) const; + + /* get all the sorted incoming edges for a IPIN rr_node at a given side and ipin_id */ + std::vector get_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& ipin_id) const; + + /* get the segment id of a channel rr_node */ + RRSegmentId get_chan_node_segment(const e_side& side, const size_t& track_id) const; + + /* Get the number of IPIN rr_nodes on a side */ + size_t get_num_ipin_nodes(const e_side& side) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_ipin_node(const e_side& side, const size_t& node_id) const; + + /* Get the number of OPIN rr_nodes on a side */ + size_t get_num_opin_nodes(const e_side& side) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_opin_node(const e_side& side, const size_t& node_id) const; + + int get_cb_chan_node_index(const t_rr_type& cb_type, const RRNodeId& node) const; + + int get_chan_node_index(const e_side& node_side, const RRNodeId& node) const; + + /* Get the node index in the array, return -1 if not found */ + int get_node_index(const RRGraphView& rr_graph, const RRNodeId& node, const e_side& node_side, const PORTS& node_direction) const; + + /* Given a rr_node, try to find its side and index in the Switch block */ + void get_node_side_and_index(const RRGraphView& rr_graph, const RRNodeId& node, const PORTS& node_direction, e_side& node_side, int& node_index) const; + + /* Check if the node exist in the opposite side of this Switch Block */ + bool is_sb_node_exist_opposite_side(const RRGraphView& rr_graph, const RRNodeId& node, const e_side& node_side) const; + + public: /* Accessors: to identify mirrors */ + /* check if the connect block exists in the GSB */ + bool is_cb_exist(const t_rr_type& cb_type) const; + + /* check if the switch block exists in the GSB, this function checks if a switch block physically exists (no routing wires, no OPIN nodes, and no interconnecting wires) */ + bool is_sb_exist(const RRGraphView& rr_graph) const; + + /* Check if the node imply a short connection inside the SB, which happens to long wires across a FPGA fabric */ + bool is_sb_node_passing_wire(const RRGraphView& rr_graph, const e_side& node_side, const size_t& track_id) const; + + /* check if the candidate SB satisfy the basic requirements + * on being a mirror of the current one + */ + bool is_sb_mirrorable(const RRGraphView& rr_graph, const RRGSB& cand) const; + + public: /* Cooridinator conversion and output */ + size_t get_x() const; /* get the x coordinate of this switch block */ + size_t get_y() const; /* get the y coordinate of this switch block */ + size_t get_sb_x() const; /* get the x coordinate of this switch block */ + size_t get_sb_y() const; /* get the y coordinate of this switch block */ + vtr::Point get_sb_coordinate() const; /* Get the coordinate of the SB */ + size_t get_cb_x(const t_rr_type& cb_type) const; /* get the x coordinate of this X/Y-direction block */ + size_t get_cb_y(const t_rr_type& cb_type) const; /* get the y coordinate of this X/Y-direction block */ + vtr::Point get_cb_coordinate(const t_rr_type& cb_type) const; /* Get the coordinate of the X/Y-direction CB */ + e_side get_cb_chan_side(const t_rr_type& cb_type) const; /* get the side of a Connection block */ + e_side get_cb_chan_side(const e_side& ipin_side) const; /* get the side of a Connection block */ + vtr::Point get_side_block_coordinate(const e_side& side) const; + vtr::Point get_grid_coordinate() const; + + public: /* Mutators */ + /* get a copy from a source */ + void set(const RRGSB& src); + void set_coordinate(const size_t& x, const size_t& y); + + /* Allocate the vectors with the given number of sides */ + void init_num_sides(const size_t& num_sides); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_chan_node(const e_side& node_side, + const RRChan& rr_chan, + const std::vector& rr_chan_dir); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_ipin_node(const RRNodeId& node, + const e_side& node_side); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_opin_node(const RRNodeId& node, + const e_side& node_side); + + /* Sort all the incoming edges for routing channel rr_node */ + void sort_chan_node_in_edges(const RRGraphView& rr_graph); + /* Sort all the incoming edges for input pin rr_node */ + void sort_ipin_node_in_edges(const RRGraphView& rr_graph); + + public: /* Mutators: cleaners */ + void clear(); + + /* Clean the chan_width of a side */ + void clear_chan_nodes(const e_side& node_side); + + /* Clean the number of IPINs of a side */ + void clear_ipin_nodes(const e_side& node_side); + + /* Clean the number of OPINs of a side */ + void clear_opin_nodes(const e_side& node_side); + + /* Clean chan/opin/ipin nodes at one side */ + void clear_one_side(const e_side& node_side); + + private: /* Private Mutators: edge sorting */ + /* Sort all the incoming edges for one channel rr_node */ + void sort_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& track_id); + + /* Sort all the incoming edges for one input pin rr_node */ + void sort_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& ipin_id); + + private: /* internal functions */ + size_t get_track_id_first_short_connection(const RRGraphView& rr_graph, const e_side& node_side) const; + + private: /* internal validators */ + bool validate_num_sides() const; + bool validate_side(const e_side& side) const; + bool validate_track_id(const e_side& side, const size_t& track_id) const; + bool validate_opin_node_id(const e_side& side, const size_t& node_id) const; + bool validate_ipin_node_id(const e_side& side, const size_t& node_id) const; + bool validate_cb_type(const t_rr_type& cb_type) const; + + private: /* Internal Data */ + /* Coordinator */ + vtr::Point coordinate_; + + /* Routing channel data + * Each GSB may have four sides of routing track nodes + */ + /* Node id in rr_graph denoting each routing track */ + std::vector chan_node_; + + /* Direction of a port when the channel node appear in the GSB module */ + std::vector> chan_node_direction_; + + /* Sequence of edge ids for each routing channel node, + * this is sorted by the location of edge source nodes in the context of GSB + * The edge sorting is critical to uniquify the routing modules in OpenFPGA + * This is due to that VPR allocate and sort edges randomly when building the rr_graph + * As a result, previous nodes of a chan node may be the same in different GSBs + * but their sequence is not. This will cause graph comparison to fail when uniquifying + * the routing modules. Therefore, edge sorting can be done inside the GSB + * + * Storage organization: + * [chan_side][chan_node][edge_id_in_gsb_context] + */ + std::vector>> chan_node_in_edges_; + /* Sequence of edge ids for each input pin node. Same rules applied as the channel nodes */ + std::vector>> ipin_node_in_edges_; + + /* Logic Block Inputs data */ + std::vector> ipin_node_; + + /* Logic Block Outputs data */ + std::vector> opin_node_; +}; + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_chan_details_builder.cpp b/vpr/src/tileable_rr_graph/tileable_chan_details_builder.cpp new file mode 100644 index 00000000000..c0def1dfa69 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_chan_details_builder.cpp @@ -0,0 +1,230 @@ +/************************************************************************ + * This file contains a builder for the ChanNodeDetails data structure + * Different from VPR rr_graph builders, this builder aims to create a + * highly regular routing channel. Thus, it is called tileable, + * which brings significant advantage in producing large FPGA fabrics. + ***********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" + +/************************************************************************ + * Generate the number of tracks for each types of routing segments + * w.r.t. the frequency of each of segments and channel width + * Note that if we dertermine the number of tracks per type using + * chan_width * segment_frequency / total_freq may cause + * The total track num may not match the chan_width, + * therefore, we assign tracks one by one until we meet the frequency requirement + * In this way, we can assign the number of tracks with repect to frequency + ***********************************************************************/ +std::vector get_num_tracks_per_seg_type(const size_t& chan_width, + const std::vector& segment_inf, + const bool& use_full_seg_groups) { + std::vector result; + std::vector demand; + /* Make sure a clean start */ + result.resize(segment_inf.size()); + demand.resize(segment_inf.size()); + + /* Scale factor so we can divide by any length + * and still use integers */ + /* Get the sum of frequency */ + size_t scale = 1; + size_t freq_sum = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + scale *= segment_inf[iseg].length; + freq_sum += segment_inf[iseg].frequency; + } + size_t reduce = scale * freq_sum; + + /* Init assignments to 0 and set the demand values */ + /* Get the fraction of each segment type considering the frequency: + * num_track_per_seg = chan_width * (freq_of_seg / sum_freq) + */ + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + result[iseg] = 0; + demand[iseg] = scale * chan_width * segment_inf[iseg].frequency; + if (true == use_full_seg_groups) { + demand[iseg] /= segment_inf[iseg].length; + } + } + + /* check if the sum of num_tracks, matches the chan_width */ + /* Keep assigning tracks until we use them up */ + size_t assigned = 0; + size_t size = 0; + size_t imax = 0; + while (assigned < chan_width) { + /* Find current maximum demand */ + double max = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + if (demand[iseg] > max) { + imax = iseg; + } + max = std::max(demand[iseg], max); + } + + /* Assign tracks to the type and reduce the types demand */ + size = (use_full_seg_groups ? segment_inf[imax].length : 1); + demand[imax] -= reduce; + result[imax] += size; + assigned += size; + } + + /* Undo last assignment if we were closer to goal without it */ + if ((assigned - chan_width) > (size / 2)) { + result[imax] -= size; + } + + return result; +} + +/************************************************************************ + * Adapt the number of channel width to a tileable routing architecture + ***********************************************************************/ +int adapt_to_tileable_route_chan_width(const int& chan_width, + const std::vector& segment_infs) { + int tileable_chan_width = 0; + + /* Estimate the number of segments per type by the given ChanW*/ + std::vector num_tracks_per_seg_type = get_num_tracks_per_seg_type(chan_width, + segment_infs, + true); /* Force to use the full segment group */ + /* Sum-up the number of tracks */ + for (size_t iseg = 0; iseg < num_tracks_per_seg_type.size(); ++iseg) { + tileable_chan_width += num_tracks_per_seg_type[iseg]; + } + + return tileable_chan_width; +} + +/************************************************************************ + * Build details of routing tracks in a channel + * The function will + * 1. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * + * 2. The starting point of each segment in the channel will be assigned + * For each segment group with same directionality (tracks have the same length), + * every L track will be a starting point (where L denotes the length of segments) + * In this case, if the number of tracks is not a multiple of L, + * indeed we may have some | Yes | No | + * +---------------------------------------+--------------+ + * | 1 | <--------MUX | Yes | No | + * +---------------------------------------+--------------+ + * | 2 | --------> | No | No | + * +---------------------------------------+--------------+ + * | 3 | <-------- | No | No | + * +---------------------------------------+--------------+ + * | 4 | --------> | No | No | + * +---------------------------------------+--------------+ + * | 5 | <-------- | No | No | + * +---------------------------------------+--------------+ + * | 7 | -------->MUX | No | Yes | + * +---------------------------------------+--------------+ + * | 8 | MUX<-------- | No | Yes | + * +---------------------------------------+--------------+ + * | 9 | MUX--------> | Yes | No | + * +---------------------------------------+--------------+ + * | 10 | <--------MUX | Yes | No | + * +---------------------------------------+--------------+ + * | 11 | -------->MUX | No | Yes | + * +------------------------------------------------------+ + * | 12 | <-------- | No | No | + * +------------------------------------------------------+ + * + * 3. SPECIAL for fringes: TOP|RIGHT|BOTTOM|RIGHT + * if device_side is NUM_SIDES, we assume this channel does not locate on borders + * All segments will start and ends with no exception + * + * 4. IMPORTANT: we should be aware that channel width maybe different + * in X-direction and Y-direction channels!!! + * So we will load segment details for different channels + ***********************************************************************/ +ChanNodeDetails build_unidir_chan_node_details(const size_t& chan_width, + const size_t& max_seg_length, + const bool& force_start, + const bool& force_end, + const std::vector& segment_inf) { + ChanNodeDetails chan_node_details; + size_t actual_chan_width = find_unidir_routing_channel_width(chan_width); + VTR_ASSERT(0 == actual_chan_width % 2); + + /* Reserve channel width */ + chan_node_details.reserve(chan_width); + /* Return if zero width is forced */ + if (0 == actual_chan_width) { + return chan_node_details; + } + + /* Find the number of segments required by each group */ + std::vector num_tracks = get_num_tracks_per_seg_type(actual_chan_width / 2, segment_inf, false); + + /* Add node to ChanNodeDetails */ + size_t cur_track = 0; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + /* segment length will be set to maxium segment length if this is a longwire */ + size_t seg_len = segment_inf[iseg].length; + if (true == segment_inf[iseg].longline) { + seg_len = max_seg_length; + } + for (size_t itrack = 0; itrack < num_tracks[iseg]; ++itrack) { + bool seg_start = false; + bool seg_end = false; + /* Every first track of a group of Length-N wires, we set a starting point */ + if (0 == itrack % seg_len) { + seg_start = true; + } + /* Every last track of a group of Length-N wires or this is the last track in this group, we set an ending point */ + if ((seg_len - 1 == itrack % seg_len) + || (itrack == num_tracks[iseg] - 1)) { + seg_end = true; + } + /* Since this is a unidirectional routing architecture, + * Add a pair of tracks, 1 INC track and 1 DEC track + */ + int seg_index = segment_inf[iseg].seg_index; + chan_node_details.add_track(cur_track, Direction::INC, seg_index, seg_len, seg_start, seg_end); + cur_track++; + chan_node_details.add_track(cur_track, Direction::DEC, seg_index, seg_len, seg_start, seg_end); + cur_track++; + } + } + /* Check if all the tracks have been satisified */ + VTR_ASSERT(cur_track == actual_chan_width); + + /* If this is on the border of a device/heterogeneous blocks, segments should start/end */ + if (true == force_start) { + /* INC should all start */ + chan_node_details.set_tracks_start(Direction::INC); + /* DEC should all end */ + chan_node_details.set_tracks_end(Direction::DEC); + } + + /* If this is on the border of a device/heterogeneous blocks, segments should start/end */ + if (true == force_end) { + /* INC should all end */ + chan_node_details.set_tracks_end(Direction::INC); + /* DEC should all start */ + chan_node_details.set_tracks_start(Direction::DEC); + } + + return chan_node_details; +} diff --git a/vpr/src/tileable_rr_graph/tileable_chan_details_builder.h b/vpr/src/tileable_rr_graph/tileable_chan_details_builder.h new file mode 100644 index 00000000000..5ce79b61221 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_chan_details_builder.h @@ -0,0 +1,27 @@ +#ifndef TILEABLE_CHAN_DETAILS_BUILDER_H +#define TILEABLE_CHAN_DETAILS_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "physical_types.h" +#include "chan_node_details.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +std::vector get_num_tracks_per_seg_type(const size_t& chan_width, + const std::vector& segment_inf, + const bool& use_full_seg_groups); + +int adapt_to_tileable_route_chan_width(const int& chan_width, const std::vector& segment_inf); + +ChanNodeDetails build_unidir_chan_node_details(const size_t& chan_width, + const size_t& max_seg_length, + const bool& force_start, + const bool& force_end, + const std::vector& segment_inf); + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.cpp new file mode 100644 index 00000000000..7a42967952f --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.cpp @@ -0,0 +1,324 @@ +/************************************************************************ + * This file contains a builder for the complex rr_graph data structure + * Different from VPR rr_graph builders, this builder aims to create a + * highly regular rr_graph, where each Connection Block (CB), Switch + * Block (SB) is the same (except for those on the borders). Thus, the + * rr_graph is called tileable, which brings significant advantage in + * producing large FPGA fabrics. + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_time.h" +#include "vtr_log.h" +#include "vtr_memory.h" + +#include "vpr_error.h" +#include "vpr_utils.h" + +#include "rr_graph.h" +#include "check_rr_graph.h" +#include "get_parallel_segs.h" +#include "device_grid_annotation.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_node_builder.h" +#include "tileable_rr_graph_edge_builder.h" +#include "tileable_rr_graph_builder.h" + +#include "globals.h" + +/************************************************************************ + * Main function of this file + * Builder for a detailed uni-directional tileable rr_graph + * Global graph is not supported here, the VPR rr_graph generator can be used + * It follows the procedures to complete the rr_graph generation + * 1. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * 2. Estimate the number of nodes in the rr_graph + * This will estimate the number of + * a. IPINs, input pins of each grid + * b. OPINs, output pins of each grid + * c. SOURCE, virtual node which drives OPINs + * d. SINK, virtual node which is connected to IPINs + * e. CHANX and CHANY, routing segments of each channel + * 3. Create the connectivity of OPINs + * a. Evenly assign connections to OPINs to routing tracks + * b. the connection pattern should be same across the fabric + * 4. Create the connectivity of IPINs + * a. Evenly assign connections from routing tracks to IPINs + * b. the connection pattern should be same across the fabric + * 5. Create the switch block patterns, + * It is based on the type of switch block, the supported patterns are + * a. Disjoint, which connects routing track (i)th from (i)th and (i)th routing segments + * b. Universal, which connects routing track (i)th from (i)th and (M-i)th routing segments + * c. Wilton, which rotates the connection of Disjoint by 1 track + * 6. Allocate rr_graph, fill the node information + * For each node, fill + * a. basic information: coordinate(xlow, xhigh, ylow, yhigh), ptc_num + * b. edges (both incoming and outcoming) + * c. handle direct-connections + * 7. Build fast look-up for the rr_graph + * 8. Allocate external data structures + * a. cost_index + * b. RC tree + ***********************************************************************/ + +void build_tileable_unidir_rr_graph(const std::vector& types, + const DeviceGrid& grids, + const t_chan_width& chan_width, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const std::vector& segment_inf, + const int& delayless_switch, + const int& wire_to_arch_ipin_switch, + const float R_minW_nmos, + const float R_minW_pmos, + const enum e_base_cost_type& base_cost_type, + const t_direct_inf* directs, + const int& num_directs, + int* wire_to_rr_ipin_switch, + const bool& shrink_boundary, + const bool& through_channel, + const bool& wire_opposite_side, + int* Warnings) { + vtr::ScopedStartFinishTimer timer("Build tileable routing resource graph"); + + /* Reset warning flag */ + *Warnings = RR_GRAPH_NO_WARN; + + /* Create a matrix of grid */ + /* Create a vector of channel width, we support X-direction and Y-direction has different W */ + vtr::Point device_chan_width(chan_width.x_max, chan_width.y_max); + + VTR_LOG("X-direction routing channel width is %lu\n", device_chan_width.x()); + VTR_LOG("Y-direction routing channel width is %lu\n", device_chan_width.y()); + + /* Get a mutable device ctx so that we have a mutable rr_graph */ + DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); + + /* Annotate the device grid on the boundry */ + DeviceGridAnnotation device_grid_annotation(device_ctx.grid); + + /* The number of segments are in general small, reserve segments may not bring + * significant memory efficiency */ + device_ctx.rr_graph_builder.reserve_segments(segment_inf.size()); + /* Create the segments */ + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + device_ctx.rr_graph_builder.add_rr_segment(segment_inf[iseg]); + } + + /* TODO: Load architecture switch to rr_graph switches + * Draft the switches as internal data of RRGraph object + * These are temporary switches copied from arch switches + * We use them to build the edges + * We will reset all the switches in the function + * alloc_and_load_rr_switch_inf() + */ + /* TODO: Spot the switch id in the architecture switch list */ + RRSwitchId wire_to_ipin_rr_switch = RRSwitchId::INVALID(); + RRSwitchId delayless_rr_switch = RRSwitchId::INVALID(); + + device_ctx.rr_graph_builder.reserve_switches(device_ctx.arch_switch_inf.size()); + /* Create the switches */ + for (size_t iswitch = 0; iswitch < device_ctx.arch_switch_inf.size(); ++iswitch) { + const t_rr_switch_inf& temp_rr_switch = create_rr_switch_from_arch_switch(device_ctx.arch_switch_inf[iswitch], R_minW_nmos, R_minW_pmos); + RRSwitchId rr_switch = device_ctx.rr_graph_builder.add_rr_switch(temp_rr_switch); + if ((int)iswitch == wire_to_arch_ipin_switch) { + wire_to_ipin_rr_switch = rr_switch; + } + if ((int)iswitch == delayless_switch) { + delayless_rr_switch = rr_switch; + } + } + + /* Validate the special switches */ + VTR_ASSERT(true == device_ctx.rr_graph.valid_switch(wire_to_ipin_rr_switch)); + VTR_ASSERT(true == device_ctx.rr_graph.valid_switch(delayless_rr_switch)); + + /* A temp data about the driver switch ids for each rr_node */ + vtr::vector rr_node_driver_switches; + + /* A temp data about the track ids for each CHANX and CHANY rr_node */ + std::map> rr_node_track_ids; + + /* Get the routing segments on X-axis and Y-axis separately */ + t_unified_to_parallel_seg_index segment_index_map; + std::vector segment_inf_x = get_parallel_segs(segment_inf, segment_index_map, X_AXIS, true); + std::vector segment_inf_y = get_parallel_segs(segment_inf, segment_index_map, Y_AXIS, true); + + /************************ + * Allocate the rr_nodes + ************************/ + alloc_tileable_rr_graph_nodes(device_ctx.rr_graph_builder, + rr_node_driver_switches, + grids, 0, + device_chan_width, + segment_inf_x, segment_inf_y, + device_grid_annotation, + shrink_boundary, + through_channel); + + /************************ + * Create all the rr_nodes + ************************/ + create_tileable_rr_graph_nodes(device_ctx.rr_graph, + device_ctx.rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + device_ctx.rr_rc_data, + grids, 0, + device_chan_width, + segment_inf_x, segment_inf_y, + segment_index_map, + wire_to_ipin_rr_switch, + delayless_rr_switch, + device_grid_annotation, + shrink_boundary, + through_channel); + + /************************************************************************ + * Create the connectivity of OPINs + * a. Evenly assign connections to OPINs to routing tracks + * b. the connection pattern should be same across the fabric + * + * Create the connectivity of IPINs + * a. Evenly assign connections from routing tracks to IPINs + * b. the connection pattern should be same across the fabric + ***********************************************************************/ + /* Global routing uses a single longwire track */ + int max_chan_width = find_unidir_routing_channel_width(chan_width.max); + VTR_ASSERT(max_chan_width > 0); + + /* get maximum number of pins across all blocks */ + int max_pins = types[0].num_pins; + for (const auto& type : types) { + if (is_empty_type(&type)) { + continue; + } + + if (type.num_pins > max_pins) { + max_pins = type.num_pins; + } + } + + /* Fc assignment still uses the old function from VPR. + * Should use tileable version so that we have can have full control + */ + std::vector num_tracks = get_num_tracks_per_seg_type(max_chan_width / 2, segment_inf, false); + int* sets_per_seg_type = (int*)vtr::malloc(sizeof(int) * segment_inf.size()); + VTR_ASSERT(num_tracks.size() == segment_inf.size()); + for (size_t iseg = 0; iseg < num_tracks.size(); ++iseg) { + sets_per_seg_type[iseg] = num_tracks[iseg]; + } + + bool Fc_clipped = false; + /* [0..num_types-1][0..num_pins-1] */ + std::vector> Fc_in; + Fc_in = alloc_and_load_actual_fc(types, max_pins, segment_inf, sets_per_seg_type, (const t_chan_width*)&chan_width, + e_fc_type::IN, UNI_DIRECTIONAL, &Fc_clipped, false); + if (Fc_clipped) { + *Warnings |= RR_GRAPH_WARN_FC_CLIPPED; + } + + Fc_clipped = false; + /* [0..num_types-1][0..num_pins-1] */ + std::vector> Fc_out; + Fc_out = alloc_and_load_actual_fc(types, max_pins, segment_inf, sets_per_seg_type, (const t_chan_width*)&chan_width, + e_fc_type::OUT, UNI_DIRECTIONAL, &Fc_clipped, false); + + if (Fc_clipped) { + *Warnings |= RR_GRAPH_WARN_FC_CLIPPED; + } + + /************************************************************************ + * Build the connections tile by tile: + * We classify rr_nodes into a general switch block (GSB) data structure + * where we create edges to each rr_nodes in the GSB with respect to + * Fc_in and Fc_out, switch block patterns + * In addition, we will also handle direct-connections: + * Add edges that bridge OPINs and IPINs to the rr_graph + ***********************************************************************/ + /* Create edges for a tileable rr_graph */ + build_rr_graph_edges(device_ctx.rr_graph, + device_ctx.rr_graph_builder, + rr_node_driver_switches, + grids, 0, + device_chan_width, + segment_inf, segment_inf_x, segment_inf_y, + Fc_in, Fc_out, + sb_type, Fs, sb_subtype, subFs, + wire_opposite_side); + + /************************************************************************ + * Build direction connection lists + * TODO: use tile direct builder + ***********************************************************************/ + /* Create data structure of direct-connections */ + t_clb_to_clb_directs* clb_to_clb_directs = NULL; + if (num_directs > 0) { + clb_to_clb_directs = alloc_and_load_clb_to_clb_directs(directs, num_directs, delayless_switch); + } + std::vector arch_directs; + std::vector clb2clb_directs; + for (int idirect = 0; idirect < num_directs; ++idirect) { + arch_directs.push_back(directs[idirect]); + clb2clb_directs.push_back(clb_to_clb_directs[idirect]); + } + + build_rr_graph_direct_connections(device_ctx.rr_graph, device_ctx.rr_graph_builder, device_ctx.grid, 0, delayless_rr_switch, + arch_directs, clb2clb_directs); + + /* Allocate and load routing resource switches, which are derived from the switches from the architecture file, + * based on their fanin in the rr graph. This routine also adjusts the rr nodes to point to these new rr switches */ + device_ctx.rr_graph_builder.init_fan_in(); + alloc_and_load_rr_switch_inf(device_ctx.rr_graph_builder, device_ctx.switch_fanin_remap, device_ctx.all_sw_inf, R_minW_nmos, R_minW_pmos, wire_to_arch_ipin_switch, wire_to_rr_ipin_switch); + + /* Save the channel widths for the newly constructed graph */ + device_ctx.chan_width = chan_width; + + /* Save the track ids for tileable routing resource graph */ + device_ctx.rr_node_track_ids = rr_node_track_ids; + + /* Build incoming edges */ + device_ctx.rr_graph_builder.partition_edges(); + device_ctx.rr_graph_builder.build_in_edges(); + + /************************************************************************ + * Allocate external data structures + * a. cost_index + * b. RC tree + ***********************************************************************/ + rr_graph_externals(segment_inf, segment_inf_x, segment_inf_y, + *wire_to_rr_ipin_switch, base_cost_type); + + /************************************************************************ + * Sanitizer for the rr_graph, check connectivities of rr_nodes + ***********************************************************************/ + /* Essential check for rr_graph, build look-up and */ + if (false == device_ctx.rr_graph_builder.validate()) { + /* Error out if built-in validator of rr_graph fails */ + vpr_throw(VPR_ERROR_ROUTE, + __FILE__, + __LINE__, + "Fundamental errors occurred when validating rr_graph object!\n"); + } + + /* No clock network support yet; Does not support flatten rr_graph yet */ + check_rr_graph(device_ctx.rr_graph, types, device_ctx.rr_indexed_data, grids, device_ctx.chan_width, GRAPH_UNIDIR, OPEN, false); + + /************************************************************************ + * Free all temp stucts + ***********************************************************************/ + free(sets_per_seg_type); + + if (nullptr != clb_to_clb_directs) { + free(clb_to_clb_directs); + } +} diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.h b/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.h new file mode 100644 index 00000000000..089dcb7a7c4 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_builder.h @@ -0,0 +1,37 @@ +#ifndef TILEABLE_RR_GRAPH_BUILDER_H +#define TILEABLE_RR_GRAPH_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "physical_types.h" +#include "device_grid.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void build_tileable_unidir_rr_graph(const std::vector& types, + const DeviceGrid& grids, + const t_chan_width& chan_width, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const std::vector& segment_inf, + const int& delayless_switch, + const int& wire_to_arch_ipin_switch, + const float R_minW_nmos, + const float R_minW_pmos, + const enum e_base_cost_type& base_cost_type, + const t_direct_inf* directs, + const int& num_directs, + int* wire_to_rr_ipin_switch, + const bool& shrink_boundary, + const bool& through_channel, + const bool& wire_opposite_side, + int* Warnings); + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp new file mode 100644 index 00000000000..36aab795680 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp @@ -0,0 +1,193 @@ +/************************************************************************ + * This file contains functions that are used to build edges + * between nodes of a tileable routing resource graph + ***********************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +#include "vpr_utils.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_rr_graph_gsb.h" +#include "tileable_rr_graph_edge_builder.h" + +/************************************************************************ + * Build the edges for all the SOURCE and SINKs nodes: + * 1. create edges between SOURCE and OPINs + ***********************************************************************/ +void build_rr_graph_edges_for_source_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + size_t& num_edges_to_create) { + size_t edge_count = 0; + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass all the non OPIN nodes */ + if (OPIN != rr_graph.node_type(node)) { + continue; + } + /* Now, we have an OPIN node, we get the source node index */ + short xlow = rr_graph.node_xlow(node); + short ylow = rr_graph.node_ylow(node); + short src_node_class_num = get_grid_pin_class_index(grids, layer, xlow, ylow, + rr_graph.node_pin_num(node)); + /* Create edges between SOURCE and OPINs */ + t_physical_tile_loc tile_loc(xlow, ylow, layer); + RRNodeId src_node = rr_graph.node_lookup().find_node(layer, + xlow - grids.get_width_offset(tile_loc), + ylow - grids.get_height_offset(tile_loc), + SOURCE, src_node_class_num); + VTR_ASSERT(true == rr_graph.valid_node(src_node)); + + /* add edges to the src_node */ + rr_graph_builder.create_edge(src_node, node, rr_node_driver_switches[node], false); + edge_count++; + } + /* Allocate edges for all the source nodes */ + rr_graph_builder.build_edges(true); + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build the edges for all the SINKs nodes: + * 1. create edges between IPINs and SINKs + ***********************************************************************/ +void build_rr_graph_edges_for_sink_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + size_t& num_edges_to_create) { + size_t edge_count = 0; + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass all the non IPIN nodes */ + if (IPIN != rr_graph.node_type(node)) { + continue; + } + /* Now, we have an OPIN node, we get the source node index */ + short xlow = rr_graph.node_xlow(node); + short ylow = rr_graph.node_ylow(node); + short sink_node_class_num = get_grid_pin_class_index(grids, layer, xlow, ylow, + rr_graph.node_pin_num(node)); + /* 1. create edges between IPINs and SINKs */ + t_physical_tile_loc tile_loc(xlow, ylow, 0); + const RRNodeId& sink_node = rr_graph.node_lookup().find_node(layer, + xlow - grids.get_width_offset(tile_loc), + ylow - grids.get_height_offset(tile_loc), + SINK, sink_node_class_num, SIDES[0]); + VTR_ASSERT(true == rr_graph.valid_node(sink_node)); + + /* add edges to connect the IPIN node to SINK nodes */ + rr_graph_builder.create_edge(node, sink_node, rr_node_driver_switches[sink_node], false); + edge_count++; + } + /* Allocate edges for all the source nodes */ + rr_graph_builder.build_edges(true); + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build the edges of each rr_node tile by tile: + * We classify rr_nodes into a general switch block (GSB) data structure + * where we create edges to each rr_nodes in the GSB with respect to + * Fc_in and Fc_out, switch block patterns + * For each GSB: + * 1. create edges between CHANX | CHANY and IPINs (connections inside connection blocks) + * 2. create edges between OPINs, CHANX and CHANY (connections inside switch blocks) + * 3. create edges between OPINs and IPINs (direct-connections) + ***********************************************************************/ +void build_rr_graph_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const std::vector>& Fc_in, + const std::vector>& Fc_out, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const bool& wire_opposite_side) { + size_t num_edges_to_create = 0; + /* Create edges for SOURCE and SINK nodes for a tileable rr_graph */ + build_rr_graph_edges_for_source_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, layer, num_edges_to_create); + build_rr_graph_edges_for_sink_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, layer, num_edges_to_create); + + vtr::Point gsb_range(grids.width() - 2, grids.height() - 2); + + /* Go Switch Block by Switch Block */ + for (size_t ix = 0; ix <= gsb_range.x(); ++ix) { + for (size_t iy = 0; iy <= gsb_range.y(); ++iy) { + //vpr_printf(TIO_MESSAGE_INFO, "Building edges for GSB[%lu][%lu]\n", ix, iy); + + vtr::Point gsb_coord(ix, iy); + /* Create a GSB object */ + const RRGSB& rr_gsb = build_one_tileable_rr_gsb(grids, rr_graph, + device_chan_width, segment_inf_x, segment_inf_y, + layer, gsb_coord); + + /* adapt the track_to_ipin_lookup for the GSB nodes */ + t_track2pin_map track2ipin_map; /* [0..track_gsb_side][0..num_tracks][ipin_indices] */ + track2ipin_map = build_gsb_track_to_ipin_map(rr_graph, rr_gsb, grids, segment_inf, Fc_in); + + /* adapt the opin_to_track_map for the GSB nodes */ + t_pin2track_map opin2track_map; /* [0..gsb_side][0..num_opin_node][track_indices] */ + opin2track_map = build_gsb_opin_to_track_map(rr_graph, rr_gsb, grids, segment_inf, Fc_out); + + /* adapt the switch_block_conn for the GSB nodes */ + t_track2track_map sb_conn; /* [0..from_gsb_side][0..chan_width-1][track_indices] */ + sb_conn = build_gsb_track_to_track_map(rr_graph, rr_gsb, + sb_type, Fs, sb_subtype, subFs, wire_opposite_side, + segment_inf); + + /* Build edges for a GSB */ + build_edges_for_one_tileable_rr_gsb(rr_graph_builder, rr_gsb, + track2ipin_map, opin2track_map, + sb_conn, rr_node_driver_switches, num_edges_to_create); + /* Finish this GSB, go to the next*/ + rr_graph_builder.build_edges(true); + } + } +} + +/************************************************************************ + * Build direct edges for Grids * + ***********************************************************************/ +void build_rr_graph_direct_connections(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const size_t& layer, + const RRSwitchId& delayless_switch, + const std::vector& directs, + const std::vector& clb_to_clb_directs) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + t_physical_tile_loc tile_loc(ix, iy, layer); + /* Skip EMPTY tiles */ + if (true == is_empty_type(grids.get_physical_type(tile_loc))) { + continue; + } + /* Skip height > 1 or width > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(tile_loc)) + || (0 < grids.get_height_offset(tile_loc))) { + continue; + } + vtr::Point from_grid_coordinate(ix, iy); + build_direct_connections_for_one_gsb(rr_graph, + rr_graph_builder, + grids, layer, + from_grid_coordinate, + delayless_switch, + directs, clb_to_clb_directs); + } + } +} diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.h b/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.h new file mode 100644 index 00000000000..7cbf219fb0f --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_edge_builder.h @@ -0,0 +1,63 @@ +#ifndef TILEABLE_RR_GRAPH_EDGE_BUILDER_H +#define TILEABLE_RR_GRAPH_EDGE_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_ndmatrix.h" +#include "vtr_geometry.h" + +#include "physical_types.h" +#include "device_grid.h" +#include "rr_graph_obj.h" +#include "rr_graph_type.h" +#include "rr_graph_view.h" +#include "rr_graph.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void build_rr_graph_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const std::vector>& Fc_in, + const std::vector>& Fc_out, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const bool& wire_opposite_side); + +void build_rr_graph_direct_connections(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const size_t& layer, + const RRSwitchId& delayless_switch, + const std::vector& directs, + const std::vector& clb_to_clb_directs); + +void build_rr_graph_edges_for_source_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + size_t& num_edges_to_create); + +void build_rr_graph_edges_for_sink_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + size_t& num_edges_to_create); + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp new file mode 100644 index 00000000000..fca7d569dfc --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -0,0 +1,1445 @@ +/************************************************************************ + * This file contains a builder for track-to-track connections inside a + * tileable General Switch Block (GSB). + ***********************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "openfpga_side_manager.h" + +#include "vpr_utils.h" +#include "rr_graph_view_util.h" +#include "openfpga_rr_graph_utils.h" +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_gsb.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Internal data structures + ***********************************************************************/ +typedef std::vector> t_track_group; + +/************************************************************************ + * A enumeration to list the status of a track inside a GSB + * 1. start; 2. end; 3. passing + * This is used to group tracks which ease the building of + * track-to-track mapping matrix + ***********************************************************************/ +enum e_track_status { + TRACK_START, + TRACK_END, + TRACK_PASS, + NUM_TRACK_STATUS /* just a place holder to get the number of status */ +}; + +/************************************************************************ + * Check if a track starts from this GSB or not + * (xlow, ylow) should be same as the GSB side coordinate + * + * Check if a track ends at this GSB or not + * (xhigh, yhigh) should be same as the GSB side coordinate + ***********************************************************************/ +static enum e_track_status determine_track_status_of_gsb(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& gsb_side, + const size_t& track_id) { + enum e_track_status track_status = TRACK_PASS; + /* Get the rr_node */ + RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + /* Get the coordinate of where the track starts */ + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* INC_DIRECTION start_track: (xlow, ylow) should be same as the GSB side coordinate */ + /* DEC_DIRECTION start_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + if ((track_start.x() == side_coordinate.x()) + && (track_start.y() == side_coordinate.y()) + && (OUT_PORT == rr_gsb.get_chan_node_direction(gsb_side, track_id))) { + /* Double check: start track should be an OUTPUT PORT of the GSB */ + track_status = TRACK_START; + } + + /* Get the coordinate of where the track ends */ + vtr::Point track_end = get_track_rr_node_end_coordinate(rr_graph, track_node); + + /* INC_DIRECTION end_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + /* DEC_DIRECTION end_track: (xlow, ylow) should be same as the GSB side coordinate */ + if ((track_end.x() == side_coordinate.x()) + && (track_end.y() == side_coordinate.y()) + && (IN_PORT == rr_gsb.get_chan_node_direction(gsb_side, track_id))) { + /* Double check: end track should be an INPUT PORT of the GSB */ + track_status = TRACK_END; + } + + return track_status; +} + +/************************************************************************ + * Check if the GSB is in the Connection Block (CB) population list of the segment + * SB population of a L4 wire: 1 0 0 1 + * + * +----+ +----+ +----+ +----+ + * | CB |--->| CB |--->| CB |--->| CB | + * +----+ +----+ +----+ +----+ + * Engage CB connection Yes No No Yes + * + * We will find the offset between gsb_side_coordinate and (xlow,ylow) of the track + * Use the offset to check if the tracks should engage in this GSB connection + ***********************************************************************/ +static bool is_gsb_in_track_cb_population(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_side& gsb_side, + const int& track_id, + const std::vector& segment_inf) { + /* Get the rr_node */ + RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* Get the offset */ + size_t offset = std::abs((int)side_coordinate.x() - (int)track_start.x()) + + std::abs((int)side_coordinate.y() - (int)track_start.y()); + + /* Get segment id */ + RRSegmentId seg_id = rr_gsb.get_chan_node_segment(gsb_side, track_id); + /* validate offset */ + VTR_ASSERT(offset < segment_inf[size_t(seg_id)].cb.size()); + + /* Get the SB population */ + bool in_cb_population = false; + if (true == segment_inf[size_t(seg_id)].cb[offset]) { + in_cb_population = true; + } + + return in_cb_population; +} + +/************************************************************************ + * Check if the GSB is in the Switch Block (SB) population list of the segment + * SB population of a L3 wire: 1 0 0 1 + * + * +----+ +----+ +----+ +----+ + * | SB |--->| SB |--->| SB |--->| SB | + * +----+ +----+ +----+ +----+ + * Engage SB connection Yes No No Yes + * + * We will find the offset between gsb_side_coordinate and (xlow,ylow) of the track + * Use the offset to check if the tracks should engage in this GSB connection + ***********************************************************************/ +static bool is_gsb_in_track_sb_population(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_side& gsb_side, + const int& track_id, + const std::vector& segment_inf) { + /* Get the rr_node */ + const RRNodeId& track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* Get the offset */ + size_t offset = std::abs((int)side_coordinate.x() - (int)track_start.x()) + + std::abs((int)side_coordinate.y() - (int)track_start.y()); + + /* Get segment id */ + RRSegmentId seg_id = rr_gsb.get_chan_node_segment(gsb_side, track_id); + /* validate offset */ + VTR_ASSERT(offset < segment_inf[size_t(seg_id)].sb.size()); + + /* Get the SB population */ + bool in_sb_population = false; + if (true == segment_inf[size_t(seg_id)].sb[offset]) { + in_sb_population = true; + } + + return in_sb_population; +} + +/************************************************************************ + * Create a list of track_id based on the to_track and num_to_tracks + * We consider the following list [to_track, to_track + Fs/3 - 1] + * if the [to_track + Fs/3 - 1] exceeds the num_to_tracks, we start over from 0! + ***********************************************************************/ +static std::vector get_to_track_list(const int& Fs, const int& to_track, const int& num_to_tracks) { + std::vector to_tracks; + + for (int i = 0; i < Fs; i = i + 3) { + /* TODO: currently, for Fs > 3, I always search the next from_track until Fs is satisfied + * The optimal track selection should be done in a more scientific way!!! + */ + int to_track_i = to_track + i; + /* make sure the track id is still in range */ + if (to_track_i > num_to_tracks - 1) { + to_track_i = to_track_i % num_to_tracks; + } + /* Ensure we are in the range */ + VTR_ASSERT(to_track_i < num_to_tracks); + /* from track must be connected */ + to_tracks.push_back(to_track_i); + } + return to_tracks; +} + +/************************************************************************ + * This function aims to return the track indices that drive the from_track + * in a Switch Block + * The track_ids to return will depend on different topologies of SB + * SUBSET, UNIVERSAL, and WILTON. + ***********************************************************************/ +static std::vector get_switch_block_to_track_id(const e_switch_block_type& switch_block_type, + const int& Fs, + const e_side& from_side, + const int& from_track, + const e_side& to_side, + const int& num_to_tracks) { + /* This routine returns the track number to which the from_track should + * connect. It supports any Fs % 3 == 0, switch blocks. + */ + std::vector to_tracks; + + /* TODO: currently, for Fs > 3, I always search the next from_track until Fs is satisfied + * The optimal track selection should be done in a more scientific way!!! + */ + VTR_ASSERT(0 == Fs % 3); + + /* Adapt from_track to fit in the range of num_to_tracks */ + size_t actual_from_track = from_track % num_to_tracks; + + switch (switch_block_type) { + case SUBSET: /* NB: Global routing uses SUBSET too */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + /* Finish, we return */ + return to_tracks; + case UNIVERSAL: + if ((from_side == LEFT) + || (from_side == RIGHT)) { + /* For the prev_side, to_track is from_track + * For the next_side, to_track is num_to_tracks - 1 - from_track + * For the opposite_side, to_track is always from_track + */ + SideManager side_manager(from_side); + if ((to_side == side_manager.get_opposite()) + || (to_side == side_manager.get_rotate_counterclockwise())) { + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == side_manager.get_rotate_clockwise()) { + to_tracks = get_to_track_list(Fs, num_to_tracks - 1 - actual_from_track, num_to_tracks); + } + } + + if ((from_side == TOP) + || (from_side == BOTTOM)) { + /* For the next_side, to_track is from_track + * For the prev_side, to_track is num_to_tracks - 1 - from_track + * For the opposite_side, to_track is always from_track + */ + SideManager side_manager(from_side); + if ((to_side == side_manager.get_opposite()) + || (to_side == side_manager.get_rotate_clockwise())) { + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == side_manager.get_rotate_counterclockwise()) { + to_tracks = get_to_track_list(Fs, num_to_tracks - 1 - actual_from_track, num_to_tracks); + } + } + /* Finish, we return */ + return to_tracks; + /* End switch_block_type == UNIVERSAL case. */ + case WILTON: + /* See S. Wilton Phd thesis, U of T, 1996 p. 103 for details on following. */ + if (from_side == LEFT) { + if (to_side == RIGHT) { /* CHANX to CHANX */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == TOP) { /* from CHANX to CHANY */ + to_tracks = get_to_track_list(Fs, (num_to_tracks - actual_from_track) % num_to_tracks, num_to_tracks); + } else if (to_side == BOTTOM) { + to_tracks = get_to_track_list(Fs, (num_to_tracks + actual_from_track - 1) % num_to_tracks, num_to_tracks); + } + } else if (from_side == RIGHT) { + if (to_side == LEFT) { /* CHANX to CHANX */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == TOP) { /* from CHANX to CHANY */ + to_tracks = get_to_track_list(Fs, (num_to_tracks + actual_from_track - 1) % num_to_tracks, num_to_tracks); + } else if (to_side == BOTTOM) { + to_tracks = get_to_track_list(Fs, (2 * num_to_tracks - 2 - actual_from_track) % num_to_tracks, num_to_tracks); + } + } else if (from_side == BOTTOM) { + if (to_side == TOP) { /* CHANY to CHANY */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == LEFT) { /* from CHANY to CHANX */ + to_tracks = get_to_track_list(Fs, (actual_from_track + 1) % num_to_tracks, num_to_tracks); + } else if (to_side == RIGHT) { + to_tracks = get_to_track_list(Fs, (2 * num_to_tracks - 2 - actual_from_track) % num_to_tracks, num_to_tracks); + } + } else if (from_side == TOP) { + if (to_side == BOTTOM) { /* CHANY to CHANY */ + to_tracks = get_to_track_list(Fs, from_track, num_to_tracks); + } else if (to_side == LEFT) { /* from CHANY to CHANX */ + to_tracks = get_to_track_list(Fs, (num_to_tracks - actual_from_track) % num_to_tracks, num_to_tracks); + } else if (to_side == RIGHT) { + to_tracks = get_to_track_list(Fs, (actual_from_track + 1) % num_to_tracks, num_to_tracks); + } + } + /* Finish, we return */ + return to_tracks; + /* End switch_block_type == WILTON case. */ + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid switch block pattern !\n"); + exit(1); + } + + return to_tracks; +} + +/************************************************************************ + * Build the track_to_track_map[from_side][0..chan_width-1][to_side][track_indices] + * For a group of from_track nodes and to_track nodes + * For each side of from_tracks, we call a routine to get the list of to_tracks + * Then, we fill the track2track_map + ***********************************************************************/ +static void build_gsb_one_group_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const bool& wire_opposite_side, + const t_track_group& from_tracks, /* [0..gsb_side][track_indices] */ + const t_track_group& to_tracks, /* [0..gsb_side][track_indices] */ + t_track2track_map& track2track_map) { + for (size_t side = 0; side < from_tracks.size(); ++side) { + SideManager side_manager(side); + e_side from_side = side_manager.get_side(); + /* Find the other sides where the start tracks will locate */ + std::vector to_track_sides; + /* 0. opposite side */ + to_track_sides.push_back(side_manager.get_opposite()); + /* 1. prev side */ + /* Previous side definition: TOP => LEFT; RIGHT=>TOP; BOTTOM=>RIGHT; LEFT=>BOTTOM */ + to_track_sides.push_back(side_manager.get_rotate_counterclockwise()); + /* 2. next side */ + /* Next side definition: TOP => RIGHT; RIGHT=>BOTTOM; BOTTOM=>LEFT; LEFT=>TOP */ + to_track_sides.push_back(side_manager.get_rotate_clockwise()); + + for (size_t inode = 0; inode < from_tracks[side].size(); ++inode) { + for (size_t to_side_id = 0; to_side_id < to_track_sides.size(); ++to_side_id) { + enum e_side to_side = to_track_sides[to_side_id]; + SideManager to_side_manager(to_side); + size_t to_side_index = to_side_manager.to_size_t(); + /* Bypass those to_sides have no nodes */ + if (0 == to_tracks[to_side_index].size()) { + continue; + } + /* Bypass those from_side is same as to_side */ + if (from_side == to_side) { + continue; + } + /* Bypass those from_side is opposite to to_side if required */ + if ((true == wire_opposite_side) + && (to_side_manager.get_opposite() == from_side)) { + continue; + } + /* Get other track_ids depending on the switch block pattern */ + /* Find the track ids that will start at the other sides */ + std::vector to_track_ids = get_switch_block_to_track_id(sb_type, Fs, from_side, inode, + to_side, + to_tracks[to_side_index].size()); + /* Update the track2track_map: */ + for (size_t to_track_id = 0; to_track_id < to_track_ids.size(); ++to_track_id) { + size_t from_side_index = side_manager.to_size_t(); + size_t from_track_index = from_tracks[side][inode]; + /* Check the id is still in the range !*/ + VTR_ASSERT(to_track_ids[to_track_id] < to_tracks[to_side_index].size()); + size_t to_track_index = to_tracks[to_side_index][to_track_ids[to_track_id]]; + //printf("from_track(size=%lu): %lu , to_track_ids[%lu]:%lu, to_track_index: %lu in a group of %lu tracks\n", + // from_tracks[side].size(), inode, to_track_id, to_track_ids[to_track_id], + // to_track_index, to_tracks[to_side_index].size()); + const RRNodeId& to_track_node = rr_gsb.get_chan_node(to_side, to_track_index); + VTR_ASSERT(true == rr_graph.valid_node(to_track_node)); + + /* from_track should be IN_PORT */ + VTR_ASSERT(IN_PORT == rr_gsb.get_chan_node_direction(from_side, from_track_index)); + /* to_track should be OUT_PORT */ + VTR_ASSERT(OUT_PORT == rr_gsb.get_chan_node_direction(to_side, to_track_index)); + + //VTR_LOG("Consider a connection from pass tracks %d on side %s to track node %ld on side %s\n", from_track_index, SIDE_STRING[from_side], size_t(to_track_node), SIDE_STRING[to_side]); + + /* Check if the to_track_node is already in the list ! */ + std::vector::iterator it = std::find(track2track_map[from_side_index][from_track_index].begin(), + track2track_map[from_side_index][from_track_index].end(), + to_track_node); + if (it != track2track_map[from_side_index][from_track_index].end()) { + continue; /* the node_id is already in the list, go for the next */ + } + /* Clear, we should add to the list */ + track2track_map[from_side_index][from_track_index].push_back(to_track_node); + //VTR_LOG("Built a connection from pass tracks %d on side %s to track node %ld on side %s\n", from_track_index, SIDE_STRING[from_side], size_t(to_track_node), SIDE_STRING[to_side]); + } + } + } + } +} + +/************************************************************************ + * Build the track_to_track_map[from_side][0..chan_width-1][to_side][track_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * The track_indices is the indices of tracks that the node at from_side and [0..chan_width-1] will drive + * IMPORTANT: the track_indices are the indicies in the GSB context, but not the rr_graph!!! + * We separate the connections into two groups: + * Group 1: the routing tracks start from this GSB + * We will apply switch block patterns (SUBSET, UNIVERSAL, WILTON) + * Group 2: the routing tracks do not start from this GSB (bypassing wires) + * We will apply switch block patterns (SUBSET, UNIVERSAL, WILTON) + * but we will check the Switch Block (SB) population of these + * routing segments, and determine which requires connections + * + * CHANY CHANY CHANY CHANY + * [0] [1] [2] [3] + * start yes no yes no + * end +-------------------------+ start Group 1 Group 2 + * no CHANX[0] | TOP | CHANX[0] yes TOP/BOTTOM TOP/BOTTOM + * | | CHANY[0,2] CHANY[1,3] + * yes CHANX[1] | | CHANX[1] no + * | LEFT RIGHT | + * no CHANX[2] | | CHANX[2] yes + * | | + * yes CHANX[3] | BOTTOM | CHANX[3] no + * +-------------------------+ + * CHANY CHANY CHANY CHANY + * [0] [1] [2] [3] + * start yes no yes no + * + * The mapping is done in the following steps: (For each side of the GSB) + * 1. Build a list of tracks that will start from this side + * if a track starts, its xlow/ylow is the same as the x,y of this gsb + * 2. Build a list of tracks on the other sides belonging to Group 1. + * Take the example of RIGHT side, we will collect + * a. tracks that will end at the LEFT side + * b. tracks that will start at the TOP side + * c. tracks that will start at the BOTTOM side + * 3. Apply switch block patterns to Group 1 (SUBSET, UNIVERSAL, WILTON) + * 4. Build a list of tracks on the other sides belonging to Group 1. + * Take the example of RIGHT side, we will collect + * a. tracks that will bypass at the TOP side + * b. tracks that will bypass at the BOTTOM side + * 5. Apply switch block patterns to Group 2 (SUBSET, UNIVERSAL, WILTON) + ***********************************************************************/ +t_track2track_map build_gsb_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const bool& wire_opposite_side, + const std::vector& segment_inf) { + t_track2track_map track2track_map; /* [0..gsb_side][0..chan_width][track_indices] */ + + /* Categorize tracks into 3 groups: + * (1) tracks will start here + * (2) tracks will end here + * (2) tracks will just pass through the SB */ + t_track_group start_tracks; /* [0..gsb_side][track_indices] */ + t_track_group end_tracks; /* [0..gsb_side][track_indices] */ + t_track_group pass_tracks; /* [0..gsb_side][track_indices] */ + + /* resize to the number of sides */ + start_tracks.resize(rr_gsb.get_num_sides()); + end_tracks.resize(rr_gsb.get_num_sides()); + pass_tracks.resize(rr_gsb.get_num_sides()); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + e_side gsb_side = side_manager.get_side(); + /* Build a list of tracks that will start from this side */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + /* We need to check Switch block population of this track + * The track node will not be considered if there supposed to be no SB at this position + */ + if (false == is_gsb_in_track_sb_population(rr_graph, rr_gsb, gsb_side, inode, segment_inf)) { + continue; /* skip this node and go to the next */ + } + /* check if this track will start from here */ + enum e_track_status track_status = determine_track_status_of_gsb(rr_graph, rr_gsb, gsb_side, inode); + + switch (track_status) { + case TRACK_START: + /* update starting track list */ + start_tracks[side].push_back(inode); + break; + case TRACK_END: + /* Update end track list */ + end_tracks[side].push_back(inode); + break; + case TRACK_PASS: + /* Update passing track list */ + /* Note that the pass_track should be IN_PORT only !!! */ + if (IN_PORT == rr_gsb.get_chan_node_direction(gsb_side, inode)) { + pass_tracks[side].push_back(inode); + } + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid track status!\n"); + exit(1); + } + } + } + + /* Allocate track2track map */ + track2track_map.resize(rr_gsb.get_num_sides()); + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side gsb_side = side_manager.get_side(); + /* allocate track2track_map[gsb_side] */ + track2track_map[side].resize(rr_gsb.get_chan_width(gsb_side)); + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + /* allocate track2track_map[gsb_side][inode] */ + track2track_map[side][inode].clear(); + } + } + + /* For Group 1: we build connections between end_tracks and start_tracks*/ + build_gsb_one_group_track_to_track_map(rr_graph, rr_gsb, + sb_type, Fs, + true, /* End tracks should always to wired to start tracks */ + end_tracks, start_tracks, + track2track_map); + + /* For Group 2: we build connections between end_tracks and start_tracks*/ + /* Currently, I use the same Switch Block pattern for the passing tracks and end tracks, + * TODO: This can be improved with different patterns! + */ + //for (e_side curr_side : SIDES) { + // VTR_LOG("Number of pass tracks %d on side %s\n", pass_tracks[size_t(curr_side)].size(), SIDE_STRING[curr_side]); + //} + build_gsb_one_group_track_to_track_map(rr_graph, rr_gsb, + sb_subtype, subFs, + wire_opposite_side, /* Pass tracks may not be wired to start tracks */ + pass_tracks, start_tracks, + track2track_map); + + return track2track_map; +} + +/* Build a RRChan Object with the given channel type and coorindators */ +static RRChan build_one_tileable_rr_chan(const size_t& layer, + const vtr::Point& chan_coordinate, + const t_rr_type& chan_type, + const RRGraphView& rr_graph, + const ChanNodeDetails& chan_details) { + std::vector chan_rr_nodes; + + /* Create a rr_chan object and check if it is unique in the graph */ + RRChan rr_chan; + + /* Fill the information */ + rr_chan.set_type(chan_type); + + /* Collect rr_nodes for this channel */ + chan_rr_nodes = find_rr_graph_chan_nodes(rr_graph, + layer, chan_coordinate.x(), chan_coordinate.y(), + chan_type); + + /* Reserve */ + /* rr_chan.reserve_node(size_t(chan_width)); */ + + /* Fill the rr_chan */ + for (size_t itrack = 0; itrack < chan_rr_nodes.size(); ++itrack) { + size_t iseg = chan_details.get_track_segment_id(itrack); + rr_chan.add_node(rr_graph, chan_rr_nodes[itrack], RRSegmentId(iseg)); + } + + return rr_chan; +} + +/*********************************************************************** + * Build a General Switch Block (GSB) + * which includes: + * [I] A Switch Box subckt consists of following ports: + * 1. Channel Y [x][y] inputs + * 2. Channel X [x+1][y] inputs + * 3. Channel Y [x][y-1] outputs + * 4. Channel X [x][y] outputs + * 5. Grid[x][y+1] Right side outputs pins + * 6. Grid[x+1][y+1] Left side output pins + * 7. Grid[x+1][y+1] Bottom side output pins + * 8. Grid[x+1][y] Top side output pins + * 9. Grid[x+1][y] Left side output pins + * 10. Grid[x][y] Right side output pins + * 11. Grid[x][y] Top side output pins + * 12. Grid[x][y+1] Bottom side output pins + * + * -------------- -------------- + * | | CBY | | + * | Grid | ChanY | Grid | + * | [x][y+1] | [x][y+1] | [x+1][y+1] | + * | | | | + * -------------- -------------- + * ---------- + * ChanX & CBX | Switch | ChanX + * [x][y] | Box | [x+1][y] + * | [x][y] | + * ---------- + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y] | [x][y] | [x+1][y] | + * | | | | + * -------------- -------------- + * For channels chanY with INC_DIRECTION on the top side, they should be marked as outputs + * For channels chanY with DEC_DIRECTION on the top side, they should be marked as inputs + * For channels chanY with INC_DIRECTION on the bottom side, they should be marked as inputs + * For channels chanY with DEC_DIRECTION on the bottom side, they should be marked as outputs + * For channels chanX with INC_DIRECTION on the left side, they should be marked as inputs + * For channels chanX with DEC_DIRECTION on the left side, they should be marked as outputs + * For channels chanX with INC_DIRECTION on the right side, they should be marked as outputs + * For channels chanX with DEC_DIRECTION on the right side, they should be marked as inputs + * + * [II] A X-direction Connection Block [x][y] + * The connection block shares the same routing channel[x][y] with the Switch Block + * We just need to fill the ipin nodes at TOP and BOTTOM sides + * as well as properly fill the ipin_grid_side information + * [III] A Y-direction Connection Block [x][y+1] + * The connection block shares the same routing channel[x][y+1] with the Switch Block + * We just need to fill the ipin nodes at LEFT and RIGHT sides + * as well as properly fill the ipin_grid_side information + ***********************************************************************/ +RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, + const RRGraphView& rr_graph, + const vtr::Point& device_chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const size_t& layer, + const vtr::Point& gsb_coordinate) { + /* Create an object to return */ + RRGSB rr_gsb; + + /* Check */ + VTR_ASSERT(gsb_coordinate.x() <= grids.width()); + VTR_ASSERT(gsb_coordinate.y() <= grids.height()); + + /* Coordinator initialization */ + rr_gsb.set_coordinate(gsb_coordinate.x(), gsb_coordinate.y()); + + /* Basic information*/ + rr_gsb.init_num_sides(4); /* Fixed number of sides */ + + /* Find all rr_nodes of channels */ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + /* Local variables inside this for loop */ + SideManager side_manager(side); + vtr::Point coordinate = rr_gsb.get_side_block_coordinate(side_manager.get_side()); + RRChan rr_chan; + std::vector> temp_opin_rr_nodes(2); + enum e_side opin_grid_side[2] = {NUM_SIDES, NUM_SIDES}; + enum PORTS chan_dir_to_port_dir_mapping[2] = {OUT_PORT, IN_PORT}; /* 0: INC_DIRECTION => ?; 1: DEC_DIRECTION => ? */ + + /* Build a segment details, where we need the segment ids for building rr_chan + * We do not care starting and ending points here, so set chan_side as NUM_SIDES + */ + ChanNodeDetails chanx_details = build_unidir_chan_node_details(device_chan_width.x(), grids.width() - 1, + false, false, segment_inf_x); + ChanNodeDetails chany_details = build_unidir_chan_node_details(device_chan_width.y(), grids.height() - 1, + false, false, segment_inf_y); + + switch (side) { + case TOP: /* TOP = 0 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.y() == grids.height() - 1) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(layer, coordinate, CHANY, rr_graph, chany_details); + chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */ + chan_dir_to_port_dir_mapping[1] = IN_PORT; /* DEC_DIRECTION => IN_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x][y+1] RIGHT side outputs pins */ + opin_grid_side[0] = RIGHT; + /* Grid[x+1][y+1] left side outputs pins */ + opin_grid_side[1] = LEFT; + + /* Build the Switch block: opin and opin_grid_side */ + /* Include Grid[x][y+1] RIGHT side outputs pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x(), gsb_coordinate.y() + 1, + OPIN, opin_grid_side[0]); + /* Include Grid[x+1][y+1] Left side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y() + 1, + OPIN, opin_grid_side[1]); + + break; + case RIGHT: /* RIGHT = 1 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.x() == grids.width() - 1) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for top: chany[x][y+1] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(layer, coordinate, CHANX, rr_graph, chanx_details); + chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */ + chan_dir_to_port_dir_mapping[1] = IN_PORT; /* DEC_DIRECTION => IN_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x+1][y+1] BOTTOM side outputs pins */ + opin_grid_side[0] = BOTTOM; + /* Grid[x+1][y] TOP side outputs pins */ + opin_grid_side[1] = TOP; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x+1][y+1] Bottom side output pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y() + 1, + OPIN, opin_grid_side[0]); + /* include Grid[x+1][y] Top side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y(), + OPIN, opin_grid_side[1]); + break; + case BOTTOM: /* BOTTOM = 2*/ + /* For the bording, we should take special care */ + if (gsb_coordinate.y() == 0) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for bottom: chany[x][y] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(layer, coordinate, CHANY, rr_graph, chany_details); + chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */ + chan_dir_to_port_dir_mapping[1] = OUT_PORT; /* DEC_DIRECTION => OUT_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x+1][y] LEFT side outputs pins */ + opin_grid_side[0] = LEFT; + /* Grid[x][y] RIGHT side outputs pins */ + opin_grid_side[1] = RIGHT; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x+1][y] Left side output pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y(), + OPIN, opin_grid_side[0]); + /* include Grid[x][y] Right side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x(), gsb_coordinate.y(), + OPIN, opin_grid_side[1]); + break; + case LEFT: /* LEFT = 3 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.x() == 0) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for left: chanx[x][y] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(layer, coordinate, CHANX, rr_graph, chanx_details); + chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */ + chan_dir_to_port_dir_mapping[1] = OUT_PORT; /* DEC_DIRECTION => OUT_PORT */ + + /* Grid[x][y+1] BOTTOM side outputs pins */ + opin_grid_side[0] = BOTTOM; + /* Grid[x][y] TOP side outputs pins */ + opin_grid_side[1] = TOP; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x][y+1] Bottom side outputs pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x(), gsb_coordinate.y() + 1, + OPIN, opin_grid_side[0]); + /* include Grid[x][y] Top side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x(), gsb_coordinate.y(), + OPIN, opin_grid_side[1]); + + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid side index!\n"); + exit(1); + } + + /* Organize a vector of port direction */ + if (0 < rr_chan.get_chan_width()) { + std::vector rr_chan_dir; + rr_chan_dir.resize(rr_chan.get_chan_width()); + for (size_t itrack = 0; itrack < rr_chan.get_chan_width(); ++itrack) { + /* Identify the directionality, record it in rr_node_direction */ + if (Direction::INC == rr_graph.node_direction(rr_chan.get_node(itrack))) { + rr_chan_dir[itrack] = chan_dir_to_port_dir_mapping[0]; + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(rr_chan.get_node(itrack))); + rr_chan_dir[itrack] = chan_dir_to_port_dir_mapping[1]; + } + } + /* Fill chan_rr_nodes */ + rr_gsb.add_chan_node(side_manager.get_side(), rr_chan, rr_chan_dir); + } + + /* Fill opin_rr_nodes */ + /* Copy from temp_opin_rr_node to opin_rr_node */ + for (const RRNodeId& inode : temp_opin_rr_nodes[0]) { + /* Grid[x+1][y+1] Bottom side outputs pins */ + rr_gsb.add_opin_node(inode, side_manager.get_side()); + } + for (const RRNodeId& inode : temp_opin_rr_nodes[1]) { + /* Grid[x+1][y] TOP side outputs pins */ + rr_gsb.add_opin_node(inode, side_manager.get_side()); + } + + /* Clean ipin_rr_nodes */ + /* We do not have any IPIN for a Switch Block */ + rr_gsb.clear_ipin_nodes(side_manager.get_side()); + + /* Clear the temp data */ + temp_opin_rr_nodes[0].clear(); + temp_opin_rr_nodes[1].clear(); + opin_grid_side[0] = NUM_SIDES; + opin_grid_side[1] = NUM_SIDES; + } + + /* Add IPIN nodes from adjacent grids: the 4 grids sitting on the 4 corners of the Switch Block + * + * - The concept of top/bottom side of connection block in GSB domain: + * + * | Grid[x][y+1] | + * | BOTTOM side | + * +-----------------------+ + * | + * v + * +-----------------------+ + * | TOP side | + * | X- Connection Block | + * | BOTTOM side | + * +-----------------------+ + * ^ + * | + * +-----------------------+ + * | TOP side | + * | Grid[x][y] | + * + * - The concept of top/bottom side of connection block in GSB domain: + * + * ---------------+ +---------------------- ... ---------------------+ +---------------- + * Grid[x][y+1] |->| Y- Connection Block Y- Connection Block |<-| Grid[x+1][y+1] + * RIGHT side | | LEFT side ... RIGHT side | | LEFT side + * --------------+ +---------------------- ... ---------------------+ +---------------- + * + */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + size_t ix; + size_t iy; + enum e_side chan_side; + std::vector temp_ipin_rr_nodes; + enum e_side ipin_rr_node_grid_side; + + switch (side) { + case TOP: + /* Consider the routing channel that is connected to the left side of the switch block */ + chan_side = LEFT; + /* The input pins of the routing channel come from the bottom side of Grid[x][y+1] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y() + 1; + ipin_rr_node_grid_side = BOTTOM; + break; + case RIGHT: + /* Consider the routing channel that is connected to the top side of the switch block */ + chan_side = TOP; + /* The input pins of the routing channel come from the left side of Grid[x+1][y+1] */ + ix = rr_gsb.get_sb_x() + 1; + iy = rr_gsb.get_sb_y() + 1; + ipin_rr_node_grid_side = LEFT; + break; + case BOTTOM: + /* Consider the routing channel that is connected to the left side of the switch block */ + chan_side = LEFT; + /* The input pins of the routing channel come from the top side of Grid[x][y] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y(); + ipin_rr_node_grid_side = TOP; + break; + case LEFT: + /* Consider the routing channel that is connected to the top side of the switch block */ + chan_side = TOP; + /* The input pins of the routing channel come from the right side of Grid[x][y+1] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y() + 1; + ipin_rr_node_grid_side = RIGHT; + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid side index!\n"); + exit(1); + } + + /* If there is no channel at this side, we skip ipin_node annotation */ + if (0 == rr_gsb.get_chan_width(chan_side)) { + continue; + } + /* Collect IPIN rr_nodes*/ + temp_ipin_rr_nodes = find_rr_graph_grid_nodes(rr_graph, grids, + layer, ix, iy, IPIN, ipin_rr_node_grid_side); + /* Fill the ipin nodes of RRGSB */ + for (const RRNodeId& inode : temp_ipin_rr_nodes) { + rr_gsb.add_ipin_node(inode, side_manager.get_side()); + } + /* Clear the temp data */ + temp_ipin_rr_nodes.clear(); + } + + return rr_gsb; +} + +/************************************************************************ + * Create edges for each rr_node of a General Switch Blocks (GSB): + * 1. create edges between CHANX | CHANY and IPINs (connections inside connection blocks) + * 2. create edges between OPINs, CHANX and CHANY (connections inside switch blocks) + * 3. create edges between OPINs and IPINs (direct-connections) + ***********************************************************************/ +void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const t_track2pin_map& track2ipin_map, + const t_pin2track_map& opin2track_map, + const t_track2track_map& track2track_map, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges_to_create) { + size_t edge_count = 0; + /* Walk through each sides */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side gsb_side = side_manager.get_side(); + + /* Find OPINs */ + for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(gsb_side); ++inode) { + const RRNodeId& opin_node = rr_gsb.get_opin_node(gsb_side, inode); + + /* 1. create edges between OPINs and CHANX|CHANY, using opin2track_map */ + /* add edges to the opin_node */ + for (const RRNodeId& track_node : opin2track_map[gsb_side][inode]) { + rr_graph_builder.create_edge(opin_node, track_node, rr_node_driver_switches[track_node], false); + edge_count++; + } + } + + /* Find CHANX or CHANY */ + /* For TRACKs to IPINs, we only care LEFT and TOP sides + * Skip RIGHT and BOTTOM for the ipin2track_map since they should be handled in other GSBs + */ + if ((side_manager.get_side() == rr_gsb.get_cb_chan_side(CHANX)) + || (side_manager.get_side() == rr_gsb.get_cb_chan_side(CHANY))) { + /* 2. create edges between CHANX|CHANY and IPINs, using ipin2track_map */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); + for (const RRNodeId& ipin_node : track2ipin_map[gsb_side][inode]) { + rr_graph_builder.create_edge(chan_node, ipin_node, rr_node_driver_switches[ipin_node], false); + edge_count++; + } + } + } + + /* 3. create edges between CHANX|CHANY and CHANX|CHANY, using track2track_map */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); + for (const RRNodeId& track_node : track2track_map[gsb_side][inode]) { + rr_graph_builder.create_edge(chan_node, track_node, rr_node_driver_switches[track_node], false); + edge_count++; + } + } + } + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build track2ipin_map for an IPIN + * 1. build a list of routing tracks which are allowed for connections + * We will check the Connection Block (CB) population of each routing track. + * By comparing current chan_y - ylow, we can determine if a CB connection + * is required for each routing track + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between IPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +static void build_gsb_one_ipin_track2pin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& ipin_side, + const size_t& ipin_node_id, + const std::vector& Fc, + const size_t& offset, + const std::vector& segment_inf, + t_track2pin_map& track2ipin_map) { + /* Get a list of segment_ids*/ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + std::vector seg_list = rr_gsb.get_chan_segment_ids(chan_side); + size_t chan_width = rr_gsb.get_chan_width(chan_side); + SideManager ipin_side_manager(ipin_side); + const RRNodeId& ipin_node = rr_gsb.get_ipin_node(ipin_side, ipin_node_id); + + for (size_t iseg = 0; iseg < seg_list.size(); ++iseg) { + /* Get a list of node that have the segment id */ + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(chan_side, seg_list[iseg]); + /* Refine the track_list: keep those will have connection blocks in the GSB */ + std::vector actual_track_list; + for (size_t inode = 0; inode < track_list.size(); ++inode) { + /* Check if tracks allow connection blocks in the GSB*/ + if (false == is_gsb_in_track_cb_population(rr_graph, rr_gsb, chan_side, track_list[inode], segment_inf)) { + continue; /* Bypass condition */ + } + /* Push the node to actual_track_list */ + actual_track_list.push_back(track_list[inode]); + } + /* Check the actual track list */ + VTR_ASSERT(0 == actual_track_list.size() % 2); + + /* Scale Fc */ + int actual_Fc = std::ceil((float)Fc[iseg] * (float)actual_track_list.size() / (float)chan_width); + /* Minimum Fc should be 2 : ensure we will connect to a pair of routing tracks */ + actual_Fc = std::max(1, actual_Fc); + /* Compute the step between two connection from this IPIN to tracks: + * step = W' / Fc', W' and Fc' are the adapted W and Fc from actual_track_list and Fc_in + */ + size_t track_step = std::floor((float)actual_track_list.size() / (float)actual_Fc); + /* Make sure step should be at least 2 */ + track_step = std::max(1, (int)track_step); + /* Adapt offset to the range of actual_track_list */ + size_t actual_offset = offset % actual_track_list.size(); + /* rotate the track list by an offset */ + if (0 < actual_offset) { + std::rotate(actual_track_list.begin(), actual_track_list.begin() + actual_offset, actual_track_list.end()); + } + + /* Assign tracks: since we assign 2 track per round, we increment itrack by 2* step */ + int track_cnt = 0; + /* Keep assigning until we meet the Fc requirement */ + for (size_t itrack = 0; itrack < actual_track_list.size(); itrack = itrack + 2 * track_step) { + /* Update pin2track map */ + size_t chan_side_index = chan_side_manager.to_size_t(); + /* itrack may exceed the size of actual_track_list, adapt it */ + size_t actual_itrack = itrack % actual_track_list.size(); + /* track_index may exceed the chan_width(), adapt it */ + size_t track_index = actual_track_list[actual_itrack] % chan_width; + + track2ipin_map[chan_side_index][track_index].push_back(ipin_node); + + /* track_index may exceed the chan_width(), adapt it */ + track_index = (actual_track_list[actual_itrack] + 1) % chan_width; + + track2ipin_map[chan_side_index][track_index].push_back(ipin_node); + + track_cnt += 2; + } + + /* Ensure the number of tracks is similar to Fc */ + /* Give a warning if Fc is < track_cnt */ + /* + * if (actual_Fc != track_cnt) { + * vpr_printf(TIO_MESSAGE_INFO, + * "IPIN Node(%lu) will have a different Fc(=%lu) than specified(=%lu)!\n", + * ipin_node - rr_graph->rr_node, track_cnt, actual_Fc); + * } + */ + } +} + +/************************************************************************ + * Build opin2track_map for an OPIN + * 1. build a list of routing tracks which are allowed for connections + * We will check the Switch Block (SB) population of each routing track. + * By comparing current chan_y - ylow, we can determine if a SB connection + * is required for each routing track + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between OPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +static void build_gsb_one_opin_pin2track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& opin_side, + const size_t& opin_node_id, + const std::vector& Fc, + const size_t& offset, + const std::vector& segment_inf, + t_pin2track_map& opin2track_map) { + /* Get a list of segment_ids*/ + std::vector seg_list = rr_gsb.get_chan_segment_ids(opin_side); + enum e_side chan_side = opin_side; + size_t chan_width = rr_gsb.get_chan_width(chan_side); + SideManager opin_side_manager(opin_side); + + for (size_t iseg = 0; iseg < seg_list.size(); ++iseg) { + /* Get a list of node that have the segment id */ + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(chan_side, seg_list[iseg]); + /* Refine the track_list: keep those will have connection blocks in the GSB */ + std::vector actual_track_list; + for (size_t inode = 0; inode < track_list.size(); ++inode) { + /* Check if tracks allow connection blocks in the GSB*/ + if (false == is_gsb_in_track_sb_population(rr_graph, rr_gsb, chan_side, track_list[inode], segment_inf)) { + continue; /* Bypass condition */ + } + if (TRACK_START != determine_track_status_of_gsb(rr_graph, rr_gsb, chan_side, track_list[inode])) { + continue; /* Bypass condition */ + } + /* Push the node to actual_track_list */ + actual_track_list.push_back(track_list[inode]); + } + + /* Go the next segment if offset is zero or actual_track_list is empty */ + if (0 == actual_track_list.size()) { + continue; + } + + /* Scale Fc */ + int actual_Fc = std::ceil((float)Fc[iseg] * (float)actual_track_list.size() / (float)chan_width); + /* Minimum Fc should be 1 : ensure we will drive 1 routing track */ + actual_Fc = std::max(1, actual_Fc); + /* Compute the step between two connection from this IPIN to tracks: + * step = W' / Fc', W' and Fc' are the adapted W and Fc from actual_track_list and Fc_in + */ + size_t track_step = std::floor((float)actual_track_list.size() / (float)actual_Fc); + /* Track step mush be a multiple of 2!!!*/ + /* Make sure step should be at least 1 */ + track_step = std::max(1, (int)track_step); + /* Adapt offset to the range of actual_track_list */ + size_t actual_offset = offset % actual_track_list.size(); + + /* No need to rotate if offset is zero */ + if (0 < actual_offset) { + /* rotate the track list by an offset */ + std::rotate(actual_track_list.begin(), actual_track_list.begin() + actual_offset, actual_track_list.end()); + } + + /* Assign tracks */ + int track_cnt = 0; + /* Keep assigning until we meet the Fc requirement */ + for (size_t itrack = 0; itrack < actual_track_list.size(); itrack = itrack + track_step) { + /* Update pin2track map */ + size_t opin_side_index = opin_side_manager.to_size_t(); + /* itrack may exceed the size of actual_track_list, adapt it */ + size_t actual_itrack = itrack % actual_track_list.size(); + size_t track_index = actual_track_list[actual_itrack]; + const RRNodeId& track_rr_node_index = rr_gsb.get_chan_node(chan_side, track_index); + opin2track_map[opin_side_index][opin_node_id].push_back(track_rr_node_index); + /* update track counter */ + track_cnt++; + /* Stop when we have enough Fc: this may lead to some tracks have zero drivers. + * So I comment it. And we just make sure its track_cnt >= actual_Fc + * if (actual_Fc == track_cnt) { + * break; + * } + */ + } + + /* Ensure the number of tracks is similar to Fc */ + /* Give a warning if Fc is < track_cnt */ + /* + * if (actual_Fc != track_cnt) { + * vpr_printf(TIO_MESSAGE_INFO, + * "OPIN Node(%lu) will have a different Fc(=%lu) than specified(=%lu)!\n", + * opin_node_id, track_cnt, actual_Fc); + * } + */ + } +} + +/************************************************************************ + * Build the track_to_ipin_map[gsb_side][0..chan_width-1][ipin_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * This function supports both X-directional and Y-directional tracks + * The mapping is done in the following steps: + * 1. Build ipin_to_track_map[gsb_side][0..num_ipin_nodes-1][track_indices] + * For each IPIN, we ensure at least one connection to the tracks. + * Then, we assign IPINs to tracks evenly while satisfying the actual_Fc + * 2. Convert the ipin_to_track_map to track_to_ipin_map + ***********************************************************************/ +t_track2pin_map build_gsb_track_to_ipin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_in) { + t_track2pin_map track2ipin_map; + /* Resize the matrix */ + track2ipin_map.resize(rr_gsb.get_num_sides()); + + /* offset counter: it aims to balance the track-to-IPIN for each connection block */ + size_t offset_size = 0; + std::vector offset; + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side ipin_side = side_manager.get_side(); + /* Get the chan_side */ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + /* resize offset to the maximum chan_side*/ + offset_size = std::max(offset_size, chan_side_manager.to_size_t() + 1); + } + /* Initial offset */ + offset.resize(offset_size); + offset.assign(offset.size(), 0); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side ipin_side = side_manager.get_side(); + /* Get the chan_side */ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + /* This track2pin mapping is for Connection Blocks, so we only care two sides! */ + /* Get channel width and resize the matrix */ + size_t chan_width = rr_gsb.get_chan_width(chan_side); + track2ipin_map[chan_side_manager.to_size_t()].resize(chan_width); + /* Find the ipin/opin nodes */ + for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(ipin_side); ++inode) { + const RRNodeId& ipin_node = rr_gsb.get_ipin_node(ipin_side, inode); + t_physical_tile_loc ipin_node_phy_tile_loc(rr_graph.node_xlow(ipin_node), rr_graph.node_ylow(ipin_node), 0); + /* Skip EMPTY type */ + if (true == is_empty_type(grids.get_physical_type(ipin_node_phy_tile_loc))) { + continue; + } + + int grid_type_index = grids.get_physical_type(ipin_node_phy_tile_loc)->index; + /* Get Fc of the ipin */ + /* skip Fc = 0 or unintialized, those pins are in the */ + bool skip_conn2track = true; + std::vector ipin_Fc_out; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + int ipin_Fc = Fc_in[grid_type_index][rr_graph.node_pin_num(ipin_node)][iseg]; + ipin_Fc_out.push_back(ipin_Fc); + if (0 != ipin_Fc) { + skip_conn2track = false; + continue; + } + } + + if (true == skip_conn2track) { + continue; + } + + //VTR_ASSERT(ipin_Fc_out.size() == segment_inf.size()); + + /* Build track2ipin_map for this IPIN */ + build_gsb_one_ipin_track2pin_map(rr_graph, rr_gsb, ipin_side, inode, ipin_Fc_out, + /* Give an offset for the first track that this ipin will connect to */ + offset[chan_side_manager.to_size_t()], + segment_inf, track2ipin_map); + /* update offset */ + offset[chan_side_manager.to_size_t()] += 2; + //printf("offset[%lu]=%lu\n", chan_side_manager.to_size_t(), offset[chan_side_manager.to_size_t()]); + } + } + + return track2ipin_map; +} + +/************************************************************************ + * Build the opin_to_track_map[gsb_side][0..num_opin_nodes-1][track_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * This function supports both X-directional and Y-directional tracks + * The mapping is done in the following steps: + * 1. Build a list of routing tracks whose starting points locate at this GSB + * (xlow - gsb_x == 0) + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between OPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +t_pin2track_map build_gsb_opin_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_out) { + t_pin2track_map opin2track_map; + /* Resize the matrix */ + opin2track_map.resize(rr_gsb.get_num_sides()); + + /* offset counter: it aims to balance the OPIN-to-track for each switch block */ + std::vector offset; + /* Get the chan_side: which is the same as the opin side */ + offset.resize(rr_gsb.get_num_sides()); + /* Initial offset */ + offset.assign(offset.size(), 0); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side opin_side = side_manager.get_side(); + /* Get the chan_side */ + /* This track2pin mapping is for Connection Blocks, so we only care two sides! */ + /* Get channel width and resize the matrix */ + size_t num_opin_nodes = rr_gsb.get_num_opin_nodes(opin_side); + opin2track_map[side].resize(num_opin_nodes); + /* Find the ipin/opin nodes */ + for (size_t inode = 0; inode < num_opin_nodes; ++inode) { + const RRNodeId& opin_node = rr_gsb.get_opin_node(opin_side, inode); + t_physical_tile_loc opin_node_phy_tile_loc(rr_graph.node_xlow(opin_node), rr_graph.node_ylow(opin_node), 0); + /* Skip EMPTY type */ + if (true == is_empty_type(grids.get_physical_type(opin_node_phy_tile_loc))) { + continue; + } + int grid_type_index = grids.get_physical_type(opin_node_phy_tile_loc)->index; + + /* Get Fc of the ipin */ + /* skip Fc = 0 or unintialized, those pins are in the */ + bool skip_conn2track = true; + std::vector opin_Fc_out; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + int opin_Fc = Fc_out[grid_type_index][rr_graph.node_pin_num(opin_node)][iseg]; + opin_Fc_out.push_back(opin_Fc); + if (0 != opin_Fc) { + skip_conn2track = false; + continue; + } + } + + if (true == skip_conn2track) { + continue; + } + VTR_ASSERT(opin_Fc_out.size() == segment_inf.size()); + + /* Build track2ipin_map for this IPIN */ + build_gsb_one_opin_pin2track_map(rr_graph, rr_gsb, opin_side, inode, opin_Fc_out, + /* Give an offset for the first track that this ipin will connect to */ + offset[side_manager.to_size_t()], + segment_inf, opin2track_map); + /* update offset: aim to rotate starting tracks by 1*/ + offset[side_manager.to_size_t()] += 1; + } + + /* Check: + * 1. We want to ensure that each OPIN will drive at least one track + * 2. We want to ensure that each track will be driven by at least 1 OPIN */ + } + + return opin2track_map; +} + +/************************************************************************ + * Add all direct clb-pin-to-clb-pin edges to given opin + ***********************************************************************/ +void build_direct_connections_for_one_gsb(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& from_grid_coordinate, + const RRSwitchId& delayless_switch, + const std::vector& directs, + const std::vector& clb_to_clb_directs) { + VTR_ASSERT(directs.size() == clb_to_clb_directs.size()); + + t_physical_tile_type_ptr grid_type = grids.get_physical_type(t_physical_tile_loc(from_grid_coordinate.x(), from_grid_coordinate.y(), layer)); + + /* Iterate through all direct connections */ + for (size_t i = 0; i < directs.size(); ++i) { + /* Bypass unmatched direct clb-to-clb connections */ + if (grid_type != clb_to_clb_directs[i].from_clb_type) { + continue; + } + + /* This opin is specified to connect directly to an ipin, + * now compute which ipin to connect to + */ + vtr::Point to_grid_coordinate(from_grid_coordinate.x() + directs[i].x_offset, + from_grid_coordinate.y() + directs[i].y_offset); + + /* Bypass unmatched direct clb-to-clb connections */ + t_physical_tile_type_ptr to_grid_type = grids.get_physical_type(t_physical_tile_loc(to_grid_coordinate.x(), to_grid_coordinate.y(), layer)); + /* Check if to_grid if the same grid */ + if (to_grid_type != clb_to_clb_directs[i].to_clb_type) { + continue; + } + + bool swap; + int max_index, min_index; + /* Compute index of opin with regards to given pins */ + if (clb_to_clb_directs[i].from_clb_pin_start_index + > clb_to_clb_directs[i].from_clb_pin_end_index) { + swap = true; + max_index = clb_to_clb_directs[i].from_clb_pin_start_index; + min_index = clb_to_clb_directs[i].from_clb_pin_end_index; + } else { + swap = false; + min_index = clb_to_clb_directs[i].from_clb_pin_start_index; + max_index = clb_to_clb_directs[i].from_clb_pin_end_index; + } + + /* get every opin in the range */ + for (int opin = min_index; opin <= max_index; ++opin) { + int offset = opin - min_index; + + if ((to_grid_coordinate.x() < grids.width() - 1) + && (to_grid_coordinate.y() < grids.height() - 1)) { + int ipin = OPEN; + if (clb_to_clb_directs[i].to_clb_pin_start_index + > clb_to_clb_directs[i].to_clb_pin_end_index) { + if (true == swap) { + ipin = clb_to_clb_directs[i].to_clb_pin_end_index + offset; + } else { + ipin = clb_to_clb_directs[i].to_clb_pin_start_index - offset; + } + } else { + if (true == swap) { + ipin = clb_to_clb_directs[i].to_clb_pin_end_index - offset; + } else { + ipin = clb_to_clb_directs[i].to_clb_pin_start_index + offset; + } + } + + /* Get the pin index in the rr_graph */ + t_physical_tile_loc from_tile_loc(from_grid_coordinate.x(), from_grid_coordinate.y(), layer); + int from_grid_width_ofs = grids.get_width_offset(from_tile_loc); + int from_grid_height_ofs = grids.get_height_offset(from_tile_loc); + t_physical_tile_loc to_tile_loc(to_grid_coordinate.x(), to_grid_coordinate.y(), layer); + int to_grid_width_ofs = grids.get_width_offset(to_tile_loc); + int to_grid_height_ofs = grids.get_height_offset(to_tile_loc); + + /* Find the side of grid pins, the pin location should be unique! + * Pin location is required by searching a node in rr_graph + */ + std::vector opin_grid_side = find_grid_pin_sides(grids, layer, from_grid_coordinate.x(), from_grid_coordinate.y(), opin); + VTR_ASSERT(1 == opin_grid_side.size()); + + std::vector ipin_grid_side = find_grid_pin_sides(grids, layer, to_grid_coordinate.x(), to_grid_coordinate.y(), ipin); + VTR_ASSERT(1 == ipin_grid_side.size()); + + RRNodeId opin_node_id = rr_graph.node_lookup().find_node(layer, + from_grid_coordinate.x() - from_grid_width_ofs, + from_grid_coordinate.y() - from_grid_height_ofs, + OPIN, opin, opin_grid_side[0]); + RRNodeId ipin_node_id = rr_graph.node_lookup().find_node(layer, + to_grid_coordinate.x() - to_grid_width_ofs, + to_grid_coordinate.y() - to_grid_height_ofs, + IPIN, ipin, ipin_grid_side[0]); + + /* add edges to the opin_node */ + VTR_ASSERT(opin_node_id && ipin_node_id); + rr_graph_builder.create_edge(opin_node_id, ipin_node_id, delayless_switch, false); + } + } + } + /* Build actual edges */ + rr_graph_builder.build_edges(true); +} diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.h b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.h new file mode 100644 index 00000000000..6c1208ebf46 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_gsb.h @@ -0,0 +1,80 @@ +#ifndef TILEABLE_RR_GRAPH_GSB_H +#define TILEABLE_RR_GRAPH_GSB_H +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "vtr_vector.h" +#include "vtr_geometry.h" + +#include "physical_types.h" +#include "device_grid.h" + +#include "rr_gsb.h" +#include "rr_graph_obj.h" +#include "rr_graph.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/************************************************************************ + * Data stuctures related to the functions + ***********************************************************************/ +typedef std::vector>> t_track2track_map; +typedef std::vector>> t_track2pin_map; +typedef std::vector>> t_pin2track_map; + +/************************************************************************ + * Functions + ***********************************************************************/ +t_track2track_map build_gsb_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& subFs, + const bool& wire_opposite_side, + const std::vector& segment_inf); + +RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, + const RRGraphView& rr_graph, + const vtr::Point& device_chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const size_t& layer, + const vtr::Point& gsb_coordinate); + +void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const t_track2pin_map& track2ipin_map, + const t_pin2track_map& opin2track_map, + const t_track2track_map& track2track_map, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges); + +t_track2pin_map build_gsb_track_to_ipin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_in); + +t_pin2track_map build_gsb_opin_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_out); + +void build_direct_connections_for_one_gsb(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& from_grid_coordinate, + const RRSwitchId& delayless_switch, + const std::vector& directs, + const std::vector& clb_to_clb_directs); + +#endif diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp new file mode 100644 index 00000000000..ce87b037c83 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.cpp @@ -0,0 +1,1203 @@ +/************************************************************************ + * This file contains functions that are used to allocate nodes + * for the tileable routing resource graph builder + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_geometry.h" + +/* Headers from openfpgautil library */ +#include "openfpga_side_manager.h" + +#include "vpr_types.h" +#include "vpr_utils.h" + +#include "rr_node.h" + +#include "rr_graph_builder_utils.h" +#include "rr_graph_builder.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_node_builder.h" +#include "rr_rc_data.h" + +/************************************************************************ + * Find the number output pins by considering all the grid + ***********************************************************************/ +static size_t estimate_num_grid_rr_nodes_by_type(const DeviceGrid& grids, + const size_t& layer, + const t_rr_type& node_type) { + size_t num_grid_rr_nodes = 0; + + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + t_physical_tile_loc tile_loc(ix, iy, layer); + /* Skip EMPTY tiles */ + if (true == is_empty_type(grids.get_physical_type(tile_loc))) { + continue; + } + + /* Skip height > 1 or width > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(tile_loc)) + || (0 < grids.get_height_offset(tile_loc))) { + continue; + } + + enum e_side io_side = NUM_SIDES; + + /* If this is the block on borders, we consider IO side */ + if (true == is_io_type(grids.get_physical_type(tile_loc))) { + vtr::Point io_device_size(grids.width() - 1, grids.height() - 1); + vtr::Point grid_coordinate(ix, iy); + io_side = determine_io_grid_pin_side(io_device_size, grid_coordinate); + } + + switch (node_type) { + case OPIN: + /* get the number of OPINs */ + num_grid_rr_nodes += get_grid_num_pins(grids, layer, ix, iy, DRIVER, io_side); + break; + case IPIN: + /* get the number of IPINs */ + num_grid_rr_nodes += get_grid_num_pins(grids, layer, ix, iy, RECEIVER, io_side); + break; + case SOURCE: + /* SOURCE: number of classes whose type is DRIVER */ + num_grid_rr_nodes += get_grid_num_classes(grids, layer, ix, iy, DRIVER); + break; + case SINK: + /* SINK: number of classes whose type is RECEIVER */ + num_grid_rr_nodes += get_grid_num_classes(grids, layer, ix, iy, RECEIVER); + break; + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid routing resource node!\n"); + exit(1); + } + } + } + + return num_grid_rr_nodes; +} + +/************************************************************************ + * For X-direction Channel: CHANX + * We pair each x-direction routing channel to the grid below it + * as they share the same coordinate + * + * As such, the range of CHANX coordinate starts from x = 1, y = 0 + * which is the grid (I/O) at the left bottom of the fabric + * + * As such, the range of CHANX coordinate ends to x = width - 2, y = height - 2 + * which is the grid at the top right of the core fabric + * Note that the I/O ring is + * + * TOP SIDE OF FPGA + * + * +-------------+ +-------------+ +---------------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][height-1] | + * +-------------+ +-------------+ +---------------------+ + * + * +-------------+ +-------------+ +---------------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][0] | | [2][0] | | [width-2][height-2] | + * +-------------+ +-------------+ +---------------------+ + * + * +-------------+ +-------------+ +---------------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][height-2] | + * +-------------+ +-------------+ +---------------------+ + * + * ... ... ... + * + * +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][1] | | [2][1] | | [width-2][1] | + * +-------------+ +-------------+ +--------------+ + * + * LEFT +-------------+ +-------------+ +--------------+ RIGHT + * SIDE | Grid | | Grid | ... | Grid | SIDE + * GRID | [1][1] | | [2][1] | | [width-2][1] | GRID + * +-------------+ +-------------+ +--------------+ + * + * +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][0] | | [2][0] | | [width-2][0] | + * +-------------+ +-------------+ +--------------+ + * + * +-------------+ +-------------+ +--------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][0] | + * +-------------+ +-------------+ +--------------+ + * + * BOTTOM SIDE OF FPGA + * + * The figure above describe how the X-direction routing channels are + * organized in a homogeneous FPGA fabric + * Note that we talk about general-purpose uni-directional routing architecture here + * It means that a routing track may span across multiple grids + * However, the hard limits are as follows + * All the routing tracks will start at the most LEFT routing channel + * All the routing tracks will end at the most RIGHT routing channel + * + * Things will become more complicated in terms of track starting and end + * in the context of heterogeneous FPGAs + * We may have a grid which span multiple column and rows, as exemplified in the figure below + * In such case, + * all the routing tracks [x-1][y] at the left side of the grid [x][y] are forced to end + * all the routing tracks [x+2][y] at the right side of the grid [x][y] are forced to start + * And there are no routing tracks inside the grid[x][y] + * It means that X-channel [x][y] & [x+1][y] will no exist + * + * +------------+ +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | | X-Channel | | X-Channel | + * | [x-1][y+2] | | [x][y+2] | | [x+1][y+2] | | [x+2][y+2] | + * +------------+ +-------------+ +-------------+ +--------------+ + * + * +------------+ +-----------------------------------+ +--------------+ + * | Grid | | | | Grid | + * | [x-1][y+1] | | | | [x+2][y+1] | + * +------------+ | | +--------------+ + * | | + * +------------+ | | +--------------+ + * | X-channel | | Grid | | X-Channel | + * | [x-1][y] | | [x][y] - [x+1][y+1] | | [x+2][y] | + * +------------+ | | +--------------+ + * | | + * +------------+ | | +--------------+ + * | Grid | | | | Grid | + * | [x-1][y] | | | | [x+2][y] | + * +------------+ +-----------------------------------+ +--------------+ + * + * + * + ***********************************************************************/ +static size_t estimate_num_chanx_rr_nodes(const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel) { + size_t num_chanx_rr_nodes = 0; + + for (size_t iy = 0; iy < grids.height() - 1; ++iy) { + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + vtr::Point chanx_coord(ix, iy); + + /* Bypass if the routing channel does not exist when through channels are not allowed */ + if ((false == through_channel) + && (false == is_chanx_exist(grids, layer, chanx_coord))) { + continue; + } + /* Bypass if the routing channel does not exist when a shrink boundary is considered */ + if (shrink_boundary && !device_grid_annotation.is_chanx_exist(chanx_coord)) { + continue; + } + + bool force_start = false; + bool force_end = false; + + /* All the tracks have to start when + * - the routing channel touch the RIGHT side a heterogeneous block + * - the routing channel touch the LEFT side of FPGA + */ + if (true == is_chanx_right_to_multi_height_grid(grids, layer, chanx_coord, through_channel)) { + force_start = true; + } + if (shrink_boundary && device_grid_annotation.is_chanx_start(chanx_coord)) { + force_start = true; + } + + /* All the tracks have to end when + * - the routing channel touch the LEFT side a heterogeneous block + * - the routing channel touch the RIGHT side of FPGA + */ + if (true == is_chanx_left_to_multi_height_grid(grids, layer, chanx_coord, through_channel)) { + force_end = true; + } + if (shrink_boundary && device_grid_annotation.is_chanx_end(chanx_coord)) { + force_end = true; + } + + /* Evaluate if the routing channel locates in the middle of a grid */ + ChanNodeDetails chanx_details = build_unidir_chan_node_details(chan_width, grids.width() - 2, force_start, force_end, segment_infs); + /* When an INC_DIRECTION CHANX starts, we need a new rr_node */ + num_chanx_rr_nodes += chanx_details.get_num_starting_tracks(Direction::INC); + /* When an DEC_DIRECTION CHANX ends, we need a new rr_node */ + num_chanx_rr_nodes += chanx_details.get_num_ending_tracks(Direction::DEC); + } + } + + return num_chanx_rr_nodes; +} + +/************************************************************************ + * Estimate the number of CHANY rr_nodes for Y-direction routing channels + * The technical rationale is very similar to the X-direction routing channel + * Refer to the detailed explanation there + ***********************************************************************/ +static size_t estimate_num_chany_rr_nodes(const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel) { + size_t num_chany_rr_nodes = 0; + + for (size_t ix = 0; ix < grids.width() - 1; ++ix) { + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + vtr::Point chany_coord(ix, iy); + + /* Bypass if the routing channel does not exist when through channel are not allowed */ + if ((false == through_channel) + && (false == is_chany_exist(grids, layer, chany_coord))) { + continue; + } + + /* Bypass if the routing channel does not exist when a shrink boundary is considered */ + if (shrink_boundary && !device_grid_annotation.is_chany_exist(chany_coord)) { + continue; + } + + bool force_start = false; + bool force_end = false; + + /* All the tracks have to start when + * - the routing channel touch the TOP side a heterogeneous block + * - the routing channel touch the BOTTOM side of FPGA + */ + if (true == is_chany_top_to_multi_width_grid(grids, layer, chany_coord, through_channel)) { + force_start = true; + } + if (shrink_boundary && device_grid_annotation.is_chany_start(chany_coord)) { + force_start = true; + } + + /* All the tracks have to end when + * - the routing channel touch the BOTTOM side a heterogeneous block + * - the routing channel touch the TOP side of FPGA + */ + if (true == is_chany_bottom_to_multi_width_grid(grids, layer, chany_coord, through_channel)) { + force_end = true; + } + if (shrink_boundary && device_grid_annotation.is_chany_end(chany_coord)) { + force_end = true; + } + + ChanNodeDetails chany_details = build_unidir_chan_node_details(chan_width, grids.height() - 2, force_start, force_end, segment_infs); + /* When an INC_DIRECTION CHANX starts, we need a new rr_node */ + num_chany_rr_nodes += chany_details.get_num_starting_tracks(Direction::INC); + /* When an DEC_DIRECTION CHANX ends, we need a new rr_node */ + num_chany_rr_nodes += chany_details.get_num_ending_tracks(Direction::DEC); + } + } + + return num_chany_rr_nodes; +} + +/************************************************************************ + * Estimate the number of nodes by each type in a routing resource graph + ***********************************************************************/ +static std::vector estimate_num_rr_nodes(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel) { + /* Reset the OPIN, IPIN, SOURCE, SINK counter to be zero */ + std::vector num_rr_nodes_per_type(NUM_RR_TYPES, 0); + + /** + * 1 Find number of rr nodes related to grids + */ + num_rr_nodes_per_type[OPIN] = estimate_num_grid_rr_nodes_by_type(grids, layer, OPIN); + num_rr_nodes_per_type[IPIN] = estimate_num_grid_rr_nodes_by_type(grids, layer, IPIN); + num_rr_nodes_per_type[SOURCE] = estimate_num_grid_rr_nodes_by_type(grids, layer, SOURCE); + num_rr_nodes_per_type[SINK] = estimate_num_grid_rr_nodes_by_type(grids, layer, SINK); + + /** + * 2. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * + * SPECIAL for fringes: + * All segments will start and ends with no exception + * + * IMPORTANT: we should be aware that channel width maybe different + * in X-direction and Y-direction channels!!! + * So we will load segment details for different channels + */ + num_rr_nodes_per_type[CHANX] = estimate_num_chanx_rr_nodes(grids, layer, + chan_width.x(), + segment_inf_x, + device_grid_annotation, + shrink_boundary, + through_channel); + num_rr_nodes_per_type[CHANY] = estimate_num_chany_rr_nodes(grids, layer, + chan_width.y(), + segment_inf_y, + device_grid_annotation, + shrink_boundary, + through_channel); + + return num_rr_nodes_per_type; +} + +/************************************************************************ + * Allocate rr_nodes to a rr_graph object + * This function just allocate the memory and ensure its efficiency + * It will NOT fill detailed information for each node!!! + * + * Note: ensure that there are NO nodes in the rr_graph + ***********************************************************************/ +void alloc_tileable_rr_graph_nodes(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel) { + VTR_ASSERT(0 == rr_graph_builder.rr_nodes().size()); + + std::vector num_rr_nodes_per_type = estimate_num_rr_nodes(grids, + layer, + chan_width, + segment_inf_x, + segment_inf_y, + device_grid_annotation, + shrink_boundary, + through_channel); + + /* Reserve the number of node to be memory efficient */ + size_t num_nodes = 0; + for (const size_t& num_node_per_type : num_rr_nodes_per_type) { + num_nodes += num_node_per_type; + } + + rr_graph_builder.reserve_nodes(num_nodes); + + rr_node_driver_switches.resize(num_nodes); +} + +/************************************************************************ + * Configure OPIN rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_opin_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const e_side& io_side, + const RRSwitchId& delayless_switch) { + SideManager io_side_manager(io_side); + + /* Walk through the width height of each grid, + * get pins and configure the rr_nodes + */ + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + for (int width = 0; width < phy_tile_type->width; ++width) { + for (int height = 0; height < phy_tile_type->height; ++height) { + /* Walk through sides */ + for (e_side side : SIDES) { + SideManager side_manager(side); + /* skip unwanted sides */ + if ((true == is_io_type(phy_tile_type)) + && (side != io_side_manager.to_size_t()) && (NUM_SIDES != io_side)) { + continue; + } + /* Find OPINs */ + /* Configure pins by pins */ + std::vector opin_list = get_grid_side_pins(grids, layer, grid_coordinate.x(), grid_coordinate.y(), DRIVER, side_manager.get_side(), + width, height); + for (const int& pin_num : opin_list) { + /* Create a new node and fill information */ + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x() + width, grid_coordinate.y() + height, OPIN, pin_num, side); + + /* node bounding box */ + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x() + width, + grid_coordinate.y() + height, + grid_coordinate.x() + width, + grid_coordinate.y() + height); + rr_graph_builder.add_node_side(node, side_manager.get_side()); + rr_graph_builder.set_node_pin_num(node, pin_num); + + rr_graph_builder.set_node_capacity(node, 1); + rr_graph_builder.set_node_layer(node, layer); + + /* cost index is a FIXED value for OPIN */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(OPIN_COST_INDEX)); + + /* Switch info */ + rr_node_driver_switches[node] = delayless_switch; + + /* RC data */ + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } /* End of loading OPIN rr_nodes */ + } /* End of side enumeration */ + } /* End of height enumeration */ + } /* End of width enumeration */ +} + +/************************************************************************ + * Configure IPIN rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_ipin_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const e_side& io_side, + const RRSwitchId& wire_to_ipin_switch) { + SideManager io_side_manager(io_side); + + /* Walk through the width and height of each grid, + * get pins and configure the rr_nodes + */ + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + for (int width = 0; width < phy_tile_type->width; ++width) { + for (int height = 0; height < phy_tile_type->height; ++height) { + /* Walk through sides */ + for (e_side side : SIDES) { + SideManager side_manager(side); + /* skip unwanted sides */ + if ((true == is_io_type(phy_tile_type)) + && (side != io_side_manager.to_size_t()) && (NUM_SIDES != io_side)) { + continue; + } + + /* Find IPINs */ + /* Configure pins by pins */ + std::vector ipin_list = get_grid_side_pins(grids, layer, grid_coordinate.x(), grid_coordinate.y(), RECEIVER, side_manager.get_side(), width, height); + for (const int& pin_num : ipin_list) { + /* Create a new node and fill information */ + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x() + width, grid_coordinate.y() + height, IPIN, pin_num, side); + + /* node bounding box */ + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x() + width, + grid_coordinate.y() + height, + grid_coordinate.x() + width, + grid_coordinate.y() + height); + rr_graph_builder.add_node_side(node, side_manager.get_side()); + rr_graph_builder.set_node_pin_num(node, pin_num); + + rr_graph_builder.set_node_capacity(node, 1); + rr_graph_builder.set_node_layer(node, layer); + + /* cost index is a FIXED value for OPIN */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(IPIN_COST_INDEX)); + + /* Switch info */ + rr_node_driver_switches[node] = wire_to_ipin_switch; + + /* RC data */ + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } /* End of loading IPIN rr_nodes */ + } /* End of side enumeration */ + } /* End of height enumeration */ + } /* End of width enumeration */ +} + +/************************************************************************ + * Configure SOURCE rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_source_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const e_side& io_side, + const RRSwitchId& delayless_switch) { + SideManager io_side_manager(io_side); + + /* Set a SOURCE rr_node for each DRIVER class */ + t_physical_tile_loc tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + for (size_t iclass = 0; iclass < phy_tile_type->class_inf.size(); ++iclass) { + /* Set a SINK rr_node for the OPIN */ + if (DRIVER != phy_tile_type->class_inf[iclass].type) { + continue; + } + + /* Create a new node and fill information */ + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x(), grid_coordinate.y(), SOURCE, iclass); + + /* node bounding box */ + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x(), + grid_coordinate.y(), + grid_coordinate.x() + phy_tile_type->width - 1, + grid_coordinate.y() + phy_tile_type->height - 1); + rr_graph_builder.set_node_class_num(node, iclass); + rr_graph_builder.set_node_layer(node, (int)layer); + + /* The capacity should be the number of pins in this class*/ + rr_graph_builder.set_node_capacity(node, phy_tile_type->class_inf[iclass].num_pins); + + /* cost index is a FIXED value for SOURCE */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(SOURCE_COST_INDEX)); + + /* Switch info */ + rr_node_driver_switches[node] = delayless_switch; + + /* RC data */ + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } /* End of class enumeration */ +} + +/************************************************************************ + * Configure SINK rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_sink_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const e_side& io_side, + const RRSwitchId& delayless_switch) { + SideManager io_side_manager(io_side); + + /* Set a SINK rr_node for each RECEIVER class */ + t_physical_tile_loc tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + for (size_t iclass = 0; iclass < phy_tile_type->class_inf.size(); ++iclass) { + /* Set a SINK rr_node for the OPIN */ + if (RECEIVER != phy_tile_type->class_inf[iclass].type) { + continue; + } + + /* Create a new node and fill information */ + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x(), grid_coordinate.y(), SINK, iclass); + + /* node bounding box */ + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x(), + grid_coordinate.y(), + grid_coordinate.x() + phy_tile_type->width - 1, + grid_coordinate.y() + phy_tile_type->height - 1); + rr_graph_builder.set_node_class_num(node, iclass); + rr_graph_builder.set_node_layer(node, layer); + + rr_graph_builder.set_node_capacity(node, 1); + + /* The capacity should be the number of pins in this class*/ + rr_graph_builder.set_node_capacity(node, phy_tile_type->class_inf[iclass].num_pins); + + /* cost index is a FIXED value for SINK */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(SINK_COST_INDEX)); + + /* Switch info */ + rr_node_driver_switches[node] = delayless_switch; + + /* RC data */ + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } /* End of class enumeration */ +} + +/************************************************************************ + * Create all the rr_nodes for grids + ***********************************************************************/ +static void load_grid_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const size_t& layer, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + t_physical_tile_loc tile_loc(ix, iy, layer); + /* Skip EMPTY tiles */ + if (true == is_empty_type(grids.get_physical_type(tile_loc))) { + continue; + } + + /* We only build rr_nodes for grids with width_offset = 0 and height_offset = 0 */ + if ((0 < grids.get_width_offset(tile_loc)) + || (0 < grids.get_height_offset(tile_loc))) { + continue; + } + + vtr::Point grid_coordinate(ix, iy); + enum e_side io_side = NUM_SIDES; + + std::vector wanted_sides{TOP, RIGHT, BOTTOM, LEFT}; + + /* If this is the block on borders, we consider IO side */ + if (true == is_io_type(grids.get_physical_type(tile_loc))) { + vtr::Point io_device_size(grids.width() - 1, grids.height() - 1); + io_side = determine_io_grid_pin_side(io_device_size, grid_coordinate); + wanted_sides.clear(); + wanted_sides.push_back(io_side); + } + + for (e_side side : wanted_sides) { + for (int width_offset = 0; width_offset < grids.get_physical_type(tile_loc)->width; ++width_offset) { + int x_tile = ix + width_offset; + for (int height_offset = 0; height_offset < grids.get_physical_type(tile_loc)->height; ++height_offset) { + int y_tile = iy + height_offset; + rr_graph_builder.node_lookup().reserve_nodes(layer, x_tile, y_tile, OPIN, grids.get_physical_type(tile_loc)->num_pins, side); + rr_graph_builder.node_lookup().reserve_nodes(layer, x_tile, y_tile, IPIN, grids.get_physical_type(tile_loc)->num_pins, side); + } + } + } + + /* Configure source rr_nodes for this grid */ + load_one_grid_source_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + grids, + io_side, + delayless_switch); + + /* Configure sink rr_nodes for this grid */ + load_one_grid_sink_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + grids, + io_side, + delayless_switch); + + /* Configure opin rr_nodes for this grid */ + load_one_grid_opin_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + grids, + io_side, + delayless_switch); + + /* Configure ipin rr_nodes for this grid */ + load_one_grid_ipin_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + grids, + io_side, + wire_to_ipin_switch); + } + } + //Copy the SOURCE/SINK nodes to all offset positions for blocks with width > 1 and/or height > 1 + // This ensures that look-ups on non-root locations will still find the correct SOURCE/SINK + for (size_t x = 0; x < grids.width(); x++) { + for (size_t y = 0; y < grids.height(); y++) { + t_physical_tile_loc tile_loc(x, y, 0); + int width_offset = grids.get_width_offset(tile_loc); + int height_offset = grids.get_height_offset(tile_loc); + if (width_offset != 0 || height_offset != 0) { + int root_x = x - width_offset; + int root_y = y - height_offset; + + rr_graph_builder.node_lookup().mirror_nodes(0, + vtr::Point(root_x, root_y), + vtr::Point(x, y), + SOURCE, + SIDES[0]); + rr_graph_builder.node_lookup().mirror_nodes(0, + vtr::Point(root_x, root_y), + vtr::Point(x, y), + SINK, + SIDES[0]); + } + } + } +} + +/************************************************************************ + * Initialize the basic information of routing track rr_nodes + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + ***********************************************************************/ +static void load_one_chan_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + const size_t& layer, + const vtr::Point& chan_coordinate, + const t_rr_type& chan_type, + ChanNodeDetails& chan_details, + const std::vector& segment_infs, + const t_unified_to_parallel_seg_index& seg_index_map, + const int& cost_index_offset) { + /* Check each node_id(potential ptc_num) in the channel : + * If this is a starting point, we set a new rr_node with xlow/ylow, ptc_num + * If this is a ending point, we set xhigh/yhigh and track_ids + * For other nodes, we set changes in track_ids + */ + for (size_t itrack = 0; itrack < chan_details.get_chan_width(); ++itrack) { + /* For INC direction, a starting point requires a new chan rr_node */ + if (((true == chan_details.is_track_start(itrack)) + && (Direction::INC == chan_details.get_track_direction(itrack))) + /* For DEC direction, an ending point requires a new chan rr_node */ + || ((true == chan_details.is_track_end(itrack)) + && (Direction::DEC == chan_details.get_track_direction(itrack)))) { + /* Create a new chan rr_node */ + RRNodeId node = rr_graph_builder.create_node(layer, chan_coordinate.x(), chan_coordinate.y(), chan_type, itrack); + + rr_graph_builder.set_node_direction(node, chan_details.get_track_direction(itrack)); + rr_graph_builder.add_node_track_num(node, chan_coordinate, itrack); + rr_node_track_ids[node].push_back(itrack); + + rr_graph_builder.set_node_capacity(node, 1); + rr_graph_builder.set_node_layer(node, layer); + + /* assign switch id */ + size_t seg_id = chan_details.get_track_segment_id(itrack); + e_parallel_axis wanted_axis = chan_type == CHANX ? X_AXIS : Y_AXIS; + size_t parallel_seg_id = find_parallel_seg_index(seg_id, seg_index_map, wanted_axis); + rr_node_driver_switches[node] = RRSwitchId(segment_infs[parallel_seg_id].arch_opin_switch); + + /* Update chan_details with node_id */ + chan_details.set_track_node_id(itrack, size_t(node)); + + /* cost index depends on the segment index */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(cost_index_offset + parallel_seg_id)); + /* Finish here, go to next */ + } + + /* For INC direction, an ending point requires an update on xhigh and yhigh */ + if (((true == chan_details.is_track_end(itrack)) + && (Direction::INC == chan_details.get_track_direction(itrack))) + || + /* For DEC direction, an starting point requires an update on xlow and ylow */ + ((true == chan_details.is_track_start(itrack)) + && (Direction::DEC == chan_details.get_track_direction(itrack)))) { + /* Get the node_id */ + const RRNodeId& rr_node_id = RRNodeId(chan_details.get_track_node_id(itrack)); + + /* Do a quick check, make sure we do not mistakenly modify other nodes */ + VTR_ASSERT(chan_type == rr_graph.node_type(rr_node_id)); + VTR_ASSERT(chan_details.get_track_direction(itrack) == rr_graph.node_direction(rr_node_id)); + + /* set xhigh/yhigh and push changes to track_ids */ + rr_graph_builder.set_node_coordinates(rr_node_id, rr_graph.node_xlow(rr_node_id), + rr_graph.node_ylow(rr_node_id), + chan_coordinate.x(), + chan_coordinate.y()); + + /* Do not update track_ids for length-1 wires, they should have only 1 track_id */ + if ((rr_graph.node_xhigh(rr_node_id) > rr_graph.node_xlow(rr_node_id)) + || (rr_graph.node_yhigh(rr_node_id) > rr_graph.node_ylow(rr_node_id))) { + rr_node_track_ids[rr_node_id].push_back(itrack); + rr_graph_builder.add_node_track_num(rr_node_id, chan_coordinate, itrack); + } + /* Finish here, go to next */ + } + + /* Finish processing starting and ending tracks */ + if ((true == chan_details.is_track_start(itrack)) + || (true == chan_details.is_track_end(itrack))) { + /* Finish here, go to next */ + continue; + } + + /* For other nodes, we get the node_id and just update track_ids */ + /* Ensure those nodes are neither starting nor ending points */ + VTR_ASSERT((false == chan_details.is_track_start(itrack)) + && (false == chan_details.is_track_end(itrack))); + + /* Get the node_id */ + const RRNodeId& rr_node_id = RRNodeId(chan_details.get_track_node_id(itrack)); + + /* Do a quick check, make sure we do not mistakenly modify other nodes */ + VTR_ASSERT(chan_type == rr_graph.node_type(rr_node_id)); + VTR_ASSERT(chan_details.get_track_direction(itrack) == rr_graph.node_direction(rr_node_id)); + + /* Deposit xhigh and yhigh using the current chan_coordinate + * We will update when this track ends + */ + rr_graph_builder.set_node_coordinates(rr_node_id, rr_graph.node_xlow(rr_node_id), + rr_graph.node_ylow(rr_node_id), + chan_coordinate.x(), + chan_coordinate.y()); + + /* Update track_ids */ + rr_node_track_ids[rr_node_id].push_back(itrack); + rr_graph_builder.add_node_track_num(rr_node_id, chan_coordinate, itrack); + /* Finish here, go to next */ + } +} + +/************************************************************************ + * Initialize the basic information of X-channel rr_nodes: + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + * grid_info : pb_graph_pin + ***********************************************************************/ +static void load_chanx_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const t_unified_to_parallel_seg_index& segment_index_map, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel) { + /* For X-direction Channel: CHANX */ + for (size_t iy = 0; iy < grids.height() - 1; ++iy) { + /* Keep a vector of node_ids for the channels, because we will rotate them when walking through ix */ + std::vector track_node_ids; + + for (size_t ix = 1; ix < grids.width() - 1; ++ix) { + vtr::Point chanx_coord(ix, iy); + + /* Bypass if the routing channel does not exist when through channels are not allowed */ + if ((false == through_channel) + && (false == is_chanx_exist(grids, layer, chanx_coord))) { + continue; + } + /* Bypass if the routing channel does not exist when a shrink boundary is considered */ + if (shrink_boundary && !device_grid_annotation.is_chanx_exist(chanx_coord)) { + continue; + } + + bool force_start = false; + bool force_end = false; + + /* All the tracks have to start when + * - the routing channel touch the RIGHT side a heterogeneous block + * - the routing channel touch the LEFT side of FPGA + */ + if (true == is_chanx_right_to_multi_height_grid(grids, layer, chanx_coord, through_channel)) { + force_start = true; + } + if (shrink_boundary && device_grid_annotation.is_chanx_start(chanx_coord)) { + force_start = true; + } + + /* All the tracks have to end when + * - the routing channel touch the LEFT side a heterogeneous block + * - the routing channel touch the RIGHT side of FPGA + */ + if (true == is_chanx_left_to_multi_height_grid(grids, layer, chanx_coord, through_channel)) { + force_end = true; + } + if (shrink_boundary && device_grid_annotation.is_chanx_end(chanx_coord)) { + force_end = true; + } + + ChanNodeDetails chanx_details = build_unidir_chan_node_details(chan_width, grids.width() - 2, + force_start, force_end, segment_infs); + /* Force node_ids from the previous chanx */ + if (0 < track_node_ids.size()) { + /* Rotate should be done based on a typical case of routing tracks. + * Tracks on the borders are not regularly started and ended, + * which causes the node_rotation malfunction + */ + ChanNodeDetails chanx_details_tt = build_unidir_chan_node_details(chan_width, grids.width() - 2, + false, false, segment_infs); + chanx_details_tt.set_track_node_ids(track_node_ids); + + /* TODO: + * Do NOT rotate the tracks when the routing channel + * locates inside a multi-height and multi-width grid + * Let the routing channel passing through the grid (if through channel is allowed!) + * An example: + * + * +------------------------------ + * | | + * | Grid | + * track0 ----->+-----------------------------+----> track0 + * | | + */ + if (true == is_chanx_exist(grids, layer, chanx_coord, through_channel)) { + /* Rotate the chanx_details by an offset of ix - 1, the distance to the most left channel */ + /* For INC_DIRECTION, we use clockwise rotation + * node_id A ----> -----> node_id D + * node_id B ----> / ----> node_id A + * node_id C ----> / ----> node_id B + * node_id D ----> ----> node_id C + */ + chanx_details_tt.rotate_track_node_id(1, Direction::INC, true); + /* For DEC_DIRECTION, we use clockwise rotation + * node_id A <----- <----- node_id B + * node_id B <----- \ <----- node_id C + * node_id C <----- \ <----- node_id D + * node_id D <----- <----- node_id A + */ + chanx_details_tt.rotate_track_node_id(1, Direction::DEC, false); + } + + track_node_ids = chanx_details_tt.get_track_node_ids(); + chanx_details.set_track_node_ids(track_node_ids); + } + + /* Configure CHANX in this channel */ + load_one_chan_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + layer, chanx_coord, CHANX, + chanx_details, + segment_infs, + segment_index_map, + CHANX_COST_INDEX_START); + /* Get a copy of node_ids */ + track_node_ids = chanx_details.get_track_node_ids(); + } + } +} + +/************************************************************************ + * Initialize the basic information of Y-channel rr_nodes: + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + ***********************************************************************/ +static void load_chany_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const size_t& num_segment_x, + const t_unified_to_parallel_seg_index& seg_index_map, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel) { + /* For Y-direction Channel: CHANY */ + for (size_t ix = 0; ix < grids.width() - 1; ++ix) { + /* Keep a vector of node_ids for the channels, because we will rotate them when walking through ix */ + std::vector track_node_ids; + + for (size_t iy = 1; iy < grids.height() - 1; ++iy) { + vtr::Point chany_coord(ix, iy); + + /* Bypass if the routing channel does not exist when through channel are not allowed */ + if ((false == through_channel) + && (false == is_chany_exist(grids, layer, chany_coord))) { + continue; + } + /* Bypass if the routing channel does not exist when a shrink boundary is considered */ + if (shrink_boundary && !device_grid_annotation.is_chany_exist(chany_coord)) { + continue; + } + + bool force_start = false; + bool force_end = false; + + /* All the tracks have to start when + * - the routing channel touch the TOP side a heterogeneous block + * - the routing channel touch the BOTTOM side of FPGA + */ + if (true == is_chany_top_to_multi_width_grid(grids, layer, chany_coord, through_channel)) { + force_start = true; + } + if (shrink_boundary && device_grid_annotation.is_chany_start(chany_coord)) { + force_start = true; + } + + /* All the tracks have to end when + * - the routing channel touch the BOTTOM side a heterogeneous block + * - the routing channel touch the TOP side of FPGA + */ + if (true == is_chany_bottom_to_multi_width_grid(grids, layer, chany_coord, through_channel)) { + force_end = true; + } + if (shrink_boundary && device_grid_annotation.is_chany_end(chany_coord)) { + force_end = true; + } + + ChanNodeDetails chany_details = build_unidir_chan_node_details(chan_width, grids.height() - 2, + force_start, force_end, segment_infs); + /* Force node_ids from the previous chany + * This will not be applied when the routing channel is cut off (force to start) + */ + if (0 < track_node_ids.size()) { + /* Rotate should be done based on a typical case of routing tracks. + * Tracks on the borders are not regularly started and ended, + * which causes the node_rotation malfunction + */ + ChanNodeDetails chany_details_tt = build_unidir_chan_node_details(chan_width, grids.height() - 2, + false, false, segment_infs); + + chany_details_tt.set_track_node_ids(track_node_ids); + + /* TODO: + * Do NOT rotate the tracks when the routing channel + * locates inside a multi-height and multi-width grid + * Let the routing channel passing through the grid (if through channel is allowed!) + * An example: + * + * +------------------------------ + * | | + * | Grid | + * track0 ----->+-----------------------------+----> track0 + * | | + * we should rotate only once at the bottom side of a grid + */ + if (true == is_chany_exist(grids, layer, chany_coord, through_channel)) { + /* Rotate the chany_details by an offset of 1*/ + /* For INC_DIRECTION, we use clockwise rotation + * node_id A ----> -----> node_id D + * node_id B ----> / ----> node_id A + * node_id C ----> / ----> node_id B + * node_id D ----> ----> node_id C + */ + chany_details_tt.rotate_track_node_id(1, Direction::INC, true); + /* For DEC_DIRECTION, we use clockwise rotation + * node_id A <----- <----- node_id B + * node_id B <----- \ <----- node_id C + * node_id C <----- \ <----- node_id D + * node_id D <----- <----- node_id A + */ + chany_details_tt.rotate_track_node_id(1, Direction::DEC, false); + } + + track_node_ids = chany_details_tt.get_track_node_ids(); + chany_details.set_track_node_ids(track_node_ids); + } + /* Configure CHANX in this channel */ + load_one_chan_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + layer, chany_coord, CHANY, + chany_details, + segment_infs, + seg_index_map, + CHANX_COST_INDEX_START + num_segment_x); + /* Get a copy of node_ids */ + track_node_ids = chany_details.get_track_node_ids(); + } + } +} + +/************************************************************************ + * Reverse the track_ids of CHANX and CHANY nodes in DEC_DIRECTION + * This is required as the track ids are allocated in the sequence + * of incrementing x and y + * However, DEC direction routing tracks should have a reversed sequence in + * track ids + ***********************************************************************/ +static void reverse_dec_chan_rr_node_track_ids(const RRGraphView& rr_graph, + std::map>& rr_node_track_ids) { + // this should call rr_graph_builder to do the job + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass condition: only focus on CHANX and CHANY in DEC_DIRECTION */ + if (CHANX != rr_graph.node_type(node) && CHANY != rr_graph.node_type(node)) { + continue; + } + /* Reach here, we must have a node of CHANX or CHANY */ + if (Direction::DEC != rr_graph.node_direction(node)) { + continue; + } + std::reverse(rr_node_track_ids[node].begin(), + rr_node_track_ids[node].end()); + } +} + +/************************************************************************ + * Create all the rr_nodes covering both grids and routing channels + ***********************************************************************/ +void create_tileable_rr_graph_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const t_unified_to_parallel_seg_index& segment_index_map, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel) { + /* Allocates and loads all the structures needed for fast lookups of the * + * index of an rr_node. rr_node_indices is a matrix containing the index * + * of the *first* rr_node at a given (i,j) location. */ + + /* Alloc the lookup table + * .. warning: It is mandatory. There are bugs in resize() when called incrementally in RRSpatialLookup. + * When comment the following block out, you will see errors */ + for (t_rr_type rr_type : RR_TYPES) { + if (rr_type == CHANX) { + rr_graph_builder.node_lookup().resize_nodes(layer, grids.height(), grids.width(), rr_type, NUM_SIDES); + } else { + rr_graph_builder.node_lookup().resize_nodes(layer, grids.width(), grids.height(), rr_type, NUM_SIDES); + } + } + + load_grid_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + grids, layer, + wire_to_ipin_switch, + delayless_switch); + + load_chanx_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + grids, layer, + chan_width.x(), + segment_inf_x, + segment_index_map, + device_grid_annotation, + shrink_boundary, + through_channel); + + load_chany_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + grids, layer, + chan_width.y(), + segment_inf_y, + segment_inf_x.size(), + segment_index_map, + device_grid_annotation, + shrink_boundary, + through_channel); + + reverse_dec_chan_rr_node_track_ids(rr_graph, + rr_node_track_ids); + + /* Update node look-up for CHANX and CHANY nodes */ + for (const RRNodeId& rr_node_id : rr_graph.nodes()) { + if (CHANX == rr_graph.node_type(rr_node_id) || CHANY == rr_graph.node_type(rr_node_id)) { + rr_graph_builder.add_track_node_to_lookup(rr_node_id); + } + } +} diff --git a/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.h b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.h new file mode 100644 index 00000000000..27fea172338 --- /dev/null +++ b/vpr/src/tileable_rr_graph/tileable_rr_graph_node_builder.h @@ -0,0 +1,53 @@ +#ifndef TILEABLE_RR_GRAPH_NODE_BUILDER_H +#define TILEABLE_RR_GRAPH_NODE_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from vpr library */ +#include "device_grid.h" +#include "device_grid_annotation.h" +#include "rr_node_types.h" +#include "rr_graph_type.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void alloc_tileable_rr_graph_nodes(RRGraphBuilder& rr_graph_builder, + vtr::vector& driver_switches, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel); + +void create_tileable_rr_graph_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const t_unified_to_parallel_seg_index& segment_index_map, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& through_channel); + +#endif diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index 6157c9b980d..574a6d0fc84 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -21,6 +21,7 @@ #include "pack_types.h" #include "device_grid.h" #include "timing_fail_error.h" +#include "route_constraint.h" #include "re_cluster_util.h" /* This module contains subroutines that are used in several unrelated parts * @@ -75,8 +76,6 @@ static AtomPinId find_atom_pin_for_pb_route_id(ClusterBlockId clb, int pb_route_ static bool block_type_contains_blif_model(t_logical_block_type_ptr type, const std::regex& blif_model_regex); static bool pb_type_contains_blif_model(const t_pb_type* pb_type, const std::regex& blif_model_regex); -static t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type); -static void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type); /******************** Subroutine definitions *********************************/ @@ -1248,7 +1247,7 @@ static void load_pb_graph_pin_lookup_from_index_rec(t_pb_graph_pin** pb_graph_pi } /* Create a lookup that returns a pb_graph_pin pointer given the pb_graph_pin index */ -static t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type) { +t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type) { t_pb_graph_pin** pb_graph_pin_lookup_from_type = nullptr; t_pb_graph_node* pb_graph_head = type->pb_graph_head; @@ -1276,7 +1275,7 @@ static t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_ } /* Free pb_graph_pin lookup array */ -static void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type) { +void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type) { if (pb_graph_pin_lookup_from_type == nullptr) { return; } @@ -2291,6 +2290,22 @@ bool is_inter_cluster_node(t_physical_tile_type_ptr physical_tile, } } +void apply_route_constraints(VprConstraints& vpr_constraint) { + ClusteringContext& mutable_cluster_ctx = g_vpr_ctx.mutable_clustering(); + for (auto net_id : mutable_cluster_ctx.clb_nlist.nets()) { + std::string net_name = mutable_cluster_ctx.clb_nlist.net_name(net_id); + const RouteConstraint rc = vpr_constraint.get_route_constraint_by_net_name(net_name); + if (rc.is_valid()) { + mutable_cluster_ctx.clb_nlist.set_net_is_global(net_id, true); + if (rc.route_model() == "route") { + mutable_cluster_ctx.clb_nlist.set_net_is_ignored(net_id, false); + } else { + mutable_cluster_ctx.clb_nlist.set_net_is_ignored(net_id, true); + } + } + } +} + int get_rr_node_max_ptc(const RRGraphView& rr_graph_view, RRNodeId node_id, bool is_flat) { diff --git a/vpr/src/util/vpr_utils.h b/vpr/src/util/vpr_utils.h index 1ba3dcb35b7..dd16e0fd03c 100644 --- a/vpr/src/util/vpr_utils.h +++ b/vpr/src/util/vpr_utils.h @@ -180,7 +180,9 @@ bool primitive_type_feasible(AtomBlockId blk_id, const t_pb_type* cur_pb_type); t_pb_graph_pin* get_pb_graph_node_pin_from_model_port_pin(const t_model_ports* model_port, const int model_pin, const t_pb_graph_node* pb_graph_node); const t_pb_graph_pin* find_pb_graph_pin(const AtomNetlist& netlist, const AtomLookup& netlist_lookup, const AtomPinId pin_id); t_pb_graph_pin* get_pb_graph_node_pin_from_block_pin(ClusterBlockId iblock, int ipin); +t_pb_graph_pin** alloc_and_load_pb_graph_pin_lookup_from_index(t_logical_block_type_ptr type); vtr::vector alloc_and_load_pin_id_to_pb_mapping(); +void free_pb_graph_pin_lookup_from_index(t_pb_graph_pin** pb_graph_pin_lookup_from_type); void free_pin_id_to_pb_mapping(vtr::vector& pin_id_to_pb_mapping); std::tuple get_cluster_blk_physical_spec(ClusterBlockId cluster_blk_id); @@ -311,4 +313,8 @@ t_arch_switch_inf create_internal_arch_sw(float delay); void add_pb_child_to_list(std::list& pb_list, const t_pb* parent_pb); +// apply route constraints for route flow +class VprConstraints; +void apply_route_constraints(VprConstraints& constraint); + #endif diff --git a/vpr/test/test_server_convertutils.cpp b/vpr/test/test_server_convertutils.cpp new file mode 100644 index 00000000000..d7851832cc0 --- /dev/null +++ b/vpr/test/test_server_convertutils.cpp @@ -0,0 +1,15 @@ +#include "convertutils.h" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +TEST_CASE("test_server_convert_utils_to_int", "[vpr]") { + REQUIRE(std::optional{-2} == tryConvertToInt("-2")); + REQUIRE(std::optional{0} == tryConvertToInt("0")); + REQUIRE(std::optional{2} == tryConvertToInt("2")); + REQUIRE(std::nullopt == tryConvertToInt("2.")); + REQUIRE(std::nullopt == tryConvertToInt("2.0")); + REQUIRE(std::nullopt == tryConvertToInt("two")); + REQUIRE(std::nullopt == tryConvertToInt("2k")); + REQUIRE(std::nullopt == tryConvertToInt("k2")); +} diff --git a/vpr/test/test_server_taskresolver.cpp b/vpr/test/test_server_taskresolver.cpp new file mode 100644 index 00000000000..0561e5ca7cd --- /dev/null +++ b/vpr/test/test_server_taskresolver.cpp @@ -0,0 +1,81 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "taskresolver.h" + +namespace { + +TEST_CASE("test_server_taskresolver_cmdSpamFilter", "[vpr]") { + server::TaskResolver resolver; + resolver.addTask(server::Task{1, 1}); + resolver.addTask(server::Task{2, 1}); + resolver.addTask(server::Task{3, 1}); + resolver.addTask(server::Task{4, 1}); + resolver.addTask(server::Task{5, 1}); + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(finished.size() == 4); + + for (const auto& task : finished) { + REQUIRE(task.isFinished()); + REQUIRE(task.hasError()); + REQUIRE(task.jobId() != 1); + } + REQUIRE(resolver.tasksNum() == 1); + server::Task task = resolver.tasks()[0]; + REQUIRE(task.jobId() == 1); + REQUIRE(task.cmd() == 1); +} + +TEST_CASE("test_server_taskresolver_cmdOverrideFilter", "[vpr]") { + server::TaskResolver resolver; + resolver.addTask(server::Task{1, 2, "1"}); + resolver.addTask(server::Task{2, 2, "11"}); + resolver.addTask(server::Task{3, 2, "222"}); + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(finished.size() == 2); + + for (const auto& task : finished) { + REQUIRE(task.isFinished()); + REQUIRE(task.hasError()); + REQUIRE(task.jobId() != 3); + } + REQUIRE(resolver.tasksNum() == 1); + server::Task task = resolver.tasks()[0]; + REQUIRE(task.jobId() == 3); + REQUIRE(task.cmd() == 2); + REQUIRE(task.options() == "222"); +} + +TEST_CASE("test_server_taskresolver_cmdSpamAndOverrideOptions", "[vpr]") { + server::TaskResolver resolver; + resolver.addTask(server::Task{1, 2, "1"}); + resolver.addTask(server::Task{2, 2, "11"}); + resolver.addTask(server::Task{3, 2, "222"}); + resolver.addTask(server::Task{4, 2, "222"}); + resolver.addTask(server::Task{5, 1}); + resolver.addTask(server::Task{6, 1}); + resolver.addTask(server::Task{7, 1}); + + std::vector finished; + resolver.takeFinished(finished); + + REQUIRE(resolver.tasksNum() == 2); + server::Task task0 = resolver.tasks()[0]; + server::Task task1 = resolver.tasks()[1]; + + REQUIRE(task0.jobId() == 3); + REQUIRE(task0.cmd() == 2); + REQUIRE(task0.options() == "222"); + + REQUIRE(task1.jobId() == 5); + REQUIRE(task1.cmd() == 1); + REQUIRE(task1.options() == ""); +} + +} // namespace \ No newline at end of file diff --git a/vpr/test/test_server_telegrambuffer.cpp b/vpr/test/test_server_telegrambuffer.cpp new file mode 100644 index 00000000000..e08ecc45fe8 --- /dev/null +++ b/vpr/test/test_server_telegrambuffer.cpp @@ -0,0 +1,89 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "telegrambuffer.h" + +namespace { + +TEST_CASE("test_server_bytearray", "[vpr]") { + comm::ByteArray array1{"111"}; + comm::ByteArray array2{"222"}; + comm::ByteArray array{array1}; + array.append(array2); + + REQUIRE(array.at(0) == '1'); + REQUIRE(array.at(1) == '1'); + REQUIRE(array.at(2) == '1'); + REQUIRE(array.at(3) == '2'); + REQUIRE(array.at(4) == '2'); + REQUIRE(array.at(5) == '2'); + + REQUIRE(array.to_string() == "111222"); + + REQUIRE(array.size() == 6); + + array.append('3'); + + REQUIRE(array.size() == 7); + REQUIRE(array.to_string() == "1112223"); + + REQUIRE(array.at(6) == '3'); + + array.clear(); + + REQUIRE(array.size() == 0); + REQUIRE(array.to_string() == ""); +} + +TEST_CASE("test_server_telegrambuffer_oneOpened", "[vpr]") { + comm::TelegramBuffer buff{1024}; + buff.append(comm::ByteArray{"111"}); + buff.append(comm::ByteArray{"222"}); + + auto frames = buff.takeFrames(); + REQUIRE(frames.size() == 0); + + REQUIRE(buff.data().to_string() == "111222"); +} + +TEST_CASE("test_server_telegrambuffer_oneFinishedOneOpened", "[vpr]") { + comm::TelegramBuffer buff{1024}; + buff.append(comm::ByteArray{"111\x17"}); + buff.append(comm::ByteArray{"222"}); + + auto frames = buff.takeFrames(); + REQUIRE(frames.size() == 1); + + REQUIRE(frames[0].to_string() == "111"); + + REQUIRE(buff.data().to_string() == "222"); +} + +TEST_CASE("test_server_telegrambuffer_twoFinished", "[vpr]") { + comm::TelegramBuffer buff{1024}; + buff.append(comm::ByteArray{"111\x17"}); + buff.append(comm::ByteArray{"222\x17"}); + + auto frames = buff.takeFrames(); + REQUIRE(frames.size() == 2); + + REQUIRE(frames[0].to_string() == "111"); + REQUIRE(frames[1].to_string() == "222"); + + REQUIRE(buff.data().to_string() == ""); +} + +TEST_CASE("test_server_telegrambuffer_twoCleared", "[vpr]") { + comm::TelegramBuffer buff{1024}; + buff.append(comm::ByteArray{"111\x17"}); + buff.append(comm::ByteArray{"222\x17"}); + + buff.clear(); + + auto frames = buff.takeFrames(); + REQUIRE(frames.size() == 0); + + REQUIRE(buff.data().to_string() == ""); +} + +} // namespace \ No newline at end of file diff --git a/vpr/test/test_server_telegramoptions.cpp b/vpr/test/test_server_telegramoptions.cpp new file mode 100644 index 00000000000..36ff80157f1 --- /dev/null +++ b/vpr/test/test_server_telegramoptions.cpp @@ -0,0 +1,19 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +#include "telegramoptions.h" + +namespace { + +TEST_CASE("test_server_telegramoptions", "[vpr]") { + server::TelegramOptions options{"int:path_num:11;string:path_type:debug;int:details_level:3;bool:is_flat_routing:0", {"path_num", "path_type", "details_level", "is_flat_routing"}}; + + REQUIRE(options.errorsStr() == ""); + + REQUIRE(options.getString("path_type") == "debug"); + REQUIRE(options.getInt("path_num", -1) == 11); + REQUIRE(options.getInt("details_level", -1) == 3); + REQUIRE(options.getBool("is_flat_routing", true) == false); +} + +} // namespace diff --git a/vpr/test/test_server_telegramparser.cpp b/vpr/test/test_server_telegramparser.cpp new file mode 100644 index 00000000000..5f7ecc6062f --- /dev/null +++ b/vpr/test/test_server_telegramparser.cpp @@ -0,0 +1,14 @@ +#include "telegramparser.h" + +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_all.hpp" + +TEST_CASE("test_server_telegram_parser_base", "[vpr]") { + const std::string tdata{R"({"JOB_ID":"7","CMD":"2","OPTIONS":"type1:name1:value1;type2:name2:v a l u e 2;t3:n3:v3","DATA":"some_data...","STATUS":"1"})"}; + + REQUIRE(std::optional{7} == comm::TelegramParser::tryExtractFieldJobId(tdata)); + REQUIRE(std::optional{2} == comm::TelegramParser::tryExtractFieldCmd(tdata)); + REQUIRE(std::optional{"type1:name1:value1;type2:name2:v a l u e 2;t3:n3:v3"} == comm::TelegramParser::tryExtractFieldOptions(tdata)); + REQUIRE(std::optional{"some_data..."} == comm::TelegramParser::tryExtractFieldData(tdata)); + REQUIRE(std::optional{1} == comm::TelegramParser::tryExtractFieldStatus(tdata)); +} diff --git a/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml b/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml new file mode 100644 index 00000000000..5193264205e --- /dev/null +++ b/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml b/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml new file mode 100644 index 00000000000..6b027fe6a21 --- /dev/null +++ b/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml @@ -0,0 +1,635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/benchmarks/microbenchmarks/and_clk.blif b/vtr_flow/benchmarks/microbenchmarks/and_clk.blif new file mode 100644 index 00000000000..3f31b82b8d7 --- /dev/null +++ b/vtr_flow/benchmarks/microbenchmarks/and_clk.blif @@ -0,0 +1,12 @@ + +.model discrete_dffn +.inputs clk_ni d_i +.outputs d_o + +.names clk_ni int_clk_reg_exp_0 +0 1 + +.subckt dff D=d_i Q=d_o C=int_clk_reg_exp_0 + +.end + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt new file mode 100755 index 00000000000..fbf3358dd11 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt @@ -0,0 +1,29 @@ +############################################ +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/blif/4 + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=diffeq.blif +circuit_list_add=ex5p.blif +circuit_list_add=s298.blif + +# Add architectures to list to sweep +arch_list_add=k4_N4_tileable_90nm.xml + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements.txt + +script_params=-starting_stage vpr -track_memory_usage + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt new file mode 100644 index 00000000000..8576e7f7dc7 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt @@ -0,0 +1,4 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem yosys_synth_time max_yosys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time placed_wirelength_est place_mem place_time place_quench_time placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time +k4_N4_tileable_90nm.xml diffeq.blif common 9.82 vpr 61.45 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 417 64 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 62920 64 39 1935 1974 1 1104 520 23 23 529 clb auto 18.8 MiB 0.14 10227 61.2 MiB 0.57 0.01 6.71028 -1587.65 -6.71028 6.71028 0.25 0.0007736 0.0005813 0.0896507 0.0699632 34 17207 50 983127 929624 921133. 1741.27 7.25 0.536827 0.433199 14874 22 8645 29434 2727679 902790 6.8984 6.8984 -1779.84 -6.8984 0 0 1.17586e+06 2222.80 0.07 0.39 0.058014 0.0505961 +k4_N4_tileable_90nm.xml ex5p.blif common 15.22 vpr 52.07 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 346 8 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 53324 8 63 1072 1135 0 909 417 21 21 441 clb auto 14.2 MiB 0.10 11660 52.1 MiB 0.46 0.00 7.1886 -311.061 -7.1886 nan 0.20 0.000496 0.0003719 0.0573232 0.0458367 56 22886 46 804782 771343 1.13430e+06 2572.11 12.88 0.310125 0.253318 18533 21 8880 28500 4332796 1534540 7.97924 nan -339.269 -7.97924 0 0 1.45200e+06 3292.52 0.08 0.50 0.0368923 0.0322975 +k4_N4_tileable_90nm.xml s298.blif common 21.05 vpr 75.85 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 571 4 -1 -1 success v8.0.0-6725-gff83963de-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.3.0 on Linux-4.4.0-19041-Microsoft x86_64 2022-11-01T15:38:22 LAPTOP-CVNHOGSN /home/tangxifan/vtr-verilog-to-routing/vtr_flow/tasks 77672 4 6 1942 1948 1 1193 581 26 26 676 clb auto 21.3 MiB 0.14 14254 75.9 MiB 0.69 0.01 13.2884 -103 -13.2884 13.2884 0.33 0.0008181 0.0006197 0.0966404 0.0764946 42 26119 42 1.28409e+06 1.27294e+06 1.41510e+06 2093.35 17.58 0.615507 0.493511 22147 19 9442 45483 4771423 1297512 13.6006 13.6006 -107.558 -13.6006 0 0 1.86822e+06 2763.64 0.19 0.67 0.0640435 0.0560669 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt new file mode 100644 index 00000000000..a3ceed7c14d --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/config.txt @@ -0,0 +1,27 @@ +############################################## +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/microbenchmarks + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=and_clk.blif + +# Add architectures to list to sweep +arch_list_add=k4_frac_N4_fracff_localClkGen_40nm.xml + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements.txt + +# Script parameters +script_params_common =-starting_stage vpr --route_chan_width 300 --max_router_iterations 400 --router_lookahead map --initial_pres_fac 1.0 --router_profiler_astar_fac 1.5 --seed 3 --read_vpr_constraints ../../../../floor_plan.xml:../../../../route_constraint.xml --write_vpr_constraints vpr_constraints.xml --clock_modeling ideal diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/golden_results.txt new file mode 100644 index 00000000000..1de222ff286 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/config/golden_results.txt @@ -0,0 +1,2 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time placed_wirelength_est place_mem place_time place_quench_time placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_num_rr_graph_nodes crit_path_num_rr_graph_edges crit_path_collapsed_nodes crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops crit_path_total_internal_heap_pushes crit_path_total_internal_heap_pops crit_path_total_external_heap_pushes crit_path_total_external_heap_pops crit_path_total_external_SOURCE_pushes crit_path_total_external_SOURCE_pops crit_path_total_internal_SOURCE_pushes crit_path_total_internal_SOURCE_pops crit_path_total_external_SINK_pushes crit_path_total_external_SINK_pops crit_path_total_internal_SINK_pushes crit_path_total_internal_SINK_pops crit_path_total_external_IPIN_pushes crit_path_total_external_IPIN_pops crit_path_total_internal_IPIN_pushes crit_path_total_internal_IPIN_pops crit_path_total_external_OPIN_pushes crit_path_total_external_OPIN_pops crit_path_total_internal_OPIN_pushes crit_path_total_internal_OPIN_pops crit_path_total_external_CHANX_pushes crit_path_total_external_CHANX_pops crit_path_total_internal_CHANX_pushes crit_path_total_internal_CHANX_pops crit_path_total_external_CHANY_pushes crit_path_total_external_CHANY_pops crit_path_total_internal_CHANY_pushes crit_path_total_internal_CHANY_pops crit_path_rt_node_SOURCE_pushes crit_path_rt_node_SINK_pushes crit_path_rt_node_IPIN_pushes crit_path_rt_node_OPIN_pushes crit_path_rt_node_CHANX_pushes crit_path_rt_node_CHANY_pushes crit_path_adding_all_rt crit_path_adding_high_fanout_rt crit_path_total_number_of_adding_all_rt_from_calling_high_fanout_rt critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_create_rr_graph_time crit_path_create_intra_cluster_rr_graph_time crit_path_tile_lookahead_computation_time crit_path_router_lookahead_computation_time crit_path_total_timing_analysis_time crit_path_total_sta_time +k4_frac_N4_fracff_localClkGen_40nm.xml and_clk.blif common 0.07 vpr 46.89 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 2 2 -1 -1 success bd5999406-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.15.0-67-generic x86_64 2023-03-17T17:48:18 workstation /home/tao/works/dev/route/vtr-verilog-to-routing/vtr_flow/tasks 48016 2 1 4 5 1 4 5 4 4 16 clb auto 7.9 MiB 0.00 11 46.9 MiB 0.00 0.00 1.13498 -1.13498 -1.13498 1.13498 0.00 3.031e-06 1.541e-06 3.5814e-05 2.5748e-05 -1 8 1 215576 107788 120301. 7518.81 0.00 0.00010342 7.9026e-05 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml new file mode 100644 index 00000000000..861adf40966 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/floor_plan.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml new file mode 100644 index 00000000000..ca3d794706b --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vpr_constraint/route_constraint.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt index 0e973fc00d0..14b58d67652 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt @@ -1,3 +1,4 @@ +regression_tests/vtr_reg_strong/koios regression_tests/vtr_reg_strong/strong_absorb_buffers regression_tests/vtr_reg_strong/strong_analysis_only regression_tests/vtr_reg_strong/strong_analytic_placer @@ -81,5 +82,7 @@ regression_tests/vtr_reg_strong/koios regression_tests/vtr_reg_strong/koios_no_complex_dsp regression_tests/vtr_reg_strong/strong_timing_fail regression_tests/vtr_reg_strong/strong_timing_no_fail +regression_tests/vtr_reg_strong/strong_vpr_constraint +regression_tests/vtr_reg_strong/strong_tileable_rr_graph regression_tests/vtr_reg_strong/strong_noc regression_tests/vtr_reg_strong/strong_flat_router