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/command_line_usage.rst b/doc/src/vpr/command_line_usage.rst index 57d5723e143..3140841f700 100644 --- a/doc/src/vpr/command_line_usage.rst +++ b/doc/src/vpr/command_line_usage.rst @@ -1,3 +1,5 @@ +.. _vpr_command_line_usage: + Command-line Options ==================== .. program:: vpr diff --git a/doc/src/vpr/custom_rr_graph.rst b/doc/src/vpr/custom_rr_graph.rst new file mode 100644 index 00000000000..1dcb84d78d3 --- /dev/null +++ b/doc/src/vpr/custom_rr_graph.rst @@ -0,0 +1,187 @@ +.. _custom_rr_graph_generator: + +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`` + +Below is an example of a switch block map file where there is no switch block at the perimeter, and DSP blocks are placed from block 2 to 10 (inclusive) with a step of 2. For all other locations, the same switch block template is used. + +.. code-block:: yaml + + SB_MAPS: + SB_0__0_: null + SB_1__: sb_perimeter.csv + SB1: sb_perimeter.csv + SB[2:10:2]: sb_dsp.csv + SB*_: sb_main.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 lane is a group of routing wires that shuffle only within the boundaries of their own lane region as they propagate across tiles. + +**Tap:** A switch block location where a wire passes through and can have fan-out connections. + +See the figure below for an illustration: + +.. figure:: lane_and_tap.png + :alt: Lane and Tap + :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:: lane_and_tap_realistic.png + :alt: Lane and Tap Realistic + :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. The delay for that connections is retrieved from the sitch type specified in the architecture file. +* 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 number of fan-ins to the sink node (optional) +4. **Row 4 - Lane Number:** The lane to which the sink node belongs + +*Note:* Given that currently we only support unidirectional segments, the tap is not specified because a wire can only be driven from its starting point. + +Example +^^^^^^^ + +Consider an architecture with a channel width of ``160`` containing only wire +segments of length ``4`` (L4). + +The number of **rows** in the template file is computed as: + +- ``4`` (number of sides) × ``160/2`` (number of tracks in one direction) + = ``320`` rows. + +On each side, we have: + +- ``20`` lanes (``80 / 4``), and +- each lane requires ``4`` rows (Lane width is equal to the segment length). + +The number of **columns** is: + +- ``4`` (number of sides) × ``160/2`` (number of tracks in one direction) ÷ ``4`` (starting tracks per lane) + = ``80`` columns. + +**Note:** The reason for dividing by 4 is that only the first track of each lane can be driven from each switch location. + +---- + +To illustrate how a particular cell in the template file is interpreted, assume +the following row and column specifications: + +**Row specification:** + +- Direction: ``left`` +- Segment type: ``l4`` +- Lane number: ``2`` +- Tap number: ``3`` + +**Column specification:** + +- Direction: ``right`` +- Segment type: ``l4`` +- Lane number: ``2`` + +Assuming ``l4`` is the only segment type, the row specification corresponds to a +source wire entering the switch block from the *left* side. The starting PTC +(track) number for lane 2 is: + +.. code-block:: none + + (2 - 1) * 4 * 2 = 8 + +Thus, the PTC sequence for this L4 wire is (it increases by 2 since the odd PTC numbers are used for wires coming from the other side): + +.. code-block:: none + + 8, 10, 12, 14 + +The row refers to the node associated with the **third tap**, meaning the +source node corresponds to the PTC value ``12``. With the starting and ending of the track, +and the PTC values, VPR can determine the source **Node ID**. + +For the sink node, the column specification describes a wire exiting the switch +block on the *right* side, using the same PTC sequence: + +.. code-block:: none + + 8, 10, 12, 14 + +Using this sequence, VPR determines the sink **Node ID**. + +After computing both Node IDs, the tool inserts the switch defined in the +template file, with the associated delay value, into the RR Graph. + +---- + +Each switch block template file must contain the number of rows and columns +calculated in the previous section. After creating a correctly sized template +file, populate it by placing switch delay values in the cells representing valid +connections between source and sink nodes, or if you want to use the switches defined +in the architecture file, you can mark the connection with an 'x'. + +Once the switch block map file and template files are created, include the +following arguments in the VPR command line: + +- ``--sb_maps `` +- ``--sb_templates `` +- ``--sb_count_dir `` (optional): + If provided, VPR generates a CSV file for each switch block template, + showing how many times each switch specified in the template is used in the + final routing results. + +For additional arguments, refer to the command-line usage section in +:ref:`vpr_command_line_usage`. diff --git a/doc/src/vpr/index.rst b/doc/src/vpr/index.rst index 61d60030bdb..d614eed2fe1 100644 --- a/doc/src/vpr/index.rst +++ b/doc/src/vpr/index.rst @@ -60,3 +60,4 @@ The purpose of VPR is to make the packing, placement, and routing stages of the file_formats debug_aids + custom_rr_graph_generator 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/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 289044c0fb2..0a2fd20db3c 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1799,6 +1799,12 @@ struct t_arch_switch_inf { e_power_buffer_type power_buffer_type = POWER_BUFFER_TYPE_AUTO; float power_buffer_size = 0.; + // The template ID of the switch. This is metadata stored for each switch to + // simplify certain analyses. For example, when generating the CRR graph, the + // template ID is used to determine which switch in the template is used most + // or least frequently. + std::string template_id = ""; + bool intra_tile = false; public: diff --git a/libs/librrgraph/src/base/rr_graph_builder.cpp b/libs/librrgraph/src/base/rr_graph_builder.cpp index 85b7d2bf967..9ae8edba2fb 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.cpp +++ b/libs/librrgraph/src/base/rr_graph_builder.cpp @@ -176,7 +176,10 @@ 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) { +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); 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..4a635482608 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,10 @@ 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); /** @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,7 +336,10 @@ 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) { + inline void emplace_back_edge(RRNodeId src, + RRNodeId dest, + short edge_switch, + bool remapped) { node_storage_.emplace_back_edge(src, dest, edge_switch, remapped); } /** @brief Append 1 more RR node to the RR graph. */ @@ -401,6 +408,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 +437,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..cd1bef24121 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.cpp +++ b/libs/librrgraph/src/base/rr_graph_storage.cpp @@ -18,7 +18,10 @@ void t_rr_graph_storage::reserve_edges(size_t num_edges) { edge_remapped_.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) { // Cannot mutate edges once edges have been read! VTR_ASSERT(!edges_read_); edge_src_node_.emplace_back(src); diff --git a/libs/librrgraph/src/base/rr_graph_storage.h b/libs/librrgraph/src/base/rr_graph_storage.h index 9fe67e7f56a..44cc7352719 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 * @@ -826,7 +827,10 @@ 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); /** @brief Adds a batch of edges.*/ void alloc_and_load_edges(const t_rr_edge_info_set* rr_edges_to_create); 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..d418bb739a5 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" diff --git a/libs/librrgraph/src/base/rr_node_types.h b/libs/librrgraph/src/base/rr_node_types.h index d0a00f195b4..9f428ed4616 100644 --- a/libs/librrgraph/src/base/rr_node_types.h +++ b/libs/librrgraph/src/base/rr_node_types.h @@ -56,6 +56,30 @@ constexpr vtr::array rr "CHANX", "CHANY", "CHANZ", "MUX"}; +inline e_rr_type get_rr_type(const std::string& type_name) { + if (type_name == "SOURCE") { + return e_rr_type::SOURCE; + } else if (type_name == "SINK") { + return e_rr_type::SINK; + } else if (type_name == "IPIN") { + return e_rr_type::IPIN; + } else if (type_name == "OPIN") { + return e_rr_type::OPIN; + } else if (type_name == "CHANX") { + return e_rr_type::CHANX; + } else if (type_name == "CHANY") { + return e_rr_type::CHANY; + } else if (type_name == "CHANZ") { + return e_rr_type::CHANZ; + } else if (type_name == "MUX") { + return e_rr_type::MUX; + } else { + std::string error_message = "Invalid RR type name: " + type_name; + VTR_ASSERT_MSG(false, error_message.c_str()); + return e_rr_type::NUM_RR_TYPES; + } +} + /** * @enum Direction * @brief Represents the wire direction for a routing resource node. diff --git a/libs/librrgraph/src/base/rr_switch.h b/libs/librrgraph/src/base/rr_switch.h index 3043b1ac1a2..be00c11c485 100644 --- a/libs/librrgraph/src/base/rr_switch.h +++ b/libs/librrgraph/src/base/rr_switch.h @@ -31,6 +31,12 @@ struct t_rr_switch_inf { e_power_buffer_type power_buffer_type = POWER_BUFFER_TYPE_UNDEFINED; float power_buffer_size = 0.; + // The template ID of the switch. This is metadata stored for each switch to + // simplify certain analyses. For example, when generating the CRR graph, the + // template ID is used to determine which switch in the template is used most + // or least frequently. + std::string template_id = ""; + /// Indicate whether this rr_switch is a switch type used inside clusters. /// These switch types are not specified in the architecture description file /// and are added when flat router is enabled. diff --git a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h index 15b13ed88eb..533e1b5c52e 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h @@ -6,7 +6,7 @@ * * Cmdline: uxsdcxx/uxsdcxx.py /dsoft/amohaghegh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd * Input file: /dsoft/amohaghegh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd - * md5sum of input file: e14523c72a5db9cc83592d3baaf45780 + * md5sum of input file: 45774433f1b54981c349fecadf578b11 */ #include @@ -238,8 +238,8 @@ constexpr const char *atok_lookup_t_sizing[] = {"buf_size", "mux_trans_size"}; enum class gtok_t_switch {TIMING, SIZING}; constexpr const char *gtok_lookup_t_switch[] = {"timing", "sizing"}; -enum class atok_t_switch {ID, NAME, TYPE}; -constexpr const char *atok_lookup_t_switch[] = {"id", "name", "type"}; +enum class atok_t_switch {ID, NAME, TEMPLATE_ID, TYPE}; +constexpr const char *atok_lookup_t_switch[] = {"id", "name", "template_id", "type"}; enum class gtok_t_switches {SWITCH}; constexpr const char *gtok_lookup_t_switches[] = {"switch"}; @@ -665,6 +665,29 @@ inline atok_t_switch lex_attr_t_switch(const char *in, const std::function.").c_str()); @@ -2397,7 +2420,7 @@ inline void load_sizing_required_attributes(const pugi::xml_node &root, float * } inline void load_switch_required_attributes(const pugi::xml_node &root, int * id, const std::function * report_error){ - std::bitset<3> astate = 0; + std::bitset<4> astate = 0; for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ atok_t_switch in = lex_attr_t_switch(attr.name(), report_error); if(astate[(int)in] == 0) astate[(int)in] = 1; @@ -2409,13 +2432,16 @@ inline void load_switch_required_attributes(const pugi::xml_node &root, int * id case atok_t_switch::NAME: /* Attribute name set after element init */ break; + case atok_t_switch::TEMPLATE_ID: + /* Attribute template_id set after element init */ + break; case atok_t_switch::TYPE: /* Attribute type set after element init */ break; default: break; /* Not possible. */ } } - std::bitset<3> test_astate = astate | std::bitset<3>(0b100); + std::bitset<4> test_astate = astate | std::bitset<4>(0b1100); if(!test_astate.all()) attr_error(test_astate, atok_lookup_t_switch, report_error); } @@ -2886,6 +2912,9 @@ inline void load_switch(const pugi::xml_node &root, T &out, Context &context, co case atok_t_switch::NAME: out.set_switch_name(attr.value(), context); break; + case atok_t_switch::TEMPLATE_ID: + out.set_switch_template_id(attr.value(), context); + break; case atok_t_switch::TYPE: out.set_switch_type(lex_enum_switch_type(attr.value(), true, report_error), context); break; @@ -4071,6 +4100,8 @@ inline void write_switches(T &in, std::ostream &os, Context &context){ os << ""; diff --git a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h index de659d56450..7ec8bee8c22 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h @@ -6,7 +6,7 @@ * * Cmdline: uxsdcxx/uxsdcap.py /dsoft/amohaghegh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd * Input file: /dsoft/amohaghegh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd - * md5sum of input file: e14523c72a5db9cc83592d3baaf45780 + * md5sum of input file: 45774433f1b54981c349fecadf578b11 */ #include @@ -416,7 +416,7 @@ inline void load_rr_graph_capnp(T &out, kj::ArrayPtr data, std::function report_error = [filename, &out, &stack](const char *message){ std::stringstream msg; msg << message << std::endl; - msg << "Error occured at "; + msg << "Error occurred at "; for(size_t i = 0; i < stack.size(); ++i) { msg << stack[i].first << "[" << stack[i].second << "]"; if(i+1 < stack.size()) { @@ -554,6 +554,7 @@ inline void load_switch_capnp_type(const ucap::Switch::Reader &root, T &out, Con (void)stack; out.set_switch_name(root.getName().cStr(), context); + out.set_switch_template_id(root.getTemplateId().cStr(), context); out.set_switch_type(conv_enum_switch_type(root.getType(), report_error), context); stack->push_back(std::make_pair("getTiming", 0)); if (root.hasTiming()) { @@ -1086,6 +1087,8 @@ inline void write_switches_capnp_type(T &in, ucap::Switches::Builder &root, Cont auto child_context = in.get_switches_switch(i, context); switches_switch.setId(in.get_switch_id(child_context)); switches_switch.setName(in.get_switch_name(child_context)); + if((bool)in.get_switch_template_id(child_context)) + switches_switch.setTemplateId(in.get_switch_template_id(child_context)); if((bool)in.get_switch_type(child_context)) switches_switch.setType(conv_to_enum_switch_type(in.get_switch_type(child_context))); write_switch_capnp_type(in, switches_switch, 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 743636237ae..d0c570717fc 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h @@ -6,7 +6,7 @@ * * Cmdline: uxsdcxx/uxsdcxx.py /dsoft/amohaghegh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd * Input file: /dsoft/amohaghegh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd - * md5sum of input file: e14523c72a5db9cc83592d3baaf45780 + * md5sum of input file: 45774433f1b54981c349fecadf578b11 */ #include @@ -200,11 +200,14 @@ class RrGraphBase { * * * + * * */ virtual inline int get_switch_id(typename ContextTypes::SwitchReadContext &ctx) = 0; virtual inline const char * get_switch_name(typename ContextTypes::SwitchReadContext &ctx) = 0; virtual inline void set_switch_name(const char * name, typename ContextTypes::SwitchWriteContext &ctx) = 0; + virtual inline const char * get_switch_template_id(typename ContextTypes::SwitchReadContext &ctx) = 0; + virtual inline void set_switch_template_id(const char * template_id, typename ContextTypes::SwitchWriteContext &ctx) = 0; virtual inline enum_switch_type get_switch_type(typename ContextTypes::SwitchReadContext &ctx) = 0; virtual inline void set_switch_type(enum_switch_type type, typename ContextTypes::SwitchWriteContext &ctx) = 0; virtual inline typename ContextTypes::TimingWriteContext init_switch_timing(typename ContextTypes::SwitchWriteContext &ctx) = 0; diff --git a/libs/librrgraph/src/io/rr_graph.xsd b/libs/librrgraph/src/io/rr_graph.xsd index ac81554e0ca..b672ed4ae5d 100644 --- a/libs/librrgraph/src/io/rr_graph.xsd +++ b/libs/librrgraph/src/io/rr_graph.xsd @@ -136,6 +136,7 @@ xs:simpleType "switch_type" defined above. --> + diff --git a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h index 8619faf1375..8ea3b55f550 100644 --- a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h +++ b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h @@ -546,6 +546,13 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return sw->name.c_str(); } + inline void set_switch_template_id(const char* template_id, t_rr_switch_inf*& sw) final { + sw->template_id = template_id; + } + inline const char* get_switch_template_id(const t_rr_switch_inf*& sw) final { + return sw->template_id.c_str(); + } + inline void set_switch_type(uxsd::enum_switch_type type, t_rr_switch_inf*& sw) final { sw->set_type(from_uxsd_switch_type(type)); } diff --git a/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp b/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp index c2400b228e7..71becdc9c4a 100644 --- a/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp +++ b/libs/libvtrcapnproto/gen/rr_graph_uxsdcxx.capnp @@ -4,9 +4,9 @@ # # Cmdline: uxsdcxx/uxsdcap.py /dsoft/amohaghegh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd # Input file: /dsoft/amohaghegh/vtr-verilog-to-routing/libs/librrgraph/src/io/rr_graph.xsd -# md5sum of input file: e14523c72a5db9cc83592d3baaf45780 +# md5sum of input file: 45774433f1b54981c349fecadf578b11 -@0xef4b0a4204785218; +@0xe2c2faca29085645; using Cxx = import "/capnp/c++.capnp"; $Cxx.namespace("ucap"); @@ -116,9 +116,10 @@ struct Sizing { struct Switch { id @0 :Int32; name @1 :Text; - type @2 :SwitchType; - timing @3 :Timing; - sizing @4 :Sizing; + templateId @2 :Text; + type @3 :SwitchType; + timing @4 :Timing; + sizing @5 :Sizing; } struct Switches { 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..1c08c2e686f 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -3280,6 +3280,43 @@ 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 specifies 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.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..54829092b64 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -284,6 +284,15 @@ 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 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..cb2d92e1bde 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,16 @@ 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.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..9d0c2c3bc86 100644 --- a/vpr/src/base/stats.cpp +++ b/vpr/src/base/stats.cpp @@ -1,10 +1,14 @@ #include "stats.h" -#include #include +#include +#include +#include +#include #include #include +#include #include "physical_types.h" #include "physical_types_util.h" @@ -23,8 +27,93 @@ #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 +// Using anonymous namespace to avoid polluting the global namespace +namespace { +struct SBKeyParts { + std::string filename; + int row; + int col; +}; +} // namespace + +// 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()) { + VTR_LOG_ERROR("Failed to open file: %s\n", filepath.c_str()); + 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 +218,111 @@ 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 RRGraphView& rr_graph = g_vpr_ctx.device().rr_graph; + const RoutingContext& 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); + RRSwitchId switch_id = RRSwitchId(rr_graph.edge_switch(edges[0])); + const std::string& sw_template_id = rr_graph.rr_switch_inf(switch_id).template_id; + if (sw_template_id.empty()) { + continue; + } + if (sb_count.find(sw_template_id) == sb_count.end()) { + sb_count[sw_template_id] = 0; + } + sb_count[sw_template_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, std::get<0>(entry)); + max_col = std::max(max_col, std::get<1>(entry)); + } + + // 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 = std::get<0>(entry); + int col = std::get<1>(entry); + int count = std::get<2>(entry); + 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..7f404586bc4 100644 --- a/vpr/src/base/stats.h +++ b/vpr/src/base/stats.h @@ -23,6 +23,17 @@ void routing_stats(const Netlist<>& net_list, e_directionality directionality, bool is_flat); +/** + * @brief Writes the number of times each switch in template file is used to + * a file with the same name as the template file in the given directory. + * @param net_list The netlist + * @param sb_map_dir The directory containing switch block template files + * @param sb_count_dir The directory to write the switchbox count statistics to + */ +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..2f62ffedc27 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1410,6 +1410,17 @@ 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; + 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 6a1b012da5f..c41f9979998 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) { @@ -498,6 +499,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, @@ -515,6 +517,7 @@ void create_rr_graph(e_graph_type graph_type, det_routing_arch.opin2all_sides, // Allow opin of grid to directly drive routing tracks at all sides of a switch block det_routing_arch.concat_wire, // Allow end-point tracks to be wired to a starting point track on the opposite in a switch block.It means a wire can be continued in the same direction to another wire det_routing_arch.concat_pass_wire, // Allow passing tracks to be wired to the routing tracks in the same direction in a switch block. It means that a pass wire can jump in the same direction to another + router_opts.route_verbosity, Warnings); } } 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/rr_graph_switch_utils.cpp b/vpr/src/route/rr_graph_generation/rr_graph_switch_utils.cpp index 3dba5370730..77fa1b730dc 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph_switch_utils.cpp +++ b/vpr/src/route/rr_graph_generation/rr_graph_switch_utils.cpp @@ -116,6 +116,8 @@ void load_rr_switch_from_arch_switch(RRGraphBuilder& rr_graph_builder, rr_graph_builder.rr_switch()[rr_switch_idx].name = arch_sw_inf.at(arch_switch_idx).name; rr_graph_builder.rr_switch()[rr_switch_idx].power_buffer_type = arch_sw_inf.at(arch_switch_idx).power_buffer_type; rr_graph_builder.rr_switch()[rr_switch_idx].power_buffer_size = arch_sw_inf.at(arch_switch_idx).power_buffer_size; + + rr_graph_builder.rr_switch()[rr_switch_idx].template_id = arch_sw_inf.at(arch_switch_idx).template_id; } t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_sw_inf, @@ -146,6 +148,8 @@ t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_ rr_switch_inf.power_buffer_type = arch_sw_inf.power_buffer_type; rr_switch_inf.power_buffer_size = arch_sw_inf.power_buffer_size; + rr_switch_inf.template_id = arch_sw_inf.template_id; + rr_switch_inf.intra_tile = arch_sw_inf.intra_tile; return rr_switch_inf; 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..2b63a418421 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_common.h @@ -0,0 +1,117 @@ +#pragma once + +/** + * @file + * @brief Common data structures used by custom RR graph generator. + */ + +#include +#include +#include +#include + +#include "rr_graph_fwd.h" +#include "rr_node_types.h" +namespace crrgenerator { +// Number of empty rows and columns in the switch template file +constexpr int NUM_EMPTY_ROWS = 5; +constexpr int NUM_EMPTY_COLS = 4; + +// e_sw_template_dir types +enum class e_sw_template_dir { LEFT = 0, + RIGHT, + TOP, + BOTTOM, + IPIN, + OPIN, + NUM_SIDES }; + +inline e_sw_template_dir get_sw_template_dir(const std::string& name) { + if (name == "LEFT") { + return e_sw_template_dir::LEFT; + } else if (name == "RIGHT") { + return e_sw_template_dir::RIGHT; + } else if (name == "TOP") { + return e_sw_template_dir::TOP; + } else if (name == "BOTTOM") { + return e_sw_template_dir::BOTTOM; + } else if (name == "IPIN") { + return e_sw_template_dir::IPIN; + } else if (name == "OPIN") { + return e_sw_template_dir::OPIN; + } else { + std::string error_message = "Invalid switch template direction name: " + name; + VTR_ASSERT_MSG(false, error_message.c_str()); + return e_sw_template_dir::NUM_SIDES; + } +} +constexpr vtr::array template_side_name = {"LEFT", "RIGHT", + "TOP", "BOTTOM", + "IPIN", "OPIN"}; + +/** + * @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 sw_template_id) noexcept + : sink_node_(sink_node) + , src_node_(src_node) + , delay_ps_(delay_ps) + , sw_template_id_(std::move(sw_template_id)) {} + + RRNodeId sink_node() const { return sink_node_; } + RRNodeId src_node() const { return src_node_; } + int delay_ps() const { return delay_ps_; } + std::string sw_template_id() const { return sw_template_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 sw_template_id_; +}; + +// Node hash type for lookups +// The first element is the node type +// The second element is the PTC sequence +// The third element is the x-low coordinate of the node +// The fourth element is the x-high coordinate of the node +// The fifth element is the y-low coordinate of the node +// The sixth element is the y-high coordinate of the node +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; + } +}; + +} // 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..99dc1ab5e4f --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.cpp @@ -0,0 +1,531 @@ +#include "crr_connection_builder.h" + +#include +#include +#include +#include +#include + +#include "vtr_log.h" +#include "vtr_util.h" +#include "vtr_assert.h" +#include "rr_node_types.h" + +namespace crrgenerator { + +static bool is_integer(const std::string& s) { + int value; + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); + // Uses std::from_chars to parse the entire string as an integer. + // Returns true only if: + // 1. The conversion succeeds without errors (ec == std::errc()) + // 2. The entire string was consumed (ptr points to the end) + // This ensures strings like "123abc" or "12.5" return false, while + // "123" or "-456" return true. + return ec == std::errc() && ptr == s.data() + s.size(); +} + +CRRConnectionBuilder::CRRConnectionBuilder(const RRGraphView& rr_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager, + const int verbosity) + : rr_graph_(rr_graph) + , node_lookup_(node_lookup) + , sb_manager_(sb_manager) + , verbosity_(verbosity) {} + +void CRRConnectionBuilder::initialize(int fpga_grid_x, + int fpga_grid_y, + bool is_annotated) { + + fpga_grid_x_ = fpga_grid_x; + fpga_grid_y_ = fpga_grid_y; + is_annotated_ = is_annotated; + + // Total locations is the number of locations on the FPGA grid minus the 4 + // corner locations. + int number_of_tiles = fpga_grid_x_ * fpga_grid_y_; + total_locations_ = static_cast(number_of_tiles); + processed_locations_ = 0; +} + +std::vector CRRConnectionBuilder::build_connections_for_location(size_t x, + size_t y) const { + std::vector tile_connections; + // Find matching switch block pattern + std::string pattern = sb_manager_.find_matching_pattern(x, y); + if (pattern.empty()) { + // If no pattern is found, it means that no switch block is defined for this location + // Thus, we return an empty vector of connections. + VTR_LOG_ERROR("No pattern found for switch block at (%zu, %zu)\n", x, y); + return {}; + } + std::string sw_block_file_name = sb_manager_.get_pattern_file_name(pattern); + if (sw_block_file_name.empty()) { + VTR_LOGV(verbosity_ > 1, "No switch block is associated with the pattern '%s' at (%zu, %zu)\n", pattern.c_str(), x, y); + return {}; + } + + const DataFrame* df = sb_manager_.get_switch_block_dataframe(pattern); + if (df == nullptr) { + VTR_LOG_ERROR("No dataframe found for pattern '%s' at (%zu, %zu)\n", pattern.c_str(), x, y); + return {}; + } + + VTR_LOGV(verbosity_ > 1, "Processing switch block with pattern '%s' at (%zu, %zu)\n", + 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_tile_source_nodes(x, y, *df, combined_nodes); + auto sink_nodes = get_tile_sink_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()) { + continue; + } + + 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()) { + continue; + } + + 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 sw_template_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, sw_template_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, sw_template_id); + } + } + } + + // Uniqueify the connections + vtr::uniquify(tile_connections); + tile_connections.shrink_to_fit(); + + VTR_LOGV(verbosity_ > 1, "Generated %zu connections for location (%zu, %zu)\n", + tile_connections.size(), x, y); + + return tile_connections; +} + +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); + + return tile_connections; +} + +std::map CRRConnectionBuilder::get_tile_source_nodes(int x, + int 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; + e_sw_template_dir prev_side = e_sw_template_dir::NUM_SIDES; + int prev_ptc_number = 0; + + for (size_t row = NUM_EMPTY_ROWS; row < df.rows(); ++row) { + SegmentInfo info = parse_segment_info(df, row, true); + if (!info.is_valid()) { + continue; + } + + RRNodeId node_id; + if (info.side == e_sw_template_dir::IPIN || info.side == e_sw_template_dir::OPIN) { + node_id = process_opin_ipin_node(info, x, y, node_lookup); + } else if (info.side == e_sw_template_dir::LEFT || info.side == e_sw_template_dir::RIGHT || info.side == e_sw_template_dir::TOP || info.side == e_sw_template_dir::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_tile_sink_nodes(int x, + int 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; + e_sw_template_dir prev_side = e_sw_template_dir::NUM_SIDES; + 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.is_valid()) { + continue; + } + RRNodeId node_id; + + if (info.side == e_sw_template_dir::IPIN) { + node_id = process_opin_ipin_node(info, x, y, node_lookup); + } else if (info.side == e_sw_template_dir::LEFT || info.side == e_sw_template_dir::RIGHT || info.side == e_sw_template_dir::TOP || info.side == e_sw_template_dir::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()) { + std::string side_str_cap = side_cell.as_string(); + std::transform(side_str_cap.begin(), side_str_cap.end(), side_str_cap.begin(), ::toupper); + info.side = get_sw_template_dir(side_str_cap); + } + 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()) { + std::string side_str_cap = side_cell.as_string(); + std::transform(side_str_cap.begin(), side_str_cap.end(), side_str_cap.begin(), ::toupper); + info.side = get_sw_template_dir(side_str_cap); + } + 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, + int x, + int y, + const std::unordered_map& node_lookup) const { + VTR_ASSERT(info.side == e_sw_template_dir::OPIN || info.side == e_sw_template_dir::IPIN); + e_rr_type node_type = (info.side == e_sw_template_dir::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, + int x, + int y, + const std::unordered_map& node_lookup, + int& prev_seg_index, + e_sw_template_dir& prev_side, + std::string& prev_seg_type, + int& prev_ptc_number, + bool is_vertical) const { + 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); + VTR_ASSERT(direction == Direction::INC || direction == Direction::DEC); + + // Calculate segment coordinates + int 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) ? 0 : 1; + seg_index += (direction == Direction::DEC) ? 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(get_rr_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_LOGV(verbosity_ > 1, "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, + int x, + int y, + int& x_low, + int& x_high, + int& y_low, + int& 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 e_sw_template_dir::LEFT: + x_high = x + (seg_length - tap); + x_low = x - (tap - 1); + y_high = y; + y_low = y; + break; + case e_sw_template_dir::RIGHT: + x_high = x + tap; + x_low = x + tap + 1 - seg_length; + y_high = y; + y_low = y; + break; + case e_sw_template_dir::TOP: + x_high = x; + x_low = x; + y_high = y + tap; + y_low = y + 1 - seg_length + tap; + break; + case e_sw_template_dir::BOTTOM: + x_high = x; + x_low = x; + y_high = y + seg_length - tap; + y_low = y - tap + 1; + break; + default: + x_high = x; + x_low = x; + y_high = y; + y_low = y; + break; + } + } else { + switch (info.side) { + case e_sw_template_dir::LEFT: + x_high = x + tap - 1; + x_low = x - seg_length + tap; + y_high = y; + y_low = y; + break; + case e_sw_template_dir::RIGHT: + x_high = x + seg_length; + x_low = x + 1; + y_high = y; + y_low = y; + break; + case e_sw_template_dir::TOP: + x_high = x; + x_low = x; + y_high = y + seg_length; + y_low = y + 1; + break; + case e_sw_template_dir::BOTTOM: + x_high = x; + x_low = x; + y_high = y; + y_low = y - seg_length + tap; + break; + default: + x_high = x; + x_low = x; + y_high = y; + y_low = y; + break; + } + } + + // Calculate truncation + truncated = (std::max(x_low, 1) - x_low) - (x_high - std::min(x_high, fpga_grid_x_)); + truncated += (std::max(y_low, 1) - y_low) - (y_high - std::min(y_high, fpga_grid_y_)); + + // Apply grid boundaries + x_low = std::max(x_low, 1); + y_low = std::max(y_low, 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(e_sw_template_dir side, + bool is_vertical) const { + if (is_vertical) { + return (side == e_sw_template_dir::RIGHT || side == e_sw_template_dir::TOP) ? Direction::DEC + : Direction::INC; + } else { + return (side == e_sw_template_dir::RIGHT || side == e_sw_template_dir::TOP) ? Direction::INC + : Direction::DEC; + } +} + +std::string CRRConnectionBuilder::get_segment_type_label(e_sw_template_dir side) const { + return (side == e_sw_template_dir::LEFT || side == e_sw_template_dir::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) { + 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 { + VTR_ASSERT(direction == Direction::INC); + 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_) { + // 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 { + return -1; + } +} + +bool CRRConnectionBuilder::is_valid_grid_location(int x, + int 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_LOGV(verbosity_ > 1, "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..2c9cd83b349 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_connection_builder.h @@ -0,0 +1,153 @@ +#pragma once + +/** + * @file crr_connection_builder.h + * @brief Implements functions used to retrieve source and sink Node IDs + * which should be connected together based on switch block templates. + */ + +#include "rr_graph_view.h" +#include "physical_types.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 + */ +class CRRConnectionBuilder { + public: + CRRConnectionBuilder(const RRGraphView& rr_graph, + const NodeLookupManager& node_lookup, + const SwitchBlockManager& sb_manager, + const int verbosity); + + /** + * @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(int fpga_grid_x, + int fpga_grid_y, + bool is_annotated); + + /** + * @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; + + private: + // Info from config + int fpga_grid_x_; + int fpga_grid_y_; + bool is_annotated_; + + // Dependencies + const RRGraphView& rr_graph_; + const NodeLookupManager& node_lookup_; + const SwitchBlockManager& sb_manager_; + int verbosity_; + + // Processing state + std::atomic processed_locations_{0}; + size_t total_locations_{0}; + + // Connection building methods + std::vector build_connections_for_location(size_t x, + size_t y) const; + + // Node processing methods + std::map get_tile_source_nodes(int x, + int y, + const DataFrame& df, + const std::unordered_map& node_lookup) const; + + std::map get_tile_sink_nodes(int x, + int 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 { + e_sw_template_dir side; + std::string seg_type; + int seg_index; + int tap; + + SegmentInfo() + : side(e_sw_template_dir::NUM_SIDES) + , seg_index(-1) + , tap(-1) {} + SegmentInfo(e_sw_template_dir s, const std::string& type, int index, int t = 1) + : side(s) + , seg_type(type) + , seg_index(index) + , tap(t) {} + bool is_valid() const { + return side != e_sw_template_dir::NUM_SIDES; + } + }; + + SegmentInfo parse_segment_info(const DataFrame& df, size_t row_or_col, bool is_vertical) const; + + RRNodeId process_opin_ipin_node(const SegmentInfo& info, + int x, + int y, + const std::unordered_map& node_lookup) const; + + RRNodeId process_channel_node(const SegmentInfo& info, + int x, + int y, + const std::unordered_map& node_lookup, + int& prev_seg_index, + e_sw_template_dir& 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, + int x, + int y, + int& x_low, + int& x_high, + int& y_low, + int& y_high, + int& physical_length, + int& truncated, + bool is_vertical) const; + + Direction get_direction_for_side(e_sw_template_dir side, bool is_vertical) const; + std::string get_segment_type_label(e_sw_template_dir 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(int x, int 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..c1f1b3a8ea5 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.cpp @@ -0,0 +1,106 @@ +#include "crr_edge_builder.h" +#include "globals.h" + +#include "physical_types.h" +#include "crr_connection_builder.h" + +/** + * @brief Get the name of a CRR switch + * @param delay_ps Delay in picoseconds + * @return Name of the switch + */ +static std::string get_crr_switch_name(const int delay_ps) { + if (delay_ps == 0) { + return "sw_zero"; + } else { + return "sw_" + std::to_string(delay_ps); + } +} + +/** + * @brief Create an architecture switch for CRR + * @param delay_ps Delay in picoseconds + * @param sw_template_id Template ID of the switch + * @return CRR switch + */ +static t_arch_switch_inf create_crr_switch(const int delay_ps, const std::string& sw_template_id) { + std::string switch_name = get_crr_switch_name(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, static_cast(delay_ps) * 1e-12); + 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; + arch_switch_inf.template_id = sw_template_id; + + return arch_switch_inf; +} + +/** + * @brief Find or create a CRR switch ID + * @param delay_ps Delay in picoseconds + * @param sw_template_id Template ID of the switch + * @return CRR switch ID + */ +static RRSwitchId find_or_create_crr_switch_id(const int delay_ps, + const std::string& sw_template_id, + const int verbosity) { + + std::map& all_sw_inf = g_vpr_ctx.mutable_device().all_sw_inf; + + int found_sw_id = -1; + + // Iterate over map entries (O(n), no accidental inserts) + for (const auto& [sw_id, sw_inf] : all_sw_inf) { + if (sw_inf.template_id == sw_template_id) { + found_sw_id = sw_id; + break; + } + } + + if (found_sw_id == -1) { + t_arch_switch_inf new_arch_switch_inf = create_crr_switch(delay_ps, sw_template_id); + + found_sw_id = static_cast(all_sw_inf.size()); + all_sw_inf.emplace(found_sw_id, std::move(new_arch_switch_inf)); + + VTR_LOGV(verbosity > 1, "Created new CRR switch: delay=%d ps, template id=%s\n", delay_ps, sw_template_id.c_str()); + } + + return RRSwitchId(found_sw_id); +} + +void build_crr_gsb_edges(RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const RRGSB& rr_gsb, + const crrgenerator::CRRConnectionBuilder& connection_builder, + const int verbosity) { + 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; + int delay_ps = connection.delay_ps(); + // If the delay is -1, it means the switch type should be determined from the switches defined in the architecture file. + if (delay_ps == -1) { + rr_switch_id = rr_node_driver_switches[connection.sink_node()]; + } else { + rr_switch_id = find_or_create_crr_switch_id(delay_ps, + connection.sw_template_id(), + verbosity); + } + VTR_ASSERT(rr_switch_id != RRSwitchId::INVALID()); + rr_graph_builder.create_edge_in_cache(connection.src_node(), + connection.sink_node(), + rr_switch_id, + false); + } +} 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..772824f414d --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_edge_builder.h @@ -0,0 +1,28 @@ +#pragma once + +/** + * @file crr_edge_builder.h + * @brief Builds edges for CRR graphs + * + * This file contains the main function to be called from RR Graph Generator to build CRR edges. + */ + +#include "vtr_vector.h" +#include "rr_gsb.h" +#include "rr_graph_builder.h" + +#include "crr_connection_builder.h" + +/** + * @brief Builds edges for a CRR GSB + * @param rr_graph_builder The RR graph builder + * @param rr_node_driver_switches The driver switches for each RR node + * @param rr_gsb The GSB for which edges are to be built + * @param connection_builder The connection builder to use to get the connections at each tile + * @param verbosity The verbosity level of the log + */ +void build_crr_gsb_edges(RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const RRGSB& rr_gsb, + const crrgenerator::CRRConnectionBuilder& connection_builder, + const int verbosity); 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..628c5b59a10 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_pattern_matcher.h @@ -0,0 +1,176 @@ +#pragma once + +/** + * @file crr_pattern_matcher.h + * @brief Helper class used by the CRR Generator to determine which switch block pattern + * should be applied to each tile. It does so by finding the first matching pattern + * for each location. + */ + +#include +#include +#include +#include +#include + +namespace crrgenerator { + +/** + * @brief Helper class used by the CRR Generator to determine which switch block pattern + * should be applied to each tile. It does so by finding the first matching pattern + * for each location. + */ +class CRRPatternMatcher { + 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 (step == 0) return false; + + 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..7770ec46f47 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.cpp @@ -0,0 +1,165 @@ +#include + +#include +#include + +#include + +#include "crr_switch_block_manager.h" +#include "crr_pattern_matcher.h" + +#include "vtr_log.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) + "_"; +} + +SwitchBlockManager::SwitchBlockManager(const std::string& sb_maps_file, + const std::string& sb_templates_dir, + const int log_verbosity) + : log_verbosity_(log_verbosity) { + VTR_LOG("Initializing SwitchBlockManager with maps file: %s\n", sb_maps_file.c_str()); + + // Load YAML configuration + 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 sw_template_file = item.second.as(); + if (item.second.IsNull()) { + sw_template_file = ""; + } + + std::string full_path = std::filesystem::path(sb_templates_dir) / sw_template_file; + if (sw_template_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)) { + VTR_LOGV(log_verbosity_ > 1, "Attempting to read switch template file: %s\n", full_path.c_str()); + DataFrame df = processor_.read_csv(full_path); + processor_.process_dataframe(df, + NUM_EMPTY_ROWS, + NUM_EMPTY_COLS); + file_cache_[full_path] = std::move(df); + VTR_LOGV(log_verbosity_ > 1, "Processed %zu connections in %s file\n", + file_cache_[full_path].connections, + std::filesystem::path(full_path).filename().string().c_str()); + } else { + VTR_LOG_ERROR("Required switch template 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]; + } + } + + print_statistics(); +} + +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(size_t x, size_t y) const { + std::string sw_name = get_switch_block_name(x, y); + for (const std::string& pattern : ordered_switch_block_patterns_) { + if (CRRPatternMatcher::matches_pattern(sw_name, pattern)) { + return pattern; + } + } + return ""; +} + +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 switch template files: %zu\n", file_cache_.size()); + VTR_LOG("Total connections: %zu\n", get_total_connections()); + + // 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..b317139582d --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/crr_switch_block_manager.h @@ -0,0 +1,121 @@ +#pragma once + +/** + * @file crr_switch_block_manager.h + * @brief Manages switch block configurations and switch template file processing + * + * This class handles reading YAML configuration files, processing switch template files + * containing switch block data, and managing switch block patterns. + */ + +#include +#include + +#include + +#include "data_frame_processor.h" + +namespace crrgenerator { + +/** + * @brief Manages switch block configurations and switch template file processing + * + * This class handles reading YAML configuration files, processing switch template files + * containing switch block data, and managing switch block patterns. + */ +class SwitchBlockManager { + public: + /** + * @brief Constructor + * @param log_verbosity Verbosity level for logging messages + * @param sb_maps_file Path to the YAML configuration file + * @param sb_templates_dir Directory containing switch template files + */ + SwitchBlockManager(const std::string& sb_maps_file, + const std::string& sb_templates_dir, + const int log_verbosity); + + /** + * @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 for the switch block at the given location + * @param x X coordinate of the switch block location + * @param y Y coordinate of the switch block location + * @return Matching pattern or empty string if no match + */ + std::string find_matching_pattern(size_t x, size_t y) 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; + + 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_; + + /** + * @brief Maps switch block patterns to their corresponding full file paths. + */ + std::unordered_map switch_block_to_file_; + + /** + * @brief Maps switch block patterns to their corresponding dataframes. + */ + std::unordered_map dataframes_; + + /** + * @brief Maps full file paths to their corresponding dataframes. + */ + std::unordered_map file_cache_; + + /** + * @brief Processor for reading and processing switch block template files. + */ + DataFrameProcessor processor_; + + /** + * @brief Verbosity level for logging messages + */ + int log_verbosity_; + + // Validation + void validate_yaml_structure(const YAML::Node& root); +}; + +} // 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..535540c9e01 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.cpp @@ -0,0 +1,351 @@ +#include "data_frame_processor.h" + +#include "vtr_log.h" + +#include +#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; +} + +void 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); +} + +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..0e7b81f6c1a --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/data_frame_processor.h @@ -0,0 +1,278 @@ +#pragma once + +/** + * @file data_frame_processor.h + * @brief Simple dataframe implementation for processing switch template data + * + * This class provides functions for processing switch template files + * containing switch block configuration data. + */ + +#include +#include +#include + +#include "crr_common.h" +namespace crrgenerator { + +/** + * @brief Represents a cell in the dataframe + */ +struct Cell { + using Value = std::variant; + + Value value; + + Cell() = default; + explicit Cell(const std::string& v) + : value(v) {} + explicit Cell(int64_t v) + : value(v) {} + + bool is_empty() const { + return std::holds_alternative(value); + } + + bool is_string() const { + return std::holds_alternative(value); + } + + bool is_integer() const { + return std::holds_alternative(value); + } + + bool is_number() const { + return is_integer(); + } + + int64_t as_int() const { + if (auto p = std::get_if(&value)) { + return *p; + } + if (auto p = std::get_if(&value)) { + return std::stoll(*p); + } + return 0; + } + + std::string as_string() const { + if (auto p = std::get_if(&value)) { + return *p; + } + if (auto p = std::get_if(&value)) { + return std::to_string(*p); + } + return {}; + } +}; + +/** + * @brief Simple dataframe implementation for processing switch template data + * + * This class provides functions for processing switch template files + * containing switch block configuration data. + */ +class DataFrame { + public: + DataFrame() = default; + DataFrame(size_t rows, size_t cols); + + // Access methods + /** + * @brief Get a cell at a specific row and column + * @param row Row index + * @param col Column index + * @return Cell at the specified row and column + */ + 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 + /** + * @brief Get the number of rows in the dataframe + * @return Number of rows + */ + size_t rows() const { return rows_; } + /** + * @brief Get the number of columns in the dataframe + * @return Number of columns + */ + size_t cols() const { return cols_; } + /** + * @brief Get the shape of the dataframe + * @return Pair of rows and columns + */ + std::pair shape() const { return {rows_, cols_}; } + + // Resize operations + /** + * @brief Resize the dataframe + * @param rows Number of rows + * @param cols Number of columns + */ + void resize(size_t rows, size_t cols); + /** + * @brief Clear the dataframe + */ + void clear(); + + // Row/column operations + /** + * @brief Get a row from the dataframe + * @param row Row index + * @return Row of cells + */ + std::vector get_row(size_t row) const; + /** + * @brief Get a column from the dataframe + * @param col Column index + * @return Column of cells + */ + std::vector get_column(size_t col) const; + /** + * @brief Set a row in the dataframe + */ + void set_row(size_t row, const std::vector& values); + /** + * @brief Set a column in the dataframe + * @param col Column index + * @param values Values to set + */ + void set_column(size_t col, const std::vector& values); + + // Statistics and counting + /** + * @brief Count the number of non-empty cells in the dataframe + * @return Number of non-empty cells + */ + size_t count_non_empty() const; + /** + * @brief Count the number of non-empty cells in a range of the dataframe + * @param start_row Start row index + * @param start_col Start column index + * @param end_row End row index + * @param end_col End column index + * @return Number of non-empty cells in the range + */ + 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 switch template files and converts them to DataFrames + * + * This class handles reading switch template files and performing the cell merging + * operations similar to the Python pandas functionality. + */ +class DataFrameProcessor { + public: + DataFrameProcessor() = default; + + /** + * @brief Read switch template file and create DataFrame + * @param filename Path to switch template file + * @return DataFrame containing the switch template 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 + */ + void 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: + // switch template 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..1d970e81cfc --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.cpp @@ -0,0 +1,130 @@ +#include "node_lookup_manager.h" + +#include "rr_graph_utils.h" + +#include "vtr_log.h" +#include "vtr_assert.h" +#include "vtr_time.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) { + vtr::ScopedStartFinishTimer timer("Initialize NodeLookupManager"); + + // Make sure lookup is not initialized + VTR_ASSERT(column_lookup_.empty() && row_lookup_.empty()); + + // 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, + 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..4b13dee2200 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/crr_generator/node_lookup_manager.h @@ -0,0 +1,85 @@ +#pragma once + +/** + * @file node_lookup_manager.h + * @brief Manages efficient node lookup and indexing for CRR graphs + * + * This class creates spatial indexes for nodes to enable fast lookup + * by coordinates, type, and other attributes. + * @note Eventually, this class should be removed and the lookup already created for + * RR graph should be used. + */ + +#include "rr_graph_fwd.h" +#include "rr_graph_view.h" + +#include "crr_common.h" + +namespace crrgenerator { + +/** + * @brief Manages efficient node lookup and indexing for CRR graphs + * + * This class creates spatial indexes for nodes to enable fast lookup + * by coordinates, type, and other attributes. + * @note Eventually, this class should be removed and the lookup already created for + * RR graph should be used. + */ +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 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..c5001f46920 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,16 @@ #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 + * + * @note This is a very expensive operation, so it should be called only when necessary + */ +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 +83,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, @@ -88,6 +101,7 @@ void build_tileable_unidir_rr_graph(const std::vector& typ const bool& opin2all_sides, const bool& concat_wire, const bool& wire_opposite_side, + const int route_verbosity, int* Warnings) { vtr::ScopedStartFinishTimer timer("Build tileable routing resource graph"); @@ -248,6 +262,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, @@ -256,7 +271,8 @@ void build_tileable_unidir_rr_graph(const std::vector& typ perimeter_cb, opin2all_sides, concat_wire, wire_opposite_side, - delayless_rr_switch); + delayless_rr_switch, + route_verbosity); // Build direction connection lists // TODO: use tile direct builder @@ -275,6 +291,13 @@ 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 requested, remove dangling chan nodes from the rr graph. This is done + // before allocating and loading routing resource switches since removing these nodes + // may affect the fanin of the switches. + 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 +333,50 @@ 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; + + // Iterate over chan nodes and find dangling ones + 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); + + // After removing dangling chan nodes, we need to update the node lookup + // since some nodes may have been removed, and the node ids have changed + 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); + } + + // Update other data structures related to lookup after removing dangling chan nodes + 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); + } + } + + // Initialize the fanin of the rr graph since the fan-in of nodes connected to + // remove nodes is changed + 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..f9ef7a0ed86 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, @@ -33,6 +34,7 @@ void build_tileable_unidir_rr_graph(const std::vector& typ const bool& opin2all_sides, const bool& concat_wire, const bool& wire_opposite_side, + const int route_verbosity, int* Warnings); #endif 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..5459af5455d 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,8 @@ #include "tileable_rr_graph_gsb.h" #include "tileable_rr_graph_edge_builder.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 +106,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, @@ -121,7 +124,8 @@ void build_rr_graph_edges(const RRGraphView& rr_graph, const bool& opin2all_sides, const bool& concat_wire, const bool& wire_opposite_side, - const RRSwitchId& delayless_switch) { + const RRSwitchId& delayless_switch, + const int route_verbosity) { if (!vib_grid.is_empty()) { build_rr_graph_vib_edges(rr_graph, @@ -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, @@ -155,7 +160,8 @@ void build_rr_graph_edges(const RRGraphView& rr_graph, perimeter_cb, opin2all_sides, concat_wire, - wire_opposite_side); + wire_opposite_side, + route_verbosity); } } @@ -294,7 +300,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, @@ -310,7 +317,10 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, const bool& perimeter_cb, const bool& opin2all_sides, const bool& concat_wire, - const bool& wire_opposite_side) { + const bool& wire_opposite_side, + const int route_verbosity) { + vtr::ScopedStartFinishTimer timer("Build RR Graph Edges"); + 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,10 +328,30 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, vtr::Point gsb_range(grids.width() - 1, grids.height() - 1); + // Building CRR Graph + std::unique_ptr crr_connection_builder; + std::unique_ptr sb_manager; + std::unique_ptr node_lookup; + if (build_crr_edges) { + sb_manager = std::make_unique(crr_opts.sb_maps, + crr_opts.sb_templates, + route_verbosity); + node_lookup = std::make_unique(rr_graph, + grids.width(), + grids.height()); + crr_connection_builder = std::make_unique(rr_graph, + *node_lookup, + *sb_manager, + route_verbosity); + crr_connection_builder->initialize(grids.width(), + grids.height(), + crr_opts.annotated_rr_graph); + } + /* Go Switch Block by Switch Block */ for (size_t ix = 0; ix <= gsb_range.x(); ++ix) { for (size_t iy = 0; iy <= gsb_range.y(); ++iy) { - //vpr_printf(TIO_MESSAGE_INFO, "Building edges for GSB[%lu][%lu]\n", ix, iy); + VTR_LOG("Building edges for GSB[%lu][%lu]\n", ix, iy); vtr::Point gsb_coord(ix, iy); /* Create a GSB object */ @@ -329,25 +359,50 @@ 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_edges(rr_graph_builder, + rr_node_driver_switches, + rr_gsb, + *crr_connection_builder, + route_verbosity); + } + } 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..afdd7e7714f 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, @@ -40,7 +41,8 @@ void build_rr_graph_edges(const RRGraphView& rr_graph, const bool& opin2all_sides, const bool& concat_wire, const bool& wire_opposite_side, - const RRSwitchId& delayless_switch); + const RRSwitchId& delayless_switch, + const int route_verbosity); void build_rr_graph_direct_connections(const RRGraphView& rr_graph, RRGraphBuilder& rr_graph_builder, @@ -78,7 +80,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, @@ -94,4 +97,5 @@ void build_rr_graph_regular_edges(const RRGraphView& rr_graph, const bool& perimeter_cb, const bool& opin2all_sides, const bool& concat_wire, - const bool& wire_opposite_side); + const bool& wire_opposite_side, + const int route_verbosity); 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 129450ebabe..2873ca2a370 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++; + } } } }