diff --git a/.gitmodules b/.gitmodules index 3f0d561525d..4a56207a063 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,6 +16,9 @@ [submodule "libs/EXTERNAL/yosys-slang"] path = libs/EXTERNAL/yosys-slang url = https://github.com/povik/yosys-slang.git +[submodule "libs/EXTERNAL/yaml-cpp"] + path = libs/EXTERNAL/yaml-cpp + url = https://github.com/jbeder/yaml-cpp.git [submodule "libs/EXTERNAL/libsdcparse"] path = libs/EXTERNAL/libsdcparse url = https://github.com/verilog-to-routing/libsdcparse.git diff --git a/doc/src/vpr/custom_rr_graph.rst b/doc/src/vpr/custom_rr_graph.rst new file mode 100644 index 00000000000..983d7aeb3b1 --- /dev/null +++ b/doc/src/vpr/custom_rr_graph.rst @@ -0,0 +1,102 @@ +.. _custom_rr_graph: + +Custom RR Graph +=============== + +For users who want more control over how connections are made in the routing resource graph, VPR provides a way to describe them through a Custom RR Graph (CRR) generator. + +Currently, the CRR Generator is only based on the tileable RR Graph. Support for the default VPR RR Graph Generator is in progress. To generate a CRR, the following files are required: + +* Switch block map file +* Switch block template files + +Switch Block Map File +~~~~~~~~~~~~~~~~~~~~~~ + +This is a YAML file that specifies which switch block template should be used for each tile. The mapping format supports the following patterns: + +* ``SB_1__1_: [sb_template.csv]`` - Tile at location (1, 1) uses switch block template ``sb_template.csv`` +* ``SB_1__*_: [sb_template.csv]`` - All tiles with x coordinate 1 use switch block template ``sb_template.csv`` +* ``SB_[7,20]__[2:32:3]_: [sb_template.csv]`` - Tiles with x equal to 7 or 20, and y coordinates from 2 to 32 (inclusive) with step 3 use switch block template ``sb_template.csv`` + +**Important:** The order in which patterns are defined matters, as the first matching pattern is used. + +Switch Block Template Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Terminology +----------- + +Before describing the template files, let's define some key terminology: + +**Lane:** A group of wires with the same length. The starting points of consecutive wires in a lane are one switch block apart. + +**Tap:** A switch block location where a wire passes through and can have fan-out connections. + +See the figure below for an illustration: + +.. figure:: images/lane_and_tap.png + :alt: Lane and Tap + :width: 50% + :align: center + + In the figure above, the taps for the red wire are shown, and the lanes are separated by dotted lines. Note that the figure is simplified for illustration purposes. + +In the actual RR Graph, when a wire passes through a switch block, its track number changes. Below is a more realistic example: + +.. figure:: images/lane_and_tap_realistic.png + :alt: Lane and Tap Realistic + :width: 50% + :align: center + + A more realistic example of a lane and a tap. Each box contains a lane. + +Template File Format +-------------------- + +There should be a directory containing the pattern files specified in the switch block maps file. Each template is a CSV file with the following format: + +* **Rows** represent source nodes +* **Columns** represent sink nodes +* An **'x' mark** at an intersection indicates that the source and sink nodes are connected +* A **number** at an intersection indicates that the nodes are connected with the switch delay specified by that number + +**Note:** The pattern currently only supports uni-directional segments. Therefore, wires can only be driven from their starting point. + +Row Headers (Source Nodes) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Each row has four header columns describing the source node: + +1. **Column 1 - Direction:** The side from which the source node is entering the switch block (e.g., 'left', 'right', 'top', 'bottom') +2. **Column 2 - Segment Type:** The segment length to which the source node belongs +3. **Column 3 - Lane Number:** The lane to which the source node belongs (see terminology above) +4. **Column 4 - Tap Number:** Which tap of the source node is at this switch block + +Column Headers (Sink Nodes) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Each column has header rows describing the sink node: + +1. **Row 1 - Direction:** The side from which the sink node is exiting the switch block +2. **Row 2 - Segment Type:** The segment length to which the sink node belongs +3. **Row 3 - Fan-in:** The fan-in of the sink node (optional) +4. **Row 4 - Lane Number:** The lane to which the sink node belongs + +Example +^^^^^^^ + +Let’s consider an architecture with a channel width of 16 that contains only wire segments of length 4. + +The number of rows should be calculated as: + +- ``4`` (number of sides) × ``160/2`` (number of tracks in one direction) = ``320`` rows. + +On each side, there should be: + +- ``20`` lanes (``80 / 4``), and +- each lane requires ``4`` rows (because length-4 wires require 4 tap positions). + +For the columns, the count should be: + +- ``4`` (number of sides) × ``160/2`` (number of tracks in one direction) ÷ ``4`` (number of starting tracks per lane) = ``80`` columns. \ No newline at end of file diff --git a/doc/src/vpr/lane_and_tap.png b/doc/src/vpr/lane_and_tap.png new file mode 100644 index 00000000000..afce50cea44 Binary files /dev/null and b/doc/src/vpr/lane_and_tap.png differ diff --git a/doc/src/vpr/lane_and_tap_realistic.png b/doc/src/vpr/lane_and_tap_realistic.png new file mode 100644 index 00000000000..2cdd0a91e55 Binary files /dev/null and b/doc/src/vpr/lane_and_tap_realistic.png differ diff --git a/libs/EXTERNAL/CMakeLists.txt b/libs/EXTERNAL/CMakeLists.txt index 816ea7f57c9..2e18a8f68b9 100644 --- a/libs/EXTERNAL/CMakeLists.txt +++ b/libs/EXTERNAL/CMakeLists.txt @@ -12,6 +12,16 @@ add_subdirectory(libsdcparse) add_subdirectory(libblifparse) add_subdirectory(libtatum) add_subdirectory(libcatch2) + +add_subdirectory(yaml-cpp) +# Treat yaml-cpp headers as system headers to suppress warnings +if(TARGET yaml-cpp) + get_target_property(YAML_INCLUDE_DIRS yaml-cpp INTERFACE_INCLUDE_DIRECTORIES) + set_target_properties(yaml-cpp PROPERTIES + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${YAML_INCLUDE_DIRS}" + ) +endif() + #add_subdirectory(parmys) #Proc numbers diff --git a/libs/EXTERNAL/yaml-cpp b/libs/EXTERNAL/yaml-cpp new file mode 160000 index 00000000000..a83cd31548b --- /dev/null +++ b/libs/EXTERNAL/yaml-cpp @@ -0,0 +1 @@ +Subproject commit a83cd31548b19d50f3f983b069dceb4f4d50756d diff --git a/libs/librrgraph/src/base/rr_edge.h b/libs/librrgraph/src/base/rr_edge.h index aca62e9a855..9852b5d7213 100644 --- a/libs/librrgraph/src/base/rr_edge.h +++ b/libs/librrgraph/src/base/rr_edge.h @@ -1,20 +1,24 @@ #pragma once +#include + #include "physical_types.h" #include "librrgraph_types.h" #include "rr_graph_fwd.h" struct t_rr_edge_info { - t_rr_edge_info(RRNodeId from, RRNodeId to, short type, bool is_remapped) noexcept + t_rr_edge_info(RRNodeId from, RRNodeId to, short type, bool is_remapped, std::string crr_id_="") noexcept : from_node(from) , to_node(to) , switch_type(type) - , remapped(is_remapped) {} + , remapped(is_remapped) + , crr_id(std::move(crr_id_)) {} RRNodeId from_node = RRNodeId::INVALID(); RRNodeId to_node = RRNodeId::INVALID(); short switch_type = LIBRRGRAPH_UNDEFINED_VAL; bool remapped = false; + std::string crr_id = ""; friend bool operator<(const t_rr_edge_info& lhs, const t_rr_edge_info& rhs) { VTR_ASSERT(lhs.remapped == rhs.remapped); diff --git a/libs/librrgraph/src/base/rr_graph_builder.cpp b/libs/librrgraph/src/base/rr_graph_builder.cpp index 85b7d2bf967..ec025c69a45 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.cpp +++ b/libs/librrgraph/src/base/rr_graph_builder.cpp @@ -176,8 +176,8 @@ void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_ }); } -void RRGraphBuilder::create_edge_in_cache(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped) { - edges_to_build_.emplace_back(src, dest, size_t(edge_switch), remapped); +void RRGraphBuilder::create_edge_in_cache(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped, std::string crr_id_) { + edges_to_build_.emplace_back(src, dest, size_t(edge_switch), remapped, std::move(crr_id_)); is_edge_dirty_ = true; // Adding a new edge revokes the flag is_incoming_edge_dirty_ = true; } diff --git a/libs/librrgraph/src/base/rr_graph_builder.h b/libs/librrgraph/src/base/rr_graph_builder.h index ed1d3d015c8..d4ac6b26f23 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.h +++ b/libs/librrgraph/src/base/rr_graph_builder.h @@ -12,6 +12,7 @@ * */ +#include #include "rr_graph_storage.h" #include "rr_spatial_lookup.h" #include "metadata_storage.h" @@ -289,7 +290,7 @@ class RRGraphBuilder { /** @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_in_cache(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped); + void create_edge_in_cache(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped, std::string crr_id_=""); /** @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! */ @@ -332,8 +333,8 @@ class RRGraphBuilder { * remap the arch switch id to rr switch id, the edge switch id of this edge shouldn't be changed. For example, when the intra-cluster graph * is built and the rr-graph related to global resources are read from a file, this parameter is true since the intra-cluster switches are * also listed in rr-graph file. So, we use that list to use the rr switch id instead of passing arch switch id for intra-cluster edges.*/ - inline void emplace_back_edge(RRNodeId src, RRNodeId dest, short edge_switch, bool remapped) { - node_storage_.emplace_back_edge(src, dest, edge_switch, remapped); + inline void emplace_back_edge(RRNodeId src, RRNodeId dest, short edge_switch, bool remapped, std::string crr_id="") { + node_storage_.emplace_back_edge(src, dest, edge_switch, remapped, std::move(crr_id)); } /** @brief Append 1 more RR node to the RR graph. */ inline void emplace_back() { @@ -401,6 +402,16 @@ class RRGraphBuilder { return node_storage_.count_rr_switches(arch_switch_inf, arch_switch_fanins); } + /** + * @brief Unlock storage; required to modify an routing resource graph after edge is read + * @note This function is used by OpenFPGA and currently doesn't have any use in VPR code. + */ + 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, @@ -420,7 +431,6 @@ class RRGraphBuilder { node_storage_.resize(size); } - /** @brief This function resize rr_switch to accommodate size RR Switch. */ inline void resize_switches(size_t size) { rr_switch_inf_.resize(size); diff --git a/libs/librrgraph/src/base/rr_graph_storage.cpp b/libs/librrgraph/src/base/rr_graph_storage.cpp index 199510ad93a..7e6cfc94e73 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.cpp +++ b/libs/librrgraph/src/base/rr_graph_storage.cpp @@ -16,15 +16,17 @@ void t_rr_graph_storage::reserve_edges(size_t num_edges) { edge_dest_node_.reserve(num_edges); edge_switch_.reserve(num_edges); edge_remapped_.reserve(num_edges); + edge_crr_id_.reserve(num_edges); } -void t_rr_graph_storage::emplace_back_edge(RRNodeId src, RRNodeId dest, short edge_switch, bool remapped) { +void t_rr_graph_storage::emplace_back_edge(RRNodeId src, RRNodeId dest, short edge_switch, bool remapped, std::string crr_id) { // Cannot mutate edges once edges have been read! VTR_ASSERT(!edges_read_); edge_src_node_.emplace_back(src); edge_dest_node_.emplace_back(dest); edge_switch_.emplace_back(edge_switch); edge_remapped_.emplace_back(remapped); + edge_crr_id_.emplace_back(crr_id); } // Typical node to edge ratio. This allows a preallocation guess for the edges @@ -49,6 +51,7 @@ void t_rr_graph_storage::alloc_and_load_edges(const t_rr_edge_info_set* rr_edges edge_dest_node_.reserve(new_capacity); edge_switch_.reserve(new_capacity); edge_remapped_.reserve(new_capacity); + edge_crr_id_.reserve(new_capacity); } for (const t_rr_edge_info& new_edge : *rr_edges_to_create) { @@ -56,7 +59,8 @@ void t_rr_graph_storage::alloc_and_load_edges(const t_rr_edge_info_set* rr_edges new_edge.from_node, new_edge.to_node, new_edge.switch_type, - new_edge.remapped); + new_edge.remapped, + new_edge.crr_id); } } @@ -87,6 +91,7 @@ void t_rr_graph_storage::remove_edges(std::vector& rr_edges_to_remove) edge_src_node_[erase_idx] = edge_src_node_[RREdgeId(edge_list_end)]; edge_switch_[erase_idx] = edge_switch_[RREdgeId(edge_list_end)]; edge_remapped_[erase_idx] = edge_remapped_[RREdgeId(edge_list_end)]; + edge_crr_id_[erase_idx] = edge_crr_id_[RREdgeId(edge_list_end)]; // At this point we have no copies of what was at erase_idx and two copies of // what was at the end of the list. If we make the list one element shorter, @@ -101,6 +106,7 @@ void t_rr_graph_storage::remove_edges(std::vector& rr_edges_to_remove) edge_src_node_.erase(edge_src_node_.begin() + edge_list_end + 1, edge_src_node_.end()); edge_switch_.erase(edge_switch_.begin() + edge_list_end + 1, edge_switch_.end()); edge_remapped_.erase(edge_remapped_.begin() + edge_list_end + 1, edge_remapped_.end()); + edge_crr_id_.erase(edge_crr_id_.begin() + edge_list_end + 1, edge_crr_id_.end()); VTR_ASSERT(edge_dest_node_.size() == (starting_edge_count - rr_edges_to_remove.size())); @@ -126,6 +132,7 @@ void t_rr_graph_storage::assign_first_edges() { VTR_ASSERT(edge_dest_node_.size() == num_edges); VTR_ASSERT(edge_switch_.size() == num_edges); VTR_ASSERT(edge_remapped_.size() == num_edges); + VTR_ASSERT(edge_crr_id_.size() == num_edges); while (true) { VTR_ASSERT(first_edge_id < num_edges); @@ -808,6 +815,7 @@ t_rr_graph_view t_rr_graph_storage::view() const { vtr::make_const_array_view_id(edge_src_node_), vtr::make_const_array_view_id(edge_dest_node_), vtr::make_const_array_view_id(edge_switch_), + vtr::make_const_array_view_id(edge_crr_id_), virtual_clock_network_root_idx_, vtr::make_const_array_view_id(node_bend_start_), vtr::make_const_array_view_id(node_bend_end_)); @@ -846,6 +854,7 @@ void t_rr_graph_storage::reorder(const vtr::vector& order, auto old_edge_dest_node = edge_dest_node_; auto old_edge_switch = edge_switch_; auto old_edge_remapped = edge_remapped_; + auto old_edge_crr_id = edge_crr_id_; RREdgeId cur_edge(0); // Reorder edges by source node @@ -859,6 +868,7 @@ void t_rr_graph_storage::reorder(const vtr::vector& order, edge_dest_node_[cur_edge] = order[old_edge_dest_node[e]]; edge_switch_[cur_edge] = old_edge_switch[e]; edge_remapped_[cur_edge] = old_edge_remapped[e]; + edge_crr_id_[cur_edge] = old_edge_crr_id[e]; cur_edge = RREdgeId(size_t(cur_edge) + 1); } } diff --git a/libs/librrgraph/src/base/rr_graph_storage.h b/libs/librrgraph/src/base/rr_graph_storage.h index 9fe67e7f56a..d880b1f4b5e 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.h +++ b/libs/librrgraph/src/base/rr_graph_storage.h @@ -20,6 +20,7 @@ #include #include #include +#include #include /* Main structure describing one routing resource node. Everything in * @@ -484,6 +485,10 @@ class t_rr_graph_storage { return edge_source_node(edge_id(id, iedge)); } + std::string edge_crr_id(RREdgeId edge) const { + return edge_crr_id_[edge]; + } + /** * @brief Get the switch used for the specified edge. */ @@ -626,6 +631,7 @@ class t_rr_graph_storage { edge_dest_node_.clear(); edge_switch_.clear(); edge_remapped_.clear(); + edge_crr_id_.clear(); edges_read_ = false; partitioned_ = false; remapped_edges_ = false; @@ -662,6 +668,7 @@ class t_rr_graph_storage { edge_dest_node_.shrink_to_fit(); edge_switch_.shrink_to_fit(); edge_remapped_.shrink_to_fit(); + edge_crr_id_.shrink_to_fit(); } /** @brief Append 1 more RR node to the RR graph.*/ @@ -826,7 +833,7 @@ class t_rr_graph_storage { * of the node. Also, the information about switches is fly-weighted and are accessible with IDs. Thus, * the number of rr switch types can be higher than the number of arch switch types. */ - void emplace_back_edge(RRNodeId src, RRNodeId dest, short edge_switch, bool remapped); + void emplace_back_edge(RRNodeId src, RRNodeId dest, short edge_switch, bool remapped, std::string crr_id); /** @brief Adds a batch of edges.*/ void alloc_and_load_edges(const t_rr_edge_info_set* rr_edges_to_create); @@ -926,6 +933,7 @@ class t_rr_graph_storage { array_rearrange(edge_src_node_, RRNodeId::INVALID()); array_rearrange(edge_dest_node_, RRNodeId::INVALID()); array_rearrange(edge_switch_, LIBRRGRAPH_UNDEFINED_VAL); + array_rearrange(edge_crr_id_, ""); array_rearrange(edge_remapped_, false); } @@ -1093,6 +1101,8 @@ class t_rr_graph_storage { */ vtr::vector edge_remapped_; + vtr::vector edge_crr_id_; + /** @brief * The following data structures are only used for tileable routing resource graph. * The tileable flag is set to true by tileable routing resource graph builder. @@ -1157,6 +1167,7 @@ class t_rr_graph_view { const vtr::array_view_id edge_src_node, const vtr::array_view_id edge_dest_node, const vtr::array_view_id edge_switch, + const vtr::array_view_id edge_crr_id, const std::unordered_map& virtual_clock_network_root_idx, const vtr::array_view_id node_bend_start, const vtr::array_view_id node_bend_end) @@ -1169,6 +1180,7 @@ class t_rr_graph_view { , edge_src_node_(edge_src_node) , edge_dest_node_(edge_dest_node) , edge_switch_(edge_switch) + , edge_crr_id_(edge_crr_id) , virtual_clock_network_root_idx_(virtual_clock_network_root_idx) , node_bend_start_(node_bend_start) , node_bend_end_(node_bend_end) {} @@ -1357,6 +1369,10 @@ class t_rr_graph_view { return edge_switch_[edge]; } + std::string edge_crr_id(RREdgeId edge) const { + return edge_crr_id_[edge]; + } + private: RREdgeId first_edge(RRNodeId id) const { return node_first_edge_[id]; @@ -1375,6 +1391,7 @@ class t_rr_graph_view { vtr::array_view_id edge_src_node_; vtr::array_view_id edge_dest_node_; vtr::array_view_id edge_switch_; + vtr::array_view_id edge_crr_id_; const std::unordered_map& virtual_clock_network_root_idx_; vtr::array_view_id node_bend_start_; diff --git a/libs/librrgraph/src/base/rr_graph_view.cpp b/libs/librrgraph/src/base/rr_graph_view.cpp index 4f5cbb77739..63c27895e06 100644 --- a/libs/librrgraph/src/base/rr_graph_view.cpp +++ b/libs/librrgraph/src/base/rr_graph_view.cpp @@ -61,9 +61,10 @@ std::vector RRGraphView::node_non_configurable_in_edges(RRNodeId node) std::vector RRGraphView::find_edges(RRNodeId src_node, 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)); + for (auto iedge: node_out_edges(src_node)) { + RREdgeId edge = node_storage_.edge_id(src_node, iedge); + if (edge_sink_node(edge) == des_node) { + edge_list.push_back(edge); } } return edge_list; diff --git a/libs/librrgraph/src/base/rr_graph_view.h b/libs/librrgraph/src/base/rr_graph_view.h index f9af258ffce..21e9d0aef97 100644 --- a/libs/librrgraph/src/base/rr_graph_view.h +++ b/libs/librrgraph/src/base/rr_graph_view.h @@ -63,6 +63,7 @@ */ #include "metadata_storage.h" +#include "rr_graph_fwd.h" #include "rr_node.h" #include "physical_types.h" #include "rr_node_types.h" @@ -514,6 +515,10 @@ class RRGraphView { return node_storage_.edge_source_node(id, iedge); } + inline std::string edge_crr_id(RREdgeId edge) const { + return node_storage_.edge_crr_id(edge); + } + /** @brief Check if the edge is a configurable edge * @note A configurable edge represents a programmable switch between routing resources, which could be * - a multiplexer diff --git a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h index 0dd0969e66e..46744b7c08c 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h +++ b/libs/librrgraph/src/io/gen/rr_graph_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/uxsdcxx.py /home/soheil/vtr/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd - * Input file: /home/soheil/vtr/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd - * md5sum of input file: 040903603053940a1b24392c38663b59 + * Cmdline: uxsdcxx/uxsdcxx.py /home/soheil/vpr_repos/libs/librrgraph/src/io/rr_graph.xsd + * Input file: /home/soheil/vpr_repos/libs/librrgraph/src/io/rr_graph.xsd + * md5sum of input file: 5d51b89242fe6e463629ac43a72e4606 */ #include @@ -201,10 +201,15 @@ inline void write_rr_graph_xml(T &in, Context &context, std::ostream &os){ } +#if defined(_MSC_VER) +typedef const uint32_t __declspec(align(1)) triehash_uu32; +typedef const uint64_t __declspec(align(1)) triehash_uu64; +#else typedef const uint32_t __attribute__((aligned(1))) triehash_uu32; typedef const uint64_t __attribute__((aligned(1))) triehash_uu64; static_assert(alignof(triehash_uu32) == 1, "Unaligned 32-bit access not found."); static_assert(alignof(triehash_uu64) == 1, "Unaligned 64-bit access not found."); +#endif #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define onechar(c, s, l) (((uint64_t)(c)) << (s)) #else @@ -275,8 +280,8 @@ constexpr const char *atok_lookup_t_grid_loc[] = {"block_type_id", "height_offse enum class gtok_t_grid_locs {GRID_LOC}; constexpr const char *gtok_lookup_t_grid_locs[] = {"grid_loc"}; -enum class atok_t_node_loc {LAYER_HIGH, LAYER_LOW, PTC, SIDE, XHIGH, XLOW, YHIGH, YLOW}; -constexpr const char *atok_lookup_t_node_loc[] = {"layer_high", "layer_low", "ptc", "side", "xhigh", "xlow", "yhigh", "ylow"}; +enum class atok_t_node_loc {LAYER, PTC, SIDE, TWIST, XHIGH, XLOW, YHIGH, YLOW}; +constexpr const char *atok_lookup_t_node_loc[] = {"layer", "ptc", "side", "twist", "xhigh", "xlow", "yhigh", "ylow"}; enum class atok_t_node_timing {C, R}; @@ -1156,49 +1161,34 @@ inline atok_t_node_loc lex_attr_t_node_loc(const char *in, const std::function constexpr const char *lookup_switch_type[] = {"UXSD_INVALID", "mux", "tristate", "pass_gate", "short", "buffer"}; constexpr const char *lookup_segment_res_type[] = {"UXSD_INVALID", "GENERAL", "GCLK"}; constexpr const char *lookup_pin_type[] = {"UXSD_INVALID", "OPEN", "OUTPUT", "INPUT"}; -constexpr const char *lookup_node_type[] = {"UXSD_INVALID", "CHANX", "CHANY", "CHANZ", "SOURCE", "SINK", "OPIN", "IPIN", "MUX"}; +constexpr const char *lookup_node_type[] = {"UXSD_INVALID", "CHANX", "CHANY", "SOURCE", "SINK", "OPIN", "IPIN"}; constexpr const char *lookup_node_direction[] = {"UXSD_INVALID", "INC_DIR", "DEC_DIR", "BI_DIR", "NONE"}; constexpr const char *lookup_node_clk_res_type[] = {"UXSD_INVALID", "VIRTUAL_SINK"}; constexpr const char *lookup_loc_side[] = {"UXSD_INVALID", "LEFT", "RIGHT", "TOP", "BOTTOM", "RIGHT_LEFT", "RIGHT_BOTTOM", "RIGHT_BOTTOM_LEFT", "TOP_RIGHT", "TOP_BOTTOM", "TOP_LEFT", "TOP_RIGHT_BOTTOM", "TOP_RIGHT_LEFT", "TOP_BOTTOM_LEFT", "TOP_RIGHT_BOTTOM_LEFT", "BOTTOM_LEFT"}; @@ -1813,24 +1803,6 @@ inline enum_pin_type lex_enum_pin_type(const char *in, bool throw_on_invalid, co inline enum_node_type lex_enum_node_type(const char *in, bool throw_on_invalid, const std::function * report_error){ unsigned int len = strlen(in); switch(len){ - case 3: - switch(in[0]){ - case onechar('M', 0, 8): - switch(in[1]){ - case onechar('U', 0, 8): - switch(in[2]){ - case onechar('X', 0, 8): - return enum_node_type::MUX; - break; - default: break; - } - break; - default: break; - } - break; - default: break; - } - break; case 4: switch(*((triehash_uu32*)&in[0])){ case onechar('I', 0, 32) | onechar('P', 8, 32) | onechar('I', 16, 32) | onechar('N', 24, 32): @@ -1855,9 +1827,6 @@ inline enum_node_type lex_enum_node_type(const char *in, bool throw_on_invalid, case onechar('Y', 0, 8): return enum_node_type::CHANY; break; - case onechar('Z', 0, 8): - return enum_node_type::CHANZ; - break; default: break; } break; @@ -2511,11 +2480,8 @@ inline void load_node_loc_required_attributes(const pugi::xml_node &root, int * if(astate[(int)in] == 0) astate[(int)in] = 1; else noreturn_report(report_error, ("Duplicate attribute " + std::string(attr.name()) + " in .").c_str()); switch(in){ - case atok_t_node_loc::LAYER_HIGH: - /* Attribute layer_high set after element init */ - break; - case atok_t_node_loc::LAYER_LOW: - /* Attribute layer_low set after element init */ + case atok_t_node_loc::LAYER: + /* Attribute layer set after element init */ break; case atok_t_node_loc::PTC: /* Attribute ptc set after element init */ @@ -2523,6 +2489,9 @@ inline void load_node_loc_required_attributes(const pugi::xml_node &root, int * case atok_t_node_loc::SIDE: /* Attribute side set after element init */ break; + case atok_t_node_loc::TWIST: + /* Attribute twist set after element init */ + break; case atok_t_node_loc::XHIGH: *xhigh = load_int(attr.value(), report_error); break; @@ -2538,7 +2507,7 @@ inline void load_node_loc_required_attributes(const pugi::xml_node &root, int * default: break; /* Not possible. */ } } - std::bitset<8> test_astate = astate | std::bitset<8>(0b00001011); + std::bitset<8> test_astate = astate | std::bitset<8>(0b00001101); if(!test_astate.all()) attr_error(test_astate, atok_lookup_t_node_loc, report_error); } @@ -3453,11 +3422,8 @@ inline void load_node_loc(const pugi::xml_node &root, T &out, Context &context, for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ atok_t_node_loc in = lex_attr_t_node_loc(attr.name(), report_error); switch(in){ - case atok_t_node_loc::LAYER_HIGH: - out.set_node_loc_layer_high(load_int(attr.value(), report_error), context); - break; - case atok_t_node_loc::LAYER_LOW: - out.set_node_loc_layer_low(load_int(attr.value(), report_error), context); + case atok_t_node_loc::LAYER: + out.set_node_loc_layer(load_int(attr.value(), report_error), context); break; case atok_t_node_loc::PTC: out.set_node_loc_ptc(attr.value(), context); @@ -4199,8 +4165,7 @@ inline void write_node(T &in, std::ostream &os, Context &context){ { auto child_context = in.get_node_loc(context); os << " @@ -220,8 +220,6 @@ inline enum_node_type conv_enum_node_type(ucap::NodeType e, const std::function< return enum_node_type::CHANX; case ucap::NodeType::CHANY: return enum_node_type::CHANY; - case ucap::NodeType::CHANZ: - return enum_node_type::CHANZ; case ucap::NodeType::SOURCE: return enum_node_type::SOURCE; case ucap::NodeType::SINK: @@ -230,8 +228,6 @@ inline enum_node_type conv_enum_node_type(ucap::NodeType e, const std::function< return enum_node_type::OPIN; case ucap::NodeType::IPIN: return enum_node_type::IPIN; - case ucap::NodeType::MUX: - return enum_node_type::MUX; default: (*report_error)("Unknown enum_node_type"); throw std::runtime_error("Unreachable!"); @@ -246,8 +242,6 @@ inline ucap::NodeType conv_to_enum_node_type(enum_node_type e) { return ucap::NodeType::CHANX; case enum_node_type::CHANY: return ucap::NodeType::CHANY; - case enum_node_type::CHANZ: - return ucap::NodeType::CHANZ; case enum_node_type::SOURCE: return ucap::NodeType::SOURCE; case enum_node_type::SINK: @@ -256,8 +250,6 @@ inline ucap::NodeType conv_to_enum_node_type(enum_node_type e) { return ucap::NodeType::OPIN; case enum_node_type::IPIN: return ucap::NodeType::IPIN; - case enum_node_type::MUX: - return ucap::NodeType::MUX; default: throw std::runtime_error("Unknown enum_node_type"); } @@ -769,8 +761,7 @@ inline void load_node_loc_capnp_type(const ucap::NodeLoc::Reader &root, T &out, (void)report_error; (void)stack; - out.set_node_loc_layer_high(root.getLayerHigh(), context); - out.set_node_loc_layer_low(root.getLayerLow(), context); + out.set_node_loc_layer(root.getLayer(), context); out.set_node_loc_ptc(root.getPtc().cStr(), context); out.set_node_loc_side(conv_enum_loc_side(root.getSide(), report_error), context); } @@ -1228,8 +1219,7 @@ inline void write_node_capnp_type(T &in, ucap::Node::Builder &root, Context &con { auto child_context = in.get_node_loc(context); auto node_loc = root.initLoc(); - node_loc.setLayerHigh(in.get_node_loc_layer_high(child_context)); - node_loc.setLayerLow(in.get_node_loc_layer_low(child_context)); + node_loc.setLayer(in.get_node_loc_layer(child_context)); node_loc.setPtc(in.get_node_loc_ptc(child_context)); if((bool)in.get_node_loc_side(child_context)) node_loc.setSide(conv_to_enum_loc_side(in.get_node_loc_side(child_context))); diff --git a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h index bbaae2e5e5f..ae7187161b5 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h +++ b/libs/librrgraph/src/io/gen/rr_graph_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/uxsdcxx.py /home/soheil/vtr/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd - * Input file: /home/soheil/vtr/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd - * md5sum of input file: 040903603053940a1b24392c38663b59 + * Cmdline: uxsdcxx/uxsdcxx.py /home/soheil/vpr_repos/libs/librrgraph/src/io/rr_graph.xsd + * Input file: /home/soheil/vpr_repos/libs/librrgraph/src/io/rr_graph.xsd + * md5sum of input file: 5d51b89242fe6e463629ac43a72e4606 */ #include @@ -27,7 +27,7 @@ enum class enum_segment_res_type {UXSD_INVALID = 0, GENERAL, GCLK}; enum class enum_pin_type {UXSD_INVALID = 0, OPEN, OUTPUT, INPUT}; -enum class enum_node_type {UXSD_INVALID = 0, CHANX, CHANY, CHANZ, SOURCE, SINK, OPIN, IPIN, MUX}; +enum class enum_node_type {UXSD_INVALID = 0, CHANX, CHANY, SOURCE, SINK, OPIN, IPIN}; enum class enum_node_direction {UXSD_INVALID = 0, INC_DIR, DEC_DIR, BI_DIR, NONE}; @@ -371,8 +371,7 @@ class RrGraphBase { /** Generated for complex type "node_loc": * - * - * + * * * * @@ -381,10 +380,8 @@ class RrGraphBase { * * */ - virtual inline int get_node_loc_layer_high(typename ContextTypes::NodeLocReadContext &ctx) = 0; - virtual inline void set_node_loc_layer_high(int layer_high, typename ContextTypes::NodeLocWriteContext &ctx) = 0; - virtual inline int get_node_loc_layer_low(typename ContextTypes::NodeLocReadContext &ctx) = 0; - virtual inline void set_node_loc_layer_low(int layer_low, typename ContextTypes::NodeLocWriteContext &ctx) = 0; + virtual inline int get_node_loc_layer(typename ContextTypes::NodeLocReadContext &ctx) = 0; + virtual inline void set_node_loc_layer(int layer, typename ContextTypes::NodeLocWriteContext &ctx) = 0; virtual inline const char * get_node_loc_ptc(typename ContextTypes::NodeLocReadContext &ctx) = 0; virtual inline void set_node_loc_ptc(const char * ptc, typename ContextTypes::NodeLocWriteContext &ctx) = 0; virtual inline enum_loc_side get_node_loc_side(typename ContextTypes::NodeLocReadContext &ctx) = 0; diff --git a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h index 4ad8e5df5ef..5ece3370987 100644 --- a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h +++ b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h @@ -322,7 +322,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { , is_flat_(is_flat) { // Initialize internal data init_side_map(); - init_segment_inf_xyz(); + init_segment_inf_x_y(); curr_tmp_block_type_id = -1; curr_tmp_height_offset = -1; curr_tmp_width_offset = -1; @@ -387,9 +387,9 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { /** * @brief This function separates the segments in segment_inf_ based on whether their parallel axis - * is X or Y or Z, and it stores them in segment_inf_x_ and segment_inf_y_ and segment_inf_z_. + * is X or Y, and it stores them in segment_inf_x_ and segment_inf_y_. */ - void init_segment_inf_xyz(){ + void init_segment_inf_x_y(){ /* Create a temp copy to convert from vtr::vector to std::vector * This is required because the ``alloc_and_load_rr_indexed_data()`` function supports only std::vector data @@ -406,7 +406,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { t_unified_to_parallel_seg_index seg_index_map; segment_inf_x_ = get_parallel_segs(rr_segs, seg_index_map, e_parallel_axis::X_AXIS); segment_inf_y_ = get_parallel_segs(rr_segs, seg_index_map, e_parallel_axis::Y_AXIS); - segment_inf_z_ = get_parallel_segs(rr_segs, seg_index_map, e_parallel_axis::Z_AXIS); + } /** @@ -419,16 +419,13 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { int find_segment_index_along_axis(int segment_id, e_parallel_axis axis) const { const std::vector* segment_inf_vec_ptr; - if (axis == e_parallel_axis::X_AXIS) { + if (axis == e_parallel_axis::X_AXIS) segment_inf_vec_ptr = &segment_inf_x_; - } else if (axis == e_parallel_axis::Y_AXIS) { + else segment_inf_vec_ptr = &segment_inf_y_; - } else { - segment_inf_vec_ptr = &segment_inf_z_; - } - for (size_t i = 0; i < segment_inf_vec_ptr->size(); i++){ - if ((*segment_inf_vec_ptr)[i].seg_index == segment_id) + for(std::vector::size_type i=0; i < (*segment_inf_vec_ptr).size(); i++){ + if((*segment_inf_vec_ptr)[i].seg_index == segment_id) return static_cast(i); } @@ -709,20 +706,17 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { inline void set_node_loc_ptc(const char* ptc, int& inode) final { t_rr_node node = (*rr_nodes_)[inode]; RRNodeId node_id = node.id(); - std::vector ptc_numbers = parse_ptc_numbers(ptc); - return rr_graph_builder_->set_node_ptc_nums(node_id, ptc_numbers); + std::vector ptc_nums = parse_ptc_numbers(std::string(ptc)); + return rr_graph_builder_->set_node_ptc_nums(node_id, ptc_nums); } inline const char* get_node_loc_ptc(const t_rr_node& node) final { temp_string_ = node_ptc_number_to_string(*rr_graph_, node.id()); return temp_string_.c_str(); } - inline int get_node_loc_layer_low(const t_rr_node& node) final { + inline int get_node_loc_layer(const t_rr_node& node) final { return rr_graph_->node_layer_low(node.id()); } - inline int get_node_loc_layer_high(const t_rr_node& node) final { - return rr_graph_->node_layer_high(node.id()); - } inline int get_node_loc_xhigh(const t_rr_node& node) final { return rr_graph_->node_xhigh(node.id()); } @@ -736,25 +730,13 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return rr_graph_->node_ylow(node.id()); } - inline void set_node_loc_layer_low(int layer_num, int& inode) final { - const t_rr_node& node = (*rr_nodes_)[inode]; + inline void set_node_loc_layer(int layer_num, int& inode) final { + auto node = (*rr_nodes_)[inode]; RRNodeId node_id = node.id(); - // Currently, we only support two layers - VTR_ASSERT(layer_num >= 0 && layer_num <= 1); - char layer_high = rr_graph_->node_layer_high(node_id); - rr_graph_builder_->set_node_layer(node_id, layer_num, layer_high); - } - - inline void set_node_loc_layer_high(int layer_num, int& inode) final { - const t_rr_node& node = (*rr_nodes_)[inode]; - RRNodeId node_id = node.id(); - - // Currently, we only support two layers - VTR_ASSERT(layer_num >= 0 && layer_num <= 1); - char layer_low = rr_graph_->node_layer_low(node_id); - rr_graph_builder_->set_node_layer(node_id, layer_low, layer_num); + VTR_ASSERT(layer_num >= 0); + rr_graph_builder_->set_node_layer(node_id, layer_num, layer_num); } inline void set_node_loc_side(uxsd::enum_loc_side side, int& inode) final { @@ -803,8 +785,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { inline int init_node_timing(int& inode, float C, float R) final { auto node = (*rr_nodes_)[inode]; RRNodeId node_id = node.id(); - const NodeRCIndex rc_index = find_create_rr_rc_data(R, C, *rr_rc_data_); - rr_graph_builder_->set_node_rc_index(node_id, rc_index); + rr_graph_builder_->set_node_rc_index(node_id, NodeRCIndex(find_create_rr_rc_data(R, C, *rr_rc_data_))); return inode; } inline void finish_node_timing(int& /*inode*/) final {} @@ -849,8 +830,9 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { rr_graph_builder_->set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + segment_inf_x_.size() + seg_ind_y)); seg_index_[rr_graph.node_cost_index(node.id())] = segment_id; } else if (rr_graph.node_type(node.id()) == e_rr_type::CHANZ) { - int seg_ind_z = find_segment_index_along_axis(segment_id, e_parallel_axis::Z_AXIS); - rr_graph_builder_->set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + segment_inf_x_.size() + segment_inf_y_.size() + seg_ind_z)); + // TODO: Don't use CHANX info + int seg_ind_z = find_segment_index_along_axis(segment_id, e_parallel_axis::X_AXIS); + rr_graph_builder_->set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + seg_ind_z)); seg_index_[rr_graph.node_cost_index(node.id())] = segment_id; } return inode; @@ -931,7 +913,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { type); } - rr_graph_builder_->set_node_rc_index(node_id, find_create_rr_rc_data(0, 0, *rr_rc_data_)); + rr_graph_builder_->set_node_rc_index(node_id, NodeRCIndex(find_create_rr_rc_data(0, 0, *rr_rc_data_))); return id; } @@ -1041,7 +1023,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { inline void* init_rr_graph_rr_nodes(void*& /*ctx*/) final { rr_nodes_->clear(); - seg_index_.resize(CHANX_COST_INDEX_START + segment_inf_x_.size() + segment_inf_y_.size() + segment_inf_z_.size(), -1); + seg_index_.resize(CHANX_COST_INDEX_START + segment_inf_x_.size() + segment_inf_y_.size(), -1); return nullptr; } inline void finish_rr_graph_rr_nodes(void*& /*ctx*/) final { @@ -1996,8 +1978,6 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return e_rr_type::CHANX; case uxsd::enum_node_type::CHANY: return e_rr_type::CHANY; - case uxsd::enum_node_type::CHANZ: - return e_rr_type::CHANZ; case uxsd::enum_node_type::SOURCE: return e_rr_type::SOURCE; case uxsd::enum_node_type::SINK: @@ -2006,8 +1986,6 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return e_rr_type::OPIN; case uxsd::enum_node_type::IPIN: return e_rr_type::IPIN; - case uxsd::enum_node_type::MUX: - return e_rr_type::MUX; default: report_error( "Invalid node type %d", @@ -2020,8 +1998,6 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return uxsd::enum_node_type::CHANX; case e_rr_type::CHANY: return uxsd::enum_node_type::CHANY; - case e_rr_type::CHANZ: - return uxsd::enum_node_type::CHANZ; case e_rr_type::SOURCE: return uxsd::enum_node_type::SOURCE; case e_rr_type::SINK: @@ -2030,8 +2006,6 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return uxsd::enum_node_type::OPIN; case e_rr_type::IPIN: return uxsd::enum_node_type::IPIN; - case e_rr_type::MUX: - return uxsd::enum_node_type::MUX; default: report_error( "Invalid type %d", type); diff --git a/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp b/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp index d43f8738382..1a044dfa116 100644 --- a/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp +++ b/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp @@ -2,11 +2,12 @@ # https://github.com/duck2/uxsdcxx # Modify only if your build process doesn't involve regenerating this file. # -# Cmdline: uxsdcxx/uxsdcap.py /home/soheil/vtr/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd -# Input file: /home/soheil/vtr/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd -# md5sum of input file: 040903603053940a1b24392c38663b59 +# Cmdline: uxsdcxx/uxsdcap.py /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd +# Input file: /home/mohagh18/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd -@0xe7650575a8718aa2; +# md5sum of input file: 65eddcc840064bbb91d7f4cf0b8bf821 + +@0xe787bf7696810419; using Cxx = import "/capnp/c++.capnp"; $Cxx.namespace("ucap"); @@ -36,12 +37,10 @@ enum NodeType { uxsdInvalid @0; chanx @1; chany @2; - chanz @3; - source @4; - sink @5; - opin @6; - ipin @7; - mux @8; + source @3; + sink @4; + opin @5; + ipin @6; } enum NodeDirection { @@ -178,10 +177,10 @@ struct GridLocs { } struct NodeLoc { - layerHigh @0 :Int32 = 0; - layerLow @1 :Int32 = 0; - ptc @2 :Text; - side @3 :LocSide; + layer @0 :Int32 = 0; + ptc @1 :Text; + side @2 :LocSide; + twist @3 :Int32; xhigh @4 :Int32; xlow @5 :Int32; yhigh @6 :Int32; diff --git a/utils/route_diag/src/main.cpp b/utils/route_diag/src/main.cpp index 3dd599504a0..af2f7833022 100644 --- a/utils/route_diag/src/main.cpp +++ b/utils/route_diag/src/main.cpp @@ -296,6 +296,7 @@ int main(int argc, const char **argv) { alloc_routing_structs(chan_width, vpr_setup.RouterOpts, + vpr_setup.CRROpts, vpr_setup.RoutingArch, vpr_setup.Segments, Arch.directs, diff --git a/vpr/CMakeLists.txt b/vpr/CMakeLists.txt index 2a30974832d..ca416c68703 100644 --- a/vpr/CMakeLists.txt +++ b/vpr/CMakeLists.txt @@ -119,6 +119,7 @@ target_link_libraries(libvpr libpugixml librrgraph ZLIB::ZLIB + yaml-cpp::yaml-cpp ) if(VPR_USE_SERVER) diff --git a/vpr/src/analytical_place/analytical_placement_flow.cpp b/vpr/src/analytical_place/analytical_placement_flow.cpp index 371fff0164e..6858e5bbcd8 100644 --- a/vpr/src/analytical_place/analytical_placement_flow.cpp +++ b/vpr/src/analytical_place/analytical_placement_flow.cpp @@ -238,6 +238,7 @@ void run_analytical_placement_flow(t_vpr_setup& vpr_setup) { if (pre_cluster_timing_manager.is_valid()) { place_delay_model = PlacementDelayModelCreator::create_delay_model(vpr_setup.PlacerOpts, vpr_setup.RouterOpts, + vpr_setup.CRROpts, (const Netlist<>&)atom_nlist, vpr_setup.RoutingArch, vpr_setup.Segments, diff --git a/vpr/src/analytical_place/detailed_placer.cpp b/vpr/src/analytical_place/detailed_placer.cpp index c764191524a..c2c375802a6 100644 --- a/vpr/src/analytical_place/detailed_placer.cpp +++ b/vpr/src/analytical_place/detailed_placer.cpp @@ -64,6 +64,7 @@ AnnealerDetailedPlacer::AnnealerDetailedPlacer(const BlkLocRegistry& curr_cluste if (vpr_setup.PlacerOpts.place_algorithm.is_timing_driven()) { place_delay_model = PlacementDelayModelCreator::create_delay_model(vpr_setup.PlacerOpts, vpr_setup.RouterOpts, + vpr_setup.CRROpts, (const Netlist<>&)clustered_netlist, vpr_setup.RoutingArch, vpr_setup.Segments, diff --git a/vpr/src/base/place_and_route.cpp b/vpr/src/base/place_and_route.cpp index 5adef6398bb..b3e97bb6de7 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -42,6 +42,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, const Netlist<>& router_net_list, const t_placer_opts& placer_opts_ref, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, const t_noc_opts& noc_opts, const t_file_name_opts& filename_opts, @@ -172,6 +173,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, try_place(placement_net_list, placer_opts, router_opts, + crr_opts, analysis_opts, noc_opts, arch->Chans, @@ -184,6 +186,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, success = route(router_net_list, current, router_opts, + crr_opts, analysis_opts, det_routing_arch, segment_inf, net_delay, @@ -314,7 +317,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, break; if (placer_opts.place_freq == PLACE_ALWAYS) { placer_opts.place_chan_width = current; - try_place(placement_net_list, placer_opts, router_opts, analysis_opts, noc_opts, + try_place(placement_net_list, placer_opts, router_opts, crr_opts, analysis_opts, noc_opts, arch->Chans, det_routing_arch, segment_inf, arch->directs, FlatPlacementInfo(), // Pass empty flat placement info. @@ -324,6 +327,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, success = route(router_net_list, current, router_opts, + crr_opts, analysis_opts, det_routing_arch, segment_inf, @@ -374,6 +378,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, det_routing_arch, segment_inf, router_opts, + crr_opts, arch->directs, &warnings, is_flat); diff --git a/vpr/src/base/place_and_route.h b/vpr/src/base/place_and_route.h index a67712d694d..356eae042a5 100644 --- a/vpr/src/base/place_and_route.h +++ b/vpr/src/base/place_and_route.h @@ -21,6 +21,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, const Netlist<>& router_net_list, const t_placer_opts& placer_opts_ref, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, const t_noc_opts& noc_opts, const t_file_name_opts& filename_opts, diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index 605e89e9c71..033088aa43f 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -3280,6 +3280,48 @@ argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_optio .default_value("off") .show_in(argparse::ShowIn::HELP_ONLY); + auto& crr_grp = parser.add_argument_group("CRR options"); + + crr_grp.add_argument(args.sb_maps, "--sb_maps") + .help("Switch block map file that specifed the switch block template used for each location") + .default_value("") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.sb_templates, "--sb_templates") + .help("Directory containing the switch block templates") + .default_value("") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.crr_num_threads, "--crr_num_threads") + .help("Number of threads to use for the CRR generation") + .default_value("1") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.preserve_input_pin_connections, "--preserve_input_pin_connections") + .help("If it set to on, the input pin connections will be generated by the default flow and not from the CRR template") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.preserve_output_pin_connections, "--preserve_output_pin_connections") + .help("If it set to on, the output pin connections will be generated by the default flow and not from the CRR template") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.annotated_rr_graph, "--annotated_rr_graph") + .help("Whether the generated CRR should be annotated with delay") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.remove_dangling_nodes, "--remove_dangling_nodes") + .help("Whether the generated CRR should remove CHANX and CHANY nodes that have no fan-in") + .default_value("off") + .show_in(argparse::ShowIn::HELP_ONLY); + + crr_grp.add_argument(args.sb_count_dir, "--sb_count_dir") + .help("Directory to store csv files showing how many times each switch specified in the switch block templates is used") + .default_value("") + .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 5485f058c49..dc3d1edc274 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -284,6 +284,16 @@ struct t_options { argparse::ArgValue write_timing_summary; argparse::ArgValue skip_sync_clustering_and_routing_results; argparse::ArgValue generate_net_timing_report; + + /* CRR options */ + argparse::ArgValue sb_maps; + argparse::ArgValue sb_templates; + argparse::ArgValue crr_num_threads; + argparse::ArgValue preserve_input_pin_connections; + argparse::ArgValue preserve_output_pin_connections; + argparse::ArgValue annotated_rr_graph; + argparse::ArgValue remove_dangling_nodes; + argparse::ArgValue sb_count_dir; }; argparse::ArgumentParser create_arg_parser(const std::string& prog_name, t_options& args); diff --git a/vpr/src/base/setup_vpr.cpp b/vpr/src/base/setup_vpr.cpp index 8bdbed39dcc..0f3df462acc 100644 --- a/vpr/src/base/setup_vpr.cpp +++ b/vpr/src/base/setup_vpr.cpp @@ -48,6 +48,7 @@ static void setup_switches(const t_arch& Arch, t_det_routing_arch& RoutingArch, const std::vector& arch_switches); static void setup_analysis_opts(const t_options& Options, t_analysis_opts& analysis_opts); +static void setup_crr_opts(const t_options& Options, t_crr_opts& crr_opts); static void setup_power_opts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch); /** @@ -99,6 +100,7 @@ void SetupVPR(const t_options* options, t_ap_opts* apOpts, t_router_opts* routerOpts, t_analysis_opts* analysisOpts, + t_crr_opts* crrOpts, t_noc_opts* nocOpts, t_server_opts* serverOpts, t_det_routing_arch& routingArch, @@ -151,6 +153,7 @@ void SetupVPR(const t_options* options, setup_anneal_sched(*options, &placerOpts->anneal_sched); setup_router_opts(*options, routerOpts); setup_analysis_opts(*options, *analysisOpts); + setup_crr_opts(*options, *crrOpts); setup_power_opts(*options, powerOpts, arch); setup_noc_opts(*options, nocOpts); setup_server_opts(*options, serverOpts); @@ -755,6 +758,17 @@ static void setup_analysis_opts(const t_options& Options, t_analysis_opts& analy analysis_opts.generate_net_timing_report = Options.generate_net_timing_report; } +static void setup_crr_opts(const t_options& Options, t_crr_opts& crr_opts) { + crr_opts.sb_maps = Options.sb_maps; + crr_opts.sb_templates = Options.sb_templates; + crr_opts.crr_num_threads = Options.crr_num_threads; + crr_opts.preserve_input_pin_connections = Options.preserve_input_pin_connections; + crr_opts.preserve_output_pin_connections = Options.preserve_output_pin_connections; + crr_opts.annotated_rr_graph = Options.annotated_rr_graph; + crr_opts.remove_dangling_nodes = Options.remove_dangling_nodes; + crr_opts.sb_count_dir = Options.sb_count_dir; +} + static void setup_power_opts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch) { DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); diff --git a/vpr/src/base/setup_vpr.h b/vpr/src/base/setup_vpr.h index f72bb231bd3..1e6eaa58120 100644 --- a/vpr/src/base/setup_vpr.h +++ b/vpr/src/base/setup_vpr.h @@ -16,6 +16,7 @@ void SetupVPR(const t_options* Options, t_ap_opts* APOpts, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, + t_crr_opts* CRROpts, t_noc_opts* NocOpts, t_server_opts* ServerOpts, t_det_routing_arch& RoutingArch, diff --git a/vpr/src/base/stats.cpp b/vpr/src/base/stats.cpp index 7b484732d90..13c40808c80 100644 --- a/vpr/src/base/stats.cpp +++ b/vpr/src/base/stats.cpp @@ -1,8 +1,11 @@ #include "stats.h" -#include #include +#include +#include +#include +#include #include #include @@ -23,8 +26,89 @@ #include "segment_stats.h" #include "channel_stats.h" +#include "crr_common.h" + +namespace fs = std::filesystem; + /********************** Subroutines local to this module *********************/ +// Helper struct to parse the sb_id keys +struct SBKeyParts { + std::string filename; + int row; + int col; +}; + +// Parse the key to extract filename, row, and column +static SBKeyParts parse_sb_key(const std::string& key) { + SBKeyParts parts; + + // Find the last two underscores + size_t last_underscore = key.rfind('_'); + size_t second_last_underscore = key.rfind('_', last_underscore - 1); + + parts.filename = key.substr(0, second_last_underscore); + parts.row = std::stoi(key.substr(second_last_underscore + 1, last_underscore - second_last_underscore - 1)); + parts.col = std::stoi(key.substr(last_underscore + 1)); + + return parts; +} + +// Read CSV file and keep first NUM_EMPTY_ROWS rows completely and first NUM_EMPTY_COLS columns of all other rows +static std::vector> read_and_trim_csv(const std::string& filepath) { + std::vector> data; + std::ifstream file(filepath); + + if (!file.is_open()) { + return data; + } + + std::string line; + int row_count = 0; + while (std::getline(file, line)) { + std::vector row; + std::stringstream ss(line); + std::string cell; + int col_count = 0; + + if (row_count < crrgenerator::NUM_EMPTY_ROWS) { + // Keep entire row for first NUM_EMPTY_ROWS rows + while (std::getline(ss, cell, ',')) { + row.push_back(cell); + } + } else { + // Keep only first NUM_EMPTY_COLS columns for other rows + while (std::getline(ss, cell, ',') && col_count < crrgenerator::NUM_EMPTY_COLS) { + row.push_back(cell); + col_count++; + } + } + + data.push_back(row); + row_count++; + } + + file.close(); + return data; +} + +// Write 2D vector to CSV file +static void write_csv(const std::string& filepath, const std::vector>& data) { + std::ofstream file(filepath); + + for (size_t i = 0; i < data.size(); ++i) { + for (size_t j = 0; j < data[i].size(); ++j) { + file << data[i][j]; + if (j < data[i].size() - 1) { + file << ","; + } + } + file << "\n"; + } + + file.close(); +} + /** * @brief Loads the two arrays passed in with the total occupancy at each of the * channel segments in the FPGA. @@ -129,6 +213,110 @@ void routing_stats(const Netlist<>& net_list, } } +void write_sb_count_stats(const Netlist<>& net_list, + const std::string& sb_map_dir, + const std::string& sb_count_dir) { + const auto& rr_graph = g_vpr_ctx.device().rr_graph; + const auto& route_ctx = g_vpr_ctx.routing(); + std::unordered_map sb_count; + + for (ParentNetId net_id : net_list.nets()) { + if (!net_list.net_is_ignored(net_id) && net_list.net_sinks(net_id).size() != 0) { + const vtr::optional& tree = route_ctx.route_trees[net_id]; + if (!tree) { + continue; + } + + for (const RouteTreeNode& rt_node : tree.value().all_nodes()) { + auto parent = rt_node.parent(); + // Skip the root node + if (!parent) { + continue; + } + + const RouteTreeNode& parent_rt_node = parent.value(); + + RRNodeId src_node = parent_rt_node.inode; + RRNodeId sink_node = rt_node.inode; + std::vector edges = rr_graph.find_edges(src_node, sink_node); + VTR_ASSERT(edges.size() == 1); + std::string sb_id = rr_graph.edge_crr_id(edges[0]); + if (sb_id.empty()) { + continue; + } + if (sb_count.find(sb_id) == sb_count.end()) { + sb_count[sb_id] = 0; + } + sb_count[sb_id]++; + } + } + } + + // Write the sb_count to a file + // First, read all CSV files from directory and trim them + std::unordered_map>> csv_data; + + for (const auto& entry : fs::directory_iterator(sb_map_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".csv") { + std::string filename = entry.path().filename().string(); + csv_data[filename] = read_and_trim_csv(entry.path().string()); + } + } + + // Group sb_count entries by filename + std::unordered_map>>> file_groups; + + for (const auto& [sb_id, count] : sb_count) { + SBKeyParts parts = parse_sb_key(sb_id); + file_groups[parts.filename].push_back({parts.row, {parts.col, count}}); + } + + // Process each file + for (auto& [filename, data] : csv_data) { + // Check if this file has updates from sb_count + if (file_groups.find(filename) == file_groups.end()) { + // No updates for this file, just write trimmed version + std::string output_path = sb_count_dir + "/" + filename; + write_csv(output_path, data); + VTR_LOG("Written trimmed CSV: %s\n", filename.c_str()); + continue; + } + + const auto& entries = file_groups[filename]; + + // Find maximum row and column needed + int max_row = 0, max_col = 0; + for (const auto& entry : entries) { + max_row = std::max(max_row, entry.first); + max_col = std::max(max_col, entry.second.first); + } + + // Expand data structure if needed + while (data.size() <= static_cast(max_row)) { + data.push_back(std::vector()); + } + + for (auto& row : data) { + while (row.size() <= static_cast(max_col)) { + row.push_back(""); + } + } + + // Update values from sb_count + for (const auto& entry : entries) { + int row = entry.first; + int col = entry.second.first; + int count = entry.second.second; + data[row][col] = std::to_string(count); + } + + // Write updated file + std::string output_path = sb_count_dir + "/" + filename; + write_csv(output_path, data); + VTR_LOG("Written switchbox counts to: %s\n", filename.c_str()); + } +} + void length_and_bends_stats(const Netlist<>& net_list, bool is_flat) { int max_bends = 0; int total_bends = 0; diff --git a/vpr/src/base/stats.h b/vpr/src/base/stats.h index aefc22a052a..4dd6083996e 100644 --- a/vpr/src/base/stats.h +++ b/vpr/src/base/stats.h @@ -23,6 +23,10 @@ void routing_stats(const Netlist<>& net_list, e_directionality directionality, bool is_flat); +void write_sb_count_stats(const Netlist<>& net_list, + const std::string& sb_map_dir, + const std::string& sb_count_dir); + void print_wirelen_prob_dist(bool is_flat); void print_lambda(); diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 1867659297e..b2388e92237 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -297,6 +297,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a &vpr_setup->APOpts, &vpr_setup->RouterOpts, &vpr_setup->AnalysisOpts, + &vpr_setup->CRROpts, &vpr_setup->NocOpts, &vpr_setup->ServerOpts, vpr_setup->RoutingArch, @@ -879,6 +880,7 @@ void vpr_place(const Netlist<>& net_list, try_place(net_list, vpr_setup.PlacerOpts, vpr_setup.RouterOpts, + vpr_setup.CRROpts, vpr_setup.AnalysisOpts, vpr_setup.NocOpts, arch.Chans, @@ -1095,6 +1097,7 @@ RouteStatus vpr_route_fixed_W(const Netlist<>& net_list, status = route(net_list, fixed_channel_width, vpr_setup.RouterOpts, + vpr_setup.CRROpts, vpr_setup.AnalysisOpts, vpr_setup.RoutingArch, vpr_setup.Segments, @@ -1127,6 +1130,7 @@ RouteStatus vpr_route_min_W(const Netlist<>& net_list, net_list, vpr_setup.PlacerOpts, router_opts, + vpr_setup.CRROpts, vpr_setup.AnalysisOpts, vpr_setup.NocOpts, vpr_setup.FileNameOpts, @@ -1207,6 +1211,7 @@ void vpr_create_rr_graph(t_vpr_setup& vpr_setup, const t_arch& arch, int chan_wi det_routing_arch, vpr_setup.Segments, router_opts, + vpr_setup.CRROpts, arch.directs, &warnings, is_flat); @@ -1349,6 +1354,7 @@ void vpr_setup_vpr(t_options* Options, t_ap_opts* APOpts, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, + t_crr_opts* CRROpts, t_noc_opts* NocOpts, t_server_opts* ServerOpts, t_det_routing_arch& RoutingArch, @@ -1372,6 +1378,7 @@ void vpr_setup_vpr(t_options* Options, APOpts, RouterOpts, AnalysisOpts, + CRROpts, NocOpts, ServerOpts, RoutingArch, @@ -1502,6 +1509,12 @@ void vpr_analysis(const Netlist<>& net_list, vpr_setup.RoutingArch.directionality, is_flat); + if (!vpr_setup.CRROpts.sb_count_dir.empty()) { + write_sb_count_stats(net_list, + vpr_setup.CRROpts.sb_templates, + vpr_setup.CRROpts.sb_count_dir); + } + if (vpr_setup.TimingEnabled) { //Load the net delays diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index 7147691ba73..90f156e2ca8 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -181,6 +181,7 @@ void vpr_setup_vpr(t_options* Options, t_ap_opts* APOpts, t_router_opts* RouterOpts, t_analysis_opts* AnalysisOpts, + t_crr_opts* CRROpts, t_noc_opts* NocOpts, t_server_opts* ServerOpts, t_det_routing_arch& RoutingArch, diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 80a662ad5be..da9d705cabf 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1410,6 +1410,18 @@ struct t_analysis_opts { bool skip_sync_clustering_and_routing_results; }; +/// Stores CRR specific options +struct t_crr_opts { + std::string sb_maps; + std::string sb_templates; + int crr_num_threads; + bool preserve_input_pin_connections; + bool preserve_output_pin_connections; + bool annotated_rr_graph; + bool remove_dangling_nodes; + std::string sb_count_dir; +}; + /// Stores NoC specific options, when supplied as an input by the user struct t_noc_opts { bool noc; ///& segment_ std::unique_ptr PlacementDelayModelCreator::create_delay_model(const t_placer_opts& placer_opts, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const Netlist<>& net_list, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, @@ -36,7 +37,13 @@ PlacementDelayModelCreator::create_delay_model(const t_placer_opts& placer_opts, t_chan_width chan_width = setup_chan_width(router_opts, chan_width_dist); - alloc_routing_structs(chan_width, router_opts, det_routing_arch, segment_inf, directs, is_flat); + alloc_routing_structs(chan_width, + router_opts, + crr_opts, + det_routing_arch, + segment_inf, + directs, + is_flat); const RouterLookahead* router_lookahead = get_cached_router_lookahead(det_routing_arch, router_opts.lookahead_type, diff --git a/vpr/src/place/delay_model/PlacementDelayModelCreator.h b/vpr/src/place/delay_model/PlacementDelayModelCreator.h index 6da4ac56f13..802d59ddcb2 100644 --- a/vpr/src/place/delay_model/PlacementDelayModelCreator.h +++ b/vpr/src/place/delay_model/PlacementDelayModelCreator.h @@ -8,6 +8,7 @@ class PlaceDelayModel; struct t_placer_opts; struct t_router_opts; +struct t_crr_opts; struct t_det_routing_arch; struct t_segment_inf; struct t_chan_width_dist; @@ -20,6 +21,7 @@ class PlacementDelayModelCreator { static std::unique_ptr create_delay_model(const t_placer_opts& placer_opts, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const Netlist<>& net_list, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, diff --git a/vpr/src/place/place.cpp b/vpr/src/place/place.cpp index 8a60e7b3bb9..bf7f3998a96 100644 --- a/vpr/src/place/place.cpp +++ b/vpr/src/place/place.cpp @@ -29,6 +29,7 @@ void print_clb_placement(const char* fname); void try_place(const Netlist<>& net_list, const t_placer_opts& placer_opts, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, const t_noc_opts& noc_opts, const t_chan_width_dist& chan_width_dist, @@ -83,6 +84,7 @@ void try_place(const Netlist<>& net_list, /*do this before the initial placement to avoid messing up the initial placement */ place_delay_model = PlacementDelayModelCreator::create_delay_model(placer_opts, router_opts, + crr_opts, net_list, det_routing_arch, segment_inf, diff --git a/vpr/src/place/place.h b/vpr/src/place/place.h index 37246502bca..109158d5b94 100644 --- a/vpr/src/place/place.h +++ b/vpr/src/place/place.h @@ -8,6 +8,7 @@ class FlatPlacementInfo; void try_place(const Netlist<>& net_list, const t_placer_opts& placer_opts, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, const t_noc_opts& noc_opts, const t_chan_width_dist& chan_width_dist, diff --git a/vpr/src/route/route.cpp b/vpr/src/route/route.cpp index 0696996cf85..8d4b5218836 100644 --- a/vpr/src/route/route.cpp +++ b/vpr/src/route/route.cpp @@ -17,6 +17,7 @@ bool route(const Netlist<>& net_list, int width_fac, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, t_det_routing_arch& det_routing_arch, std::vector& segment_inf, @@ -62,6 +63,7 @@ bool route(const Netlist<>& net_list, det_routing_arch, segment_inf, router_opts, + crr_opts, directs, &warning_count, is_flat); diff --git a/vpr/src/route/route.h b/vpr/src/route/route.h index 5d519228d0d..d3bf5f58f2e 100644 --- a/vpr/src/route/route.h +++ b/vpr/src/route/route.h @@ -20,6 +20,7 @@ bool route(const Netlist<>& net_list, int width_fac, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const t_analysis_opts& analysis_opts, t_det_routing_arch& det_routing_arch, std::vector& segment_inf, diff --git a/vpr/src/route/route_utils.cpp b/vpr/src/route/route_utils.cpp index 1d5118a39f8..a8e009a8434 100644 --- a/vpr/src/route/route_utils.cpp +++ b/vpr/src/route/route_utils.cpp @@ -493,6 +493,7 @@ vtr::vector>> set_net /** Wrapper for create_rr_graph() with extra checks */ void try_graph(int width_fac, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const t_chan_width_dist& chan_width_dist, @@ -525,6 +526,7 @@ void try_graph(int width_fac, det_routing_arch, segment_inf, router_opts, + crr_opts, directs, &warning_count, is_flat); diff --git a/vpr/src/route/route_utils.h b/vpr/src/route/route_utils.h index f9f4254b375..56bd7ac0876 100644 --- a/vpr/src/route/route_utils.h +++ b/vpr/src/route/route_utils.h @@ -149,6 +149,7 @@ vtr::vector>> set_net /** Wrapper for create_rr_graph() with extra checks */ void try_graph(int width_fac, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const t_chan_width_dist& chan_width_dist, diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index fcfa10335d7..208f6e7cf2a 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -238,6 +238,7 @@ vtr::vector calculate_all_path_delays_from_rr_node(RRNodeId src void alloc_routing_structs(const t_chan_width& chan_width, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const std::vector& directs, @@ -264,6 +265,7 @@ void alloc_routing_structs(const t_chan_width& chan_width, det_routing_arch, segment_inf, router_opts, + crr_opts, directs, &warnings, is_flat); diff --git a/vpr/src/route/router_delay_profiling.h b/vpr/src/route/router_delay_profiling.h index 2769a34c454..430382ab978 100644 --- a/vpr/src/route/router_delay_profiling.h +++ b/vpr/src/route/router_delay_profiling.h @@ -46,6 +46,7 @@ vtr::vector calculate_all_path_delays_from_rr_node(RRNodeId src void alloc_routing_structs(const t_chan_width& chan_width, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const std::vector& directs, diff --git a/vpr/src/route/rr_graph_generation/rr_graph.cpp b/vpr/src/route/rr_graph_generation/rr_graph.cpp index 506a35aa97c..e8f8575a34c 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph.cpp +++ b/vpr/src/route/rr_graph_generation/rr_graph.cpp @@ -420,6 +420,7 @@ void create_rr_graph(e_graph_type graph_type, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const std::vector& directs, int* Warnings, bool is_flat) { @@ -497,6 +498,7 @@ void create_rr_graph(e_graph_type graph_type, build_tileable_unidir_rr_graph(block_types, grid, nodes_per_chan, + crr_opts, det_routing_arch.switch_block_type, det_routing_arch.Fs, det_routing_arch.switch_block_subtype, diff --git a/vpr/src/route/rr_graph_generation/rr_graph.h b/vpr/src/route/rr_graph_generation/rr_graph.h index 96793b1b200..607d30afdfe 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph.h +++ b/vpr/src/route/rr_graph_generation/rr_graph.h @@ -29,6 +29,7 @@ void create_rr_graph(e_graph_type graph_type, t_det_routing_arch& det_routing_arch, const std::vector& segment_inf, const t_router_opts& router_opts, + const t_crr_opts& crr_opts, const std::vector& directs, int* Warnings, bool is_flat); diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_common.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_common.h new file mode 100644 index 00000000000..92f4fa88ffe --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_common.h @@ -0,0 +1,354 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // For getrusage on Unix/Linux + +#include "rr_graph_fwd.h" +#include "rr_node_types.h" +namespace crrgenerator { + +// Forward declarations +class SwitchBlockManager; +class NodeLookupManager; +class ConnectionBuilder; +class DataFrameProcessor; + +// Type aliases for convenience +using SwitchId = int32_t; +using SegmentId = int32_t; +using Coordinate = int32_t; +using DelayValue = float; + +// Constants +// Number of empty rows and columns in the Excel file +constexpr int NUM_EMPTY_ROWS = 5; +constexpr int NUM_EMPTY_COLS = 4; + +constexpr SwitchId DELAY_0_ID = 1; +constexpr int DEFAULT_SWITCH_DELAY_MIN = 10000; + +// Node types +enum class NodeType { SOURCE, + SINK, + IPIN, + OPIN, + CHANX, + CHANY, + INVALID }; + +// Direction types +enum class Direction { INC_DIR, + DEC_DIR }; + +// Side types +enum class Side { LEFT, + RIGHT, + TOP, + BOTTOM, + IPIN, + OPIN, + INVALID }; + +// Location structure +struct Location { + Coordinate x_low{-1}; + Coordinate x_high{-1}; + Coordinate y_low{-1}; + Coordinate y_high{-1}; + Coordinate layer{-1}; + std::string ptc; + std::string side; + + Location() = default; + Location(Coordinate xl, Coordinate xh, Coordinate yl, Coordinate yh, Coordinate l, const std::string& p, const std::string& s) + : x_low(xl) + , x_high(xh) + , y_low(yl) + , y_high(yh) + , layer(l) + , ptc(p) + , side(s) {} + + bool operator==(const Location& other) const { + return x_low == other.x_low && x_high == other.x_high && y_low == other.y_low && y_high == other.y_high && layer == other.layer && ptc == other.ptc && side == other.side; + } +}; + +// Node timing information +struct NodeTiming { + DelayValue r{0.0}; + DelayValue c{0.0}; + + NodeTiming() = default; + NodeTiming(DelayValue r_val, DelayValue c_val) + : r(r_val) + , c(c_val) {} +}; + +// Node segment information +struct NodeSegmentId { + int segment_id{-1}; + + NodeSegmentId() = default; + NodeSegmentId(int segment_id_val) + : segment_id(segment_id_val) {} + + bool empty() const { return segment_id == -1; } +}; + +// Timing information +struct Timing { + DelayValue Cin{0.0}; + DelayValue Tdel{0.0}; + + Timing() = default; + Timing(DelayValue cin_val, DelayValue tdel_val) + : Cin(cin_val) + , Tdel(tdel_val) {} +}; + +// Sizing information +struct Sizing { + DelayValue mux_trans_size{0.0}; + DelayValue buf_size{0.0}; + + Sizing() = default; + Sizing(DelayValue mux_size, DelayValue buffer_size) + : mux_trans_size(mux_size) + , buf_size(buffer_size) {} +}; + +/** + * @brief Connection class + * + * This class represents a connection between two nodes in the RR graph. + * It is used to store the connection information and to compare connections. + */ +class Connection { + public: + Connection(RRNodeId sink_node, RRNodeId src_node, int delay_ps, std::string crr_id) noexcept + : sink_node_(sink_node) + , src_node_(src_node) + , delay_ps_(delay_ps) + , crr_id_(std::move(crr_id)) {} + + RRNodeId sink_node() const { return sink_node_; } + RRNodeId src_node() const { return src_node_; } + int delay_ps() const { return delay_ps_; } + std::string crr_id() const { return crr_id_; } + + bool operator<(const Connection& other) const { + return std::tie(sink_node_, src_node_, delay_ps_) < std::tie(other.sink_node_, other.src_node_, other.delay_ps_); + } + + bool operator==(const Connection& other) const { + return sink_node_ == other.sink_node_ && src_node_ == other.src_node_ && delay_ps_ == other.delay_ps_; + } + + private: + RRNodeId sink_node_; + RRNodeId src_node_; + int delay_ps_; + std::string crr_id_; +}; + +// Node hash type for lookups +using NodeHash = std::tuple; + +// Hash function for NodeHash +struct NodeHasher { + std::size_t operator()(const NodeHash& hash) const noexcept { + auto h1 = std::hash{}(static_cast(std::get<0>(hash))); + auto h2 = std::hash{}(std::get<1>(hash)); + auto h3 = std::hash{}(std::get<2>(hash)); + auto h4 = std::hash{}(std::get<3>(hash)); + auto h5 = std::hash{}(std::get<4>(hash)); + auto h6 = std::hash{}(std::get<5>(hash)); + + // Combine hashes using a better mixing function + std::size_t result = h1; + result ^= h2 + 0x9e3779b9 + (result << 6) + (result >> 2); + result ^= h3 + 0x9e3779b9 + (result << 6) + (result >> 2); + result ^= h4 + 0x9e3779b9 + (result << 6) + (result >> 2); + result ^= h5 + 0x9e3779b9 + (result << 6) + (result >> 2); + result ^= h6 + 0x9e3779b9 + (result << 6) + (result >> 2); + + return result; + } +}; + +// Utility functions +inline std::string to_string(NodeType type) { + switch (type) { + case NodeType::SOURCE: + return "SOURCE"; + case NodeType::SINK: + return "SINK"; + case NodeType::IPIN: + return "IPIN"; + case NodeType::OPIN: + return "OPIN"; + case NodeType::CHANX: + return "CHANX"; + case NodeType::CHANY: + return "CHANY"; + default: + return "UNKNOWN"; + } +} + +inline e_rr_type string_to_node_type(const std::string& str) { + if (str == "SOURCE" || str == "source") return e_rr_type::SOURCE; + if (str == "SINK" || str == "sink") return e_rr_type::SINK; + if (str == "IPIN" || str == "ipin") return e_rr_type::IPIN; + if (str == "OPIN" || str == "opin") return e_rr_type::OPIN; + if (str == "CHANX" || str == "chanx") return e_rr_type::CHANX; + if (str == "CHANY" || str == "chany") return e_rr_type::CHANY; + throw std::invalid_argument("Unknown node type: " + str); +} + +inline std::string to_string(Direction dir) { + return (dir == Direction::INC_DIR) ? "INC_DIR" : "DEC_DIR"; +} + +inline std::string to_string(Side side) { + switch (side) { + case Side::LEFT: + return "Left"; + case Side::RIGHT: + return "Right"; + case Side::TOP: + return "Top"; + case Side::BOTTOM: + return "Bottom"; + case Side::IPIN: + return "IPIN"; + case Side::OPIN: + return "OPIN"; + default: + return "UNKNOWN"; + } +} + +inline Side string_to_side(const std::string& str) { + if (str == "Left") return Side::LEFT; + if (str == "Right") return Side::RIGHT; + if (str == "Top") return Side::TOP; + if (str == "Bottom") return Side::BOTTOM; + if (str == "IPIN") return Side::IPIN; + if (str == "OPIN") return Side::OPIN; + throw std::invalid_argument("Unknown side: " + str); +} + +/** + * @brief Get the memory usage + * @return Memory usage in KB + */ +inline size_t get_memory_usage() { +#ifdef _WIN32 + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { + return pmc.WorkingSetSize / 1024; // Convert bytes to KB + } + return 0; +#else + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); + return static_cast(usage.ru_maxrss); // Already in KB on Linux, bytes on macOS +#ifdef __APPLE__ + return static_cast(usage.ru_maxrss / 1024); // Convert bytes to KB on macOS +#endif +#endif +} + +/** + * @brief Get the peak memory usage + * @return Peak memory usage in KB + */ +inline size_t get_peak_memory_usage() { +#ifdef _WIN32 + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { + return pmc.PeakWorkingSetSize / 1024; // Convert bytes to KB + } + return 0; +#else + // On Unix/Linux, we need to read /proc/self/status for peak memory + std::ifstream status_file("/proc/self/status"); + std::string line; + while (std::getline(status_file, line)) { + if (line.substr(0, 6) == "VmHWM:") { + std::istringstream iss(line); + std::string label, value, unit; + iss >> label >> value >> unit; + return std::stoul(value); // Already in KB + } + } + + // Fallback to getrusage if /proc/self/status is not available + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); +#ifdef __APPLE__ + return static_cast(usage.ru_maxrss / 1024); // Convert bytes to KB on macOS +#else + return static_cast(usage.ru_maxrss); // Already in KB on Linux +#endif +#endif +} + +// Exception classes +class RRGeneratorException : public std::runtime_error { + public: + explicit RRGeneratorException(const std::string& message) + : std::runtime_error(message) {} +}; + +class ConfigException : public RRGeneratorException { + public: + explicit ConfigException(const std::string& message) + : RRGeneratorException("Configuration error: " + message) {} +}; + +class FileException : public RRGeneratorException { + public: + explicit FileException(const std::string& message) + : RRGeneratorException("File error: " + message) {} +}; + +class ParseException : public RRGeneratorException { + public: + explicit ParseException(const std::string& message) + : RRGeneratorException("Parse error: " + message) {} +}; + +class SwitchException : public RRGeneratorException { + public: + explicit SwitchException(const std::string& message) + : RRGeneratorException("Switch error: " + message) {} +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.cpp new file mode 100644 index 00000000000..013a3eb8435 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.cpp @@ -0,0 +1,574 @@ +#include "crr_connection_builder.h" + +#include +#include +#include +// #include +#include +#include + +#include "vtr_log.h" +#include "vtr_assert.h" +#include "rr_node_types.h" + +namespace crrgenerator { + +static std::string get_switch_block_name(size_t x, size_t y) { + return "SB_" + std::to_string(x) + "__" + std::to_string(y) + "_"; +} + +static bool is_integer(const std::string& s) { + int value; + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); + return ec == std::errc() && ptr == s.data() + s.size(); +} + +CRRConnectionBuilder::CRRConnectionBuilder(const RRGraphView& rr_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager) + : rr_graph_(rr_graph) + , node_lookup_(node_lookup) + , sb_manager_(sb_manager) {} + +void CRRConnectionBuilder::initialize( + Coordinate fpga_grid_x, + Coordinate fpga_grid_y, + bool is_annotated_excel) { + + fpga_grid_x_ = fpga_grid_x; + fpga_grid_y_ = fpga_grid_y; + is_annotated_excel_ = is_annotated_excel; + + // for (const auto& original_switch : rr_graph_.rr_switches()) { + // std::string switch_name = original_switch.name; + // std::transform(switch_name.begin(), switch_name.end(), switch_name.begin(), + // ::tolower); + + // if (switch_name.find("delayless") != std::string::npos) { + // VTR_LOG("Adding delayless switch: %s\n", switch_name.c_str()); + // default_switch_id_["delayless"] = original_switch.get_id(); + // } else if (switch_name.find("ipin") != std::string::npos) { + // VTR_LOG("Adding ipin switch: %s\n", switch_name.c_str()); + // default_switch_id_["ipin"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l1(_.*)?)"))) { + // VTR_LOG("Adding l1 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l1"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l2(_.*)?)"))) { + // VTR_LOG("Adding l2 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l2"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l4(_.*)?)"))) { + // VTR_LOG("Adding l4 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l4"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l8(_.*)?)"))) { + // VTR_LOG("Adding l8 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l8"] = original_switch.get_id(); + // } else if (std::regex_match(switch_name, std::regex(R"(l12(_.*)?)"))) { + // VTR_LOG("Adding l12 switch: %s\n", switch_name.c_str()); + // default_switch_id_["l12"] = original_switch.get_id(); + // } else { + // VTR_LOG_ERROR("Unknown switch type: %s\n", switch_name.c_str()); + // } + // } + + // assert(default_switch_id_.size() == crr_graph_.get_switches().size()); + // sw_zero_id_ = static_cast(crr_graph_.get_switches().size()); + + // Total locations is the number of locations on the FPGA grid minus the 4 + // corner locations. + total_locations_ = static_cast(fpga_grid_x_ * fpga_grid_y_) - 4; + processed_locations_ = 0; + + all_connections_.resize(static_cast(fpga_grid_x_ + 1), + std::vector>(static_cast(fpga_grid_y_ + 1))); + + VTR_LOG("CRRConnectionBuilder initialized for %d x %d grid (%zu locations)\n", + fpga_grid_x_, fpga_grid_y_, total_locations_); +} + +void CRRConnectionBuilder::build_connections_for_location(size_t x, + size_t y, + std::vector& tile_connections) const { + + // Find matching switch block pattern + std::string sw_name = get_switch_block_name(x, y); + std::string pattern = sb_manager_.find_matching_pattern(sw_name); + std::string sw_block_file_name = sb_manager_.get_pattern_file_name(pattern); + tile_connections.clear(); + + if (pattern.empty()) { + VTR_LOG_DEBUG("No pattern found for switch block at (%zu, %zu)\n", x, y); + return; + } + + const DataFrame* df = sb_manager_.get_switch_block_dataframe(pattern); + if (df == nullptr) { + VTR_LOG_WARN("No dataframe found for pattern '%s' at (%zu, %zu)\n", pattern.c_str(), x, y); + return; + } + + VTR_LOG("Processing switch block '%s' with pattern '%s' at (%zu, %zu)\n", + sw_name.c_str(), pattern.c_str(), x, y); + + // Get combined nodes for this location + auto combined_nodes = node_lookup_.get_combined_nodes(x, y); + + // Get vertical and horizontal nodes + auto source_nodes = get_vertical_nodes(x, y, *df, combined_nodes); + auto sink_nodes = get_horizontal_nodes(x, y, *df, combined_nodes); + + // Build connections based on dataframe + for (auto row_iter = df->begin(); row_iter != df->end(); ++row_iter) { + size_t row_idx = row_iter.get_row_index(); + if (row_idx < NUM_EMPTY_ROWS) { + continue; + } + + for (size_t col_idx = NUM_EMPTY_COLS; col_idx < df->cols(); ++col_idx) { + const Cell& cell = df->at(row_idx, col_idx); + + if (!cell.is_empty()) { + auto source_it = source_nodes.find(row_idx); + auto sink_it = sink_nodes.find(col_idx); + + if (source_it != source_nodes.end() && sink_it != sink_nodes.end()) { + RRNodeId source_node = source_it->second; + e_rr_type source_node_type = rr_graph_.node_type(source_node); + RRNodeId sink_node = sink_it->second; + e_rr_type sink_node_type = rr_graph_.node_type(sink_node); + std::string crr_id = sw_block_file_name + "_" + std::to_string(row_idx) + "_" + std::to_string(col_idx); + // If the source node is an IPIN, then it should be considered as + // a sink of the connection. + if (source_node_type == e_rr_type::IPIN) { + int delay_ps = get_connection_delay_ps(cell.as_string(), + rr_node_typename[source_node_type], + sink_node, + source_node); + + tile_connections.emplace_back(source_node, sink_node, delay_ps, crr_id); + } else { + int segment_length = -1; + if (sink_node_type == e_rr_type::CHANX || sink_node_type == e_rr_type::CHANY) { + segment_length = rr_graph_.node_length(sink_node); + } + int delay_ps = get_connection_delay_ps(cell.as_string(), + rr_node_typename[sink_node_type], + source_node, + sink_node, + segment_length); + + tile_connections.emplace_back(sink_node, source_node, delay_ps, crr_id); + } + } + } + } + } + + std::sort(tile_connections.begin(), tile_connections.end()); + tile_connections.erase(std::unique(tile_connections.begin(), + tile_connections.end()), + tile_connections.end()); + tile_connections.shrink_to_fit(); + + VTR_LOG_DEBUG("Generated %zu connections for location (%zu, %zu)\n", + tile_connections.size(), x, y); +} + +std::vector CRRConnectionBuilder::get_tile_connections(size_t tile_x, size_t tile_y) const { + std::vector tile_connections; + build_connections_for_location(tile_x, tile_y, tile_connections); + + return tile_connections; +} + +std::map CRRConnectionBuilder::get_vertical_nodes(Coordinate x, + Coordinate y, + const DataFrame& df, + const std::unordered_map& node_lookup) const { + std::map source_nodes; + std::string prev_seg_type = ""; + int prev_seg_index = -1; + Side prev_side = Side::INVALID; + int prev_ptc_number = 0; + + for (size_t row = NUM_EMPTY_ROWS; row < df.rows(); ++row) { + SegmentInfo info = parse_segment_info(df, row, true); + RRNodeId node_id; + + if (info.side == Side::IPIN || info.side == Side::OPIN) { + node_id = process_opin_ipin_node(info, x, y, node_lookup); + } else if (info.side == Side::LEFT || info.side == Side::RIGHT || info.side == Side::TOP || info.side == Side::BOTTOM) { + node_id = process_channel_node(info, x, y, node_lookup, prev_seg_index, + prev_side, prev_seg_type, prev_ptc_number, true); + } + + if (node_id != RRNodeId::INVALID()) { + source_nodes[row] = node_id; + } + + prev_seg_type = info.seg_type; + prev_side = info.side; + } + + return source_nodes; +} + +std::map CRRConnectionBuilder::get_horizontal_nodes(Coordinate x, + Coordinate y, + const DataFrame& df, + const std::unordered_map& node_lookup) const { + std::map sink_nodes; + std::string prev_seg_type = ""; + int prev_seg_index = -1; + Side prev_side = Side::INVALID; + int prev_ptc_number = 0; + + for (size_t col = NUM_EMPTY_COLS; col < df.cols(); ++col) { + SegmentInfo info = parse_segment_info(df, col, false); + if (info.side == Side::INVALID) { + continue; + } + RRNodeId node_id; + + if (info.side == Side::IPIN) { + node_id = process_opin_ipin_node(info, x, y, node_lookup); + } else if (info.side == Side::LEFT || info.side == Side::RIGHT || info.side == Side::TOP || info.side == Side::BOTTOM) { + node_id = process_channel_node(info, x, y, node_lookup, prev_seg_index, + prev_side, prev_seg_type, prev_ptc_number, + false); + } + + if (node_id != RRNodeId::INVALID()) { + sink_nodes[col] = node_id; + } + + prev_seg_type = info.seg_type; + prev_side = info.side; + } + + return sink_nodes; +} + +CRRConnectionBuilder::SegmentInfo CRRConnectionBuilder::parse_segment_info(const DataFrame& df, + size_t row_or_col, + bool is_vertical) const { + SegmentInfo info; + + if (is_vertical) { + // Vertical processing (rows) + const Cell& side_cell = df.at(row_or_col, 0); + const Cell& type_cell = df.at(row_or_col, 1); + const Cell& index_cell = df.at(row_or_col, 2); + const Cell& tap_cell = df.at(row_or_col, 3); + + if (!side_cell.is_empty()) { + info.side = string_to_side(side_cell.as_string()); + } + if (!type_cell.is_empty()) { + info.seg_type = type_cell.as_string(); + } + if (!index_cell.is_empty()) { + info.seg_index = static_cast(index_cell.as_int()); + } + if (!tap_cell.is_empty() && tap_cell.is_number()) { + info.tap = static_cast(tap_cell.as_int()); + } + } else { + // Horizontal processing (columns) + const Cell& side_cell = df.at(0, row_or_col); + const Cell& type_cell = df.at(1, row_or_col); + const Cell& index_cell = + df.at(3, row_or_col); // Note: row 3 for horizontal + const Cell& tap_cell = df.at(4, row_or_col); // Note: row 4 for horizontal + + if (!side_cell.is_empty()) { + info.side = string_to_side(side_cell.as_string()); + } + if (!type_cell.is_empty()) { + info.seg_type = type_cell.as_string(); + } + if (!index_cell.is_empty()) { + info.seg_index = static_cast(index_cell.as_int()); + } + if (!tap_cell.is_empty() && tap_cell.is_number()) { + info.tap = static_cast(tap_cell.as_int()); + } else { + info.tap = 1; + } + } + + return info; +} + +RRNodeId CRRConnectionBuilder::process_opin_ipin_node(const SegmentInfo& info, + Coordinate x, + Coordinate y, + const std::unordered_map& node_lookup) const { + VTR_ASSERT(info.side == Side::OPIN || info.side == Side::IPIN); + e_rr_type node_type = (info.side == Side::OPIN) ? e_rr_type::OPIN : e_rr_type::IPIN; + NodeHash hash = std::make_tuple(node_type, + std::to_string(info.seg_index), + x, x, y, y); + + auto it = node_lookup.find(hash); + if (it != node_lookup.end()) { + return it->second; + } + + return RRNodeId::INVALID(); +} + +RRNodeId CRRConnectionBuilder::process_channel_node(const SegmentInfo& info, + Coordinate x, + Coordinate y, + const std::unordered_map& node_lookup, + int& prev_seg_index, + Side& prev_side, + std::string& prev_seg_type, + int& prev_ptc_number, + bool is_vertical) const { + // Check grid boundaries + if ((info.side == Side::RIGHT && x == fpga_grid_x_) || (info.side == Side::TOP && y == fpga_grid_y_)) { + return RRNodeId::INVALID(); + } + + int seg_length = std::stoi( + info.seg_type.substr(1)); // Extract number from "L1", "L4", etc. + + // Update PTC number based on previous segment + if (prev_seg_type != info.seg_type) { + prev_ptc_number = prev_seg_index; + } + if (prev_side != info.side) { + prev_ptc_number = 0; + prev_seg_index = 0; + } + std::string seg_type_label = get_segment_type_label(info.side); + Direction direction = get_direction_for_side(info.side, is_vertical); + + // Calculate segment coordinates + Coordinate x_low, x_high, y_low, y_high; + int physical_length, truncated; + calculate_segment_coordinates(info, x, y, x_low, x_high, y_low, y_high, + physical_length, truncated, is_vertical); + + // Calculate starting PTC point + int seg_index = (info.seg_index - 1) * seg_length * 2; + seg_index += prev_ptc_number; + prev_seg_index = + std::max({prev_seg_index, seg_index + 2 * seg_length}); + + seg_index += (direction == Direction::INC_DIR) ? 0 : 1; + seg_index += (direction == Direction::DEC_DIR) ? 2 * (seg_length - 1) : 0; + + // Calculate PTC sequence + std::string seg_sequence = get_ptc_sequence( + seg_index, seg_length, physical_length, direction, truncated); + + // Create node hash and lookup + NodeHash hash = std::make_tuple(string_to_node_type(seg_type_label), + seg_sequence, + x_low, x_high, y_low, y_high); + auto it = node_lookup.find(hash); + + if (it != node_lookup.end()) { + return it->second; + } else { + VTR_LOG_DEBUG("Node not found: %s [%s] (%d,%d) -> (%d,%d)\n", seg_type_label.c_str(), + seg_sequence.c_str(), x_low, y_low, x_high, y_high); + return RRNodeId::INVALID(); + } +} + +void CRRConnectionBuilder::calculate_segment_coordinates(const SegmentInfo& info, + Coordinate x, + Coordinate y, + Coordinate& x_low, + Coordinate& x_high, + Coordinate& y_low, + Coordinate& y_high, + int& physical_length, + int& truncated, + bool is_vertical) const { + int seg_length = std::stoi(info.seg_type.substr(1)); + int tap = info.tap; + + // Calculate initial coordinates based on side + if (is_vertical) { + switch (info.side) { + case Side::LEFT: + x_high = x + (seg_length - tap); + x_low = x - (tap - 1); + y_high = y; + y_low = y; + break; + case Side::RIGHT: + x_high = x + tap; + x_low = x + tap + 1 - seg_length; + y_high = y; + y_low = y; + break; + case Side::TOP: + x_high = x; + x_low = x; + y_high = y + tap; + y_low = y + 1 - seg_length + tap; + break; + case Side::BOTTOM: + x_high = x; + x_low = x; + y_high = y + seg_length - tap; + y_low = y - tap + 1; + break; + default: + x_high = x_low = x; + y_high = y_low = y; + break; + } + } else { + switch (info.side) { + case Side::LEFT: + x_high = x + tap - 1; + x_low = x - seg_length + tap; + y_high = y; + y_low = y; + break; + case Side::RIGHT: + x_high = x + seg_length; + x_low = x + 1; + y_high = y; + y_low = y; + break; + case Side::TOP: + x_high = x; + x_low = x; + y_high = y + seg_length; + y_low = y + 1; + break; + case Side::BOTTOM: + x_high = x; + x_low = x; + y_high = y; + y_low = y - seg_length + tap; + break; + default: + x_high = x_low = x; + y_high = y_low = y; + break; + } + } + + // Calculate truncation + truncated = (std::max(x_low, static_cast(1)) - x_low) - (x_high - std::min(x_high, fpga_grid_x_)); + truncated += (std::max(y_low, static_cast(1)) - y_low) - (y_high - std::min(y_high, fpga_grid_y_)); + + // Apply grid boundaries + x_low = std::max(x_low, static_cast(1)); + y_low = std::max(y_low, static_cast(1)); + x_high = std::min(x_high, fpga_grid_x_); + y_high = std::min(y_high, fpga_grid_y_); + + // Calculate physical length + physical_length = (x_high - x_low) + (y_high - y_low) + 1; +} + +Direction CRRConnectionBuilder::get_direction_for_side(Side side, + bool is_vertical) const { + if (is_vertical) { + return (side == Side::RIGHT || side == Side::TOP) ? Direction::DEC_DIR + : Direction::INC_DIR; + } else { + return (side == Side::RIGHT || side == Side::TOP) ? Direction::INC_DIR + : Direction::DEC_DIR; + } +} + +std::string CRRConnectionBuilder::get_segment_type_label(Side side) const { + return (side == Side::LEFT || side == Side::RIGHT) ? "CHANX" : "CHANY"; +} + +std::string CRRConnectionBuilder::get_ptc_sequence(int seg_index, + int /*seg_length*/, + int physical_length, + Direction direction, + int truncated) const { + std::vector sequence; + + if (direction == Direction::DEC_DIR) { + seg_index -= (2 * truncated > 0) ? 2 * truncated : 0; + for (int i = 0; i < physical_length; ++i) { + sequence.push_back(std::to_string(seg_index - (i * 2))); + } + } else { + seg_index += (2 * truncated > 0) ? 2 * truncated : 0; + for (int i = 0; i < physical_length; ++i) { + sequence.push_back(std::to_string(seg_index + (i * 2))); + } + } + + std::string result; + for (size_t i = 0; i < sequence.size(); ++i) { + if (i > 0) result += ","; + result += sequence[i]; + } + return result; +} + +int CRRConnectionBuilder::get_connection_delay_ps(const std::string& cell_value, + const std::string& sink_node_type, + RRNodeId /*source_node*/, + RRNodeId /*sink_node*/, + int /*segment_length*/) const { + std::string lower_case_sink_node_type = sink_node_type; + std::transform(lower_case_sink_node_type.begin(), + lower_case_sink_node_type.end(), + lower_case_sink_node_type.begin(), ::tolower); + + if (is_integer(cell_value) && is_annotated_excel_) { + // TODO: This is a temporary solution. We need to have an API call to get + // the switch id from delay. + if (cell_value == "0") { + return 0; + } + int switch_delay_ps = std::stoi(cell_value); + return switch_delay_ps; + } else { + VTR_LOG_ERROR("Not implemented - get_connection_delay_ps\n"); + return static_cast(-1); + // std::string switch_id_key = ""; + // if (segment_length > 0) { + // switch_id_key = "l" + std::to_string(segment_length); + // } else { + // switch_id_key = lower_case_sink_node_type; + // } + + // std::string capitalized_switch_id_key = switch_id_key; + // std::transform(capitalized_switch_id_key.begin(), + // capitalized_switch_id_key.end(), + // capitalized_switch_id_key.begin(), ::toupper); + + // if (default_switch_id_.find(switch_id_key) != default_switch_id_.end()) { + // return default_switch_id_.at(switch_id_key); + // } else if (default_switch_id_.find(capitalized_switch_id_key) != default_switch_id_.end()) { + // return default_switch_id_.at(capitalized_switch_id_key); + // } else { + // throw std::runtime_error("Default switch id not found for Node Type: " + lower_case_sink_node_type + " and Switch ID Key: " + capitalized_switch_id_key); + // } + } +} + +bool CRRConnectionBuilder::is_valid_grid_location(Coordinate x, + Coordinate y) const { + return x >= 1 && x <= fpga_grid_x_ && y >= 1 && y <= fpga_grid_y_; +} + +void CRRConnectionBuilder::update_progress() { + size_t current = processed_locations_.fetch_add(1) + 1; + if (current % std::max(size_t(1), total_locations_ / 20) == 0 || current == total_locations_) { + double percentage = + (static_cast(current) / total_locations_) * 100.0; + VTR_LOG("Connection building progress: %zu/%zu (%.1f%%)\n", current, + total_locations_, percentage); + } +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.h new file mode 100644 index 00000000000..6a50ce5d89f --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.h @@ -0,0 +1,172 @@ +#pragma once + +#include "rr_graph_view.h" + +#include "crr_common.h" +#include "data_frame_processor.h" +#include "node_lookup_manager.h" +#include "crr_switch_block_manager.h" + +namespace crrgenerator { + +/** + * @brief Builds connections between routing nodes based on switch block + * configurations + * + * This class processes switch block configurations and generates routing + * connections between nodes, supporting both parallel and sequential + * processing. + */ +class CRRConnectionBuilder { + public: + CRRConnectionBuilder(const RRGraphView& rr_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager); + + /** + * @brief Initialize the connection builder + * @param node_lookup Node lookup manager + * @param sb_manager Switch block manager + * @param original_switches Original switches from the input graph + */ + void initialize(Coordinate fpga_grid_x, + Coordinate fpga_grid_y, + bool is_annotated_excel); + + /** + * @brief Get connections for a tile + * @param tile_x Tile x coordinate + * @param tile_y Tile y coordinate + * @return Vector of connections + */ + std::vector get_tile_connections(size_t tile_x, size_t tile_y) const; + + /** + * @brief Get all generated connections + * @return Vector of all connections + */ + const std::vector>>& get_all_connections() const { + return all_connections_; + } + + /** + * @brief Get connection count + * @return Number of connections generated + */ + size_t get_connection_count() const { + size_t count = 0; + for (const auto& x : all_connections_) { + for (const auto& y : x) { + count += y.size(); + } + } + return count; + } + + /** + * @brief Get the default switch id map + * @return Map of default switch id + */ + std::map get_default_swithes_map() const { + return default_switch_id_; + } + + /** + * @brief Clear all connections + */ + void clear() { all_connections_.clear(); } + void remove_tile_connections(Coordinate x, Coordinate y) { + all_connections_[static_cast(x)][static_cast(y)].clear(); + all_connections_[static_cast(x)][static_cast(y)].shrink_to_fit(); + } + + private: + // Info from config + Coordinate fpga_grid_x_; + Coordinate fpga_grid_y_; + bool is_annotated_excel_; + + // Dependencies + const RRGraphView& rr_graph_; + const NodeLookupManager& node_lookup_; + const SwitchBlockManager& sb_manager_; + SwitchId sw_zero_id_; + + // Generated connections + std::vector>> all_connections_; + + // Processing state + std::atomic processed_locations_{0}; + size_t total_locations_{0}; + + // Default switch id based on the node type + std::map default_switch_id_; + + // Connection building methods + void build_connections_for_location(size_t x, + size_t y, + std::vector& tile_connections) const; + + // Node processing methods + std::map get_vertical_nodes(Coordinate x, + Coordinate y, + const DataFrame& df, + const std::unordered_map& node_lookup) const; + + std::map get_horizontal_nodes(Coordinate x, + Coordinate y, + const DataFrame& df, + const std::unordered_map& node_lookup) const; + + // PTC sequence calculation + std::string get_ptc_sequence(int seg_index, + int seg_length, + int physical_length, + Direction direction, + int truncated) const; + + // Segment processing helpers + struct SegmentInfo { + Side side; + std::string seg_type; + int seg_index; + int tap; + + SegmentInfo() + : side(Side::INVALID) + , seg_index(-1) + , tap(-1) {} + SegmentInfo(Side s, const std::string& type, int index, int t = 1) + : side(s) + , seg_type(type) + , seg_index(index) + , tap(t) {} + }; + + SegmentInfo parse_segment_info(const DataFrame& df, size_t row_or_col, bool is_vertical) const; + + RRNodeId process_opin_ipin_node(const SegmentInfo& info, Coordinate x, Coordinate y, const std::unordered_map& node_lookup) const; + + RRNodeId process_channel_node(const SegmentInfo& info, Coordinate x, Coordinate y, const std::unordered_map& node_lookup, int& prev_seg_index, Side& prev_side, std::string& prev_seg_type, int& prev_ptc_number, bool is_vertical) const; + + // Coordinate and direction calculations + void calculate_segment_coordinates(const SegmentInfo& info, Coordinate x, Coordinate y, Coordinate& x_low, Coordinate& x_high, Coordinate& y_low, Coordinate& y_high, int& physical_length, int& truncated, bool is_vertical) const; + + Direction get_direction_for_side(Side side, bool is_vertical) const; + std::string get_segment_type_label(Side side) const; + + // Return the switch id of an edge between two nodes + int get_connection_delay_ps(const std::string& cell_value, + const std::string& sink_node_type, + RRNodeId source_node, + RRNodeId sink_node, + int segment_length = -1) const; + + // Validation and bounds checking + bool is_valid_grid_location(Coordinate x, Coordinate y) const; + + // Progress tracking + void update_progress(); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.cpp new file mode 100644 index 00000000000..d4ad2ab34b3 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.cpp @@ -0,0 +1,67 @@ +#include "crr_edge_builder.h" +#include "globals.h" + +#include "physical_types.h" +#include "crr_connection_builder.h" + +static t_arch_switch_inf create_crr_switch(const int delay_ps) { + std::string switch_name; + if (delay_ps == 0) { + switch_name = "sw_zero"; + } else { + switch_name = "sw_" + std::to_string(delay_ps); + } + t_arch_switch_inf arch_switch_inf; + arch_switch_inf.set_type(e_switch_type::MUX); + arch_switch_inf.name = switch_name; + arch_switch_inf.R = 0.; + arch_switch_inf.Cin = 0.; + arch_switch_inf.Cout = 0; + arch_switch_inf.set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, delay_ps); + arch_switch_inf.power_buffer_type = POWER_BUFFER_TYPE_NONE; + arch_switch_inf.mux_trans_size = 0.; + arch_switch_inf.buf_size_type = e_buffer_size::ABSOLUTE; + arch_switch_inf.buf_size = 0.; + arch_switch_inf.intra_tile = false; + + return arch_switch_inf; +} + +static RRSwitchId find_or_create_crr_switch_id(const int delay_ps) { + auto& all_sw_inf = g_vpr_ctx.mutable_device().all_sw_inf; + std::string switch_name; + int found_sw_id = -1; + if (delay_ps == 0) { + switch_name = "sw_zero"; + } else { + switch_name = "sw_" + std::to_string(delay_ps); + } + for (int sw_id = 0; sw_id < (int)all_sw_inf.size(); sw_id++) { + if (all_sw_inf[sw_id].name == switch_name) { + found_sw_id = sw_id; + break; + } + } + + if (found_sw_id == -1) { + t_arch_switch_inf new_arch_switch_inf = create_crr_switch(delay_ps); + found_sw_id = (int)all_sw_inf.size(); + all_sw_inf.insert(std::make_pair(found_sw_id, new_arch_switch_inf)); + VTR_LOG("Created new CRR switch: %s with ID: %d\n", switch_name.c_str(), found_sw_id); + } + return RRSwitchId(found_sw_id); +} + +void build_crr_gsb_track_to_track_edges(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const crrgenerator::CRRConnectionBuilder& connection_builder) { + size_t gsb_x = rr_gsb.get_sb_x(); + size_t gsb_y = rr_gsb.get_sb_y(); + + std::vector gsb_connections = connection_builder.get_tile_connections(gsb_x, gsb_y); + for (const auto& connection : gsb_connections) { + RRSwitchId rr_switch_id = find_or_create_crr_switch_id(connection.delay_ps()); + VTR_ASSERT(rr_switch_id != RRSwitchId::INVALID()); + rr_graph_builder.create_edge_in_cache(connection.src_node(), connection.sink_node(), rr_switch_id, false, connection.crr_id()); + } +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.h new file mode 100644 index 00000000000..2730c1aab86 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.h @@ -0,0 +1,21 @@ +#pragma once + +#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" + +#include "crr_connection_builder.h" + +void build_crr_gsb_track_to_track_edges(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const crrgenerator::CRRConnectionBuilder& connection_builder); diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.cpp new file mode 100644 index 00000000000..0e6879a7358 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.cpp @@ -0,0 +1,65 @@ +#include "crr_generator.h" + +#include + +#include "globals.h" +#include "vtr_log.h" +#include "vtr_assert.h" + +namespace crrgenerator { + +CRRGraphGenerator::CRRGraphGenerator(const t_crr_opts& crr_opts, + const RRGraphView& input_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager) + : crr_opts_(crr_opts) + , input_graph_(input_graph) + , node_lookup_(node_lookup) + , sb_manager_(sb_manager) {} + +void CRRGraphGenerator::run() { + auto start_time = std::chrono::steady_clock::now(); + + VTR_LOG("Starting RR Graph parsing process\n"); + + try { + // Initialize all components + initialize_components(); + + // Build connections + build_connections(); + + auto end_time = std::chrono::steady_clock::now(); + auto duration = + std::chrono::duration_cast(end_time - start_time); + + VTR_LOG("RR Graph parsing completed successfully in %ds\n", duration.count()); + } catch (const std::exception& e) { + VTR_LOG_ERROR("RR Graph parsing failed: %s\n", e.what()); + } +} + +void CRRGraphGenerator::initialize_components() { + VTR_LOG("CRR Graph Generator: Initializing components\n"); + + // Initialize connection builder + connection_builder_ = std::make_unique(input_graph_, + node_lookup_, + sb_manager_); + + VTR_LOG("CRR Graph Generator: All components initialized successfully\n"); +} + +void CRRGraphGenerator::build_connections() { + VTR_LOG("CRR Graph Generator: Building connections\n"); + + // Initialize connection builder + const DeviceGrid& grid = g_vpr_ctx.device().grid; + connection_builder_->initialize(grid.width() - 2, + grid.height() - 2, + crr_opts_.annotated_rr_graph); + + VTR_LOG("CRR Graph Generator: Connection building completed\n"); +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.h new file mode 100644 index 00000000000..f308f51fdac --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_generator.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include "vpr_types.h" +#include "crr_common.h" +#include "crr_connection_builder.h" +#include "node_lookup_manager.h" +#include "crr_switch_block_manager.h" + +namespace crrgenerator { + +/** + * @brief Main orchestrator for RR graph parsing and generation + * + * This class coordinates all subsystems to read input graphs, + * process switch block configurations, and generate output graphs. + */ +class CRRGraphGenerator { + public: + CRRGraphGenerator(const t_crr_opts& crr_opts, + const RRGraphView& input_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager); + ~CRRGraphGenerator() = default; + + /** + * @brief Run the complete parsing and generation process + */ + void run(); + + const CRRConnectionBuilder* get_connection_builder() const { + return connection_builder_.get(); + } + + private: + const t_crr_opts& crr_opts_; + const RRGraphView& input_graph_; + const NodeLookupManager& node_lookup_; + const SwitchBlockManager& sb_manager_; + + std::unique_ptr connection_builder_; + + // Processing methods + void initialize_components(); + void process_switch_blocks(); + void build_connections(); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_pattern_matcher.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_pattern_matcher.h new file mode 100644 index 00000000000..4b6ed209ffc --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_pattern_matcher.h @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include + +namespace crrgenerator { + +class PatternMatcher { + private: + // Helper function to parse range [start:end:step] or comma-separated values [7,20] + static bool matches_range(int value, const std::string& range_str) { + // Extract numbers from [start:end:step] or [7,20] format + size_t start_pos = range_str.find('['); + size_t end_pos = range_str.find(']'); + if (start_pos == std::string::npos || end_pos == std::string::npos) { + return false; + } + + std::string range_content = range_str.substr(start_pos + 1, end_pos - start_pos - 1); + + // Check if it's comma-separated values (e.g., [7,20]) + if (range_content.find(',') != std::string::npos) { + // Parse comma-separated values + std::vector values; + size_t pos = 0; + size_t comma_pos; + + while ((comma_pos = range_content.find(',', pos)) != std::string::npos) { + values.push_back(std::stoi(range_content.substr(pos, comma_pos - pos))); + pos = comma_pos + 1; + } + values.push_back(std::stoi(range_content.substr(pos))); + + // Check if value is in the list + return std::find(values.begin(), values.end(), value) != values.end(); + } + // Check if it's range notation (e.g., [2:32:3]) + else if (range_content.find(':') != std::string::npos) { + // Parse start:end:step + std::vector parts; + size_t pos = 0; + size_t colon_pos; + + while ((colon_pos = range_content.find(':', pos)) != std::string::npos) { + parts.push_back(std::stoi(range_content.substr(pos, colon_pos - pos))); + pos = colon_pos + 1; + } + parts.push_back(std::stoi(range_content.substr(pos))); + + if (parts.size() != 3) return false; + + int start = parts[0]; + int end = parts[1]; // end is NOT exclusive as per your specification + int step = parts[2]; + + if (value < start || value > end) return false; + if ((value - start) % step != 0) return false; + + return true; + } + // Single value (e.g., [5]) + else { + int single_value = std::stoi(range_content); + return value == single_value; + } + } + + // Convert pattern to regex, handling * and [start:end:step] ranges + static std::string pattern_to_regex(const std::string& pattern) { + std::string regex_pattern = "^"; + + for (size_t i = 0; i < pattern.length(); ++i) { + char c = pattern[i]; + + if (c == '*') { + regex_pattern += "([0-9]+)"; // Capture group for numbers + } else if (c == '[') { + // Find the matching closing bracket + size_t close_bracket = pattern.find(']', i); + if (close_bracket != std::string::npos) { + std::string range = pattern.substr(i, close_bracket - i + 1); + regex_pattern += "([0-9]+)"; // Capture the number, validate range later + i = close_bracket; // Skip to after the closing bracket + } else { + regex_pattern += "\\["; // Literal bracket if no closing bracket found + } + } else if (c == '\\' && i + 1 < pattern.length() && pattern[i + 1] == '*') { + // Handle escaped asterisk - treat as literal * + regex_pattern += "\\*"; + ++i; // Skip the next character + } else { + // Escape regex special characters + if (c == '.' || c == '^' || c == '$' || c == '+' || c == '?' || c == '(' || c == ')' || c == '|' || c == '{' || c == '}') { + regex_pattern += "\\"; + } + regex_pattern += c; + } + } + + regex_pattern += "$"; + return regex_pattern; + } + + public: + static bool matches_pattern(const std::string& name, const std::string& pattern) { + // Fast path for exact matches (no wildcards or ranges) + if (pattern.find('*') == std::string::npos && pattern.find('[') == std::string::npos && pattern.find('\\') == std::string::npos) { + return name == pattern; + } + + // Compile regex for this pattern + std::string regex_str = pattern_to_regex(pattern); + std::regex compiled_regex(regex_str); + + std::smatch matches; + if (!std::regex_match(name, matches, compiled_regex)) { + return false; + } + + // Now validate any range constraints + std::vector captured_numbers; + for (size_t i = 1; i < matches.size(); ++i) { + captured_numbers.push_back(matches[i].str()); + } + + // Parse pattern again to find ranges and validate them + size_t capture_index = 0; + for (size_t i = 0; i < pattern.length(); ++i) { + char c = pattern[i]; + + if (c == '*') { + // Simple wildcard, no validation needed + ++capture_index; + } else if (c == '[') { + // Find the matching closing bracket + size_t close_bracket = pattern.find(']', i); + if (close_bracket != std::string::npos) { + std::string range = pattern.substr(i, close_bracket - i + 1); + + if (capture_index < captured_numbers.size()) { + int captured_value = std::stoi(captured_numbers[capture_index]); + if (!matches_range(captured_value, range)) { + return false; + } + } + + ++capture_index; + i = close_bracket; // Skip to after the closing bracket + } + } else if (c == '\\' && i + 1 < pattern.length() && pattern[i + 1] == '*') { + ++i; // Skip escaped asterisk + } + } + + return true; + } +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.cpp new file mode 100644 index 00000000000..4ae84fb89b4 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.cpp @@ -0,0 +1,194 @@ +#include + +#include +#include + +#include + +#include "crr_switch_block_manager.h" +#include "crr_pattern_matcher.h" + +#include "vtr_log.h" + +namespace crrgenerator { + +SwitchBlockManager::SwitchBlockManager() = default; + +void SwitchBlockManager::initialize(const std::string& sb_maps_file, + const std::string& sb_annotated_dir, + const bool is_annotated_excel) { + VTR_LOG("Initializing SwitchBlockManager with maps file: %s\n", sb_maps_file.c_str()); + + annotated_dir_ = sb_annotated_dir; + + // Load YAML configuration + try { + YAML::Node config = YAML::LoadFile(sb_maps_file); + validate_yaml_structure(config); + + if (!config["SB_MAPS"]) { + VTR_LOG_ERROR("SB_MAPS section not found in YAML file\n"); + } + + YAML::Node sb_maps = config["SB_MAPS"]; + VTR_LOG_DEBUG("Found SB_MAPS section with %zu entries\n", sb_maps.size()); + + // Process each switch block mapping + std::unordered_set unique_files; + for (const auto& item : sb_maps) { + std::string pattern = item.first.as(); + + std::string excel_file = item.second.as(); + if (item.second.IsNull()) { + excel_file = ""; + } + + std::string full_path = std::filesystem::path(annotated_dir_) / excel_file; + if (excel_file.empty()) { + full_path = ""; + } + + // Handle escaped asterisks (replace \* with *) + std::regex escaped_asterisk(R"(\\\*)"); + pattern = std::regex_replace(pattern, escaped_asterisk, "*"); + + ordered_switch_block_patterns_.push_back(pattern); + switch_block_to_file_[pattern] = full_path; + if (!full_path.empty()) { + unique_files.insert(full_path); + } + } + + for (const auto& full_path : unique_files) { + if (std::filesystem::exists(full_path)) { + try { + VTR_LOG_DEBUG("Attempting to read Excel file: %s\n", full_path.c_str()); + DataFrame df = processor_.read_csv(full_path); + df = processor_.process_dataframe(std::move(df), NUM_EMPTY_ROWS, + NUM_EMPTY_COLS); + file_cache_[full_path] = std::move(df); + VTR_LOG_DEBUG("Processed %zu connections in %s file\n", + file_cache_[full_path].connections, + std::filesystem::path(full_path).filename().string().c_str()); + } catch (const std::exception& e) { + VTR_LOG_ERROR("Failed to read required Excel file '%s': %s\n", full_path.c_str(), e.what()); + } + } else { + VTR_LOG_ERROR("Required Excel file not found: %s\n", full_path.c_str()); + } + } + + // Map patterns to loaded DataFrames + for (const auto& [pattern, full_path] : switch_block_to_file_) { + if (file_cache_.find(full_path) != file_cache_.end()) { + dataframes_[pattern] = &file_cache_[full_path]; + } + } + + update_and_set_global_switch_delays(is_annotated_excel); + + print_statistics(); + } catch (const YAML::Exception& e) { + VTR_LOG_ERROR("Failed to parse YAML file %s: %s\n", sb_maps_file.c_str(), e.what()); + } catch (const std::exception& e) { + VTR_LOG_ERROR("Failed to initialize SwitchBlockManager: %s\n", e.what()); + } +} + +std::string SwitchBlockManager::get_pattern_file_name(const std::string& pattern) const { + auto it = switch_block_to_file_.find(pattern); + + if (it == switch_block_to_file_.end()) { + return ""; + } else { + std::filesystem::path path(it->second); + return path.filename().string(); + } +} + +const DataFrame* SwitchBlockManager::get_switch_block_dataframe(const std::string& pattern) const { + auto it = dataframes_.find(pattern); + return (it != dataframes_.end()) ? it->second : nullptr; +} + +bool SwitchBlockManager::has_pattern(const std::string& pattern) const { + return dataframes_.find(pattern) != dataframes_.end(); +} + +std::vector SwitchBlockManager::get_all_patterns() const { + std::vector patterns; + patterns.reserve(dataframes_.size()); + + for (const auto& [pattern, _] : dataframes_) { + patterns.push_back(pattern); + } + + return patterns; +} + +std::string SwitchBlockManager::find_matching_pattern(const std::string& sw_name) const { + for (const auto& pattern : ordered_switch_block_patterns_) { + if (PatternMatcher::matches_pattern(sw_name, pattern)) { + return pattern; + } + } + return ""; +} + +void SwitchBlockManager::update_and_set_global_switch_delays(const bool is_annotated_excel) { + if (is_annotated_excel) { + int switch_delay_max_ps = std::numeric_limits::min(); + int switch_delay_min_ps = std::numeric_limits::max(); + for (const auto& [pattern, df] : dataframes_) { + processor_.update_switch_delays(*df, switch_delay_max_ps, + switch_delay_min_ps); + } + + switch_delay_max_ps_ = switch_delay_max_ps; + switch_delay_min_ps_ = switch_delay_min_ps; + } else { + switch_delay_max_ps_ = 1000; + switch_delay_min_ps_ = 1; + } + + VTR_LOG("Global switch delay range updated: %d - %d\n", switch_delay_min_ps_, switch_delay_max_ps_); +} + +void SwitchBlockManager::print_statistics() const { + VTR_LOG("=== CRR Generator Switch Block Manager Statistics ===\n"); + VTR_LOG("Patterns loaded: %zu\n", dataframes_.size()); + VTR_LOG("Unique Excel files: %zu\n", file_cache_.size()); + VTR_LOG("Total connections: %zu\n", get_total_connections()); + VTR_LOG("Switch delay range: %d - %d\n", switch_delay_min_ps_, switch_delay_max_ps_); + + // Print file details + for (const auto& [file, df] : file_cache_) { + VTR_LOG(" %s: %zu connections (%zux%zu)\n", + std::filesystem::path(file).filename().string().c_str(), + df.connections, df.rows(), df.cols()); + } +} + +size_t SwitchBlockManager::get_total_connections() const { + size_t total = 0; + for (const auto& [file, df] : file_cache_) { + total += df.connections; + } + return total; +} + +void SwitchBlockManager::validate_yaml_structure(const YAML::Node& root) { + if (!root.IsMap()) { + VTR_LOG_ERROR("YAML root must be a map\n"); + } + + if (!root["SB_MAPS"]) { + VTR_LOG_ERROR("Required 'SB_MAPS' section not found in YAML\n"); + } + + if (!root["SB_MAPS"].IsMap()) { + VTR_LOG_ERROR("'SB_MAPS' must be a map\n"); + } +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.h new file mode 100644 index 00000000000..ab311f2f9b1 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.h @@ -0,0 +1,123 @@ +#pragma once + +#include +#include + +#include + +#include "data_frame_processor.h" + +namespace crrgenerator { + +/** + * @brief Manages switch block configurations and Excel file processing + * + * This class handles reading YAML configuration files, processing Excel files + * containing switch block data, and managing switch block patterns. + */ +class SwitchBlockManager { + public: + SwitchBlockManager(); + + /** + * @brief Initialize the manager with configuration file + * @param sb_maps_file Path to the YAML configuration file + * @param sb_annotated_dir Directory containing Excel files + * @param is_annotated_excel Whether the switches are annotated in Excel + */ + void initialize(const std::string& sb_maps_file, + const std::string& sb_annotated_dir, + const bool is_annotated_excel); + + /** + * @brief Get the switch template file name for a given pattern + */ + std::string get_pattern_file_name(const std::string& pattern) const; + + /** + * @brief Get DataFrame for a specific switch block pattern + * @param pattern Switch block pattern name (e.g., "SB_1__2_") + * @return Pointer to DataFrame or nullptr if not found + */ + const DataFrame* get_switch_block_dataframe(const std::string& pattern) const; + + /** + * @brief Check if a pattern exists in the switch block mapping + * @param pattern Pattern to check + * @return true if pattern exists + */ + bool has_pattern(const std::string& pattern) const; + + /** + * @brief Get all available patterns + * @return Vector of all pattern names + */ + std::vector get_all_patterns() const; + + /** + * @brief Find the first matching pattern in switch mapping file + * for a switch block name + * @param sw_name Switch block name to match + * @return Matching pattern or empty string if no match + */ + std::string find_matching_pattern(const std::string& sw_name) const; + + /** + * @brief Print statistics about loaded switch blocks + */ + void print_statistics() const; + + /** + * @brief Get the total number of connections across all switch blocks + * @return Total connection count + */ + size_t get_total_connections() const; + + /** + * @brief Get the maximum switch delay in picoseconds + * @return Maximum switch delay in picoseconds + */ + int get_max_switch_delay_ps() const { return switch_delay_max_ps_; } + + /** + * @brief Get the minimum switch delay in picoseconds + * @return Minimum switch delay in picoseconds + */ + int get_min_switch_delay_ps() const { return switch_delay_min_ps_; } + + private: + /** + * @brief Ordered list of switch block patterns + * + * We need to store the switch blocks in the same order defined in the YAML + * file. Later, if a switch block match to multiple patterns defined in the + * YAML file, the pattern defined earliest in the list will be used. + */ + std::vector ordered_switch_block_patterns_; + std::unordered_map switch_block_to_file_; + std::unordered_map dataframes_; + std::unordered_map file_cache_; + + // Global switch delay range + int switch_delay_max_ps_; + int switch_delay_min_ps_; + + DataFrameProcessor processor_; + std::string annotated_dir_; + + // File processing + void load_excel_file(const std::string& pattern, + const std::string& excel_file); + + /** + * @brief Update global switch delay ranges based on all loaded DataFrames + * @param is_annotated_excel Whether the switches are annotated in Excel + */ + void update_and_set_global_switch_delays(const bool is_annotated_excel); + + // Validation + void validate_yaml_structure(const YAML::Node& root); + void validate_excel_files(); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.cpp new file mode 100644 index 00000000000..8b5c2f8aa0e --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.cpp @@ -0,0 +1,352 @@ +#include "data_frame_processor.h" + +#include "vtr_log.h" + +#include +#include + +namespace crrgenerator { + +// DataFrame implementation + +DataFrame::DataFrame(size_t rows, size_t cols) + : rows_(rows) + , cols_(cols) { + data_.resize(rows); + for (auto& row : data_) { + row.resize(cols); + } +} + +Cell& DataFrame::at(size_t row, size_t col) { + validate_bounds(row, col); + return data_[row][col]; +} + +const Cell& DataFrame::at(size_t row, size_t col) const { + validate_bounds(row, col); + return data_[row][col]; +} + +void DataFrame::resize(size_t rows, size_t cols) { + rows_ = rows; + cols_ = cols; + data_.resize(rows); + for (auto& row : data_) { + row.resize(cols); + } +} + +void DataFrame::clear() { + data_.clear(); + rows_ = 0; + cols_ = 0; + source_file.clear(); + connections = 0; +} + +std::vector DataFrame::get_row(size_t row) const { + if (row >= rows_) { + VTR_LOG_ERROR("Row index out of range: %zu - max %zu", row, rows_); + } + return data_[row]; +} + +std::vector DataFrame::get_column(size_t col) const { + if (col >= cols_) { + VTR_LOG_ERROR("Column index out of range: %zu - max %zu", col, cols_); + } + + std::vector column; + column.reserve(rows_); + for (size_t row = 0; row < rows_; ++row) { + column.push_back(data_[row][col]); + } + return column; +} + +void DataFrame::set_row(size_t row, const std::vector& values) { + if (row >= rows_) { + VTR_LOG_ERROR("Row index out of range: %zu - max %zu", row, rows_); + } + + for (size_t col = 0; col < std::min(values.size(), cols_); ++col) { + data_[row][col] = values[col]; + } +} + +void DataFrame::set_column(size_t col, const std::vector& values) { + if (col >= cols_) { + VTR_LOG_ERROR("Column index out of range: %zu - max %zu", col, cols_); + } + + for (size_t row = 0; row < std::min(values.size(), rows_); ++row) { + data_[row][col] = values[row]; + } +} + +size_t DataFrame::count_non_empty() const { + size_t count = 0; + for (const auto& row : data_) { + for (const auto& cell : row) { + if (!cell.is_empty()) { + ++count; + } + } + } + return count; +} + +size_t DataFrame::count_non_empty_in_range(size_t start_row, size_t start_col, size_t end_row, size_t end_col) const { + size_t count = 0; + for (size_t row = start_row; row < std::min(end_row, rows_); ++row) { + for (size_t col = start_col; col < std::min(end_col, cols_); ++col) { + if (!data_[row][col].is_empty()) { + ++count; + } + } + } + return count; +} + +void DataFrame::validate_bounds(size_t row, size_t col) const { + if (row >= rows_ || col >= cols_) { + VTR_LOG_ERROR("DataFrame index out of range: %zu,%zu - max %zu,%zu\n", row, col, rows_, cols_); + } +} + +// DataFrameProcessor implementation +DataFrame DataFrameProcessor::read_csv(const std::string& filename) { + + validate_csv_file(filename); + + VTR_LOG_DEBUG("Reading CSV file: %s\n", filename.c_str()); + + try { + std::ifstream file(filename); + if (!file.is_open()) { + VTR_LOG_ERROR("Failed to open CSV file: %s\n", filename.c_str()); + return DataFrame(); + } + + VTR_LOG_DEBUG("File %s opened successfully\n", filename.c_str()); + + // Read all lines first to determine dimensions + std::vector lines; + std::string line; + while (std::getline(file, line)) { + if (!line.empty()) { + lines.push_back(line); + } + } + file.close(); + + if (lines.empty()) { + VTR_LOG_ERROR("CSV file appears to be empty: %s\n", filename.c_str()); + return DataFrame(); + } + + // Parse first line to get column count + size_t num_cols = count_csv_columns(lines[0]); + size_t num_rows = lines.size(); + + VTR_LOG_DEBUG("CSV dimensions: %zux%zu\n", num_rows, num_cols); + + DataFrame df(num_rows, num_cols); + VTR_LOG_DEBUG("Created DataFrame with dimensions: %zux%zu\n", num_rows, num_cols); + + // Read all cells + VTR_LOG_DEBUG("Reading cell data...\n"); + size_t cells_read = 0; + + for (size_t row = 0; row < num_rows; ++row) { + std::vector tokens = parse_csv_line(lines[row]); + VTR_ASSERT_DEBUG(tokens.size() <= num_cols); + + for (size_t col = 0; col < tokens.size(); ++col) { + df.at(row, col) = parse_csv_cell(tokens[col]); + cells_read++; + } + + // Fill remaining columns with empty cells if row has fewer columns + for (size_t col = tokens.size(); col < num_cols; ++col) { + df.at(row, col) = Cell(); + } + + // Progress logging for large files + if ((row + 1) % 100 == 0) { + VTR_LOG_DEBUG("Read %zu rows (%zu cells)\n", row + 1, cells_read); + } + } + + df.source_file = std::filesystem::path(filename).stem().string(); + VTR_LOG_DEBUG("Successfully read CSV file with dimensions: %zux%zu, %zu cells\n", + df.rows(), df.cols(), cells_read); + + return df; + + } catch (const std::exception& e) { + VTR_LOG_ERROR("Error reading CSV file %s: %s\n", filename.c_str(), e.what()); + return DataFrame(); + } +} + +size_t DataFrameProcessor::count_csv_columns(const std::string& line) { + size_t count = 1; + bool in_quotes = false; + + for (char c : line) { + if (c == '"') { + in_quotes = !in_quotes; + } else if (c == ',' && !in_quotes) { + count++; + } + } + + return count; +} + +std::vector DataFrameProcessor::parse_csv_line(const std::string& line) { + std::vector tokens; + std::string current; + bool in_quotes = false; + + for (size_t i = 0; i < line.size(); ++i) { + char c = line[i]; + + if (c == '"') { + // Handle escaped quotes ("") + if (in_quotes && i + 1 < line.size() && line[i + 1] == '"') { + current += '"'; + i++; // Skip next quote + } else { + in_quotes = !in_quotes; + } + } else if (c == ',' && !in_quotes) { + tokens.push_back(current); + current.clear(); + } else { + current += c; + } + } + + tokens.push_back(current); // Add last token + return tokens; +} + +DataFrame DataFrameProcessor::process_dataframe(DataFrame df, + int merge_rows_count, + int merge_cols_count) { + VTR_LOG_DEBUG("Processing dataframe with merge_rows=%d, merge_cols=%d\n", + merge_rows_count, merge_cols_count); + + // Perform row merging + std::vector merged_row_indices = {0, 1}; + merge_rows(df, merged_row_indices); + + // Perform column merging + std::vector merged_col_indices = {2}; + merge_columns(df, merged_col_indices); + + // Count connections in the data area + df.connections = df.count_non_empty_in_range(static_cast(merge_rows_count), + static_cast(merge_cols_count), + df.rows(), + df.cols()); + + VTR_LOG_DEBUG("Processed dataframe with %zu connections\n", df.connections); + + return df; +} + +void DataFrameProcessor::update_switch_delays(const DataFrame& df, + int& switch_delay_max_ps, + int& switch_delay_min_ps) { + for (size_t col = NUM_EMPTY_COLS; col < df.cols(); ++col) { + for (size_t row = NUM_EMPTY_ROWS; row < df.rows(); ++row) { + const Cell& cell = df.at(row, col); + + if (!cell.is_empty() && cell.is_number()) { + int switch_delay_ps = cell.as_int(); + + if (switch_delay_ps > switch_delay_max_ps) { + switch_delay_max_ps = switch_delay_ps; + } + if (switch_delay_ps < switch_delay_min_ps) { + switch_delay_min_ps = switch_delay_ps; + } + } + } + } + + VTR_LOG_DEBUG("Updated switch delays: min=%d, max=%d\n", + switch_delay_min_ps, + switch_delay_max_ps); +} + +// Parse csv cell to our Cell type +Cell DataFrameProcessor::parse_csv_cell(const std::string& value) { + // Trim whitespace + std::string trimmed = value; + trimmed.erase(0, trimmed.find_first_not_of(" \t\r\n")); + trimmed.erase(trimmed.find_last_not_of(" \t\r\n") + 1); + + if (trimmed.empty()) { + return Cell(); // Empty cell + } + + // Check if all characters are digits + bool is_integer = true; + for (char c : trimmed) { + if (!std::isdigit(static_cast(c))) { + is_integer = false; + break; + } + } + + if (is_integer) { + return Cell(std::stoi(trimmed)); + } + + return Cell(trimmed); +} + +void DataFrameProcessor::merge_rows(DataFrame& df, const std::vector& merge_row_indices) { + for (const auto& row : merge_row_indices) { + Cell value; + for (size_t col = 0; col < df.cols(); ++col) { + if (!df.at(row, col).is_empty()) { + value = df.at(row, col); + } + df.at(row, col) = value; + } + } +} + +void DataFrameProcessor::merge_columns(DataFrame& df, const std::vector& merge_col_indices) { + for (const auto& col : merge_col_indices) { + Cell value; + for (size_t row = 0; row < df.rows(); ++row) { + if (!df.at(row, col).is_empty()) { + value = df.at(row, col); + } + df.at(row, col) = value; + } + } +} + +void DataFrameProcessor::validate_csv_file(const std::string& filename) { + if (!std::filesystem::exists(filename)) { + VTR_LOG_ERROR("CSV file does not exist: %s\n", filename.c_str()); + } + + // Check file extension + std::string extension = std::filesystem::path(filename).extension().string(); + std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + + if (extension != ".csv") { + VTR_LOG_ERROR("Unsupported file format: %s. Expected .csv\n", extension.c_str()); + } +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.h new file mode 100644 index 00000000000..a4c771757dd --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.h @@ -0,0 +1,201 @@ +#pragma once + +#include +#include + +#include "crr_common.h" +namespace crrgenerator { + +/** + * @brief Represents a cell in the dataframe + */ +struct Cell { + enum class Type { EMPTY, + STRING, + INTEGER }; + + Type type = Type::EMPTY; + std::string string_value; + int64_t int_value = 0; + + Cell() = default; + explicit Cell(const std::string& value) + : type(Type::STRING) + , string_value(value) {} + explicit Cell(int64_t value) + : type(Type::INTEGER) + , int_value(value) {} + + bool is_empty() const { return type == Type::EMPTY; } + bool is_string() const { return type == Type::STRING; } + bool is_integer() const { return type == Type::INTEGER; } + bool is_number() const { return is_integer(); } + + int64_t as_int() const { + if (is_integer()) return int_value; + if (is_string()) return std::stoll(string_value); + return 0; + } + + std::string as_string() const { + if (is_string()) return string_value; + if (is_integer()) return std::to_string(int_value); + return ""; + } +}; + +/** + * @brief Simple dataframe implementation for processing Excel data + * + * This class provides pandas-like functionality for processing Excel files + * containing switch block configuration data. + */ +class DataFrame { + public: + DataFrame() = default; + DataFrame(size_t rows, size_t cols); + + // Access methods + Cell& at(size_t row, size_t col); + const Cell& at(size_t row, size_t col) const; + Cell& operator()(size_t row, size_t col) { return at(row, col); } + const Cell& operator()(size_t row, size_t col) const { return at(row, col); } + + // Dimensions + size_t rows() const { return rows_; } + size_t cols() const { return cols_; } + std::pair shape() const { return {rows_, cols_}; } + + // Resize operations + void resize(size_t rows, size_t cols); + void clear(); + + // Row/column operations + std::vector get_row(size_t row) const; + std::vector get_column(size_t col) const; + void set_row(size_t row, const std::vector& values); + void set_column(size_t col, const std::vector& values); + + // Statistics and counting + size_t count_non_empty() const; + size_t count_non_empty_in_range(size_t start_row, size_t start_col, size_t end_row, size_t end_col) const; + + // Iteration support + class RowIterator { + public: + RowIterator(DataFrame* df, size_t row) + : df_(df) + , row_(row) {} + + Cell& operator[](size_t col) { return df_->at(row_, col); } + const Cell& operator[](size_t col) const { return df_->at(row_, col); } + + size_t size() const { return df_->cols(); } + size_t get_row_index() const { return row_; } + + RowIterator& operator++() { + ++row_; + return *this; + } + bool operator!=(const RowIterator& other) const { + return df_ != other.df_ || row_ != other.row_; + } + RowIterator& operator*() { return *this; } + + private: + DataFrame* df_; + size_t row_; + }; + + class ConstRowIterator { + public: + ConstRowIterator(const DataFrame* df, size_t row) + : df_(df) + , row_(row) {} + + const Cell& operator[](size_t col) const { return df_->at(row_, col); } + + size_t size() const { return df_->cols(); } + size_t get_row_index() const { return row_; } + + ConstRowIterator& operator++() { + ++row_; + return *this; + } + bool operator!=(const ConstRowIterator& other) const { + return row_ != other.row_; + } + const ConstRowIterator& operator*() const { return *this; } + + private: + const DataFrame* df_; + size_t row_; + }; + + RowIterator begin() { return RowIterator(this, 0); } + RowIterator end() { return RowIterator(this, rows_); } + ConstRowIterator begin() const { return ConstRowIterator(this, 0); } + ConstRowIterator end() const { return ConstRowIterator(this, rows_); } + + // Metadata + std::string source_file; + size_t connections = 0; + + private: + std::vector> data_; + size_t rows_ = 0; + size_t cols_ = 0; + + void validate_bounds(size_t row, size_t col) const; +}; + +/** + * @brief Processes Excel files and converts them to DataFrames + * + * This class handles reading Excel files and performing the cell merging + * operations similar to the Python pandas functionality. + */ +class DataFrameProcessor { + public: + DataFrameProcessor() = default; + + /** + * @brief Read Excel file and create DataFrame + * @param filename Path to Excel file + * @return DataFrame containing the Excel data + * @throws FileException if file cannot be read + */ + DataFrame read_csv(const std::string& filename); + + /** + * @brief Process DataFrame with merging operations + * @param df DataFrame to process + * @param merge_rows_count Number of rows to merge + * @param merge_cols_count Number of columns to merge + * @return Processed DataFrame + */ + DataFrame process_dataframe(DataFrame df, + int merge_rows_count = NUM_EMPTY_ROWS, + int merge_cols_count = NUM_EMPTY_COLS); + + /** + * @brief Update switch delay min/max based on DataFrame content + * @param df DataFrame to analyze + * @param switch_delay_max_ps Current maximum switch delay (will be updated) + * @param switch_delay_min_ps Current minimum switch delay (will be updated) + */ + void update_switch_delays(const DataFrame& df, int& switch_delay_max_ps, int& switch_delay_min_ps); + + private: + // Excel parsing helpers + Cell parse_csv_cell(const std::string& value); + size_t count_csv_columns(const std::string& line); + std::vector parse_csv_line(const std::string& line); + void merge_rows(DataFrame& df, const std::vector& merge_row_indices); + void merge_columns(DataFrame& df, const std::vector& merge_col_indices); + + // Validation + void validate_csv_file(const std::string& filename); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.cpp new file mode 100644 index 00000000000..425f14c593e --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.cpp @@ -0,0 +1,133 @@ +#include "node_lookup_manager.h" + +#include "rr_graph_utils.h" + +#include "vtr_log.h" +#include "vtr_assert.h" + +namespace crrgenerator { + +NodeLookupManager::NodeLookupManager(const RRGraphView& rr_graph, size_t fpga_grid_x, size_t fpga_grid_y) + : rr_graph_(rr_graph) + , fpga_grid_x_(fpga_grid_x) + , fpga_grid_y_(fpga_grid_y) {} + +void NodeLookupManager::initialize() { + VTR_LOG("Initializing NodeLookupManager for %d x %d grid with %zu nodes\n", + fpga_grid_x_, fpga_grid_y_, static_cast(rr_graph_.num_nodes())); + + // Clear existing data + clear(); + + // Initialize lookup structures + column_lookup_.resize(fpga_grid_x_ + 1); + row_lookup_.resize(fpga_grid_y_ + 1); + + // Index all nodes + for (RRNodeId node_id : rr_graph_.nodes()) { + index_node(node_id); + } + + VTR_LOG("Node lookup manager initialized successfully\n"); + print_statistics(); +} + +const std::unordered_map& NodeLookupManager::get_column_nodes(size_t x) const { + VTR_ASSERT(x <= fpga_grid_x_ && x < column_lookup_.size()); + return column_lookup_[x]; +} + +const std::unordered_map& NodeLookupManager::get_row_nodes(size_t y) const { + VTR_ASSERT(y <= fpga_grid_y_ && y < row_lookup_.size()); + return row_lookup_[y]; +} + +std::unordered_map NodeLookupManager::get_combined_nodes(size_t x, size_t y) const { + std::unordered_map combined; + + // Add column nodes + const auto& col_nodes = get_column_nodes(x); + combined.insert(col_nodes.begin(), col_nodes.end()); + + // Add row nodes + const auto& row_nodes = get_row_nodes(y); + combined.insert(row_nodes.begin(), row_nodes.end()); + + return combined; +} + +void NodeLookupManager::print_statistics() const { + VTR_LOG("=== Node Lookup Manager Statistics ===\n"); + VTR_LOG("Grid dimensions: %d x %d\n", fpga_grid_x_, fpga_grid_y_); + + // Count nodes per column + std::vector col_counts; + for (const auto& col_map : column_lookup_) { + col_counts.push_back(col_map.size()); + } + + if (!col_counts.empty()) { + auto [min_col, max_col] = + std::minmax_element(col_counts.begin(), col_counts.end()); + VTR_LOG("Nodes per column: min=%d, max=%d, avg=%f\n", *min_col, *max_col, + std::accumulate(col_counts.begin(), col_counts.end(), 0.0) / col_counts.size()); + } + + // Count nodes per row + std::vector row_counts; + for (const auto& row_map : row_lookup_) { + row_counts.push_back(row_map.size()); + } + + if (!row_counts.empty()) { + auto [min_row, max_row] = + std::minmax_element(row_counts.begin(), row_counts.end()); + VTR_LOG("Nodes per row: min=%d, max=%d, avg=%f\n", *min_row, *max_row, + *max_row, + std::accumulate(row_counts.begin(), row_counts.end(), 0.0) / row_counts.size()); + } +} + +void NodeLookupManager::clear() { + column_lookup_.clear(); + row_lookup_.clear(); +} + +NodeHash NodeLookupManager::build_node_hash(RRNodeId node_id) const { + const std::string& node_ptcs = node_ptc_number_to_string(rr_graph_, node_id); + return std::make_tuple(rr_graph_.node_type(node_id), + node_ptcs, + rr_graph_.node_xlow(node_id), rr_graph_.node_xhigh(node_id), + rr_graph_.node_ylow(node_id), rr_graph_.node_yhigh(node_id)); +} + +void NodeLookupManager::index_node(RRNodeId node_id) { + NodeHash hash = build_node_hash(node_id); + + short x_low = rr_graph_.node_xlow(node_id); + short x_high = rr_graph_.node_xhigh(node_id); + short y_low = rr_graph_.node_ylow(node_id); + short y_high = rr_graph_.node_yhigh(node_id); + + VTR_ASSERT(static_cast(x_low) <= fpga_grid_x_); + VTR_ASSERT(static_cast(x_high) <= fpga_grid_x_); + VTR_ASSERT(static_cast(y_low) <= fpga_grid_y_); + VTR_ASSERT(static_cast(y_high) <= fpga_grid_y_); + + // Skip spatial indexing for source/sink nodes + if (rr_graph_.node_type(node_id) == e_rr_type::SOURCE || rr_graph_.node_type(node_id) == e_rr_type::SINK) { + return; + } + + // Add to column lookup (for single-column nodes) + if (x_low == x_high) { + column_lookup_[static_cast(x_low)][hash] = node_id; + } + + // Add to row lookup (for single-row nodes) + if (y_low == y_high) { + row_lookup_[static_cast(y_low)][hash] = node_id; + } +} + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.h new file mode 100644 index 00000000000..b09833a95d6 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.h @@ -0,0 +1,78 @@ +#pragma once + +#include "rr_graph_fwd.h" +#include "rr_graph_view.h" + +#include "crr_common.h" + +namespace crrgenerator { + +/** + * @brief Manages efficient node lookup and indexing for RR graphs + * + * This class creates spatial indexes for nodes to enable fast lookup + * by coordinates, type, and other attributes. + */ +class NodeLookupManager { + public: + /** + * @brief Constructor + * @param rr_graph RR graph to index + * @param fpga_grid_x Maximum X coordinate + * @param fpga_grid_y Maximum Y coordinate + */ + NodeLookupManager(const RRGraphView& rr_graph, size_t fpga_grid_x, size_t fpga_grid_y); + + /** + * @brief Initialize lookup tables from RR graph + */ + void initialize(); + + /** + * @brief Get nodes in a specific column + * @param x Column coordinate + * @return Map of hash to node for nodes in column + */ + const std::unordered_map& get_column_nodes(size_t x) const; + + /** + * @brief Get nodes in a specific row + * @param y Row coordinate + * @return Map of hash to node for nodes in row + */ + const std::unordered_map& get_row_nodes(size_t y) const; + + /** + * @brief Get combined nodes for column and row (for switch block processing) + * @param x Column coordinate + * @param y Row coordinate + * @return Combined map of nodes + */ + std::unordered_map get_combined_nodes(size_t x, size_t y) const; + + /** + * @brief Print lookup statistics + */ + void print_statistics() const; + + /** + * @brief Clear all lookup tables + */ + void clear(); + + private: + const RRGraphView& rr_graph_; + // Spatial indexes - SINK and SOURCE nodes are not stored in these two maps + std::vector> column_lookup_; + std::vector> row_lookup_; + + // Grid dimensions + size_t fpga_grid_x_; + size_t fpga_grid_y_; + + // Helper methods + NodeHash build_node_hash(RRNodeId node_id) const; + void index_node(RRNodeId node_id); +}; + +} // namespace crrgenerator diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp index e5b61e1fa38..f71d71992d6 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp @@ -7,6 +7,8 @@ * producing large FPGA fabrics. ***********************************************************************/ /* Headers from vtrutil library */ +#include + #include "vtr_assert.h" #include "vtr_time.h" #include "vtr_log.h" @@ -28,6 +30,14 @@ #include "globals.h" +/** + * @brief Remove dangling chan nodes (chan nodes with no incoming edges) from the rr_graph + * @param grid The device grid + * @param rr_graph_builder The rr_graph builder + */ +static void remove_dangling_chan_nodes(const DeviceGrid& grid, + RRGraphBuilder& rr_graph_builder); + /************************************************************************ * Main function of this file * Builder for a detailed uni-directional tileable rr_graph @@ -71,6 +81,7 @@ void build_tileable_unidir_rr_graph(const std::vector& types, const DeviceGrid& grids, const t_chan_width& chan_width, + const t_crr_opts& crr_opts, const e_switch_block_type& sb_type, const int& Fs, const e_switch_block_type& sb_subtype, @@ -248,6 +259,7 @@ void build_tileable_unidir_rr_graph(const std::vector& typ build_rr_graph_edges(device_ctx.rr_graph, device_ctx.rr_graph_builder, rr_node_driver_switches, + crr_opts, grids, vib_grid, 0, device_chan_width, segment_inf, segment_inf_x, segment_inf_y, @@ -275,6 +287,10 @@ void build_tileable_unidir_rr_graph(const std::vector& typ // 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(); + if (crr_opts.remove_dangling_nodes) { + remove_dangling_chan_nodes(device_ctx.grid, + device_ctx.rr_graph_builder); + } alloc_and_load_rr_switch_inf(device_ctx.rr_graph_builder, device_ctx.switch_fanin_remap, device_ctx.all_sw_inf, @@ -310,3 +326,44 @@ void build_tileable_unidir_rr_graph(const std::vector& typ check_rr_graph(device_ctx.rr_graph, types, device_ctx.rr_indexed_data, grids, vib_grid, device_ctx.chan_width, e_graph_type::UNIDIR_TILEABLE, false); } + +static void remove_dangling_chan_nodes(const DeviceGrid& grid, + RRGraphBuilder& rr_graph_builder) { + t_rr_graph_storage& rr_nodes = rr_graph_builder.rr_nodes(); + RRSpatialLookup& node_lookup = rr_graph_builder.node_lookup(); + std::vector dangling_nodes; + + for (size_t node_index = 0; node_index < rr_nodes.size(); ++node_index) { + RRNodeId node = RRNodeId(node_index); + if (rr_nodes.node_type(node) == e_rr_type::CHANX || rr_nodes.node_type(node) == e_rr_type::CHANY) { + if (rr_nodes.fan_in(node) == 0) { + dangling_nodes.push_back(node); + } + } + } + rr_nodes.remove_nodes(dangling_nodes); + + node_lookup.clear(); + // Alloc the lookup table + for (e_rr_type rr_type : RR_TYPES) { + node_lookup.resize_nodes(grid.get_num_layers(), + grid.width(), + grid.height(), + rr_type, + NUM_2D_SIDES); + } + + // Add the correct node into the vector + for (size_t node_index = 0; node_index < rr_nodes.size(); ++node_index) { + RRNodeId node = RRNodeId(node_index); + // Set track numbers as a node may have multiple ptc + if (rr_graph_builder.node_contain_multiple_ptc(node)) { + if (rr_nodes.node_type(node) == e_rr_type::CHANX || rr_nodes.node_type(node) == e_rr_type::CHANY) { + rr_graph_builder.add_track_node_to_lookup(node); + } + } else { + rr_graph_builder.add_node_to_all_locs(node); + } + } + rr_graph_builder.init_fan_in(); +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h index 5ddb5667d61..2df1f7c012d 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h @@ -16,6 +16,7 @@ void build_tileable_unidir_rr_graph(const std::vector& types, const DeviceGrid& grids, const t_chan_width& chan_width, + const t_crr_opts& crr_opts, const e_switch_block_type& sb_type, const int& Fs, const e_switch_block_type& sb_subtype, diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp index 09b25caa5d0..106dee29315 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp @@ -15,6 +15,9 @@ #include "tileable_rr_graph_gsb.h" #include "tileable_rr_graph_edge_builder.h" +#include "crr_generator.h" +#include "crr_edge_builder.h" + /************************************************************************ * Build the edges for all the SOURCE and SINKs nodes: * 1. create edges between SOURCE and OPINs @@ -104,6 +107,7 @@ void build_rr_graph_edges_for_sink_nodes(const RRGraphView& rr_graph, void build_rr_graph_edges(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, vtr::vector& rr_node_driver_switches, + const t_crr_opts& crr_opts, const DeviceGrid& grids, const VibDeviceGrid& vib_grid, const size_t& layer, @@ -140,6 +144,7 @@ void build_rr_graph_edges(const RRGraphView& rr_graph, build_rr_graph_regular_edges(rr_graph, rr_graph_builder, rr_node_driver_switches, + crr_opts, grids, layer, device_chan_width, @@ -294,7 +299,8 @@ void build_rr_graph_vib_edges(const RRGraphView& rr_graph, void build_rr_graph_regular_edges(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, - vtr::vector& rr_node_driver_switches, + const vtr::vector& rr_node_driver_switches, + const t_crr_opts& crr_opts, const DeviceGrid& grids, const size_t& layer, const vtr::Point& device_chan_width, @@ -311,6 +317,7 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, const bool& opin2all_sides, const bool& concat_wire, const bool& wire_opposite_side) { + bool build_crr_edges = !crr_opts.sb_templates.empty(); 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); @@ -318,6 +325,18 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, vtr::Point gsb_range(grids.width() - 1, grids.height() - 1); + // Building CRR Graph + const crrgenerator::CRRConnectionBuilder* crr_connection_builder = nullptr; + crrgenerator::SwitchBlockManager sb_manager; + crrgenerator::NodeLookupManager node_lookup(rr_graph, grids.width(), grids.height()); + crrgenerator::CRRGraphGenerator parser(crr_opts, rr_graph, node_lookup, sb_manager); + if (build_crr_edges) { + sb_manager.initialize(crr_opts.sb_maps, crr_opts.sb_templates, crr_opts.annotated_rr_graph); + node_lookup.initialize(); + parser.run(); + crr_connection_builder = parser.get_connection_builder(); + } + /* 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) { @@ -329,25 +348,46 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, device_chan_width, segment_inf_x, segment_inf_y, layer, gsb_coord, perimeter_cb); - /* 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); + t_pin2track_map opin2track_map; /* [0..gsb_side][0..num_opin_node][track_indices] */ + + /* adapt the track_to_ipin_lookup for the GSB nodes */ + if (!build_crr_edges || crr_opts.preserve_input_pin_connections) { + 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, opin2all_sides); + if (!build_crr_edges || crr_opts.preserve_output_pin_connections) { + opin2track_map = build_gsb_opin_to_track_map(rr_graph, rr_gsb, grids, segment_inf, Fc_out, opin2all_sides); + } /* 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, sub_fs, concat_wire, wire_opposite_side, - segment_inf); + if (build_crr_edges) { + if (ix != gsb_range.x() && iy != gsb_range.y()) { + build_crr_gsb_track_to_track_edges(rr_graph_builder, rr_gsb, *crr_connection_builder); + } + } else { + sb_conn = build_gsb_track_to_track_map(rr_graph, + rr_gsb, + sb_type, + Fs, + sb_subtype, + sub_fs, + concat_wire, + wire_opposite_side, + segment_inf); + } /* Build edges for a GSB */ - /* 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); + 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); } diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h index 73f8ff65f88..95392850114 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h @@ -23,6 +23,7 @@ void build_rr_graph_edges(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, vtr::vector& rr_node_driver_switches, + const t_crr_opts& crr_opts, const DeviceGrid& grids, const VibDeviceGrid& vib_grid, const size_t& layer, @@ -78,7 +79,8 @@ void build_rr_graph_vib_edges(const RRGraphView& rr_graph, void build_rr_graph_regular_edges(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, - vtr::vector& rr_node_driver_switches, + const vtr::vector& rr_node_driver_switches, + const t_crr_opts& crr_opts, const DeviceGrid& grids, const size_t& layer, const vtr::Point& device_chan_width, diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp index 5553ad55f69..ca6ec3691d1 100644 --- a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -1190,15 +1190,17 @@ void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, 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); - - for (size_t to_side = 0; to_side < opin2track_map[gsb_side][inode].size(); ++to_side) { - /* 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][to_side]) { - rr_graph_builder.create_edge_in_cache(opin_node, track_node, rr_node_driver_switches[track_node], false); - edge_count++; + if (opin2track_map.size() > 0) { + 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); + + for (size_t to_side = 0; to_side < opin2track_map[gsb_side][inode].size(); ++to_side) { + /* 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][to_side]) { + rr_graph_builder.create_edge_in_cache(opin_node, track_node, rr_node_driver_switches[track_node], false); + edge_count++; + } } } } @@ -1207,24 +1209,28 @@ void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, /* 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(e_rr_type::CHANX)) - || (side_manager.get_side() == rr_gsb.get_cb_chan_side(e_rr_type::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_in_cache(chan_node, ipin_node, rr_node_driver_switches[ipin_node], false); - edge_count++; + if (track2ipin_map.size() > 0) { + if ((side_manager.get_side() == rr_gsb.get_cb_chan_side(e_rr_type::CHANX)) + || (side_manager.get_side() == rr_gsb.get_cb_chan_side(e_rr_type::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_in_cache(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_in_cache(chan_node, track_node, rr_node_driver_switches[track_node], false); - edge_count++; + if (track2track_map.size() > 0) { + 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_in_cache(chan_node, track_node, rr_node_driver_switches[track_node], false); + edge_count++; + } } } } diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_custom_grid/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_custom_grid/config/golden_results.txt index f8edfed744c..631fb455535 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_custom_grid/config/golden_results.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_custom_grid/config/golden_results.txt @@ -5,4 +5,4 @@ non_column_tall_aspect_ratio.xml raygentop.v common 17.71 vpr 99.84 MiB -1 -1 2.18 42720 4 0.55 -1 -1 36960 -1 -1 129 236 1 6 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 102236 236 305 3170 2982 1 1517 677 23 46 1058 io auto 47.8 MiB 2.03 36972 13791 238357 78620 131581 28156 96.5 MiB 1.13 0.02 8.28835 4.82201 -2889.55 -4.82201 4.82201 0.99 0.00478733 0.004413 0.430224 0.390576 -1 -1 -1 -1 52 27780 44 5.05849e+07 9.87633e+06 3.17293e+06 2998.99 6.59 1.81522 1.64979 97261 632982 -1 23254 19 7012 18758 1829454 483193 5.63042 5.63042 -3129.27 -5.63042 0 0 4.15960e+06 3931.57 0.16 0.59 0.52 -1 -1 0.16 0.281399 0.264065 non_column_wide_aspect_ratio.xml raygentop.v common 18.75 vpr 114.70 MiB -1 -1 2.12 41948 4 0.57 -1 -1 36956 -1 -1 129 236 1 6 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 117448 236 305 3170 2982 1 1517 677 53 27 1431 io auto 47.8 MiB 2.10 40311.6 16087 300748 105018 149649 46081 114.7 MiB 1.42 0.02 7.83521 4.85182 -3052.38 -4.85182 4.85182 1.51 0.0047411 0.00435747 0.556583 0.504699 -1 -1 -1 -1 48 30604 34 7.18852e+07 9.87633e+06 3.96130e+06 2768.20 6.13 1.80378 1.64019 126813 766723 -1 25771 17 6698 17200 1592529 406246 4.99409 4.99409 -3278.31 -4.99409 0 0 5.06025e+06 3536.17 0.20 0.55 0.62 -1 -1 0.20 0.263761 0.248281 custom_sbloc.xml raygentop.v common 12.79 vpr 84.70 MiB -1 -1 1.92 42344 4 0.56 -1 -1 36704 -1 -1 127 236 1 6 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 86732 236 305 3177 2989 1 1517 675 19 19 361 io clb auto 45.9 MiB 1.96 26256.7 13231 237395 72213 160216 4966 84.7 MiB 1.21 0.02 6.16993 4.60407 -2878.58 -4.60407 4.60407 0.25 0.0057112 0.0049104 0.452496 0.410913 -1 -1 -1 -1 64 24087 40 1.65001e+07 9.76854e+06 1.19565e+06 3312.06 4.18 1.81435 1.65742 35881 230269 -1 20668 19 6125 17534 1714638 444346 4.91417 4.91417 -3127.09 -4.91417 0 0 1.50465e+06 4168.01 0.04 0.56 0.17 -1 -1 0.04 0.285497 0.26822 - multiple_io_types.xml raygentop.v common 77.66 vpr 510.52 MiB -1 -1 2.06 41948 4 0.57 -1 -1 36976 -1 -1 127 236 1 6 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 522768 236 305 3177 2989 1 1517 675 70 70 4900 io_left auto 45.9 MiB 2.63 80208.6 29491 101281 4797 30435 66049 510.5 MiB 0.61 0.02 10.9706 4.81705 -3822.69 -4.81705 4.81705 14.17 0.00530076 0.00468624 0.256582 0.234179 -1 -1 -1 -1 50 43945 30 2.76175e+08 9.76854e+06 1.65363e+07 3000 47.93 2.35081 2.15011 425698 2387761 -1 40421 17 8256 21235 3275501 821072 5.13584 5.13584 -4142.28 -5.13584 0 0 1.61910e+07 3304.29 0.59 0.68 1.69 -1 -1 0.59 0.21718 0.204926 + multiple_io_types.xml raygentop.v common 77.66 vpr 510.52 MiB -1 -1 2.06 41948 4 0.57 -1 -1 36976 -1 -1 127 236 1 6 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 784152 236 305 3177 2989 1 1517 675 70 70 4900 io_left auto 45.9 MiB 2.63 80208.6 29491 101281 4797 30435 66049 510.5 MiB 0.61 0.02 10.9706 4.81705 -3822.69 -4.81705 4.81705 14.17 0.00530076 0.00468624 0.256582 0.234179 -1 -1 -1 -1 50 43945 30 2.76175e+08 9.76854e+06 1.65363e+07 3000 47.93 2.35081 2.15011 425698 2387761 -1 40421 17 8256 21235 3275501 821072 5.13584 5.13584 -4142.28 -5.13584 0 0 1.61910e+07 3304.29 0.59 0.68 1.69 -1 -1 0.59 0.21718 0.204926 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_xilinx_flagship/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_xilinx_flagship/config/golden_results.txt index 76842a94d9c..17c78a5d4d1 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_xilinx_flagship/config/golden_results.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_xilinx_flagship/config/golden_results.txt @@ -1,3 +1,3 @@ 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 initial_placed_wirelength_est placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time initial_placed_CPD_est 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 ap_mem ap_time ap_full_legalizer_mem ap_full_legalizer_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 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 7series_BRAM_DSP_carry.xml stereovision3.v common 3.82 vpr 74.09 MiB -1 -1 0.22 31628 4 0.07 -1 -1 35776 -1 -1 -1 11 0 -1 success v8.0.0-14124-g5725a225d release IPO VTR_ASSERT_LEVEL=2 GNU 11.4.0 on Linux-5.15.0-119-generic x86_64 2025-10-09T09:04:20 goeders10 /home/chem3000/GitClones/vtr_pulls/vtr_ccl/vtr-verilog-to-routing/vtr_flow/tasks 75864 11 2 305 285 2 111 34 7 7 49 CLB auto 34.8 MiB 1.51 540.712 424 584 93 389 102 74.1 MiB 0.01 0.00 3.28585 3.2897 -182.249 -3.2897 3.21493 0.12 0.000279618 0.000250801 0.00524824 0.00486604 -1 -1 -1 -1 44 571 17 1.34735e+06 1.13177e+06 177202. 3616.36 1.29 0.0832215 0.0712857 6848 92556 -1 426 8 252 839 111127 49869 3.46476 3.12023 -219.956 -3.46476 -2.342 -0.04 257836. 5261.96 0.03 0.02 0.05 -1 -1 0.03 0.0108628 0.0101501 -7series_BRAM_DSP_carry.xml diffeq2.v common 49.95 vpr 119.88 MiB -1 -1 0.14 33104 5 0.08 -1 -1 38164 -1 -1 -1 66 0 -1 success v8.0.0-14124-g5725a225d release IPO VTR_ASSERT_LEVEL=2 GNU 11.4.0 on Linux-5.15.0-119-generic x86_64 2025-10-09T09:04:20 goeders10 /home/chem3000/GitClones/vtr_pulls/vtr_ccl/vtr-verilog-to-routing/vtr_flow/tasks 122760 66 96 1817 1078 1 1146 337 26 26 676 DSP auto 42.3 MiB 2.63 20173 9674 89409 23988 58684 6737 118.6 MiB 0.87 0.01 22.2237 19.8977 -1060.81 -19.8977 19.8977 3.42 0.00131729 0.00122751 0.109971 0.102313 -1 -1 -1 -1 68 12670 49 3.53732e+07 1.31946e+07 5.24855e+06 7764.12 34.52 0.723609 0.66052 131170 3079406 -1 11823 14 5719 9575 3216984 800670 21.5154 21.5154 -1313 -21.5154 -1.7 -0.034 6.96973e+06 10310.3 1.92 0.42 1.67 -1 -1 1.92 0.0555499 0.0519533 +7series_BRAM_DSP_carry.xml diffeq2.v common 49.95 vpr 119.88 MiB -1 -1 0.14 33104 5 0.08 -1 -1 38164 -1 -1 -1 66 0 -1 success v8.0.0-14124-g5725a225d release IPO VTR_ASSERT_LEVEL=2 GNU 11.4.0 on Linux-5.15.0-119-generic x86_64 2025-10-09T09:04:20 goeders10 /home/chem3000/GitClones/vtr_pulls/vtr_ccl/vtr-verilog-to-routing/vtr_flow/tasks 245520 66 96 1817 1078 1 1146 337 26 26 676 DSP auto 42.3 MiB 2.63 20173 9674 89409 23988 58684 6737 118.6 MiB 0.87 0.01 22.2237 19.8977 -1060.81 -19.8977 19.8977 3.42 0.00131729 0.00122751 0.109971 0.102313 -1 -1 -1 -1 68 12670 49 3.53732e+07 1.31946e+07 5.24855e+06 7764.12 34.52 0.723609 0.66052 131170 3079406 -1 11823 14 5719 9575 3216984 800670 21.5154 21.5154 -1313 -21.5154 -1.7 -0.034 6.96973e+06 10310.3 1.92 0.42 1.67 -1 -1 1.92 0.0555499 0.0519533 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_custom_grid/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_custom_grid/config/golden_results.txt index 7429d7c034f..89a6694a984 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_custom_grid/config/golden_results.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_custom_grid/config/golden_results.txt @@ -5,4 +5,4 @@ non_column_tall_aspect_ratio.xml raygentop.v common 15.82 vpr 99.49 MiB 0.37 31872 -1 -1 3 0.88 -1 -1 39884 -1 -1 123 214 0 8 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 101876 214 305 2963 2869 1 1441 650 23 46 1058 io auto 48.4 MiB 2.54 31907.6 12410 251096 85740 136677 28679 96.1 MiB 1.16 0.02 6.55506 4.68258 -2814.04 -4.68258 4.68258 0.98 0.00459635 0.00423049 0.452395 0.415808 -1 -1 -1 -1 52 23494 23 5.05849e+07 9.79696e+06 3.17293e+06 2998.99 5.07 1.59679 1.47145 97261 632982 -1 20696 16 5733 13978 1576274 434112 4.86141 4.86141 -3096.8 -4.86141 0 0 4.15960e+06 3931.57 0.20 0.58 0.51 -1 -1 0.20 0.275968 0.262225 non_column_wide_aspect_ratio.xml raygentop.v common 19.23 vpr 101.88 MiB 0.37 31872 -1 -1 3 0.90 -1 -1 39896 -1 -1 123 214 0 8 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 104324 214 305 2963 2869 1 1441 650 43 22 946 io auto 48.3 MiB 2.50 33702.5 13867 282050 98841 143573 39636 94.4 MiB 1.29 0.02 6.32754 4.9559 -2893.32 -4.9559 4.9559 0.87 0.00450797 0.00416286 0.528306 0.484543 -1 -1 -1 -1 50 24750 31 4.55909e+07 9.79696e+06 2.70028e+06 2854.41 9.13 2.30138 2.11561 84704 520009 -1 22155 16 5412 12820 1401514 376383 5.11908 5.11908 -3180.83 -5.11908 0 0 3.44953e+06 3646.44 0.12 0.42 0.41 -1 -1 0.12 0.212336 0.200865 custom_sbloc.xml raygentop.v common 12.43 vpr 86.75 MiB 0.27 31488 -1 -1 3 0.94 -1 -1 39508 -1 -1 123 214 0 8 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 88828 214 305 2963 2869 1 1441 650 19 19 361 io clb auto 47.0 MiB 2.56 24907.5 12293 225770 70817 148328 6625 86.7 MiB 1.11 0.02 5.21194 4.8033 -2758.45 -4.8033 4.8033 0.29 0.00449502 0.00412652 0.413606 0.379507 -1 -1 -1 -1 60 23435 27 1.65001e+07 9.79696e+06 1.11685e+06 3093.75 4.16 1.48655 1.37122 34801 214773 -1 20006 16 5631 13717 1408084 370803 4.96136 4.96136 -3017.4 -4.96136 0 0 1.41014e+06 3906.19 0.04 0.45 0.15 -1 -1 0.04 0.23404 0.221707 - multiple_io_types.xml raygentop.v common 68.41 vpr 472.32 MiB 0.27 31488 -1 -1 3 0.97 -1 -1 39732 -1 -1 123 214 0 8 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 483652 214 305 2963 2869 1 1441 650 67 67 4489 io_left auto 46.7 MiB 2.95 63663.1 26330 90698 3630 28163 58905 472.3 MiB 0.48 0.02 8.02054 4.60821 -3832.09 -4.60821 4.60821 13.04 0.00411404 0.00377571 0.166355 0.153309 -1 -1 -1 -1 50 42098 27 2.48753e+08 9.79696e+06 1.23326e+07 2747.29 40.78 2.11052 1.94491 397497 2315172 -1 37047 18 7970 19204 4037402 1017842 6.19704 6.19704 -4231.98 -6.19704 0 0 1.58642e+07 3534.01 0.61 0.86 1.76 -1 -1 0.61 0.229586 0.216267 + multiple_io_types.xml raygentop.v common 68.41 vpr 472.32 MiB 0.27 31488 -1 -1 3 0.97 -1 -1 39732 -1 -1 123 214 0 8 success v8.0.0-13084-g071ad3865 release IPO VTR_ASSERT_LEVEL=2 GNU 13.3.0 on Linux-6.8.0-60-generic x86_64 2025-06-17T09:37:40 betzgrp-wintermute /home/pooladam/vtr-verilog-to-routing 967304 214 305 2963 2869 1 1441 650 67 67 4489 io_left auto 46.7 MiB 2.95 63663.1 26330 90698 3630 28163 58905 472.3 MiB 0.48 0.02 8.02054 4.60821 -3832.09 -4.60821 4.60821 13.04 0.00411404 0.00377571 0.166355 0.153309 -1 -1 -1 -1 50 42098 27 2.48753e+08 9.79696e+06 1.23326e+07 2747.29 40.78 2.11052 1.94491 397497 2315172 -1 37047 18 7970 19204 4037402 1017842 6.19704 6.19704 -4231.98 -6.19704 0 0 1.58642e+07 3534.01 0.61 0.86 1.76 -1 -1 0.61 0.229586 0.216267