diff --git a/doc/src/Images/VIB.png b/doc/src/Images/VIB.png new file mode 100644 index 00000000000..4d84eee75e6 Binary files /dev/null and b/doc/src/Images/VIB.png differ diff --git a/doc/src/Images/bent_wires.png b/doc/src/Images/bent_wires.png new file mode 100644 index 00000000000..99e236a29c2 Binary files /dev/null and b/doc/src/Images/bent_wires.png differ diff --git a/doc/src/Images/double-level.png b/doc/src/Images/double-level.png new file mode 100644 index 00000000000..3f26df480d9 Binary files /dev/null and b/doc/src/Images/double-level.png differ diff --git a/doc/src/Images/vib_example.png b/doc/src/Images/vib_example.png new file mode 100644 index 00000000000..20a5b7177bf Binary files /dev/null and b/doc/src/Images/vib_example.png differ diff --git a/doc/src/VIB.rst b/doc/src/VIB.rst new file mode 100644 index 00000000000..521d3f877f1 --- /dev/null +++ b/doc/src/VIB.rst @@ -0,0 +1,254 @@ +.. _VIB: + +VIB Architecture +============ +The Versatile Interconnect Block (VIB) architecture adds modeling support for double-level MUX topology and bent wires. In the past, switch blocks had only one level of routing MUXes, whose inputs were driven by outputs of programmable blocks and routing tracks. Now outputs of programmable blocks can shape the first level of routing MUXes, while the inputs of second level involves the outputs of first level and other routing tracks. This can reduce the number and input sizes of routing MUXes. + +Figure 1 shows the proposed VIB architecture which is tile-based. Each tile is composed of a CLB and a VIB. Each CLB can interact with the corresponding VIB which contains all the routing programmable switches in one tile. Figure 2 shows an example of the detailed interconnect architecture in VIB. The CLB input muxes and the driving muxes of wire segments can share the same fanins. A routing path of a net with two sinks is presented red in the Figure. + +.. figure:: Images/VIB.png + :align: center + :height: 300 + + Figure 1. VIB architecture. The connections between the inputs and outputs of the LB and the routing wires are all implemented within the VIB. + +.. figure:: Images/double-level.png + :align: center + + Figure 2. Double-level MUX topology. + +Figure 3 shows the modeling for bent wires. A bent L-length wire is modeled as two segments in CHANX and CHANY respectively connected by a delayless switch. The orange and red arrows represent conterclockwise and clockwise bent wires respectively. The bent wires can connect to both bent and straight wire segments. + +.. figure:: Images/bent_wires.png + :align: center + + Figure 3. Presentation for bent wires. + +FPGA Architecture File Modification (.xml) +-------------------------- +For the original tags available for the standard FPGA architecture file see :ref:`fpga_architecture_description`. + +Modification for ```` Tag +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The content within the ```` tag consists of a group of ```` tags. +The ```` tag and its contents are described below. + +.. arch:tag:: content + + :req_param content: + The switch names and the depopulation pattern as described below. + +.. arch:tag:: int list + +.. arch:tag:: int list + +.. arch:tag:: + +For bent wires, a new content ```` is added in the ```` tag. + +.. arch:tag:: bent pattern list + + This tag describes the bent pattern for this particular wire segment. + For example, a length 4 wire has a bent pattern of ``- - U``. + A ``-`` indicates no bent at this position and a ``U`` indicates a conterclockwise bent at the position. (``D`` indicates a clockwise bent.) + + .. note:: A bent wire should remain consistent in both the x and y axes. + +New Added Top Level Tag ```` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The content within the ```` tag consists of a group of ```` tags. Different ```` tags describe the paradigms of VIB, which apply to different positions. + +.. arch:tag:: content + + :req_param name: + A unique alphanumeric name to identify this VIB type. + + :req_param pbtype_name: + The name of the block type (e.g. clb, memory) that this VIB connects to. + + .. note:: A block (e.g. clb, dsp) is connected to the VIB on its top-right side, so the input and output pins of the block should be on the top or right side. + + :req_param vib_seg_group: + The number of the segment types in this VIB. + + :req_param arch_vib_switch: + Name of the mux switch type used to drive wires in the VIB by default, and a custom switch can override this switch type for specific connections if desired. + + :req_param content: + The segment groups and the multistage MUX topology as described below. + +The ``content`` of ```` tag consists of several ```` tags and a ```` tag. +For example: + +.. code-block:: xml + + + + + + + + + + ... + + + ... + + + + + ... + + + +.. arch:tag:: + + :req_param name: + The name of the segment in this VIB described in ````. + + :req_param track_nums: + The track number of the segment in this VIB. + + .. note:: When using unidirectional segments, the track number of the segment represents the number for one direction. For example, the ``track_nums`` is ``10``, which means total ``20`` tracks of the segment in the channel for both (INC & DEC) directions. + +.. arch:tag:: content + + :req_param content: + The detailed information for first and second MUXes. + +The ``content`` of ```` tag consists of a ```` tag and a ```` tag. + +.. arch:tag:: content + + :req_param switch_name: + Name of the mux switch type used to drive first stage MUXes in the VIB. + + :req_param content: + The details of each MUX. + +The ``content`` of ```` tag consists of many ```` tags. + +.. arch:tag:: content + + :req_param name: + Name of the MUX. + + :req_param content: + A ```` tag to describe what pins and wires connect to this MUX. + +For example: + +.. code-block:: xml + + + + clb.O[0] clb.O[1:3] clb.O[4] + + + L1.E1 L1.S1 L2.E0 + + ... + + +The ```` tag in ```` describes nodes that connects to the MUX. ``clb.O[*]`` means output pin(s); ``L1.E1`` means the track ``1`` in the ``East`` direction of ``L1`` segment. + +.. arch:tag:: content + + :req_param content: + The details of each MUX. + +The ``content`` of ```` tag consists of many ```` tags. + +.. arch:tag:: content + + :req_param name: + Name of the MUX. + + :req_param content: + A ```` tag to describe where this MUX connect to and a ```` tag to describe what pins and wires connect to this MUX. + +For example: + +.. code-block:: xml + + + + clb.I[0] + clb.O[4] f_mux_0 f_mux_1 + + + L1.E1 + L1.S2 f_mux_0 f_mux_1 + + ... + + +The ```` tag describes the node this MUX connects to. ``clb.I[*]`` means input pin(s); ``L1.E1`` means the track ``1`` in the ``East`` direction of ``L1`` segment. The ```` tag in ```` describes nodes that connects to the MUX. ``clb.O[*]`` means output pin(s); ``L1.S2`` means the track ``2`` in the ``South`` direction of ``L1`` segment. ``f_mux_0`` means the name of the specific first stage MUX. + +Here is a complete example of the ```` tag: + +.. code-block:: xml + + + + + + + + clb.O[0] clb.O[1:3] clb.O[4] + + + L1.E1 L1.S1 L2.E0 + + + + + clb.I[0] + clb.O[4] f_mux_0 f_mux_1 + + + L1.E1 + L1.S2 f_mux_0 f_mux_1 + + + + + +Its corresponding detailed architecture is shown in Figure 4. + +.. figure:: Images/vib_example.png + :align: center + :height: 600 + + Figure 4. The corresponding detaied architecture of the example. + +New Added Top Level Tag ```` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Content inside this tag specifies VIB grid layout to describe different VIBs applied on different locations. + +.. arch:tag:: content + + :req_param name: + The name identifying this VIB grid layout. It should be the same as the corresponding layout name inside the ```` tag. + + :req_param content: + The content should contain a set of grid location tags. For grid location tags of vib_layout see :ref:`fpga_architecture_description`; ref:`grid_expressions` + +For example: + +.. code-block:: xml + + + + + + + ... + + + +In this VIB grid layout, ``perimeter``, ``fill``, ``col`` and so on are tags in original ```` tag to describe positions of each type of VIB block. The attribute ``type`` should correspond to the ``name`` of a ```` tag in ````. +Besides, the ``pbtype_name`` of corresponding ```` must be the same as the physical block type at this position. + +In this example, IO blocks are located on the perimeter of the layout. Memory blocks are on column 5 and CLBs are on the rest positions. The ``vib_io``, ``vib_clb`` and ``vib_memory`` are different types of vib blocks corresponding to IO, CLB and memory blocks respectively. diff --git a/doc/src/arch/concat_pass_wire.svg b/doc/src/arch/concat_pass_wire.svg new file mode 100644 index 00000000000..ccebac06be5 --- /dev/null +++ b/doc/src/arch/concat_pass_wire.svg @@ -0,0 +1,574 @@ + + + + + + + + + + + + + + + + + concat_pass_wire + + base + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wires + + + + + + + + + + + + + + + legend + + + (a) + + + + + + + + + + + + + Starting routing tracks + + + + + + + + + Ending routing tracks + + + + + + + + + Passing routing tracks + + + + + + + + + Output pin of blocks + + + + + (b) + + + + + diff --git a/doc/src/arch/concat_wire.svg b/doc/src/arch/concat_wire.svg new file mode 100644 index 00000000000..bb01dd6d5e0 --- /dev/null +++ b/doc/src/arch/concat_wire.svg @@ -0,0 +1,580 @@ + + + + + + + + + + + + + + + + + concat_wire + + base + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wires + + + + + + + + + + + + + + + + + + + + + legend + + + (a) + + + + + + + + + + + + + Starting routing tracks + + + + + + + + + Ending routing tracks + + + + + + + + + Passing routing tracks + + + + + + + + + Output pin of blocks + + + + + (b) + + + + + diff --git a/doc/src/arch/ecb.png b/doc/src/arch/ecb.png new file mode 100644 index 00000000000..5a6afd99c2d Binary files /dev/null and b/doc/src/arch/ecb.png differ diff --git a/doc/src/arch/ecb_allowed_direct_connection.png b/doc/src/arch/ecb_allowed_direct_connection.png new file mode 100644 index 00000000000..35dddf336ae Binary files /dev/null and b/doc/src/arch/ecb_allowed_direct_connection.png differ diff --git a/doc/src/arch/ecb_allowed_direct_connection_inner_tile_example.png b/doc/src/arch/ecb_allowed_direct_connection_inner_tile_example.png new file mode 100644 index 00000000000..30db9d640aa Binary files /dev/null and b/doc/src/arch/ecb_allowed_direct_connection_inner_tile_example.png differ diff --git a/doc/src/arch/ecb_allowed_direct_connection_inter_tile_example.png b/doc/src/arch/ecb_allowed_direct_connection_inter_tile_example.png new file mode 100644 index 00000000000..d3baa9ee37f Binary files /dev/null and b/doc/src/arch/ecb_allowed_direct_connection_inter_tile_example.png differ diff --git a/doc/src/arch/ecb_forbid_direct_connection_example.png b/doc/src/arch/ecb_forbid_direct_connection_example.png new file mode 100644 index 00000000000..ba936335a0e Binary files /dev/null and b/doc/src/arch/ecb_forbid_direct_connection_example.png differ diff --git a/doc/src/arch/opin2all_sides.svg b/doc/src/arch/opin2all_sides.svg new file mode 100644 index 00000000000..dff4b12228c --- /dev/null +++ b/doc/src/arch/opin2all_sides.svg @@ -0,0 +1,592 @@ + + + + + + + + + + + + + + + + + opin2all_sides + + base + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + wires + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + legend + + + (a) + + + + + + + + + + + + + Starting routing tracks + + + + + + + + + Ending routing tracks + + + + + + + + + Passing routing tracks + + + + + + + + + Output pin of blocks + + + + + (b) + + + + + diff --git a/doc/src/arch/perimeter_cb.png b/doc/src/arch/perimeter_cb.png new file mode 100644 index 00000000000..e3a65833866 Binary files /dev/null and b/doc/src/arch/perimeter_cb.png differ diff --git a/doc/src/arch/point2point_example.png b/doc/src/arch/point2point_example.png new file mode 100644 index 00000000000..a63df9d3880 Binary files /dev/null and b/doc/src/arch/point2point_example.png differ diff --git a/doc/src/arch/point2point_truthtable.png b/doc/src/arch/point2point_truthtable.png new file mode 100644 index 00000000000..d81bab33875 Binary files /dev/null and b/doc/src/arch/point2point_truthtable.png differ diff --git a/doc/src/arch/reference.rst b/doc/src/arch/reference.rst index 9a800c9bf7a..6cdb81131b9 100644 --- a/doc/src/arch/reference.rst +++ b/doc/src/arch/reference.rst @@ -2339,6 +2339,7 @@ Additional power options are specified within the ```` level ``Other value! +.. _openfpga_arch_syntax: + +Additional Syntax for Tileable Architecture +------------------------------------------- + +When tileable architecture is enabled, the following options are available in the architecture file: + +Layout +~~~~~~ + +```` may include additioinal attributes to enable tileable routing resource graph generation + +.. option:: tileable="" + + Turn ``on``/ ``off`` the tileable routing resource graph generator. + + The tileable routing architecture can minimize the number of unique modules in FPGA fabric to be physically implemented. + + Technical details can be found in :cite:`XTang_FPT_2019`. + + .. note:: It is strongly recommended to enable the tileable routing architecture when you want to PnR large FPGA fabrics, which can effectively reduce the runtime. + +.. option:: through_channel="" + + Allow routing channels to pass through multi-width and multi-height programmable blocks. This is mainly used in heterogeneous FPGAs to increase routability, as illustrated in :numref:`fig_thru_channel`. + By default, it is ``false``. + + .. _fig_thru_channel: + + .. figure:: thru_channel.png + :width: 100% + :alt: Impact of through channel + + Impact on routing architecture when through channel in multi-width and multi-height programmable blocks: (a) disabled; (b) enabled. + + .. warning:: Do NOT enable ``through_channel`` if you are not using the tileable routing resource graph generator! + + .. warning:: You cannot use ``spread`` pin location for the ``height > 1`` or ``width >1`` tiles when using the tileable routing resource graph. Otherwise, it will cause undriven pins in your device!!! + +.. option:: shrink_boundary="" + + Remove all the routing wires in empty regions. This is mainly used in non-rectangular FPGAs to avoid redundant routing wires in blank area, as illustrated in :numref:`fig_shrink_boundary`. + By default, it is ``false``. + + .. _fig_shrink_boundary: + + .. figure:: shrink_boundary.png + :width: 100% + :alt: Impact of shrink boundary + + Impact on routing architecture when shrink-boundary: (a) disabled; (b) enabled. + + .. warning:: Do NOT enable ``shrink_boundary`` if you are not using the tileable routing resource graph generator! + +.. option:: perimeter_cb="" + + Allow connection blocks to appear around the blocks on the perimeter of the device (mainly I/Os). This is designed to enhance routability of I/Os on perimeter. It is also strongly recommended when a programmable clock network is required to touch clock pins on I/Os. As illustrated in :numref:`fig_perimeter_cb`, routing tracks can access three sides of each I/O when perimeter connection blocks are created. + + **Default:** ``false`` + +.. warning:: When enabled, please only place outputs at one side of I/Os. For example, outputs of an I/O on the top side can only occur on the bottom side of the I/O tile. Otherwise, routability loss may be expected, leading to some pins being unreachable. Enable the ``opin2all_sides`` to recover routability loss. + + .. _fig_perimeter_cb: + + .. figure:: perimeter_cb.png + :width: 100% + :alt: Impact of perimeter_cb + + Impact on routing architecture when perimeter connection blocks are : (a) disabled; (b) enabled. + + .. warning:: Do NOT enable ``perimeter_cb`` if you are not using the tileable routing resource graph generator! + +.. option:: opin2all_sides="" + + Allow each output pin of a programmable block to drive the routing tracks on all the sides of its adjacent switch block (see an illustrative example in :numref:`fig_opin2all_sides`). This can improve the routability of an FPGA fabric with an increase in the sizes of routing multiplexers in each switch block. + + **Default:** ``false`` + + .. _fig_opin2all_sides: + + .. figure:: opin2all_sides.svg + :width: 100% + :alt: Impact of opin2all_sides + + Impact on routing architecture when the opin-to-all-sides: (a) disabled; (b) enabled. + + .. warning:: Do NOT enable ``opin2all_sides`` if you are not using the tileable routing resource graph generator! + +.. option:: concat_wire="" + + In each switch block, allow each routing track which ends to drive another routing track on the opposite side, as such a wire can be continued in the same direction (see an illustrative example in :numref:`fig_concat_wire`). In other words, routing wires can be concatenated in the same direction across an FPGA fabric. This can improve the routability of an FPGA fabric with an increase in the sizes of routing multiplexers in each switch block. + + **Default:** ``false`` + + .. _fig_concat_wire: + + .. figure:: concat_wire.svg + :width: 100% + :alt: Impact of concat_wire + + Impact on routing architecture when the wire concatenation: (a) disabled; (b) enabled. + + .. warning:: Do NOT enable ``concat_wire`` if you are not using the tileable routing resource graph generator! + +.. option:: concat_pass_wire="" + + In each switch block, allow each routing track which passes to drive another routing track on the opposite side, as such a pass wire can be continued in the same direction (see an illustrative example in :numref:`fig_concat_pass_wire`). This can improve the routability of an FPGA fabric with an increase in the sizes of routing multiplexers in each switch block. + + **Default:** ``false`` + + .. _fig_concat_wire: + + .. figure:: concat_pass_wire.svg + :width: 100% + :alt: Impact of concat_pass_wire + + Impact on routing architecture when the pass wire concatenation: (a) disabled; (b) enabled. + + .. warning:: Do NOT enable ``concat_pass_wire`` if you are not using the tileable routing resource graph generator! + +A quick example to show tileable routing is enabled, other options, e.g., through channels are disabled: + +.. code-block:: xml + + + + +Switch Block +~~~~~~~~~~~~ + +```` may include addition syntax to enable different connectivity for pass tracks + +.. option:: sub_type="" + + Connecting type for pass tracks in each switch block. + The supported connecting patterns are ``subset``, ``universal`` and ``wilton``, being the same as the capability of VPR. + If not specified, the pass tracks will have the same connecting patterns as start/end tracks, which are defined in ``type`` + +.. option:: sub_Fs="" + + Connectivity parameter for pass tracks in each switch block. Must be a multiple of 3. + If not specified, the pass tracks will have the same connectivity as start/end tracks, which are defined in ``fs``. + +A quick example which defines a switch block + - Starting/ending routing tracks are connected in the ``wilton`` pattern + - Each starting/ending routing track can drive 3 other starting/ending routing tracks + - Passing routing tracks are connected in the ``subset`` pattern + - Each passing routing track can drive 6 other starting/ending routing tracks + +.. code-block:: xml + + + + + +Routing Segments +~~~~~~~~~~~~~~~~ + +When using tileable architecture, it is strongly recommended to give explicit names +for each routing segment in ````. +This is used to link ``circuit_model`` to routing segments. + +A quick example which defines a length-4 uni-directional routing segment called ``L4`` : + +.. code-block:: xml + + + + + +.. note:: Currently, tileable architecture only supports uni-directional routing architectures. + +Direct Interconnect +~~~~~~~~~~~~~~~~~~~ + +This section introduces extensions on the architecture description file about direct connections between programmable blocks. + +Syntax +~~~~~~ + +The original direct connections in the directlist section are documented in :ref:`direct_interconnect`. Its description is given below: + +.. code-block:: xml + + + + + +.. note:: These options are required + +In the tileable architecture file, you may define additional attributes for each VPR's direct connection: + +.. code-block:: xml + + + + + +.. note:: these options are optional. However, if ``interconnection_type`` is set to ``inter_column`` or ``inter_row``, then ``x_dir`` and ``y_dir`` are required. + +.. option:: interconnection_type="" + + Available types are ``inner_column_or_row`` | ``part_of_cb`` | ``inter_column`` | ``inter_row`` + + - ``inner_column_or_row`` indicates the direct connections are between tiles in the same column or row. This is the default value. + - ``part_of_cb`` indicates the direct connections will drive routing multiplexers in connection blocks. Therefore, it is no longer a strict point-to-point direct connection. + - ``inter_column`` indicates the direct connections are between tiles in two columns + - ``inter_row`` indicates the direct connections are between tiles in two rows + +.. note:: The following syntax is only applicable to ``inter_column`` and ``inter_row`` + +.. option:: x_dir="" + + Available directionalities are ``positive`` | ``negative``, specifies if the next cell to connect has a bigger or lower ``x`` value. + Considering a coordinate system where (0,0) is the origin at the bottom left and ``x`` and ``y`` are positives: + + - x_dir="positive": + + - interconnection_type="inter_column": a column will be connected to a column on the ``right``, if it exists. + + - interconnection_type="inter_row": the most on the ``right`` cell from a row connection will connect the most on the ``left`` cell of next row, if it exists. + + - x_dir="negative": + + - interconnection_type="inter_column": a column will be connected to a column on the ``left``, if it exists. + + - interconnection_type="inter_row": the most on the ``left`` cell from a row connection will connect the most on the ``right`` cell of next row, if it exists. + +.. option:: y_dir="" + + Available directionalities are ``positive`` | ``negative``, specifies if the next cell to connect has a bigger or lower x value. + Considering a coordinate system where (0,0) is the origin at the bottom left and `x` and `y` are positives: + + - y_dir="positive": + + - interconnection_type="inter_column": the ``bottom`` cell of a column will be connected to the next column ``top`` cell, if it exists. + + - interconnection_type="inter_row": a row will be connected on an ``above`` row, if it exists. + + - y_dir="negative": + + - interconnection_type="inter_column": the ``top`` cell of a column will be connected to the next column ``bottom`` cell, if it exists. + + - interconnection_type="inter_row": a row will be connected on a row ``below``, if it exists. + +Enhanced Connection Block +~~~~~~~~~~~~~~~~~~~~~~~~~ + +A direct connection can also drive routing multiplexers of connection blocks. When such connection occurs in a connection block, it is called an enhanced connection block. +:numref:`fig_ecb` illustrates the difference between a regular connection block and an enhanced connection block. + +.. _fig_ecb: + +.. figure:: ecb.png + + Enhanced connection block vs. Regular connection block + +In such scenario, the type ``part_of_cb`` is required. + +.. warning:: Restrictions may be applied when building the direct connections as part of a connection block. + +Direct connections can be inside a tile or across two tiles. Currently, across more than two tiles are not supported! +:numref:`fig_ecb_allowed_direct_connection` illustrates the region (in red) where any input pin is allowed to be driven by any output pin. + +.. _fig_ecb_allowed_direct_connection: + +.. figure:: ecb_allowed_direct_connection.png + + Allowed connections inside a tile for enhanced connection block (see the highlighted region) + +:numref:`fig_ecb_allowed_direct_connection_inner_tile_example` shows a few feedback connections which can be built inside connection blocks. Note that feedback connections are fully allowed between any pins on the same side of a programmable block. + +.. _fig_ecb_allowed_direct_connection_inner_tile_example: + +.. figure:: ecb_allowed_direct_connection_inner_tile_example.png + + Example of feedback connections inside a tile for enhanced connection block + +For instance, a non-tileable VPR architecture defines: + +.. code-block:: xml + + + + + + + +:numref:`fig_ecb_allowed_direct_connection_inter_tile_example` shows a few inter-tile connections which can be built inside connection blocks. Note that inter-tile connections are subjected to the restrictions depicted in :numref:`fig_ecb_allowed_direct_connection` + +.. _fig_ecb_allowed_direct_connection_inter_tile_example: + +.. figure:: ecb_allowed_direct_connection_inter_tile_example.png + + Example of connections across two tiles for enhanced connection block + +:numref:`fig_ecb_forbid_direct_connection_example` illustrates some inner-tile and inter-tile connections which are not allowed. Note that feedback connections across different sides are restricted! + +.. _fig_ecb_forbid_direct_connection_example: + +.. figure:: ecb_forbid_direct_connection_example.png + + Restrictions on building direct connections as part of a connection block + +Inter-tile Connections +~~~~~~~~~~~~~~~~~~~~~~ + +For this example, we will study a scan-chain implementation. The description could be: + +In a non-tileable architecture: + +.. code-block:: xml + + + + + +In a tileable architecture: + +.. code-block:: xml + + + + + +:numref:`fig_p2p_exple` is the graphical representation of the above scan-chain description on a 4x4 FPGA. + +.. _fig_p2p_exple: + +.. figure:: point2point_example.png + + An example of scan-chain implementation + + +In this figure, the red arrows represent the initial direct connection. The green arrows represent the point to point connection to connect all the columns of CLB. + +A point to point connection can be applied in different ways than showed in the example section. To help the designer implement their point to point connection, a truth table with our new parameters is provided below. + +:numref:`fig_p2p_trtable` provides all possible variable combination and the connection it will generate. + +.. _fig_p2p_trtable: + +.. figure:: point2point_truthtable.png + + Point to point truth table + +VIB Architecture +~~~~~~~~~~~~~~~~ + +.. include:: VIB.rst + diff --git a/doc/src/arch/shrink_boundary.png b/doc/src/arch/shrink_boundary.png new file mode 100644 index 00000000000..acbb4518f11 Binary files /dev/null and b/doc/src/arch/shrink_boundary.png differ diff --git a/doc/src/arch/thru_channel.png b/doc/src/arch/thru_channel.png new file mode 100644 index 00000000000..26ba72ac779 Binary files /dev/null and b/doc/src/arch/thru_channel.png differ diff --git a/doc/src/z_references.bib b/doc/src/z_references.bib index fc064f2c433..7c04b202c8a 100644 --- a/doc/src/z_references.bib +++ b/doc/src/z_references.bib @@ -479,3 +479,14 @@ @ARTICLE{Spindler2008_Kraftwerk2 keywords={Cost function;Central Processing Unit;Runtime;Quality control;Convergence;Computational efficiency;Integrated circuit synthesis;Stochastic processes;Circuit simulation;Bound2Bound;force-directed;half-perimeter wirelength (HPWL);Kraftwerk2;quadratic placement;Kraftwerk2;force-directed;quadratic placement;Bound2Bound;HPWL}, doi={10.1109/TCAD.2008.925783} } + +@INPROCEEDINGS{XTang_FPT_2019, + author={X. Tang and E. Giacomin and A. Alacchi and P. Gaillardon}, + booktitle={2019 International Conference on Field-Programmable Technology (ICFPT)}, + title={A Study on Switch Block Patterns for Tileable FPGA Routing Architectures}, + year={2019}, + volume={}, + number={}, + pages={247-250}, + doi={10.1109/ICFPT47387.2019.00039} +} diff --git a/libs/libarchfpga/src/arch_check.cpp b/libs/libarchfpga/src/arch_check.cpp index a008dc80cf3..ac33f234f20 100644 --- a/libs/libarchfpga/src/arch_check.cpp +++ b/libs/libarchfpga/src/arch_check.cpp @@ -168,9 +168,9 @@ bool check_leaf_pb_model_timing_consistency(const t_pb_type* pb_type, const t_ar //Annotations always put the pin in the input_pins field VTR_ASSERT(annotation.input_pins); - for (const std::string& input_pin : vtr::split(annotation.input_pins)) { + for (const std::string& input_pin : vtr::StringToken(annotation.input_pins).split(" \t\n")) { InstPort annot_port(input_pin); - for (const std::string& clock : vtr::split(annotation.clock)) { + for (const std::string& clock : vtr::StringToken(annotation.clock).split(" \t\n")) { InstPort annot_clock(clock); //Find the model port @@ -210,9 +210,9 @@ bool check_leaf_pb_model_timing_consistency(const t_pb_type* pb_type, const t_ar } else if (annotation.input_pins && annotation.output_pins) { //Combinational annotation VTR_ASSERT_MSG(!annotation.clock, "Combinational annotations should have no clock"); - for (const std::string& input_pin : vtr::split(annotation.input_pins)) { + for (const std::string& input_pin : vtr::StringToken(annotation.input_pins).split(" \t\n")) { InstPort annot_in(input_pin); - for (const std::string& output_pin : vtr::split(annotation.output_pins)) { + for (const std::string& output_pin : vtr::StringToken(annotation.output_pins).split(" \t\n")) { InstPort annot_out(output_pin); //Find the input model port diff --git a/libs/libarchfpga/src/arch_util.cpp b/libs/libarchfpga/src/arch_util.cpp index dd13f6f2cdd..88a4da2a565 100644 --- a/libs/libarchfpga/src/arch_util.cpp +++ b/libs/libarchfpga/src/arch_util.cpp @@ -39,7 +39,7 @@ const char* get_arch_file_name() { } InstPort::InstPort(const std::string& str) { - std::vector inst_port = vtr::split(str, "."); + std::vector inst_port = vtr::StringToken(str).split("."); if (inst_port.size() == 1) { instance_ = name_index(); @@ -201,7 +201,7 @@ void free_type_descriptors(std::vector& type_descriptors) } static void free_all_pb_graph_nodes(std::vector& type_descriptors) { - for (auto& type : type_descriptors) { + for (t_logical_block_type& type : type_descriptors) { if (type.pb_type) { if (type.pb_graph_head) { free_pb_graph(type.pb_graph_head); @@ -428,8 +428,8 @@ t_logical_block_type get_empty_logical_type(const char* name /*=EMPTY_BLOCK_NAME std::unordered_set get_equivalent_sites_set(t_physical_tile_type_ptr type) { std::unordered_set equivalent_sites; - for (auto& sub_tile : type->sub_tiles) { - for (auto logical_block : sub_tile.equivalent_sites) { + for (const t_sub_tile& sub_tile : type->sub_tiles) { + for (t_logical_block_type_ptr logical_block : sub_tile.equivalent_sites) { equivalent_sites.insert(logical_block); } } @@ -700,6 +700,9 @@ void ProcessMemoryClass(t_pb_type* mem_pb_type) { mem_pb_type->model_id = LogicalModelId::INVALID(); mem_pb_type->modes[0].num_interconnect = mem_pb_type->num_ports * num_pb; + + std::string error_msg = (std::stringstream() << "Memory pb_type " << mem_pb_type->name << " has no interconnect").str(); + VTR_ASSERT_MSG(mem_pb_type->modes[0].num_interconnect > 0, error_msg.c_str()); mem_pb_type->modes[0].interconnect = new t_interconnect[mem_pb_type->modes[0].num_interconnect]; for (i = 0; i < mem_pb_type->modes[0].num_interconnect; i++) { @@ -838,7 +841,7 @@ e_power_estimation_method power_method_inherited(e_power_estimation_method paren void SyncModelsPbTypes(t_arch* arch, const std::vector& Types) { - for (auto& Type : Types) { + for (const t_logical_block_type& Type : Types) { if (Type.pb_type != nullptr) { SyncModelsPbTypes_rec(arch, Type.pb_type); } @@ -974,7 +977,7 @@ void primitives_annotation_clock_match(t_pin_to_pin_annotation* annotation, } const t_segment_inf* find_segment(const t_arch* arch, std::string_view name) { - for (const auto& segment : arch->Segments) { + for (const t_segment_inf& segment : arch->Segments) { if (segment.name == name) { return &segment; } @@ -1035,7 +1038,7 @@ bool has_sequential_annotation(const t_pb_type* pb_type, const t_model_ports* po for (const t_pin_to_pin_annotation& annotation : pb_type->annotations) { InstPort annot_in(annotation.input_pins); if (annot_in.port_name() == port->name) { - for (const auto& [key, val] : annotation.annotation_entries) { + for (const auto& [key, _] : annotation.annotation_entries) { if (key == annot_type) { return true; } @@ -1048,12 +1051,12 @@ bool has_sequential_annotation(const t_pb_type* pb_type, const t_model_ports* po bool has_combinational_annotation(const t_pb_type* pb_type, std::string_view in_port, std::string_view out_port) { for (const t_pin_to_pin_annotation& annotation : pb_type->annotations) { - for (const auto& annot_in_str : vtr::split(annotation.input_pins)) { + for (const std::string& annot_in_str : vtr::StringToken(annotation.input_pins).split(" \t\n")) { InstPort in_pins(annot_in_str); - for (const auto& annot_out_str : vtr::split(annotation.output_pins)) { + for (const std::string& annot_out_str : vtr::StringToken(annotation.output_pins).split(" \t\n")) { InstPort out_pins(annot_out_str); if (in_pins.port_name() == in_port && out_pins.port_name() == out_port) { - for (const auto& [key, val] : annotation.annotation_entries) { + for (const auto& [key, _] : annotation.annotation_entries) { if (key == E_ANNOT_PIN_TO_PIN_DELAY_MAX || key == E_ANNOT_PIN_TO_PIN_DELAY_MIN) { return true; @@ -1069,7 +1072,7 @@ bool has_combinational_annotation(const t_pb_type* pb_type, std::string_view in_ void link_physical_logical_types(std::vector& PhysicalTileTypes, std::vector& LogicalBlockTypes) { - for (auto& physical_tile : PhysicalTileTypes) { + for (t_physical_tile_type& physical_tile : PhysicalTileTypes) { if (physical_tile.index == EMPTY_TYPE_INDEX) continue; auto eq_sites_set = get_equivalent_sites_set(&physical_tile); @@ -1090,7 +1093,7 @@ void link_physical_logical_types(std::vector& PhysicalTile std::sort(equivalent_sites.begin(), equivalent_sites.end(), criteria); for (t_logical_block_type& logical_block : LogicalBlockTypes) { - for (auto site : equivalent_sites) { + for (t_logical_block_type_ptr site : equivalent_sites) { if (logical_block.name == site->pb_type->name) { logical_block.equivalent_tiles.push_back(&physical_tile); break; diff --git a/libs/libarchfpga/src/echo_arch.cpp b/libs/libarchfpga/src/echo_arch.cpp index b7985e471f1..70456a8a204 100644 --- a/libs/libarchfpga/src/echo_arch.cpp +++ b/libs/libarchfpga/src/echo_arch.cpp @@ -184,7 +184,7 @@ void PrintArchInfo(FILE* Echo, const t_arch* arch) { break; } - switch (arch->SBType) { + switch (arch->sb_type) { case (WILTON): fprintf(Echo, "\tSwitch Block: type wilton fs %d\n", arch->Fs); break; diff --git a/libs/libarchfpga/src/logic_types.h b/libs/libarchfpga/src/logic_types.h index 900e91f1d9b..07dbf5ac85e 100644 --- a/libs/libarchfpga/src/logic_types.h +++ b/libs/libarchfpga/src/logic_types.h @@ -22,6 +22,18 @@ #include #include +/** + * @brief The type of the parallel axis. + */ +enum class e_parallel_axis { + /** X_AXIS: Data that describes an x-directed wire segment (CHANX) */ + X_AXIS, + /** Y_AXIS: Data that describes an y-directed wire segment (CHANY) */ + Y_AXIS, + /** BOTH_AXIS: Data that can be applied to both x-directed and y-directed wire segment */ + BOTH_AXIS +}; + /* * Logic model data types * A logic model is described by its I/O ports and function name diff --git a/libs/libarchfpga/src/parse_switchblocks.cpp b/libs/libarchfpga/src/parse_switchblocks.cpp index 512ba6ef30a..12b48c15b2c 100644 --- a/libs/libarchfpga/src/parse_switchblocks.cpp +++ b/libs/libarchfpga/src/parse_switchblocks.cpp @@ -249,8 +249,8 @@ static t_wire_switchpoints parse_wireconn_from_to_node(pugi::xml_node node, cons t_wire_switchpoints wire_switchpoints; wire_switchpoints.segment_name = get_attribute(node, "type", loc_data).value(); - auto points_str = get_attribute(node, "switchpoint", loc_data).value(); - for (const auto& point_str : vtr::split(points_str, ",")) { + std::string points_str = get_attribute(node, "switchpoint", loc_data).value(); + for (const std::string& point_str : vtr::StringToken(points_str).split(",")) { int switchpoint = vtr::atoi(point_str); wire_switchpoints.switchpoints.push_back(switchpoint); } @@ -278,7 +278,7 @@ static void parse_switchpoint_order(const char* order, SwitchPointOrder& switchp /* parses the wire types specified in the comma-separated 'ch' char array into the vector wire_points_vec. * Spaces are trimmed off */ static void parse_comma_separated_wire_types(const char* ch, std::vector& wire_switchpoints) { - std::vector types = vtr::split(ch, ","); + std::vector types = vtr::StringToken(ch).split(","); if (types.empty()) { archfpga_throw(__FILE__, __LINE__, "parse_comma_separated_wire_types: found empty wireconn wire type entry\n"); @@ -294,7 +294,7 @@ static void parse_comma_separated_wire_types(const char* ch, std::vector& wire_switchpoints) { - std::vector points = vtr::split(ch, ","); + std::vector points = vtr::StringToken(ch).split(","); if (points.empty()) { archfpga_throw(__FILE__, __LINE__, "parse_comma_separated_wire_points: found empty wireconn wire point entry\n"); } @@ -503,7 +503,7 @@ static void check_bidir_switchblock(const t_permutation_map* permutation_map) { static void check_wireconn(const t_arch* arch, const t_wireconn_inf& wireconn) { for (const t_wire_switchpoints& wire_switchpoints : wireconn.from_switchpoint_set) { - auto seg_name = wire_switchpoints.segment_name; + std::string seg_name = wire_switchpoints.segment_name; //Make sure the segment exists const t_segment_inf* seg_info = find_segment(arch, seg_name); @@ -524,7 +524,7 @@ static void check_wireconn(const t_arch* arch, const t_wireconn_inf& wireconn) { } for (const t_wire_switchpoints& wire_switchpoints : wireconn.to_switchpoint_set) { - auto seg_name = wire_switchpoints.segment_name; + std::string seg_name = wire_switchpoints.segment_name; //Make sure the segment exists const t_segment_inf* seg_info = find_segment(arch, seg_name); diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 3b9c1b31c84..c0b407b4769 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -43,6 +43,8 @@ #include "clock_types.h" #include "switchblock_types.h" +#include "vib_inf.h" + //Forward declarations struct t_clock_network; struct t_power_arch; @@ -1628,15 +1630,6 @@ struct t_chan_width_dist { t_chan chan_y_dist; }; -/* X_AXIS: Data that describes an x-directed wire segment (CHANX) * - * Y_AXIS: Data that describes an y-directed wire segment (CHANY) * - * BOTH_AXIS: Data that can be applied to both x-directed and y-directed wire segment */ -enum e_parallel_axis { - X_AXIS, - Y_AXIS, - BOTH_AXIS -}; - /** * @brief An attribute of a segment that defines the general category of the wire segment type. * @@ -1756,7 +1749,7 @@ struct t_segment_inf { * @brief Defines what axis the segment is parallel to. See e_parallel_axis * comments for more details on the values. */ - enum e_parallel_axis parallel_axis; + e_parallel_axis parallel_axis; /// A vector of booleans indicating whether the segment can connect to a logic block. std::vector cb; @@ -1764,6 +1757,24 @@ struct t_segment_inf { /// A vector of booleans indicating whether the segment can connect to a switch block. std::vector sb; + /** + * @brief This segment is bend or not + */ + bool is_bend; + + /** + * @brief The bend type of the segment, "-"-0, "U"-1, "D"-2 + * For example: bend pattern <- - U ->; corresponding bend: [0,0,1,0] + */ + std::vector bend; + + /** + * @brief Divide the segment into several parts based on bend position. + * For example: length-5 bend segment: <- - U ->; + * Corresponding part_len: [3,2] + */ + std::vector part_len; + /** * @brief The index of the segment as stored in the appropriate Segs list. * Upon loading the architecture, we use this field to keep track of the @@ -2110,18 +2121,55 @@ struct t_noc_inf { std::string noc_router_tile_name; }; -/* Detailed routing architecture */ +// Detailed routing architecture struct t_arch { - /** Stores unique strings used as key and values in tags, - * i.e. implements a flyweight pattern to save memory.*/ + /// Stores unique strings used as key and values in tags, + /// i.e. implements a flyweight pattern to save memory. mutable vtr::string_internment strings; std::vector interned_strings; /// Secure hash digest of the architecture file to uniquely identify this architecture char* architecture_id; + // Options for tileable routing architectures + // These are used for an alternative, tilable, rr-graph generator that can produce + // OpenFPGA-compatible FPGAs that can be implemented to silicon via the OpenFPGA flow + + /// Whether the routing architecture is tileable + bool tileable; + + /// Allow connection blocks to appear around the perimeter programmable block + bool perimeter_cb; + + /// Remove all the routing wires in empty regions + bool shrink_boundary; + + /// Allow routing channels to pass through multi-width and + /// multi-height programable blocks + bool through_channel; + + /// Allow each output pin of a programmable block to drive the + /// routing tracks on all the sides of its adjacent switch block + bool opin2all_sides; + + /// Whether the routing architecture has concat wire + /// For further detail, please refer to documentation + bool concat_wire; + + /// Whether the routing architecture has concat pass wire + /// For further detail, please refer to documentation + bool concat_pass_wire; + + /// Connectivity parameter for pass tracks in each switch block + int sub_fs; + + /// Connecting type for pass tracks in each switch block + enum e_switch_block_type sb_sub_type; + + // End of tileable architecture options + t_chan_width_dist Chans; - enum e_switch_block_type SBType; + enum e_switch_block_type sb_type; std::vector switchblocks; float R_minW_nmos; float R_minW_pmos; @@ -2171,21 +2219,27 @@ struct t_arch { std::vector lut_cells; std::unordered_map> lut_elements; - //The name of the switch used for the input connection block (i.e. to - //connect routing tracks to block pins). tracks can be connected to + // The name of the switch used for the input connection block (i.e. to + // connect routing tracks to block pins). tracks can be connected to // ipins through the same die or from other dice, each of these - //types of connections requires a different switch, all names should correspond to a switch in Switches. + // types of connections requires a different switch, all names should correspond to a switch in Switches. std::vector ipin_cblock_switch_name; std::vector grid_layouts; //Set of potential device layouts - //the layout that is chosen to be used with command line options - //It is used to generate custom SB for a specific locations within the device - //If the layout is not specified in the command line options, this variable will be set to "auto" + // the layout that is chosen to be used with command line options + // It is used to generate custom SB for a specific locations within the device + // If the layout is not specified in the command line options, this variable will be set to "auto" std::string device_layout; t_clock_arch_spec clock_arch; // Clock related data types /// Stores NoC-related architectural information when there is an embedded NoC t_noc_inf* noc = nullptr; + + /// VIB grid layouts + std::vector vib_grid_layouts; + + // added for vib + std::vector vib_infs; }; diff --git a/libs/libarchfpga/src/physical_types_util.cpp b/libs/libarchfpga/src/physical_types_util.cpp index c0535943391..846e2f5a69f 100644 --- a/libs/libarchfpga/src/physical_types_util.cpp +++ b/libs/libarchfpga/src/physical_types_util.cpp @@ -772,6 +772,15 @@ std::vector block_type_class_index_to_pin_names(t_physical_tile_typ return pin_names; } +t_physical_tile_type_ptr find_tile_type_by_name(const std::string& name, const std::vector& types) { + for (t_physical_tile_type const& type : types) { + if (type.name == name) { + return &type; + } + } + return nullptr; // Not found +} + /* Access information related to pin classes */ /** get information given class physical num **/ diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 9a8acb26633..bb9ec1bdbe7 100644 --- a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -1845,14 +1845,14 @@ struct ArchReader { auto site_pins = site.getBelPins(); std::string endpoint = direction == BACKWARD ? ic->input_string : ic->output_string; - auto ic_endpoints = vtr::split(endpoint, " "); + std::vector ic_endpoints = vtr::StringToken(endpoint).split(" "); std::unordered_map> pps_map; bool is_backward = direction == BACKWARD; - for (auto ep : ic_endpoints) { - auto parts = vtr::split(ep, "."); + for (const std::string& ep : ic_endpoints) { + auto parts = vtr::StringToken(ep).split("."); auto bel = parts[0]; auto pin = parts[1]; @@ -1875,7 +1875,7 @@ struct ArchReader { if (bel_reader.getCategory() == ROUTING) { for (auto bel_pin : bel_reader.getPins()) { auto pin_reader = site_pins[bel_pin]; - auto pin_name = str(pin_reader.getName()); + std::string pin_name = str(pin_reader.getName()); if (pin_reader.getDir() != (is_backward ? INPUT : OUTPUT)) continue; @@ -1889,7 +1889,7 @@ struct ArchReader { std::string ic_to_find = bel + "." + pin_name; bool found = false; - for (auto out : vtr::split(is_backward ? other_ic->output_string : other_ic->input_string, " ")) + for (const std::string& out : vtr::StringToken(is_backward ? other_ic->output_string : other_ic->input_string).split(" ")) found |= out == ic_to_find; if (found) { @@ -1911,7 +1911,7 @@ struct ArchReader { t_interconnect* other_ic = &mode->interconnect[iic]; bool found = false; - for (auto other_ep : vtr::split(is_backward ? other_ic->output_string : other_ic->input_string, " ")) { + for (const std::string& other_ep : vtr::StringToken(is_backward ? other_ic->output_string : other_ic->input_string).split(" ")) { found |= other_ep == ep; } @@ -2327,7 +2327,7 @@ struct ArchReader { arch_->Chans.chan_y_dist.xpeak = 0; arch_->Chans.chan_y_dist.dc = 0; arch_->ipin_cblock_switch_name.push_back(std::string("generic")); - arch_->SBType = WILTON; + arch_->sb_type = WILTON; arch_->Fs = 3; default_fc_.specified = true; default_fc_.in_value_type = e_fc_value_type::FRACTIONAL; @@ -2465,7 +2465,7 @@ struct ArchReader { arch_->Segments[index].frequency = 1; arch_->Segments[index].Rmetal = 1e-12; arch_->Segments[index].Cmetal = 1e-12; - arch_->Segments[index].parallel_axis = BOTH_AXIS; + arch_->Segments[index].parallel_axis = e_parallel_axis::BOTH_AXIS; // TODO: Only bi-directional segments are created, but it the interchange format // has directionality information on PIPs, which may be used to infer the diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index c265b7d59ea..d37a0f3aa7f 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -47,6 +47,7 @@ #include "pugixml.hpp" #include "pugixml_util.hpp" +#include "read_xml_arch_file_vib.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_util.h" @@ -256,19 +257,6 @@ static void process_mode(pugi::xml_node Parent, const t_arch& arch, const pugiutil::loc_data& loc_data, int& parent_pb_idx); -/** - * @brief Processes tags. - * - * @param strings String internment storage used to store strings used - * as keys and values in tags. - * @param Parent An XML node pointing to the parent tag whose children - * are to be parsed. - * @param loc_data Points to the location in the architecture file where the parser is reading. - * @return A t_metadata_dict that stored parsed (key, value) pairs. - */ -static t_metadata_dict process_meta_data(vtr::string_internment& strings, - pugi::xml_node Parent, - const pugiutil::loc_data& loc_data); static void process_fc_values(pugi::xml_node Node, t_default_fc_spec& spec, const pugiutil::loc_data& loc_data); static void process_fc(pugi::xml_node Node, @@ -306,9 +294,13 @@ static void process_model_ports(pugi::xml_node port_group, t_model& model, std:: static void process_layout(pugi::xml_node Node, t_arch* arch, const pugiutil::loc_data& loc_data, int& num_of_avail_layer); static t_grid_def process_grid_layout(vtr::string_internment& strings, pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data, t_arch* arch, int& num_of_avail_layer); static void process_block_type_locs(t_grid_def& grid_def, int die_number, vtr::string_internment& strings, pugi::xml_node layout_block_type_tag, const pugiutil::loc_data& loc_data); -static int get_number_of_layers(pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data); static void process_device(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& arch_def_fc, const pugiutil::loc_data& loc_data); +/** + * @brief Parses tags related to tileable rr graph under tag in the architecture file. + */ +static void process_tileable_device_parameters(t_arch* arch, const pugiutil::loc_data& loc_data); + /** * @brief Parses tag in the architecture file. * @@ -388,6 +380,8 @@ static e_side string_to_side(const std::string& side_str); template static T* get_type_by_name(std::string_view type_name, std::vector& types); +static void process_bend(pugi::xml_node Node, t_segment_inf& segment, const int len, const pugiutil::loc_data& loc_data); + /* * * @@ -447,6 +441,12 @@ void xml_read_arch(const char* ArchFile, Next = get_single_child(architecture, "layout", loc_data); process_layout(Next, arch, loc_data, num_of_avail_layers); + // Precess vib_layout + Next = get_single_child(architecture, "vib_layout", loc_data, ReqOpt::OPTIONAL); + if (Next) { + process_vib_layout(Next, arch, loc_data); + } + /* Process device */ Next = get_single_child(architecture, "device", loc_data); process_device(Next, arch, arch_def_fc, loc_data); @@ -456,7 +456,7 @@ void xml_read_arch(const char* ArchFile, arch->switches = process_switches(Next, timing_enabled, loc_data); /* Process switchblocks. This depends on switches */ - bool switchblocklist_required = (arch->SBType == CUSTOM); //require this section only if custom switchblocks are used + bool switchblocklist_required = (arch->sb_type == CUSTOM); //require this section only if custom switchblocks are used SWITCHBLOCKLIST_REQD = BoolToReqOpt(switchblocklist_required); /* Process segments. This depends on switches */ @@ -485,7 +485,13 @@ void xml_read_arch(const char* ArchFile, arch->directs = process_directs(Next, arch->switches, loc_data); } - /* Process Clock Networks */ + // Process vib_arch + Next = get_single_child(architecture, "vib_arch", loc_data, ReqOpt::OPTIONAL); + if (Next) { + process_vib_arch(Next, arch, loc_data); + } + + // Process Clock Networks Next = get_single_child(architecture, "clocknetworks", loc_data, ReqOpt::OPTIONAL); if (Next) { std::vector expected_children = {"metal_layers", "clock_network", "clock_routing"}; @@ -504,11 +510,10 @@ void xml_read_arch(const char* ArchFile, loc_data); } - /* Process architecture power information */ + // Process architecture power information - /* If arch->power has been initialized, meaning the user has requested power estimation, - * then the power architecture information is required. - */ + // If arch->power has been initialized, meaning the user has requested power estimation, + // then the power architecture information is required. if (arch->power) { POWER_REQD = ReqOpt::REQUIRED; } else { @@ -520,9 +525,7 @@ void xml_read_arch(const char* ArchFile, if (arch->power) { process_power(Next, arch->power, loc_data); } else { - /* This information still needs to be read, even if it is just - * thrown away. - */ + // This information still needs to be read, even if it is just thrown away. t_power_arch* power_arch_fake = new t_power_arch(); process_power(Next, power_arch_fake, loc_data); delete power_arch_fake; @@ -535,9 +538,7 @@ void xml_read_arch(const char* ArchFile, if (arch->clocks) { process_clocks(Next, *arch->clocks, loc_data); } else { - /* This information still needs to be read, even if it is just - * thrown away. - */ + // This information still needs to be read, even if it is just thrown away. std::vector clocks_fake; process_clocks(Next, clocks_fake, loc_data); } @@ -1987,28 +1988,6 @@ static void process_mode(pugi::xml_node Parent, process_interconnect(arch.strings, Cur, mode, loc_data); } -static t_metadata_dict process_meta_data(vtr::string_internment& strings, - pugi::xml_node Parent, - const pugiutil::loc_data& loc_data) { - // - // CLBLL_L_ - // - t_metadata_dict data; - auto metadata = get_single_child(Parent, "metadata", loc_data, ReqOpt::OPTIONAL); - if (metadata) { - auto meta_tag = get_first_child(metadata, "meta", loc_data); - while (meta_tag) { - auto key = get_attribute(meta_tag, "name", loc_data).as_string(); - - auto value = meta_tag.child_value(); - data.add(strings.intern_string(vtr::string_view(key)), - strings.intern_string(vtr::string_view(value))); - meta_tag = meta_tag.next_sibling(meta_tag.name()); - } - } - return data; -} - static void process_fc_values(pugi::xml_node Node, t_default_fc_spec& spec, const pugiutil::loc_data& loc_data) { spec.specified = true; @@ -2499,7 +2478,7 @@ static void process_model_ports(pugi::xml_node port_group, t_model& model, std:: model_port->clock = std::string(attr.value()); } else if (attr.name() == std::string("combinational_sink_ports")) { - model_port->combinational_sink_ports = vtr::split(attr.value()); + model_port->combinational_sink_ports = vtr::StringToken(attr.value()).split(" \t\n"); } else { bad_attribute(attr, port, loc_data); @@ -2544,8 +2523,13 @@ static void process_model_ports(pugi::xml_node port_group, t_model& model, std:: static void process_layout(pugi::xml_node layout_tag, t_arch* arch, const pugiutil::loc_data& loc_data, int& num_of_avail_layer) { VTR_ASSERT(layout_tag.name() == std::string("layout")); - //Expect no attributes on - expect_only_attributes(layout_tag, {}, loc_data); + arch->tileable = get_attribute(layout_tag, "tileable", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->perimeter_cb = get_attribute(layout_tag, "perimeter_cb", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->shrink_boundary = get_attribute(layout_tag, "shrink_boundary", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->through_channel = get_attribute(layout_tag, "through_channel", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->opin2all_sides = get_attribute(layout_tag, "opin2all_sides", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->concat_wire = get_attribute(layout_tag, "concat_wire", loc_data, ReqOpt::OPTIONAL).as_bool(false); + arch->concat_pass_wire = get_attribute(layout_tag, "concat_pass_wire", loc_data, ReqOpt::OPTIONAL).as_bool(false); //Count the number of or tags size_t auto_layout_cnt = 0; @@ -2884,25 +2868,6 @@ static void process_block_type_locs(t_grid_def& grid_def, } } -static int get_number_of_layers(pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data) { - int max_die_num = -1; - - const auto& layer_tag = layout_type_tag.children("layer"); - for (const auto& layer_child : layer_tag) { - int die_number = get_attribute(layer_child, "die", loc_data).as_int(0); - if (die_number > max_die_num) { - max_die_num = die_number; - } - } - - if (max_die_num == -1) { - // For backwards compatibility, if no die number is specified, assume 1 layer - return 1; - } else { - return max_die_num + 1; - } -} - /* Takes in node pointing to and loads all the * child type objects. */ static void process_device(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& arch_def_fc, const pugiutil::loc_data& loc_data) { @@ -2954,24 +2919,27 @@ static void process_device(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& // tag Cur = get_single_child(Node, "switch_block", loc_data); - expect_only_attributes(Cur, {"type", "fs"}, loc_data); + expect_only_attributes(Cur, {"type", "fs", "sub_type", "sub_fs"}, loc_data); Prop = get_attribute(Cur, "type", loc_data).value(); + // Parse attribute 'type', representing the major connectivity pattern for switch blocks if (strcmp(Prop, "wilton") == 0) { - arch->SBType = WILTON; + arch->sb_type = WILTON; } else if (strcmp(Prop, "universal") == 0) { - arch->SBType = UNIVERSAL; + arch->sb_type = UNIVERSAL; } else if (strcmp(Prop, "subset") == 0) { - arch->SBType = SUBSET; + arch->sb_type = SUBSET; } else if (strcmp(Prop, "custom") == 0) { - arch->SBType = CUSTOM; + arch->sb_type = CUSTOM; custom_switch_block = true; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), vtr::string_fmt("Unknown property %s for switch block type x\n", Prop).c_str()); } - ReqOpt CUSTOM_SWITCHBLOCK_REQD = BoolToReqOpt(!custom_switch_block); - arch->Fs = get_attribute(Cur, "fs", loc_data, CUSTOM_SWITCHBLOCK_REQD).as_int(3); + ReqOpt custom_switchblock_reqd = BoolToReqOpt(!custom_switch_block); + arch->Fs = get_attribute(Cur, "fs", loc_data, custom_switchblock_reqd).as_int(3); + + process_tileable_device_parameters(arch, loc_data); Cur = get_single_child(Node, "default_fc", loc_data, ReqOpt::OPTIONAL); if (Cur) { @@ -2983,6 +2951,33 @@ static void process_device(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& } } +static void process_tileable_device_parameters(t_arch* arch, const pugiutil::loc_data& loc_data) { + pugi::xml_node cur; + + // Parse attribute 'sub_type', representing the minor connectivity pattern for switch blocks + // If not specified, the 'sub_type' is the same as major type + // This option is only valid for tileable routing resource graph builder + // Note that sub_type does not support custom switch block pattern!!! + // If 'sub_type' is specified, the custom switch block for 'type' is not allowed! + std::string sub_type_str = get_attribute(cur, "sub_type", loc_data, BoolToReqOpt(false)).as_string(""); + if (!sub_type_str.empty()) { + if (sub_type_str == "wilton") { + arch->sb_sub_type = WILTON; + } else if (sub_type_str == "universal") { + arch->sb_sub_type = UNIVERSAL; + } else if (sub_type_str == "subset") { + arch->sb_sub_type = SUBSET; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(cur), + "Unknown property %s for switch block subtype x\n", sub_type_str.c_str()); + } + } else { + arch->sb_sub_type = arch->sb_type; + } + + arch->sub_fs = get_attribute(cur, "sub_fs", loc_data, BoolToReqOpt(false)).as_int(arch->Fs); +} + /* Takes in node pointing to and loads all the * child type objects. */ static void process_chan_width_distr(pugi::xml_node Node, @@ -3519,7 +3514,7 @@ static void process_pin_locations(pugi::xml_node Locations, seen_sides.insert(side_offset); /* Go through lists of pins */ - const std::vector Tokens = vtr::split(Cur.child_value()); + const std::vector Tokens = vtr::StringToken(Cur.child_value()).split(" \t\n"); int Count = (int)Tokens.size(); if (Count > 0) { for (int pin = 0; pin < Count; ++pin) { @@ -3872,15 +3867,15 @@ static std::vector process_segments(pugi::xml_node Parent, /*Get parallel axis*/ - Segs[i].parallel_axis = BOTH_AXIS; /*DEFAULT value if no axis is specified*/ + Segs[i].parallel_axis = e_parallel_axis::BOTH_AXIS; // DEFAULT value if no axis is specified tmp = get_attribute(Node, "axis", loc_data, ReqOpt::OPTIONAL).as_string(nullptr); if (tmp) { if (strcmp(tmp, "x") == 0) { - Segs[i].parallel_axis = X_AXIS; + Segs[i].parallel_axis = e_parallel_axis::X_AXIS; x_axis_seg_found = true; } else if (strcmp(tmp, "y") == 0) { - Segs[i].parallel_axis = Y_AXIS; + Segs[i].parallel_axis = e_parallel_axis::Y_AXIS; y_axis_seg_found = true; } else { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), @@ -3933,7 +3928,9 @@ static std::vector process_segments(pugi::xml_node Parent, //Unidir requires the following tags expected_subtags.emplace_back("mux"); + expected_subtags.emplace_back("bend"); expected_subtags.emplace_back("mux_inter_die"); + //with the following two tags, we can allow the architecture file to define //different muxes with different delays for wires with different directions expected_subtags.emplace_back("mux_inc"); @@ -4066,12 +4063,21 @@ static std::vector process_segments(pugi::xml_node Parent, process_cb_sb(SubElem, Segs[i].sb, loc_data); } - /*Store the index of this segment in Segs vector*/ + // Setup the bend list if they give one, otherwise use default + if (length > 1) { + Segs[i].is_bend = false; + SubElem = get_single_child(Node, "bend", loc_data, ReqOpt::OPTIONAL); + if (SubElem) { + process_bend(SubElem, Segs[i], (length - 1), loc_data); + } + } + + // Store the index of this segment in Segs vector Segs[i].seg_index = i; - /* Get next Node */ + // Get next Node Node = Node.next_sibling(Node.name()); } - /*We need at least one type of segment that applies to each of x- and y-directed wiring.*/ + // We need at least one type of segment that applies to each of x- and y-directed wiring. if (!x_axis_seg_found || !y_axis_seg_found) { archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), @@ -4081,6 +4087,75 @@ static std::vector process_segments(pugi::xml_node Parent, return Segs; } +static void process_bend(pugi::xml_node Node, t_segment_inf& segment, const int len, const pugiutil::loc_data& loc_data) { + std::vector& list = segment.bend; + std::vector& part_len = segment.part_len; + bool& is_bend = segment.is_bend; + + std::string tmp = std::string(get_attribute(Node, "type", loc_data).value()); + if (tmp == "pattern") { + int i = 0; + + /* Get the content string */ + std::string content = std::string(Node.child_value()); + for (char c : content) { + switch (c) { + case ' ': + case '\t': + case '\n': + break; + case '-': + list.push_back(0); + break; + case 'U': + list.push_back(1); + is_bend = true; + break; + case 'D': + list.push_back(2); + is_bend = true; + break; + case 'B': + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), + "B pattern is not supported in current version\n"); + break; + default: + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), + "Invalid character %c in CB or SB depopulation list.\n", + c); + } + } + + if (list.size() != size_t(len)) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), + "Wrong length of bend list (%d). Expect %d symbols.\n", + i, len); + } + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), + "'%s' is not a valid type for specifying bend list.\n", + tmp.c_str()); + } + + // TODO: Add a comment to explain this and this function overall + int tmp_len = 1; + int sum_len = 0; + for (size_t i_len = 0; i_len < list.size(); i_len++) { + if (list[i_len] == 0) { + tmp_len++; + } else if (list[i_len] != 0) { + VTR_ASSERT(tmp_len < (int)list.size() + 1); + part_len.push_back(tmp_len); + sum_len += tmp_len; + tmp_len = 1; + } + } + + // add the last clip of segment + if (sum_len < (int)list.size() + 1) + part_len.push_back(list.size() + 1 - sum_len); +} + static void calculate_custom_SB_locations(const pugiutil::loc_data& loc_data, const pugi::xml_node& SubElem, const int grid_width, const int grid_height, t_switchblock_inf& sb) { auto startx_attr = get_attribute(SubElem, "startx", loc_data, ReqOpt::OPTIONAL); auto endx_attr = get_attribute(SubElem, "endx", loc_data, ReqOpt::OPTIONAL); @@ -4235,13 +4310,14 @@ static void process_cb_sb(pugi::xml_node Node, std::vector& list, const pu const char* tmp = nullptr; int i; int len = list.size(); - /* Check the type. We only support 'pattern' for now. - * Should add frac back eventually. */ + + // Check the type. We only support 'pattern' for now. + // Should add frac back eventually. tmp = get_attribute(Node, "type", loc_data).value(); if (0 == strcmp(tmp, "pattern")) { i = 0; - /* Get the content string */ + // Get the content string tmp = Node.child_value(); while (*tmp) { switch (*tmp) { diff --git a/libs/libarchfpga/src/read_xml_arch_file_vib.cpp b/libs/libarchfpga/src/read_xml_arch_file_vib.cpp new file mode 100644 index 00000000000..21f8022b3fc --- /dev/null +++ b/libs/libarchfpga/src/read_xml_arch_file_vib.cpp @@ -0,0 +1,469 @@ +#include "read_xml_arch_file_vib.h" + +#include "vtr_assert.h" +#include "vtr_util.h" +#include "arch_error.h" + +#include "read_xml_util.h" + +using pugiutil::ReqOpt; + +static void process_vib(pugi::xml_node Vib_node, t_arch* arch, const pugiutil::loc_data& loc_data); + +static std::vector process_first_stage(pugi::xml_node stage_node, const pugiutil::loc_data& loc_data); + +static std::vector process_second_stage(pugi::xml_node stage_node, const pugiutil::loc_data& loc_data); + +static void process_vib_block_type_locs(t_vib_grid_def& grid_def, + int die_number, + vtr::string_internment& strings, + pugi::xml_node layout_block_type_tag, + const pugiutil::loc_data& loc_data); + +void process_vib_arch(pugi::xml_node Parent, t_arch* arch, const pugiutil::loc_data& loc_data) { + int num_vibs = count_children(Parent, "vib", loc_data); + arch->vib_infs.reserve(num_vibs); + pugi::xml_node node = get_first_child(Parent, "vib", loc_data); + + for (int i_vib = 0; i_vib < num_vibs; i_vib++) { + process_vib(node, arch, loc_data); + node = node.next_sibling(node.name()); + } +} + +static void process_vib(pugi::xml_node Vib_node, t_arch* arch, const pugiutil::loc_data& loc_data) { + VibInf vib; + + std::string tmp = get_attribute(Vib_node, "name", loc_data).as_string(""); + if (!tmp.empty()) { + vib.set_name(tmp); + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Vib_node), + "No name specified for the vib!\n"); + } + + tmp = get_attribute(Vib_node, "pbtype_name", loc_data).as_string(""); + if (!tmp.empty()) { + vib.set_pbtype_name(tmp); + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Vib_node), + "No pbtype_name specified for the vib!\n"); + } + + vib.set_seg_group_num(get_attribute(Vib_node, "vib_seg_group", loc_data).as_int(1)); + + tmp = get_attribute(Vib_node, "arch_vib_switch", loc_data).as_string(""); + + if (!tmp.empty()) { + vib.set_switch_name(tmp); + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Vib_node), + "No switch specified for the vib!\n"); + } + + expect_only_children(Vib_node, {"seg_group", "multistage_muxs"}, loc_data); + + int group_num = count_children(Vib_node, "seg_group", loc_data); + VTR_ASSERT(vib.get_seg_group_num() == group_num); + pugi::xml_node node = get_first_child(Vib_node, "seg_group", loc_data); + for (int i_group = 0; i_group < group_num; i_group++) { + t_seg_group seg_group; + + tmp = get_attribute(node, "name", loc_data).as_string(""); + + if (!tmp.empty()) { + seg_group.name = tmp; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), + "No name specified for the vib seg group!\n"); + } + + seg_group.axis = e_parallel_axis::BOTH_AXIS; // Default value if no axis is specified + tmp = get_attribute(node, "axis", loc_data, ReqOpt::OPTIONAL).as_string(""); + + if (!tmp.empty()) { + if (tmp == "x") { + seg_group.axis = e_parallel_axis::X_AXIS; + } else if (tmp == "y") { + seg_group.axis = e_parallel_axis::Y_AXIS; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), "Unsupported parallel axis type: %s\n", tmp.c_str()); + } + } + + int track_num = get_attribute(node, "track_nums", loc_data).as_int(); + if (track_num > 0) { + seg_group.track_num = track_num; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(node), + "No track_num specified for the vib seg group!\n"); + } + + vib.push_seg_group(seg_group); + + node = node.next_sibling(node.name()); + } + + node = get_single_child(Vib_node, "multistage_muxs", loc_data); + expect_only_children(node, {"first_stage", "second_stage"}, loc_data); + + pugi::xml_node sub_elem = get_single_child(node, "first_stage", loc_data); + if (sub_elem) { + std::vector first_stages = process_first_stage(sub_elem, loc_data); + + for (const t_first_stage_mux_inf& first_stage : first_stages) { + vib.push_first_stage(first_stage); + } + } + + sub_elem = get_single_child(node, "second_stage", loc_data); + if (sub_elem) { + std::vector second_stages = process_second_stage(sub_elem, loc_data); + + for (const t_second_stage_mux_inf& second_stage : second_stages) { + vib.push_second_stage(second_stage); + } + } + + arch->vib_infs.push_back(vib); +} + +static std::vector process_first_stage(pugi::xml_node stage_node, const pugiutil::loc_data& loc_data) { + std::vector first_stages; + expect_only_children(stage_node, {"mux"}, loc_data); + int num_mux = count_children(stage_node, "mux", loc_data); + first_stages.reserve(num_mux); + pugi::xml_node node = get_first_child(stage_node, "mux", loc_data); + for (int i_mux = 0; i_mux < num_mux; i_mux++) { + t_first_stage_mux_inf first_stage_mux; + first_stage_mux.mux_name = get_attribute(node, "name", loc_data).as_string(); + + expect_only_children(node, {"from"}, loc_data); + pugi::xml_node sub_elem = get_first_child(node, "from", loc_data); + int from_num = count_children(node, "from", loc_data); + for (int i_from = 0; i_from < from_num; i_from++) { + std::vector from_tokens = vtr::StringToken(sub_elem.child_value()).split(" \t\n"); + first_stage_mux.from_tokens.push_back(from_tokens); + sub_elem = sub_elem.next_sibling(sub_elem.name()); + } + first_stages.push_back(first_stage_mux); + + node = node.next_sibling(node.name()); + } + return first_stages; +} + +static std::vector process_second_stage(pugi::xml_node stage_node, const pugiutil::loc_data& loc_data) { + std::vector second_stages; + expect_only_children(stage_node, {"mux"}, loc_data); + int num_mux = count_children(stage_node, "mux", loc_data); + second_stages.reserve(num_mux); + pugi::xml_node node = get_first_child(stage_node, "mux", loc_data); + for (int i_mux = 0; i_mux < num_mux; i_mux++) { + t_second_stage_mux_inf second_stage_mux; + second_stage_mux.mux_name = get_attribute(node, "name", loc_data).as_string(); + + expect_only_children(node, {"to", "from"}, loc_data); + + pugi::xml_node sub_elem = get_first_child(node, "to", loc_data); + int to_num = count_children(node, "to", loc_data); + VTR_ASSERT(to_num == 1); + std::vector to_tokens = vtr::StringToken(sub_elem.child_value()).split(" \t\n"); + VTR_ASSERT(to_tokens.size() == 1); + second_stage_mux.to_tokens = to_tokens; + + sub_elem = get_first_child(node, "from", loc_data); + int from_num = count_children(node, "from", loc_data); + for (int i_from = 0; i_from < from_num; i_from++) { + std::vector from_tokens = vtr::StringToken(sub_elem.child_value()).split(" \t\n"); + second_stage_mux.from_tokens.push_back(from_tokens); + sub_elem = sub_elem.next_sibling(sub_elem.name()); + } + + second_stages.push_back(second_stage_mux); + + node = node.next_sibling(node.name()); + } + + return second_stages; +} + +// Process vib layout +void process_vib_layout(pugi::xml_node vib_layout_tag, t_arch* arch, const pugiutil::loc_data& loc_data) { + VTR_ASSERT(vib_layout_tag.name() == std::string("vib_layout")); + + size_t auto_layout_cnt = 0; + size_t fixed_layout_cnt = 0; + for (auto layout_type_tag : vib_layout_tag.children()) { + if (std::string(layout_type_tag.name()) == "auto_layout") { + ++auto_layout_cnt; + } else if (std::string(layout_type_tag.name()) == "fixed_layout") { + ++fixed_layout_cnt; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_type_tag), + "Unexpected tag type '<%s>', expected '' or ''", layout_type_tag.name()); + } + } + + if (auto_layout_cnt == 0 && fixed_layout_cnt == 0) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(vib_layout_tag), + "Expected either an or tag"); + } + if (auto_layout_cnt > 1) { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(vib_layout_tag), + "Expected at most one tag"); + } + VTR_ASSERT_MSG(auto_layout_cnt == 0 || auto_layout_cnt == 1, " may appear at most once"); + + int num_of_avail_layer; + + for (auto vib_layout_type_tag : vib_layout_tag.children()) { + t_vib_grid_def grid_def = process_vib_grid_layout(arch->strings, vib_layout_type_tag, loc_data, arch, num_of_avail_layer); + + arch->vib_grid_layouts.emplace_back(std::move(grid_def)); + } +} + +t_vib_grid_def process_vib_grid_layout(vtr::string_internment& strings, pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data, t_arch* arch, int& num_of_avail_layer) { + t_vib_grid_def grid_def; + num_of_avail_layer = get_number_of_layers(layout_type_tag, loc_data); + bool has_layer = layout_type_tag.child("layer"); + + // Determine the grid specification type + if (std::string(layout_type_tag.name()) == "auto_layout") { + grid_def.grid_type = e_vib_grid_def_type::VIB_AUTO; + grid_def.name = "auto"; + + for (size_t i = 0; i < arch->grid_layouts.size(); i++) { + if (arch->grid_layouts[i].name == grid_def.name) { + grid_def.aspect_ratio = arch->grid_layouts[i].aspect_ratio; + } + } + + } else if (std::string(layout_type_tag.name()) == "fixed_layout") { + expect_only_attributes(layout_type_tag, {"name"}, loc_data); + + grid_def.grid_type = e_vib_grid_def_type::VIB_FIXED; + std::string name = get_attribute(layout_type_tag, "name", loc_data).value(); + + if (name == "auto") { + // We name as 'auto', so don't allow a user to specify it + archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_type_tag), + "The name '%s' is reserved for auto-sized layouts; please choose another name"); + } + + for (size_t i = 0; i < arch->grid_layouts.size(); i++) { + if (arch->grid_layouts[i].name == name) { + grid_def.width = arch->grid_layouts[i].width; + grid_def.height = arch->grid_layouts[i].height; + } + } + grid_def.name = name; + + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(layout_type_tag), + "Unexpected tag '<%s>'. Expected '' or ''.", + layout_type_tag.name()); + } + + grid_def.layers.resize(num_of_avail_layer); + arch->layer_global_routing.resize(num_of_avail_layer); + // No layer tag is specified (only one die is specified in the arch file) + // Need to process layout_type_tag children to get block types locations in the grid + if (has_layer) { + std::set seen_die_numbers; // Check that die numbers in the specific layout tag are unique + // One or more than one layer tag is specified + auto layer_tag_specified = layout_type_tag.children("layer"); + for (auto layer_child : layer_tag_specified) { + int die_number; + bool has_global_routing; + // More than one layer tag is specified, meaning that multi-die FPGA is specified in the arch file + // Need to process each tag children to get block types locations for each grid + die_number = get_attribute(layer_child, "die", loc_data).as_int(0); + has_global_routing = get_attribute(layer_child, "has_prog_routing", loc_data, ReqOpt::OPTIONAL).as_bool(true); + arch->layer_global_routing.at(die_number) = has_global_routing; + VTR_ASSERT(die_number >= 0 && die_number < num_of_avail_layer); + auto insert_res = seen_die_numbers.insert(die_number); + VTR_ASSERT_MSG(insert_res.second, "Two different layers with a same die number may have been specified in the Architecture file"); + process_vib_block_type_locs(grid_def, die_number, strings, layer_child, loc_data); + } + } else { + // If only one die is available, then global routing resources must exist in that die + int die_number = 0; + arch->layer_global_routing.at(die_number) = true; + process_vib_block_type_locs(grid_def, die_number, strings, layout_type_tag, loc_data); + } + return grid_def; +} + +static void process_vib_block_type_locs(t_vib_grid_def& grid_def, + int die_number, + vtr::string_internment& strings, + pugi::xml_node layout_block_type_tag, + const pugiutil::loc_data& loc_data) { + // Helper struct to define coordinate parameters + struct CoordParams { + std::string x_start, x_end, y_start, y_end; + std::string x_repeat = "", x_incr = "", y_repeat = "", y_incr = ""; + + CoordParams(const std::string& xs, const std::string& xe, const std::string& ys, const std::string& ye) + : x_start(xs) + , x_end(xe) + , y_start(ys) + , y_end(ye) {} + + CoordParams() = default; + }; + + // Process all the block location specifications + for (auto loc_spec_tag : layout_block_type_tag.children()) { + std::string loc_type = loc_spec_tag.name(); + auto type_name = get_attribute(loc_spec_tag, "type", loc_data).value(); + int priority = get_attribute(loc_spec_tag, "priority", loc_data).as_int(); + t_metadata_dict meta = process_meta_data(strings, loc_spec_tag, loc_data); + + auto& loc_defs = grid_def.layers.at(die_number).loc_defs; + + if (loc_type == "perimeter") { + expect_only_attributes(loc_spec_tag, {"type", "priority"}, loc_data); + + const std::vector perimeter_edges = { + {"0", "0", "0", "H - 1"}, // left (including corners) + {"W - 1", "W - 1", "0", "H - 1"}, // right (including corners) + {"1", "W - 2", "0", "0"}, // bottom (excluding corners) + {"1", "W - 2", "H - 1", "H - 1"} // top (excluding corners) + }; + + for (const CoordParams& edge : perimeter_edges) { + t_vib_grid_loc_def edge_def(type_name, priority); + edge_def.x.start_expr = edge.x_start; + edge_def.x.end_expr = edge.x_end; + edge_def.y.start_expr = edge.y_start; + edge_def.y.end_expr = edge.y_end; + loc_defs.emplace_back(std::move(edge_def)); + } + } else if (loc_type == "corners") { + expect_only_attributes(loc_spec_tag, {"type", "priority"}, loc_data); + + const std::vector corner_positions = { + {"0", "0", "0", "0"}, // bottom_left + {"0", "0", "H-1", "H-1"}, // top_left + {"W-1", "W-1", "0", "0"}, // bottom_right + {"W-1", "W-1", "H-1", "H-1"} // top_right + }; + + for (const CoordParams& corner : corner_positions) { + t_vib_grid_loc_def corner_def(type_name, priority); + corner_def.x.start_expr = corner.x_start; + corner_def.x.end_expr = corner.x_end; + corner_def.y.start_expr = corner.y_start; + corner_def.y.end_expr = corner.y_end; + loc_defs.emplace_back(std::move(corner_def)); + } + } else if (loc_type == "fill") { + expect_only_attributes(loc_spec_tag, {"type", "priority"}, loc_data); + + t_vib_grid_loc_def fill_def(type_name, priority); + fill_def.x.start_expr = "0"; + fill_def.x.end_expr = "W - 1"; + fill_def.y.start_expr = "0"; + fill_def.y.end_expr = "H - 1"; + loc_defs.push_back(std::move(fill_def)); + } else if (loc_type == "single") { + expect_only_attributes(loc_spec_tag, {"type", "priority", "x", "y"}, loc_data); + + const std::string x_pos = get_attribute(loc_spec_tag, "x", loc_data).value(); + const std::string y_pos = get_attribute(loc_spec_tag, "y", loc_data).value(); + + t_vib_grid_loc_def single_def(type_name, priority); + single_def.x.start_expr = x_pos; + single_def.x.end_expr = x_pos + " + w - 1"; + single_def.y.start_expr = y_pos; + single_def.y.end_expr = y_pos + " + h - 1"; + loc_defs.push_back(std::move(single_def)); + } else if (loc_type == "col") { + expect_only_attributes(loc_spec_tag, {"type", "priority", "startx", "repeatx", "starty", "incry"}, loc_data); + + const std::string start_x = get_attribute(loc_spec_tag, "startx", loc_data).value(); + + t_vib_grid_loc_def col_def(type_name, priority); + col_def.x.start_expr = start_x; + col_def.x.end_expr = start_x + " + w - 1"; + + // Handle optional attributes + auto repeat_attr = get_attribute(loc_spec_tag, "repeatx", loc_data, ReqOpt::OPTIONAL); + if (repeat_attr) { + col_def.x.repeat_expr = repeat_attr.value(); + } + + auto start_y_attr = get_attribute(loc_spec_tag, "starty", loc_data, ReqOpt::OPTIONAL); + if (start_y_attr) { + col_def.y.start_expr = start_y_attr.value(); + } + + auto incr_y_attr = get_attribute(loc_spec_tag, "incry", loc_data, ReqOpt::OPTIONAL); + if (incr_y_attr) { + col_def.y.incr_expr = incr_y_attr.value(); + } + + loc_defs.push_back(std::move(col_def)); + + } else if (loc_type == "row") { + expect_only_attributes(loc_spec_tag, {"type", "priority", "starty", "repeaty", "startx", "incrx"}, loc_data); + + const std::string start_y = get_attribute(loc_spec_tag, "starty", loc_data).value(); + + t_vib_grid_loc_def row_def(type_name, priority); + row_def.y.start_expr = start_y; + row_def.y.end_expr = start_y + " + h - 1"; + + // Handle optional attributes + auto repeat_attr = get_attribute(loc_spec_tag, "repeaty", loc_data, ReqOpt::OPTIONAL); + if (repeat_attr) { + row_def.y.repeat_expr = repeat_attr.value(); + } + + auto start_x_attr = get_attribute(loc_spec_tag, "startx", loc_data, ReqOpt::OPTIONAL); + if (start_x_attr) { + row_def.x.start_expr = start_x_attr.value(); + } + + auto incr_x_attr = get_attribute(loc_spec_tag, "incrx", loc_data, ReqOpt::OPTIONAL); + if (incr_x_attr) { + row_def.x.incr_expr = incr_x_attr.value(); + } + + loc_defs.push_back(std::move(row_def)); + } else if (loc_type == "region") { + expect_only_attributes(loc_spec_tag, + {"type", "priority", + "startx", "endx", "repeatx", "incrx", + "starty", "endy", "repeaty", "incry"}, + loc_data); + t_vib_grid_loc_def region_def(type_name, priority); + + // Helper lambda to set optional attribute + auto set_optional_attr = [&](const char* attr_name, std::string& target) { + auto attr = get_attribute(loc_spec_tag, attr_name, loc_data, ReqOpt::OPTIONAL); + if (attr) { + target = attr.value(); + } + }; + + // Set all optional region attributes + set_optional_attr("startx", region_def.x.start_expr); + set_optional_attr("endx", region_def.x.end_expr); + set_optional_attr("repeatx", region_def.x.repeat_expr); + set_optional_attr("incrx", region_def.x.incr_expr); + set_optional_attr("starty", region_def.y.start_expr); + set_optional_attr("endy", region_def.y.end_expr); + set_optional_attr("repeaty", region_def.y.repeat_expr); + set_optional_attr("incry", region_def.y.incr_expr); + + loc_defs.push_back(std::move(region_def)); + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(loc_spec_tag), + "Unrecognized grid location specification type '%s'\n", loc_type.c_str()); + } + } +} diff --git a/libs/libarchfpga/src/read_xml_arch_file_vib.h b/libs/libarchfpga/src/read_xml_arch_file_vib.h new file mode 100644 index 00000000000..6475c45ac55 --- /dev/null +++ b/libs/libarchfpga/src/read_xml_arch_file_vib.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include "physical_types.h" + +#include "pugixml.hpp" +#include "pugixml_util.hpp" + +void process_vib_arch(pugi::xml_node Parent, t_arch* arch, const pugiutil::loc_data& loc_data); + +void process_vib_layout(pugi::xml_node vib_layout_tag, t_arch* arch, const pugiutil::loc_data& loc_data); + +t_vib_grid_def process_vib_grid_layout(vtr::string_internment& strings, pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data, t_arch* arch, int& num_of_avail_layer); diff --git a/libs/libarchfpga/src/read_xml_util.cpp b/libs/libarchfpga/src/read_xml_util.cpp index fd2e76c4ad4..5419fc15973 100644 --- a/libs/libarchfpga/src/read_xml_util.cpp +++ b/libs/libarchfpga/src/read_xml_util.cpp @@ -140,3 +140,44 @@ void bad_attribute_value(const pugi::xml_attribute attr, throw ArchFpgaError(msg, loc_data.filename(), loc_data.line(node)); } + +int get_number_of_layers(pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data) { + int max_die_num = -1; + + const auto& layer_tag = layout_type_tag.children("layer"); + for (const auto& layer_child : layer_tag) { + int die_number = get_attribute(layer_child, "die", loc_data).as_int(0); + if (die_number > max_die_num) { + max_die_num = die_number; + } + } + + if (max_die_num == -1) { + // For backwards compatibility, if no die number is specified, assume 1 layer + return 1; + } else { + return max_die_num + 1; + } +} + +t_metadata_dict process_meta_data(vtr::string_internment& strings, + pugi::xml_node Parent, + const pugiutil::loc_data& loc_data) { + // + // CLBLL_L_ + // + t_metadata_dict data; + pugi::xml_node metadata = get_single_child(Parent, "metadata", loc_data, ReqOpt::OPTIONAL); + if (metadata) { + pugi::xml_node meta_tag = get_first_child(metadata, "meta", loc_data); + while (meta_tag) { + std::string key = get_attribute(meta_tag, "name", loc_data).as_string(); + + std::string value = meta_tag.child_value(); + data.add(strings.intern_string(vtr::string_view(key.c_str())), + strings.intern_string(vtr::string_view(value.c_str()))); + meta_tag = meta_tag.next_sibling(meta_tag.name()); + } + } + return data; +} diff --git a/libs/libarchfpga/src/read_xml_util.h b/libs/libarchfpga/src/read_xml_util.h index e9cf49d1e18..a72ed4ffd2c 100644 --- a/libs/libarchfpga/src/read_xml_util.h +++ b/libs/libarchfpga/src/read_xml_util.h @@ -23,3 +23,25 @@ void bad_attribute_value(const pugi::xml_attribute attr, InstPort make_inst_port(std::string str, pugi::xml_node node, const pugiutil::loc_data& loc_data); InstPort make_inst_port(pugi::xml_attribute attr, pugi::xml_node node, const pugiutil::loc_data& loc_data); + +/** + * @brief Returns the number of layers in the device layout. + * + * @param layout_type_tag The XML node pointing to the tag. + * @param loc_data Points to the location in the architecture file where the parser is reading. + */ +int get_number_of_layers(pugi::xml_node layout_type_tag, const pugiutil::loc_data& loc_data); + +/** + * @brief Processes tags. + * + * @param strings String internment storage used to store strings used + * as keys and values in tags. + * @param Parent An XML node pointing to the parent tag whose children + * are to be parsed. + * @param loc_data Points to the location in the architecture file where the parser is reading. + * @return A t_metadata_dict that stored parsed (key, value) pairs. + */ +t_metadata_dict process_meta_data(vtr::string_internment& strings, + pugi::xml_node Parent, + const pugiutil::loc_data& loc_data); diff --git a/libs/libarchfpga/src/vib_inf.cpp b/libs/libarchfpga/src/vib_inf.cpp new file mode 100644 index 00000000000..a5da5ea5bcd --- /dev/null +++ b/libs/libarchfpga/src/vib_inf.cpp @@ -0,0 +1,122 @@ +#include "vib_inf.h" + +VibInf::VibInf() { + name_.clear(); + pbtype_name_.clear(); + seg_group_num_ = 0; + switch_idx_ = -1; + seg_groups_.clear(); + first_stages_.clear(); + second_stages_.clear(); +} + +void VibInf::set_name(const std::string& name) { + VTR_ASSERT(!name.empty()); + name_ = name; +} + +void VibInf::set_pbtype_name(const std::string& pbtype_name) { + VTR_ASSERT(!pbtype_name.empty()); + pbtype_name_ = pbtype_name; +} + +void VibInf::set_seg_group_num(const int seg_group_num) { + VTR_ASSERT(seg_group_num >= 0); + seg_group_num_ = seg_group_num; +} + +void VibInf::set_switch_idx(const int switch_idx) { + VTR_ASSERT(switch_idx != -1); + switch_idx_ = switch_idx; +} + +void VibInf::set_switch_name(const std::string& switch_name) { + VTR_ASSERT(!switch_name.empty()); + switch_name_ = switch_name; +} + +void VibInf::set_seg_groups(const std::vector& seg_groups) { + VTR_ASSERT(!seg_groups.empty()); + seg_groups_ = seg_groups; +} + +void VibInf::push_seg_group(const t_seg_group& seg_group) { + VTR_ASSERT(!seg_group.name.empty()); + seg_groups_.push_back(seg_group); +} + +void VibInf::set_first_stages(const std::vector& first_stages) { + VTR_ASSERT(!first_stages.empty()); + first_stages_ = first_stages; +} + +void VibInf::push_first_stage(const t_first_stage_mux_inf& first_stage) { + VTR_ASSERT(!first_stage.mux_name.empty()); + first_stages_.push_back(first_stage); +} + +void VibInf::set_second_stages(const std::vector& second_stages) { + VTR_ASSERT(!second_stages.empty()); + second_stages_ = second_stages; +} + +void VibInf::push_second_stage(const t_second_stage_mux_inf& second_stage) { + VTR_ASSERT(!second_stage.mux_name.empty()); + second_stages_.push_back(second_stage); +} + +std::string VibInf::get_name() const { + VTR_ASSERT(!name_.empty()); + return name_; +} + +std::string VibInf::get_pbtype_name() const { + VTR_ASSERT(!pbtype_name_.empty()); + return pbtype_name_; +} + +int VibInf::get_seg_group_num() const { + VTR_ASSERT(seg_group_num_ >= 0); + return seg_group_num_; +} + +int VibInf::get_switch_idx() const { + VTR_ASSERT(switch_idx_ != -1); + return switch_idx_; +} + +std::string VibInf::get_switch_name() const { + VTR_ASSERT(!switch_name_.empty()); + return switch_name_; +} + +std::vector VibInf::get_seg_groups() const { + VTR_ASSERT(!seg_groups_.empty()); + return seg_groups_; +} + +std::vector VibInf::get_first_stages() const { + VTR_ASSERT(!first_stages_.empty()); + return first_stages_; +} + +std::vector VibInf::get_second_stages() const { + VTR_ASSERT(!second_stages_.empty()); + return second_stages_; +} + +size_t VibInf::mux_index_by_name(const std::string& name) const { + for (size_t i_mux = 0; i_mux < first_stages_.size(); ++i_mux) { + if (name == first_stages_[i_mux].mux_name) { + return i_mux; + } + } + VTR_LOG_ERROR("No mux named %s!", name.c_str()); + + return size_t(-1); +} + +VibDeviceGrid::VibDeviceGrid(std::string grid_name, vtr::NdMatrix vib_grid) + : name_(std::move(grid_name)) + , vib_grid_(std::move(vib_grid)) { +} diff --git a/libs/libarchfpga/src/vib_inf.h b/libs/libarchfpga/src/vib_inf.h new file mode 100644 index 00000000000..bf50eeb90e5 --- /dev/null +++ b/libs/libarchfpga/src/vib_inf.h @@ -0,0 +1,288 @@ +#pragma once +/** + * @file + * @brief Methods and classes related to the Versatile Interconnect Blocks (VIB) architecture. + * VIB is an alternative approach for creating the Routing Resource (RR) graph, where the connection block, + * switch block, and intra-cluster crossbar are combined into a single block. This means that each tile has + * only two blocks: a VIB block and a functional block. For further details, please refer to the following paper: + * https://doi.org/10.1109/ICFPT59805.2023.00014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vtr_ndmatrix.h" +#include "vtr_hash.h" +#include "vtr_bimap.h" +#include "vtr_string_interning.h" +#include "vtr_log.h" + +#include "logic_types.h" +#include "clock_types.h" + +/** + * @brief Segment group information. + */ +struct t_seg_group { + std::string name; + e_parallel_axis axis; + int seg_index; + int track_num; +}; + +/** + * @brief The type of the from or to of the multistage mux. + */ +enum class e_multistage_mux_from_or_to_type { + PB = 0, //Physical block + SEGMENT, //Segment + MUX //MUX +}; + +struct t_from_or_to_inf { + std::string type_name; + e_multistage_mux_from_or_to_type from_type; //from_or_to_type + int type_index = -1; + int phy_pin_index = -1; + char seg_dir = ' '; + int seg_index = -1; +}; + +struct t_first_stage_mux_inf { + std::string mux_name; + std::vector> from_tokens; + std::vector froms; +}; + +struct t_second_stage_mux_inf : t_first_stage_mux_inf { + std::vector to_tokens; + std::vector to; // for io type, port[pin] may map to several sinks +}; + +/* + * @brief VibInf is used to reserve the VIB information. + * + * @details For example, a VIB is described: + * + * + * + * + * + * + * + * L1.E0 L1.E1 + * clb.O[0] L1.E2 + * + * + * clb.I[0] MUX0 MUX1 + * L1.N0 MUX0 MUX1 + * + * + * + * + * @details Its corresponding figure is shown: + * + * | L1.N0 + * +-----------------|-------+ + * L1.E0-----------------|>|\ MUX-1 _| vib0|----------\ + * L1.E1-----------------|>| |----| /__\ |... } l1: 20 tracks + * | |/ | | | | / + * | MUX0 |-------| | |----------\ + * L1.E2-----------------|>|\ | | |... } l2: 20 tracks + * | | |--| | | | / + * |------------------|>|/ | | | |----------\ + * | | MUX1 |-|---------| |... } l4: 16 tracks + * | | | | | / + * | ... | | | |----------\ + * | | _|_| |... } l8: 16 tracks + * | | \__/ MUX-0 | / + * | +-------------------------+ + * O[0]| | + * +-------------+ | + * | |<---------------------- + * | clb | I[0] + * +-------------+ + * + */ + +class VibInf { + public: + VibInf(); + + public: + void set_name(const std::string& name); + void set_pbtype_name(const std::string& pbtype_name); + void set_seg_group_num(const int seg_group_num); + void set_switch_idx(const int switch_idx); + void set_switch_name(const std::string& switch_name); + void set_seg_groups(const std::vector& seg_groups); + void push_seg_group(const t_seg_group& seg_group); + void set_first_stages(const std::vector& first_stages); + void push_first_stage(const t_first_stage_mux_inf& first_stage); + void set_second_stages(const std::vector& second_stages); + void push_second_stage(const t_second_stage_mux_inf& second_stage); + + std::string get_name() const; + std::string get_pbtype_name() const; + int get_seg_group_num() const; + int get_switch_idx() const; + std::string get_switch_name() const; + std::vector get_seg_groups() const; + std::vector get_first_stages() const; + std::vector get_second_stages() const; + size_t mux_index_by_name(const std::string& name) const; + + private: + /// The name of the VIB type, "vib0" in the example. + std::string name_; + + /// The pbtype of the VIB, "clb" in the example. + std::string pbtype_name_; + + /// The number of segment groups. + int seg_group_num_; + + /// The index of corresponding switch in . + int switch_idx_; + + /// The name of the switch type used in the VIB, "mux0" in the example. + std::string switch_name_; + + /// The segments applied in the VIB. Their names correspond to segment names in . + std::vector seg_groups_; + + /// The info of first stage MUXes, including the names of the MUXes and their from info. + std::vector first_stages_; + + /// The info of second stage MUXes, including the names of the MUXes and their from/to info. + std::vector second_stages_; +}; + +/************************* VIB_GRID ***********************************/ +/* Describe different VIB type on different locations by immitating t_grid_loc_def. */ + +struct t_vib_grid_loc_spec { + t_vib_grid_loc_spec(std::string start, std::string end, std::string repeat, std::string incr) + : start_expr(std::move(start)) + , end_expr(std::move(end)) + , repeat_expr(std::move(repeat)) + , incr_expr(std::move(incr)) {} + + /// Starting position (inclusive) + std::string start_expr; + + /// Ending position (inclusive) + std::string end_expr; + + /// Distance between repeated region instances + std::string repeat_expr; + + /// Distance between block instantiations with the region + std::string incr_expr; +}; + +enum class e_vib_grid_def_type { + VIB_AUTO, + VIB_FIXED +}; + +struct t_vib_grid_loc_def { + t_vib_grid_loc_def(std::string block_type_val, int priority_val) + : block_type(block_type_val) + , priority(priority_val) + , x("0", "W-1", "max(w+1,W)", "w") //Fill in x direction, no repeat, incr by block width + , y("0", "H-1", "max(h+1,H)", "h") //Fill in y direction, no repeat, incr by block height + {} + + std::string block_type; //The block type name + + int priority = 0; //Priority of the specification. + // In case of conflicting specifications + // the largest priority wins. + + t_vib_grid_loc_spec x; //Horizontal location specification + t_vib_grid_loc_spec y; //Veritcal location specification +}; + +struct t_vib_layer_def { + std::vector loc_defs; //The list of block location definitions for this layer specification +}; + +struct t_vib_grid_def { + e_vib_grid_def_type grid_type = e_vib_grid_def_type::VIB_AUTO; //The type of this grid specification + + std::string name = ""; //The name of this device + + int width = -1; //Fixed device width (only valid for grid_type == FIXED) + int height = -1; //Fixed device height (only valid for grid_type == FIXED) + + float aspect_ratio = 1.; //Aspect ratio for auto-sized devices (only valid for + //grid_type == AUTO) + std::vector layers; +}; + +///@brief DeviceGrid represents the FPGA fabric. It is used to get information about different layers and tiles. +// TODO: All of the function that use helper functions of this class should pass the layer_num to the functions, and the default value of layer_num should be deleted eventually. +class VibDeviceGrid { + public: + VibDeviceGrid() = default; + VibDeviceGrid(std::string grid_name, vtr::NdMatrix vib_grid); + + const std::string& name() const { return name_; } + + ///@brief Return the number of layers(number of dies) + inline int get_num_layers() const { + return (int)vib_grid_.dim_size(0); + } + + ///@brief Return the width of the grid at the specified layer + size_t width() const { return vib_grid_.dim_size(1); } + ///@brief Return the height of the grid at the specified layer + size_t height() const { return vib_grid_.dim_size(2); } + + ///@brief Return the size of the flattened grid on the given layer + inline size_t grid_size() const { + return vib_grid_.size(); + } + + const VibInf* get_vib(size_t layer, size_t x, size_t y) const { + return vib_grid_[layer][x][y]; + } + + size_t num_mux_nodes(size_t layer, size_t x, size_t y) const { + return vib_grid_[layer][x][y]->get_first_stages().size(); + } + + std::string mux_node_name(size_t layer, size_t x, size_t y, size_t mux_index) const { + return vib_grid_[layer][x][y]->get_first_stages()[mux_index].mux_name; + } + + std::string vib_pbtype_name(size_t layer, size_t x, size_t y) const { + return vib_grid_[layer][x][y]->get_pbtype_name(); + } + + bool is_empty() const { + return vib_grid_.empty(); + } + + private: + std::string name_; + + /** + * @brief grid_ is a 3D matrix that represents the grid of the FPGA chip. + * @note The first dimension is the layer number (grid_[0] corresponds to the bottom layer), the second dimension is the x coordinate, and the third dimension is the y coordinate. + * @note Note that vtr::Matrix operator[] returns and intermediate type + * @note which can be used for indexing in the second dimension, allowing + * @note traditional 2-d indexing to be used + */ + vtr::NdMatrix vib_grid_; //This stores the grid of complex blocks. It is a 3D matrix: [0..num_layers-1][0..grid.width()-1][0..grid_height()-1] +}; diff --git a/libs/librrgraph/src/base/check_rr_graph.cpp b/libs/librrgraph/src/base/check_rr_graph.cpp index c7830905a80..0adda764668 100644 --- a/libs/librrgraph/src/base/check_rr_graph.cpp +++ b/libs/librrgraph/src/base/check_rr_graph.cpp @@ -50,6 +50,7 @@ void check_rr_graph(const RRGraphView& rr_graph, const std::vector& types, const vtr::vector& rr_indexed_data, const DeviceGrid& grid, + const VibDeviceGrid& vib_grid, const t_chan_width& chan_width, const e_graph_type graph_type, bool is_flat) { @@ -81,7 +82,7 @@ void check_rr_graph(const RRGraphView& rr_graph, e_rr_type rr_type = rr_graph.node_type(rr_node); int num_edges = rr_graph.num_edges(RRNodeId(inode)); - check_rr_node(rr_graph, rr_indexed_data, grid, chan_width, route_type, inode, is_flat); + check_rr_node(rr_graph, rr_indexed_data, grid, vib_grid, chan_width, route_type, inode, is_flat); // Check all the connectivity (edges, etc.) information. edges.resize(0); @@ -268,7 +269,8 @@ void check_rr_graph(const RRGraphView& rr_graph, || (rr_graph.node_xhigh(rr_node) == int(grid.width()) - 2) || (rr_graph.node_yhigh(rr_node) == int(grid.height()) - 2)); bool is_wire = (rr_graph.node_type(rr_node) == e_rr_type::CHANX - || rr_graph.node_type(rr_node) == e_rr_type::CHANY); + || rr_graph.node_type(rr_node) == e_rr_type::CHANY + || rr_graph.node_type(rr_node) == e_rr_type::MUX); if (!is_chain && !is_fringe && !is_wire) { if (rr_graph.node_type(rr_node) == e_rr_type::IPIN || rr_graph.node_type(rr_node) == e_rr_type::OPIN) { @@ -325,6 +327,7 @@ static bool rr_node_is_global_clb_ipin(const RRGraphView& rr_graph, const Device void check_rr_node(const RRGraphView& rr_graph, const vtr::vector& rr_indexed_data, const DeviceGrid& grid, + const VibDeviceGrid& vib_grid, const t_chan_width& chan_width, const enum e_route_type route_type, const int inode, @@ -401,6 +404,7 @@ void check_rr_node(const RRGraphView& rr_graph, } break; } + case e_rr_type::MUX: case e_rr_type::IPIN: case e_rr_type::OPIN: if (type == nullptr) { @@ -414,7 +418,7 @@ void check_rr_node(const RRGraphView& rr_graph, break; case e_rr_type::CHANX: - if (xlow < 1 || xhigh > grid_width - 1 || yhigh > grid_height - 1 || yhigh != ylow) { + if (xlow < 0 || xhigh > grid_width - 1 || yhigh > grid_height - 2 || yhigh != ylow) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "in check_rr_node: CHANX out of range for endpoints (%d,%d) and (%d,%d)\n", xlow, ylow, xhigh, yhigh); } @@ -425,7 +429,7 @@ void check_rr_node(const RRGraphView& rr_graph, break; case e_rr_type::CHANY: - if (xhigh > grid_width - 1 || ylow < 1 || yhigh > grid_height - 1 || xlow != xhigh) { + if (xhigh > grid_width - 2 || ylow < 0 || yhigh > grid_height - 1 || xlow != xhigh) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Error in check_rr_node: CHANY out of range for endpoints (%d,%d) and (%d,%d)\n", xlow, ylow, xhigh, yhigh); } @@ -452,6 +456,20 @@ void check_rr_node(const RRGraphView& rr_graph, int class_max_ptc = get_tile_class_max_ptc(type, is_flat); int pin_max_ptc = get_tile_pin_max_ptc(type, is_flat); + + // TODO: This is a temporary fix to ensure that the VIB architecture is supported. + // This should be removed and ** grid ** should be used instead. + // If VIB architecture is not used, these are not going to have any effect. + int mux_max_ptc = -1; + const VibInf* vib_type = nullptr; + if (vib_grid.get_num_layers() > 0) { + vib_type = vib_grid.get_vib(layer_num, xlow, ylow); + } + if (vib_type) { + mux_max_ptc = (int)vib_type->get_first_stages().size(); + } + + e_pin_type class_type = OPEN; int class_num_pins = -1; switch (rr_type) { @@ -469,7 +487,17 @@ void check_rr_node(const RRGraphView& rr_graph, "in check_rr_node: inode %d (type %d) had a capacity of %d.\n", inode, rr_type, capacity); } break; - + case e_rr_type::MUX: + VTR_ASSERT(mux_max_ptc >= 0); + if (ptc_num >= mux_max_ptc) { + VPR_ERROR(VPR_ERROR_ROUTE, + "in check_rr_node: inode %d (type %d) had a ptc_num of %d.\n", inode, rr_type, ptc_num); + } + if (capacity != 1) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "in check_rr_node: inode %d (type %d) has a capacity of %d.\n", inode, rr_type, capacity); + } + break; case e_rr_type::OPIN: case e_rr_type::IPIN: class_type = get_pin_type_from_pin_physical_num(type, ptc_num); diff --git a/libs/librrgraph/src/base/check_rr_graph.h b/libs/librrgraph/src/base/check_rr_graph.h index 4ace55ba0fc..e436e92f247 100644 --- a/libs/librrgraph/src/base/check_rr_graph.h +++ b/libs/librrgraph/src/base/check_rr_graph.h @@ -8,6 +8,7 @@ void check_rr_graph(const RRGraphView& rr_graph, const std::vector& types, const vtr::vector& rr_indexed_data, const DeviceGrid& grid, + const VibDeviceGrid& vib_grid, const t_chan_width& chan_width, const e_graph_type graph_type, bool is_flat); @@ -36,6 +37,7 @@ void check_rr_graph(const RRGraphView& rr_graph, void check_rr_node(const RRGraphView& rr_graph, const vtr::vector& rr_indexed_data, const DeviceGrid& grid, + const VibDeviceGrid& vib_grid, const t_chan_width& chan_width, const enum e_route_type route_type, const int inode, diff --git a/libs/librrgraph/src/base/get_parallel_segs.cpp b/libs/librrgraph/src/base/get_parallel_segs.cpp index 2fe07788a19..b9c0ad81a2c 100644 --- a/libs/librrgraph/src/base/get_parallel_segs.cpp +++ b/libs/librrgraph/src/base/get_parallel_segs.cpp @@ -2,14 +2,17 @@ std::vector get_parallel_segs(const std::vector& segment_inf, t_unified_to_parallel_seg_index& seg_index_map, - enum e_parallel_axis parallel_axis) { + e_parallel_axis parallel_axis, + bool keep_original_index /* = false */) { std::vector result; for (size_t i = 0; i < segment_inf.size(); ++i) { - if (segment_inf[i].parallel_axis == parallel_axis || segment_inf[i].parallel_axis == BOTH_AXIS) { + if (segment_inf[i].parallel_axis == parallel_axis || segment_inf[i].parallel_axis == e_parallel_axis::BOTH_AXIS) { result.push_back(segment_inf[i]); - result[result.size() - 1].seg_index = i; + if (!keep_original_index) { + result.back().seg_index = i; + } seg_index_map.insert(std::make_pair(i, std::make_pair(result.size() - 1, parallel_axis))); } } return result; -} \ No newline at end of file +} diff --git a/libs/librrgraph/src/base/get_parallel_segs.h b/libs/librrgraph/src/base/get_parallel_segs.h index cfd1189417f..47e64a575e2 100644 --- a/libs/librrgraph/src/base/get_parallel_segs.h +++ b/libs/librrgraph/src/base/get_parallel_segs.h @@ -13,8 +13,11 @@ * @param segment_inf Unified list of all segments. * @param seg_index_map Map from unified to axis-specific segment indices. * @param parallel_axis Axis to filter segments by. + * @param keep_original_index Whether to keep the original index of the segment. Currently, + * it is only set to true when building the tileable rr_graph. * @return Filtered list of segments for the given axis. */ std::vector get_parallel_segs(const std::vector& segment_inf, t_unified_to_parallel_seg_index& seg_index_map, - enum e_parallel_axis parallel_axis); + e_parallel_axis parallel_axis, + bool keep_original_index = false); diff --git a/libs/librrgraph/src/base/rr_graph_builder.cpp b/libs/librrgraph/src/base/rr_graph_builder.cpp index a2ebdb0c0dc..3c2cef3ca3f 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.cpp +++ b/libs/librrgraph/src/base/rr_graph_builder.cpp @@ -1,10 +1,15 @@ +#include "vtr_assert.h" #include "vtr_log.h" #include "rr_graph_builder.h" #include "vtr_time.h" +#include "vtr_util.h" #include #include -RRGraphBuilder::RRGraphBuilder() {} +RRGraphBuilder::RRGraphBuilder() { + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; +} t_rr_graph_storage& RRGraphBuilder::rr_nodes() { return node_storage_; @@ -22,6 +27,14 @@ MetadataStorage>& RRGraphBuilder::rr_edge_metadata() return rr_edge_metadata_; } +vtr::vector>& RRGraphBuilder::node_in_edge_storage() { + return node_in_edges_; +} + +vtr::vector>& RRGraphBuilder::node_ptc_storage() { + return node_tilable_track_nums_; +} + void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { e_rr_type node_type = node_storage_.node_type(node); short node_ptc_num = node_storage_.node_ptc_num(node); @@ -56,6 +69,27 @@ void RRGraphBuilder::add_node_to_all_locs(RRNodeId node) { } } +RRNodeId RRGraphBuilder::create_node(int layer, int x, int y, e_rr_type type, int ptc, e_side side) { + e_side node_side = TOTAL_2D_SIDES[0]; + // Only OPIN and IPIN nodes have sides, otherwise force to use a default side + if (e_rr_type::OPIN == type || e_rr_type::IPIN == type) { + node_side = side; + } + node_storage_.emplace_back(); + node_tilable_track_nums_.emplace_back(); + RRNodeId new_node = RRNodeId(node_storage_.size() - 1); + node_storage_.set_node_layer(new_node, layer); + node_storage_.set_node_type(new_node, type); + node_storage_.set_node_coordinates(new_node, x, y, x, y); + node_storage_.set_node_ptc_num(new_node, ptc); + if (e_rr_type::OPIN == type || e_rr_type::IPIN == type) { + node_storage_.add_node_side(new_node, node_side); + } + node_lookup_.add_node(new_node, layer, x, y, type, ptc, node_side); + + return new_node; +} + void RRGraphBuilder::init_edge_remap(bool val) { node_storage_.init_edge_remap(val); } @@ -67,10 +101,15 @@ void RRGraphBuilder::clear_temp_storage() { void RRGraphBuilder::clear() { node_lookup_.clear(); node_storage_.clear(); + node_in_edges_.clear(); + node_tilable_track_nums_.clear(); rr_node_metadata_.clear(); rr_edge_metadata_.clear(); rr_segments_.clear(); rr_switch_inf_.clear(); + edges_to_build_.clear(); + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; } void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_nodes_algorithm, @@ -126,7 +165,7 @@ void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_ } vtr::vector dest_order(v_num); cur_idx = 0; - for (auto u : src_order) + for (RRNodeId u : src_order) dest_order[u] = RRNodeId(cur_idx++); VTR_ASSERT_SAFE(node_storage_.validate(rr_switch_inf_)); @@ -142,3 +181,131 @@ void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_ std::get<2>(edge)); }); } + +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; +} + +void RRGraphBuilder::build_edges(const bool& uniquify) { + if (uniquify) { + std::sort(edges_to_build_.begin(), edges_to_build_.end()); + edges_to_build_.erase(std::unique(edges_to_build_.begin(), edges_to_build_.end()), edges_to_build_.end()); + } + alloc_and_load_edges(&edges_to_build_); + edges_to_build_.clear(); + is_edge_dirty_ = false; +} + +void RRGraphBuilder::build_in_edges() { + VTR_ASSERT(validate()); + node_in_edges_.clear(); + node_in_edges_.resize(node_storage_.size()); + + for (RRNodeId src_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + for (t_edge_size iedge : node_storage_.edges(src_node)) { + VTR_ASSERT(src_node == node_storage_.edge_source_node(node_storage_.edge_id(src_node, iedge))); + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, iedge)); + node_in_edges_[des_node].push_back(node_storage_.edge_id(src_node, iedge)); + } + } + is_incoming_edge_dirty_ = false; +} + +std::vector RRGraphBuilder::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (is_incoming_edge_dirty_) { + VTR_LOG_ERROR("Incoming edges are not built yet in routing resource graph. Please call build_in_edges()."); + return std::vector(); + } + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +void RRGraphBuilder::set_node_ptc_nums(RRNodeId node, const std::string& ptc_str) { + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ptc_tokens = vtr::StringToken(ptc_str).split(","); + VTR_ASSERT(ptc_tokens.size() >= 1); + set_node_ptc_num(node, std::stoi(ptc_tokens[0])); + if (ptc_tokens.size() > 1) { + VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); + node_tilable_track_nums_[node].resize(ptc_tokens.size()); + for (size_t iptc = 0; iptc < ptc_tokens.size(); iptc++) { + node_tilable_track_nums_[node][iptc] = std::stoi(ptc_tokens[iptc]); + } + } +} + +std::string RRGraphBuilder::node_ptc_nums_to_string(RRNodeId node) const { + if (node_tilable_track_nums_.empty()) { + return std::to_string(size_t(node_storage_.node_ptc_num(node))); + } + VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); + if (node_tilable_track_nums_[node].empty()) { + return std::to_string(size_t(node_storage_.node_ptc_num(node))); + } + std::string ret; + for (size_t iptc = 0; iptc < node_tilable_track_nums_[node].size(); iptc++) { + ret += std::to_string(size_t(node_tilable_track_nums_[node][iptc])) + ","; + } + // Remove the last comma + ret.pop_back(); + return ret; +} + +bool RRGraphBuilder::node_contain_multiple_ptc(RRNodeId node) const { + if (node_tilable_track_nums_.empty()) { + return false; + } + return node_tilable_track_nums_[node].size() > 1; +} + +void RRGraphBuilder::add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id) { + VTR_ASSERT(size_t(node) < node_storage_.size()); + VTR_ASSERT(size_t(node) < node_tilable_track_nums_.size()); + VTR_ASSERT_MSG(node_storage_.node_type(node) == e_rr_type::CHANX || node_storage_.node_type(node) == e_rr_type::CHANY, "Track number valid only for CHANX/CHANY RR nodes"); + + size_t node_length = std::abs(node_storage_.node_xhigh(node) - node_storage_.node_xlow(node)) + + std::abs(node_storage_.node_yhigh(node) - node_storage_.node_ylow(node)); + if (node_length + 1 != node_tilable_track_nums_[node].size()) { + node_tilable_track_nums_[node].resize(node_length + 1); + } + + size_t offset = node_offset.x() - node_storage_.node_xlow(node) + node_offset.y() - node_storage_.node_ylow(node); + VTR_ASSERT(offset < node_tilable_track_nums_[node].size()); + + node_tilable_track_nums_[node][offset] = track_id; +} + +void RRGraphBuilder::add_track_node_to_lookup(RRNodeId node) { + VTR_ASSERT_MSG(node_storage_.node_type(node) == e_rr_type::CHANX || node_storage_.node_type(node) == e_rr_type::CHANY, "Update track node look-up is only valid to CHANX/CHANY nodes"); + + // Compute the track id based on the (x, y) coordinate + size_t x_start = std::min(node_storage_.node_xlow(node), node_storage_.node_xhigh(node)); + size_t y_start = std::min(node_storage_.node_ylow(node), node_storage_.node_yhigh(node)); + std::vector node_x(std::abs(node_storage_.node_xlow(node) - node_storage_.node_xhigh(node)) + 1); + std::vector node_y(std::abs(node_storage_.node_ylow(node) - node_storage_.node_yhigh(node)) + 1); + + std::iota(node_x.begin(), node_x.end(), x_start); + std::iota(node_y.begin(), node_y.end(), y_start); + + VTR_ASSERT(size_t(std::max(node_storage_.node_xlow(node), node_storage_.node_xhigh(node))) == node_x.back()); + VTR_ASSERT(size_t(std::max(node_storage_.node_ylow(node), node_storage_.node_yhigh(node))) == node_y.back()); + + for (const size_t x : node_x) { + for (const size_t y : node_y) { + size_t ptc = node_storage_.node_ptc_num(node); + e_rr_type node_type = node_storage_.node_type(node); + // Routing channel nodes may have different ptc num + // Find the track ids using the x/y offset + if (e_rr_type::CHANX == node_type || e_rr_type::CHANY == node_type) { + ptc = (node_type == e_rr_type::CHANX) ? node_tilable_track_nums_[node][x - node_storage_.node_xlow(node)] : + node_tilable_track_nums_[node][y - node_storage_.node_ylow(node)]; + node_lookup_.add_node(node, node_storage_.node_layer(node), x, y, node_type, ptc); + } + } + } +} diff --git a/libs/librrgraph/src/base/rr_graph_builder.h b/libs/librrgraph/src/base/rr_graph_builder.h index 62580184118..f6b41150216 100644 --- a/libs/librrgraph/src/base/rr_graph_builder.h +++ b/libs/librrgraph/src/base/rr_graph_builder.h @@ -15,6 +15,7 @@ #include "rr_graph_storage.h" #include "rr_spatial_lookup.h" #include "metadata_storage.h" +#include "rr_edge.h" class RRGraphBuilder { /* -- Constructors -- */ @@ -35,14 +36,23 @@ class RRGraphBuilder { public: /** @brief Return a writable object for rr_nodes */ t_rr_graph_storage& rr_nodes(); + /** @brief Return a writable object for update the fast look-up of rr_node */ RRSpatialLookup& node_lookup(); + /** @warning The Metadata should stay as an independent data structure from the rest of the internal data, * e.g., node_lookup! */ /** @brief Return a writable object for the meta data on the nodes */ MetadataStorage& rr_node_metadata(); + /** @brief Return a writable object for the meta data on the edge */ MetadataStorage>& rr_edge_metadata(); + + /** @brief Return a writable object fo the incoming edge storage */ + vtr::vector>& node_in_edge_storage(); + + /** @brief Return a writable object of the node ptc storage (for tileable routing resource graph) */ + vtr::vector>& node_ptc_storage(); /** @brief Return the size for rr_node_metadata */ inline size_t rr_node_metadata_size() const { @@ -122,10 +132,17 @@ class RRGraphBuilder { node_storage_.set_node_type(id, type); } + /** @brief Create a new rr_node in the node storage and register it to the node look-up. + * Return a valid node id if succeed. Otherwise, return an invalid id. This function is + * currently only used when building the tileable rr_graph. + */ + RRNodeId create_node(int layer, int x, int y, e_rr_type type, int ptc, e_side side = NUM_2D_SIDES); + /** @brief Set the node name with a given valid id */ inline void set_node_name(RRNodeId id, std::string name) { node_storage_.set_node_name(id, name); } + /** * @brief Add an existing rr_node in the node storage to the node look-up * @@ -181,9 +198,30 @@ class RRGraphBuilder { node_storage_.set_node_coordinates(id, x1, y1, x2, y2); } - /** @brief Set the node layer (specifies which die the node is located at) */ - inline void set_node_layer(RRNodeId id, short layer){ - node_storage_.set_node_layer(id,layer); + /** @brief Set the tileable flag. This function is + * used by tileable routing resource graph builder + * only since the value of this flag is set to false by default. + */ + inline void set_tileable(bool is_tileable) { + node_storage_.set_tileable(is_tileable); + } + + /** + * @brief Set the bend start of a node + * @param id The node id + * @param bend_start The bend start + */ + inline void set_node_bend_start(RRNodeId id, size_t bend_start) { + node_storage_.set_node_bend_start(id, bend_start); + } + + /** + * @brief Set the bend end of a node + * @param id The node id + * @param bend_end The bend end + */ + inline void set_node_bend_end(RRNodeId id, size_t bend_end) { + node_storage_.set_node_bend_end(id, bend_end); } /** @brief The ptc_num carries different meanings for different node types @@ -216,16 +254,69 @@ class RRGraphBuilder { node_storage_.set_node_track_num(id, new_track_num); } - /** @brief set_ node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ + // ** The following functions are only used for tileable routing resource graph generator ** + + /** @brief Add a track id for a given node base on the offset in coordinate, applicable only to CHANX and CHANY nodes. + * This API is used by tileable routing resource graph generator, which requires each routing track has a different + * track id depending their location in FPGA fabric. + * + * @param node The node to add the track id to. + * @param node_offset Location of the portion of the node being considered. It is used + * to calculate the relative location from the beginning of the node. + * @param track_id The track id to add to the node. + */ + void add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id); + + /** @brief Update the node_lookup for a track node. This is applicable to tileable routing graph */ + void add_track_node_to_lookup(RRNodeId node); + + /** @brief set_node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ inline void set_node_class_num(RRNodeId id, int new_class_num) { node_storage_.set_node_class_num(id, new_class_num); } + /** @brief set_node_mux_num() is designed for routing mux nodes */ + inline void set_node_mux_num(RRNodeId id, int new_mux_num) { + node_storage_.set_node_mux_num(id, new_mux_num); + } + + /** @brief Add a list of ptc number in string (split by comma) to a given node. This function is used by rr graph reader only. */ + void set_node_ptc_nums(RRNodeId node, const std::string& ptc_str); + + /** @brief With a given node, output ptc numbers into a string (use comma as delima). This function is used by rr graph writer only. */ + std::string node_ptc_nums_to_string(RRNodeId node) const; + + /** @brief Identify if a node contains multiple ptc numbers. It is used for tileable RR Graph and mainly used by I/O reader only. */ + bool node_contain_multiple_ptc(RRNodeId node) const; + /** @brief Set the node direction; The node direction is only available of routing channel nodes, such as x-direction routing tracks (CHANX) and y-direction routing tracks (CHANY). For other nodes types, this value is not meaningful and should be set to NONE. */ inline void set_node_direction(RRNodeId id, Direction new_direction) { node_storage_.set_node_direction(id, new_direction); } + /** @brief Add a new edge to the cache of edges to be built + * @note This will not add an edge to storage. You need to call build_edges() after all the edges are cached. */ + void create_edge_in_cache(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped); + + /** @brief Allocate and build actual edges in storage. + * Once called, the cached edges will be uniquified and added to routing resource nodes, + * while the cache will be empty once build-up is accomplished + */ + void build_edges(const bool& uniquify = true); + + /** @brief Allocate and build incoming edges for each node. + * By default, no incoming edges are kept in storage, to be memory efficient + * Currently, this function is only called when building the tileable rr_graph. + */ + void build_in_edges(); + + /** @brief Return incoming edges for a given routing resource node + * Require build_in_edges() to be called first + */ + std::vector node_in_edges(RRNodeId node) const; + + // ** End of functions for tileable routing resource graph generator ** + /** @brief Set the node id for clock network virtual sink */ inline void set_virtual_clock_network_root_idx(RRNodeId virtual_clock_network_root_idx) { node_storage_.set_virtual_clock_network_root_idx(virtual_clock_network_root_idx); @@ -319,19 +410,25 @@ class RRGraphBuilder { inline void resize_nodes(size_t size) { node_storage_.resize(size); } + /** @brief This function resize node ptc nums. Only used by RR graph I/O reader and writers. */ + inline void resize_node_ptc_nums(size_t size) { + node_tilable_track_nums_.resize(size); + } + /** @brief This function resize rr_switch to accomidate size RR Switch. */ inline void resize_switches(size_t size) { rr_switch_inf_.resize(size); } - /** @brief Validate that edge data is partitioned correctly + /** @brief Validate that edge data is partitioned correctly. This function should be called + * when all edges in cache are added. * @note This function is used to validate the correctness of the routing resource graph in terms * of graph attributes. Strongly recommend to call it when you finish the building a routing resource * graph. If you need more advance checks, which are related to architecture features, you should * consider to use the check_rr_graph() function or build your own check_rr_graph() function. */ inline bool validate() const { - return node_storage_.validate(rr_switch_inf_); + return node_storage_.validate(rr_switch_inf_) && edges_to_build_.empty(); } /** @brief Sorts edge data such that configurable edges appears before @@ -378,25 +475,78 @@ class RRGraphBuilder { /* Fast look-up for rr nodes */ RRSpatialLookup node_lookup_; - /** Wire segment types in RR graph + /** + * @brief A cache for edge-related information, required to build edges for routing resource nodes. + * @note It is used when building a routing resource graph. It is a set of edges that have not yet been + * added to the main rr-graph edge storage to avoid an expensive edge-by-edge reallocation or re-shuffling + * of edges in the main rr-graph edge storage. + * + * @note It will be cleared after calling build_edges(). + * + * @note This data structure is only used for tileable routing resource graph generator. + * + * @warning This is a temporary data which is used to collect edges to be built for nodes + */ + t_rr_edge_info_set edges_to_build_; + + /** + * @brief Wire segment types in RR graph + * @details * - Each rr_segment contains the detailed information of a routing track, which is denoted by a node in CHANX or CHANY type. * - We use a fly-weight data structure here, in the same philosophy as the rr_indexed_data. See detailed explanation in the t_segment_inf data structure */ vtr::vector rr_segments_; /* detailed information about the segments, which are used in the RRGraph */ - vtr::vector segment_ids_; /* unique identifiers for routing segments which are used in the RRGraph */ - /* Autogenerated in build_rr_graph based on switch fan-in. + + /** + * @brief Unique identifiers for routing segments which are used in the RRGraph + */ + vtr::vector segment_ids_; + + /** + * @brief Autogenerated in build_rr_graph based on switch fan-in. + * @details * - Each rr_switch contains the detailed information of a routing switch interconnecting two routing resource nodes. * - We use a fly-weight data structure here, in the same philosophy as the rr_indexed_data. See detailed explanation in the t_rr_switch_inf data structure */ - /* Detailed information about the switches, which are used in the RRGraph */ vtr::vector rr_switch_inf_; + /** + * @brief A list of incoming edges for each routing resource node. + * @note This can be built optionally, as required by applications. + * By default, it is empty! Call build_in_edges() to construct it. + */ + vtr::vector> node_in_edges_; + + /** + * @brief Extra ptc number for each routing resource node. + * @note This is required by tileable routing resource graphs. The first index is the node id, and + * the second index is is the relative distance from the starting point of the node. + * @details + * In a tileable routing architecture, routing tracks, e.g., CHANX and CHANY, follow a staggered organization. + * Hence, a routing track may appear in different routing channels, representing different ptc/track id. + * Here is an illustrative example of a X-direction routing track (CHANX) in INC direction, which is organized in staggered way. + * + * Coord(x,y) (1,0) (2,0) (3,0) (4,0) Another track (node) + * ptc=0 ------> ------> + * \ / + * ptc=1 ------> / + * \ / + * ptc=2 ------> / + * \ / + * ptc=3 -------> + * ^ ^ + * | | + * starting point ending point + */ + vtr::vector> node_tilable_track_nums_; + /** @warning The Metadata should stay as an independent data structure from the rest of the internal data, * e.g., node_lookup! */ /* Metadata is an extra data on rr-nodes and edges, respectively, that is not used by vpr * but simply passed through the flow so that it can be used by downstream tools. * The main (perhaps only) current use of this metadata is the fasm tool of symbiflow, * which needs extra metadata on which programming bits control which switch in order to produce a bitstream.*/ + /** * @brief Attributes for each rr_node. * @@ -415,4 +565,16 @@ class RRGraphBuilder { * value: map of */ MetadataStorage> rr_edge_metadata_; + + /** + * @brief This flag indicates if all the edges in cache are added to the main rr-graph edge storage. + * To add all edges in cache to the main rr-graph edge storage, call build_edges(). + */ + bool is_edge_dirty_; + + /** + * @brief This flag indicates whether node_in_edges_ is updated with + * edges in the main rr-graph edge storage. + */ + bool is_incoming_edge_dirty_; }; diff --git a/libs/librrgraph/src/base/rr_graph_cost.h b/libs/librrgraph/src/base/rr_graph_cost.h index dc651f72344..4024cd68926 100644 --- a/libs/librrgraph/src/base/rr_graph_cost.h +++ b/libs/librrgraph/src/base/rr_graph_cost.h @@ -14,6 +14,7 @@ enum e_base_cost_type { enum e_cost_indices { SOURCE_COST_INDEX = 0, SINK_COST_INDEX, + MUX_COST_INDEX, OPIN_COST_INDEX, IPIN_COST_INDEX, CHANX_COST_INDEX_START diff --git a/libs/librrgraph/src/base/rr_graph_storage.cpp b/libs/librrgraph/src/base/rr_graph_storage.cpp index 4466eb59034..3e9edec2520 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.cpp +++ b/libs/librrgraph/src/base/rr_graph_storage.cpp @@ -388,7 +388,10 @@ void t_rr_graph_storage::assign_first_edges() { bool t_rr_graph_storage::verify_first_edges() const { size_t num_edges = edge_src_node_.size(); - VTR_ASSERT(node_first_edge_[RRNodeId(node_storage_.size())] == RREdgeId(num_edges)); + VTR_ASSERT_MSG(node_first_edge_[RRNodeId(node_storage_.size())] == RREdgeId(num_edges), + vtr::string_fmt("node first edge is '%lu' while expected edge id is '%lu'\n", + size_t(node_first_edge_[RRNodeId(node_storage_.size())]), + num_edges).c_str()); // Each edge should belong with the edge range defined by // [node_first_edge_[src_node], node_first_edge_[src_node+1]). @@ -571,8 +574,13 @@ t_edge_size t_rr_graph_storage::num_non_configurable_edges(RRNodeId node, const return num_edges(node) - num_configurable_edges(node, rr_switches); } +bool t_rr_graph_storage::edge_is_configurable(RREdgeId edge, const vtr::vector& rr_switches) const { + short iswitch = edge_switch(edge); + return rr_switches[RRSwitchId(iswitch)].configurable(); +} + bool t_rr_graph_storage::edge_is_configurable(RRNodeId id, t_edge_size iedge, const vtr::vector& rr_switches) const { - auto iswitch = edge_switch(id, iedge); + short iswitch = edge_switch(id, iedge); return rr_switches[RRSwitchId(iswitch)].configurable(); } @@ -659,6 +667,13 @@ void t_rr_graph_storage::set_node_class_num(RRNodeId id, int new_class_num) { node_ptc_[id].ptc_.class_num = new_class_num; } +void t_rr_graph_storage::set_node_mux_num(RRNodeId id, int new_mux_num) { + if (node_type(id) != e_rr_type::MUX) { + VTR_LOG_ERROR("Attempted to set RR node 'mux_num' for non-MUX type '%s'\n", node_type_string(id)); + } + node_ptc_[id].ptc_.mux_num = new_mux_num; +} + int t_rr_graph_storage::node_ptc_num(RRNodeId id) const { return node_ptc_[id].ptc_.pin_num; } @@ -693,6 +708,17 @@ static int get_node_class_num(vtr::array_view_id return node_ptc[id].ptc_.class_num; } +static int get_node_mux_num( + vtr::array_view_id node_storage, + vtr::array_view_id node_ptc, + RRNodeId id) { + e_rr_type node_type = node_storage[id].type_; + if (node_type != e_rr_type::MUX) { + VTR_LOG_ERROR("Attempted to access RR node 'mux_num' for non-MUX type '%s'\n", rr_node_typename[node_type]); + } + return node_ptc[id].ptc_.mux_num; +} + int t_rr_graph_storage::node_pin_num(RRNodeId id) const { return get_node_pin_num( vtr::make_const_array_view_id(node_storage_), @@ -711,6 +737,12 @@ int t_rr_graph_storage::node_class_num(RRNodeId id) const { vtr::make_const_array_view_id(node_ptc_), id); } +int t_rr_graph_storage::node_mux_num(RRNodeId id) const { + return get_node_mux_num( + vtr::make_const_array_view_id(node_storage_), + vtr::make_const_array_view_id(node_ptc_), + id); +} void t_rr_graph_storage::set_node_type(RRNodeId id, e_rr_type new_type) { node_storage_[id].type_ = new_type; @@ -720,7 +752,7 @@ void t_rr_graph_storage::set_node_name(RRNodeId id, const std::string& new_name) node_name_.insert(std::make_pair(id, new_name)); } void t_rr_graph_storage::set_node_coordinates(RRNodeId id, short x1, short y1, short x2, short y2) { - auto& node = node_storage_[id]; + t_rr_node_data& node = node_storage_[id]; if (x1 < x2) { node.xlow_ = x1; node.xhigh_ = x2; @@ -738,10 +770,18 @@ void t_rr_graph_storage::set_node_coordinates(RRNodeId id, short x1, short y1, s } } +void t_rr_graph_storage::set_node_bend_start(RRNodeId id, size_t bend_start) { + node_bend_start_[id] = bend_start; +} + +void t_rr_graph_storage::set_node_bend_end(RRNodeId id, size_t bend_end) { + node_bend_end_[id] = bend_end; +} + void t_rr_graph_storage::set_node_cost_index(RRNodeId id, RRIndexedDataId new_cost_index) { - auto& node = node_storage_[id]; + t_rr_node_data& node = node_storage_[id]; if ((size_t)new_cost_index >= std::numeric_limits::max()) { - VTR_LOG_ERROR("Attempted to set cost_index_ %zu above cost_index storage max value.", + VTR_LOG_ERROR("Attempted to set cost_index_ %zu above cost_index storage max value.\n", new_cost_index); } node.cost_index_ = (size_t)new_cost_index; @@ -792,16 +832,23 @@ void t_rr_graph_storage::set_virtual_clock_network_root_idx(RRNodeId virtual_clo int t_rr_graph_view::node_ptc_num(RRNodeId id) const { return node_ptc_[id].ptc_.pin_num; } + int t_rr_graph_view::node_pin_num(RRNodeId id) const { return get_node_pin_num(node_storage_, node_ptc_, id); } + int t_rr_graph_view::node_track_num(RRNodeId id) const { return get_node_track_num(node_storage_, node_ptc_, id); } + int t_rr_graph_view::node_class_num(RRNodeId id) const { return get_node_class_num(node_storage_, node_ptc_, id); } +int t_rr_graph_view::node_mux_num(RRNodeId id) const { + return get_node_mux_num(node_storage_, node_ptc_, id); +} + t_rr_graph_view t_rr_graph_storage::view() const { VTR_ASSERT(partitioned_); @@ -816,7 +863,9 @@ t_rr_graph_view t_rr_graph_storage::view() const { vtr::make_const_array_view_id(edge_src_node_), vtr::make_const_array_view_id(edge_dest_node_), vtr::make_const_array_view_id(edge_switch_), - virtual_clock_network_root_idx_); + virtual_clock_network_root_idx_, + vtr::make_const_array_view_id(node_bend_start_), + vtr::make_const_array_view_id(node_bend_end_)); } // Given `order`, a vector mapping each RRNodeId to a new one (old -> new), @@ -841,7 +890,7 @@ void t_rr_graph_storage::reorder(const vtr::vector& order, // Reorder nodes for (size_t i = 0; i < node_storage_.size(); i++) { - auto n = RRNodeId(i); + RRNodeId n = RRNodeId(i); VTR_ASSERT(n == inverse_order[order[n]]); node_storage_[order[n]] = old_node_storage[n]; } @@ -857,8 +906,8 @@ void t_rr_graph_storage::reorder(const vtr::vector& order, // Reorder edges by source node for (size_t i = 0; i < node_storage_.size(); i++) { node_first_edge_[RRNodeId(i)] = cur_edge; - auto n = inverse_order[RRNodeId(i)]; - for (auto e = old_node_first_edge[n]; + RRNodeId n = inverse_order[RRNodeId(i)]; + for (RREdgeId e = old_node_first_edge[n]; e < old_node_first_edge[RRNodeId(size_t(n) + 1)]; e = RREdgeId(size_t(e) + 1)) { edge_src_node_[cur_edge] = order[old_edge_src_node[e]]; // == n? diff --git a/libs/librrgraph/src/base/rr_graph_storage.h b/libs/librrgraph/src/base/rr_graph_storage.h index 262107715bd..c4133e2f253 100644 --- a/libs/librrgraph/src/base/rr_graph_storage.h +++ b/libs/librrgraph/src/base/rr_graph_storage.h @@ -63,13 +63,13 @@ struct alignas(16) t_rr_node_data { e_rr_type type_ = e_rr_type::NUM_RR_TYPES; /* The character is a hex number which is a 4-bit truth table for node sides - * The 4-bits in serial represent 4 sides on which a node could appear - * It follows a fixed sequence, which is (LEFT, BOTTOM, RIGHT, TOP) whose indices are (3, 2, 1, 0) + * The 4-bits in serial represent 4 sides on which a node could appear + * It follows a fixed sequence, which is (LEFT, BOTTOM, RIGHT, TOP) whose indices are (3, 2, 1, 0) * - When a node appears on a given side, it is set to "1" * - When a node does not appear on a given side, it is set to "0" * For example, - * - '1' means '0001' in hex number, which means the node appears on TOP - * - 'A' means '1100' in hex number, which means the node appears on LEFT and BOTTOM sides, + * - '1' means '0001' in hex number, which means the node appears on TOP + * - 'A' means '1100' in hex number, which means the node appears on LEFT and BOTTOM sides, */ union { Direction direction; //Valid only for CHANX/CHANY @@ -96,6 +96,7 @@ struct t_rr_node_ptc_data { int pin_num; int track_num; int class_num; + int mux_num; } ptc_; }; @@ -183,6 +184,18 @@ class t_rr_graph_storage { return node_storage_[id].yhigh_; } + void set_tileable(bool is_tileable) { + is_tileable_ = is_tileable; + } + + short node_bend_start(RRNodeId id) const { + return node_bend_start_[id]; + } + + short node_bend_end(RRNodeId id) const { + return node_bend_end_[id]; + } + short node_capacity(RRNodeId id) const { return node_storage_[id].capacity_; } @@ -220,6 +233,7 @@ class t_rr_graph_storage { int node_pin_num(RRNodeId id) const; //Same as ptc_num() but checks that type() is consistent int node_track_num(RRNodeId id) const; //Same as ptc_num() but checks that type() is consistent int node_class_num(RRNodeId id) const; //Same as ptc_num() but checks that type() is consistent + int node_mux_num(RRNodeId id) const; //Same as ptc_num() but checks that type() is consistent /** @brief Retrieve fan_in for RRNodeId, init_fan_in must have been called first. */ t_edge_size fan_in(RRNodeId id) const { @@ -323,24 +337,26 @@ class t_rr_graph_storage { * - num_non_configurable_edges(RRNodeId) * - edge_id(RRNodeId, t_edge_size) * - edge_sink_node(RRNodeId, t_edge_size) + * - edge_source_node(RRNodeId, t_edge_size) * - edge_switch(RRNodeId, t_edge_size) * * Only call these methods after partition_edges has been invoked. */ - edge_idx_range edges(const RRNodeId& id) const { + edge_idx_range edges(const RRNodeId id) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); } - edge_idx_range configurable_edges(const RRNodeId& id, const vtr::vector& rr_switches) const { + edge_idx_range configurable_edges(const RRNodeId id, const vtr::vector& rr_switches) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id) - num_non_configurable_edges(id, rr_switches))); } - edge_idx_range non_configurable_edges(const RRNodeId& id, const vtr::vector& rr_switches) const { + edge_idx_range non_configurable_edges(const RRNodeId id, const vtr::vector& rr_switches) const { return vtr::make_range(edge_idx_iterator(num_edges(id) - num_non_configurable_edges(id, rr_switches)), edge_idx_iterator(num_edges(id))); } - t_edge_size num_edges(const RRNodeId& id) const { + t_edge_size num_edges(const RRNodeId id) const { return size_t(last_edge(id)) - size_t(first_edge(id)); } + bool edge_is_configurable(RREdgeId edge, const vtr::vector& rr_switches) const; bool edge_is_configurable(RRNodeId id, t_edge_size iedge, const vtr::vector& rr_switches) const; t_edge_size num_configurable_edges(RRNodeId node, const vtr::vector& rr_switches) const; t_edge_size num_non_configurable_edges(RRNodeId node, const vtr::vector& rr_switches) const; @@ -352,7 +368,7 @@ class t_rr_graph_storage { * * If first_edge == last_edge, then a RRNodeId has no edges. */ - RREdgeId first_edge(const RRNodeId& id) const { + RREdgeId first_edge(const RRNodeId id) const { return node_first_edge_[id]; } @@ -361,7 +377,7 @@ class t_rr_graph_storage { * we always allocate that dummy node. We also assume that the edges have * been sorted by rr_node, which is true after partition_edges(). */ - RREdgeId last_edge(const RRNodeId& id) const { + RREdgeId last_edge(const RRNodeId id) const { return (&node_first_edge_[id])[1]; } @@ -399,19 +415,32 @@ class t_rr_graph_storage { return RREdgeId::INVALID(); } - /** @brief Get the source node for the specified edge. */ - RRNodeId edge_src_node(const RREdgeId& edge) const { + /** + * @brief Get the source node for the specified edge. + */ + RRNodeId edge_src_node(const RREdgeId edge) const { VTR_ASSERT_DEBUG(edge.is_valid()); return edge_src_node_[edge]; } - /** @brief Get the destination node for the specified edge. */ - RRNodeId edge_sink_node(const RREdgeId& edge) const { + /** + * @brief Get the destination node for the specified edge. + */ + RRNodeId edge_sink_node(const RREdgeId edge) const { VTR_ASSERT_DEBUG(edge.is_valid()); return edge_dest_node_[edge]; } - /** @brief Call the `apply` function with the edge id, source, and sink nodes of every edge. */ + /** + * @brief Get the source node for the specified edge. + */ + RRNodeId edge_source_node(const RREdgeId edge) const { + return edge_src_node_[edge]; + } + + /** + * @brief Call the `apply` function with the edge id, source, and sink nodes of every edge. + */ void for_each_edge(std::function apply) const { for (size_t i = 0; i < edge_dest_node_.size(); i++) { RREdgeId edge(i); @@ -419,30 +448,42 @@ class t_rr_graph_storage { } } - /** @brief Get the destination node for the iedge'th edge from specified RRNodeId. + /** + * @brief Get the destination node for the iedge'th edge from specified RRNodeId. * * This method should generally not be used, and instead first_edge and * last_edge should be used. */ - RRNodeId edge_sink_node(const RRNodeId& id, t_edge_size iedge) const { + RRNodeId edge_sink_node(const RRNodeId id, t_edge_size iedge) const { return edge_sink_node(edge_id(id, iedge)); } - /** @brief Get the switch used for the specified edge. */ - short edge_switch(const RREdgeId& edge) const { + /** + * @brief Get the source node for the iedge'th edge from specified RRNodeId. + */ + RRNodeId edge_source_node(const RRNodeId id, t_edge_size iedge) const { + return edge_source_node(edge_id(id, iedge)); + } + + /** + * @brief Get the switch used for the specified edge. + */ + short edge_switch(const RREdgeId edge) const { return edge_switch_[edge]; } - /** @brief Get the switch used for the iedge'th edge from specified RRNodeId. + /** + * @brief Get the switch used for the iedge'th edge from specified RRNodeId. * * This method should generally not be used, and instead first_edge and * last_edge should be used. */ - short edge_switch(const RRNodeId& id, t_edge_size iedge) const { + short edge_switch(const RRNodeId id, t_edge_size iedge) const { return edge_switch(edge_id(id, iedge)); } - /** @brief + /** + * @brief * Node proxy methods * * The following methods implement an interface that appears to be @@ -462,7 +503,6 @@ class t_rr_graph_storage { * methods that use RRNodeId and RREdgeId should be used. * */ - node_idx_iterator begin() const; node_idx_iterator end() const; @@ -489,9 +529,23 @@ class t_rr_graph_storage { */ void make_room_for_node(RRNodeId elem_position) { make_room_in_vector(&node_storage_, size_t(elem_position)); + + // Reserve the capacity based on node_storage_. The capacity is determined in + // make_room_in_vector(), which uses a power-of-two growth pattern to avoid + // growing the vector one element at a time. node_ptc_.reserve(node_storage_.capacity()); node_ptc_.resize(node_storage_.size()); + + node_layer_.reserve(node_storage_.capacity()); node_layer_.resize(node_storage_.size()); + + if (is_tileable_) { + node_bend_start_.reserve(node_storage_.capacity()); + node_bend_start_.resize(node_storage_.size()); + + node_bend_end_.reserve(node_storage_.capacity()); + node_bend_end_.resize(node_storage_.size()); + } } /** @brief Reserve storage for RR nodes. */ @@ -501,6 +555,10 @@ class t_rr_graph_storage { node_storage_.reserve(size); node_ptc_.reserve(size); node_layer_.reserve(size); + if (is_tileable_) { + node_bend_start_.reserve(size); + node_bend_end_.reserve(size); + } } /** @brief Resize node storage to accomidate size RR nodes. */ @@ -510,6 +568,10 @@ class t_rr_graph_storage { node_storage_.resize(size); node_ptc_.resize(size); node_layer_.resize(size); + if (is_tileable_) { + node_bend_start_.resize(size); + node_bend_end_.resize(size); + } } /** @brief Number of RR nodes that can be accessed. */ @@ -531,6 +593,8 @@ class t_rr_graph_storage { node_first_edge_.clear(); node_fan_in_.clear(); node_layer_.clear(); + node_bend_start_.clear(); + node_bend_end_.clear(); node_name_.clear(); virtual_clock_network_root_idx_.clear(); edge_src_node_.clear(); @@ -540,6 +604,7 @@ class t_rr_graph_storage { edges_read_ = false; partitioned_ = false; remapped_edges_ = false; + is_tileable_ = false; } /** @brief @@ -566,6 +631,9 @@ class t_rr_graph_storage { node_first_edge_.shrink_to_fit(); node_fan_in_.shrink_to_fit(); node_layer_.shrink_to_fit(); + node_bend_start_.shrink_to_fit(); + node_bend_end_.shrink_to_fit(); + edge_src_node_.shrink_to_fit(); edge_dest_node_.shrink_to_fit(); edge_switch_.shrink_to_fit(); @@ -579,6 +647,10 @@ class t_rr_graph_storage { node_storage_.emplace_back(); node_ptc_.emplace_back(); node_layer_.emplace_back(); + if (is_tileable_) { + node_bend_start_.emplace_back(); + node_bend_end_.emplace_back(); + } } /** @brief Given `order`, a vector mapping each RRNodeId to a new one (old -> new), @@ -595,12 +667,15 @@ class t_rr_graph_storage { void set_node_pin_num(RRNodeId id, int); //Same as set_ptc_num() by checks type() is consistent void set_node_track_num(RRNodeId id, int); //Same as set_ptc_num() by checks type() is consistent void set_node_class_num(RRNodeId id, int); //Same as set_ptc_num() by checks type() is consistent + void set_node_mux_num(RRNodeId id, int); //Same as set_ptc_num() by checks type() is consistent void set_node_type(RRNodeId id, e_rr_type new_type); void set_node_name(RRNodeId id, const std::string& new_name); void set_node_coordinates(RRNodeId id, short x1, short y1, short x2, short y2); void set_node_layer(RRNodeId id, short layer); void set_node_cost_index(RRNodeId, RRIndexedDataId new_cost_index); + void set_node_bend_start(RRNodeId id, size_t bend_start); + void set_node_bend_end(RRNodeId id, size_t bend_end); void set_node_rc_index(RRNodeId, NodeRCIndex new_rc_index); void set_node_capacity(RRNodeId, short new_capacity); void set_node_direction(RRNodeId, Direction new_direction); @@ -754,8 +829,8 @@ class t_rr_graph_storage { */ static inline bool is_node_on_specific_side( vtr::array_view_id node_storage, - const RRNodeId& id, - const e_side& side) { + const RRNodeId id, + const e_side side) { auto& node_data = node_storage[id]; if (node_data.type_ != e_rr_type::IPIN && node_data.type_ != e_rr_type::OPIN) { VTR_LOG_ERROR("Attempted to access RR node 'side' for non-IPIN/OPIN type '%s'", @@ -874,6 +949,17 @@ class t_rr_graph_storage { */ vtr::vector edge_remapped_; + /** @brief + * The following data structures are only used for tileable routing resource graph. + * The tileable flag is set to true by tileable routing resource graph builder. + * Bend start and end are used to store the bend information for each node. + * Bend start and end are only used for CHANX and CHANY nodes. + * Bend start and end are only used for tileable routing resource graph. + */ + bool is_tileable_ = false; + vtr::vector node_bend_start_; + vtr::vector node_bend_end_; + /*************** * State flags * ***************/ @@ -927,7 +1013,9 @@ class t_rr_graph_view { const vtr::array_view_id edge_src_node, const vtr::array_view_id edge_dest_node, const vtr::array_view_id edge_switch, - const std::unordered_map& virtual_clock_network_root_idx) + const std::unordered_map& virtual_clock_network_root_idx, + const vtr::array_view_id node_bend_start, + const vtr::array_view_id node_bend_end) : node_storage_(node_storage) , node_ptc_(node_ptc) , node_first_edge_(node_first_edge) @@ -937,7 +1025,9 @@ class t_rr_graph_view { , edge_src_node_(edge_src_node) , edge_dest_node_(edge_dest_node) , edge_switch_(edge_switch) - , virtual_clock_network_root_idx_(virtual_clock_network_root_idx) {} + , virtual_clock_network_root_idx_(virtual_clock_network_root_idx) + , node_bend_start_(node_bend_start) + , node_bend_end_(node_bend_end) {} /**************** * Node methods * @@ -985,6 +1075,7 @@ class t_rr_graph_view { int node_pin_num(RRNodeId id) const; //Same as ptc_num() but checks that type() is consistent int node_track_num(RRNodeId id) const; //Same as ptc_num() but checks that type() is consistent int node_class_num(RRNodeId id) const; //Same as ptc_num() but checks that type() is consistent + int node_mux_num(RRNodeId id) const; //Same as ptc_num() but checks that type() is consistent /** * @brief Retrieve the fan-in for a given RRNodeId. @@ -1142,4 +1233,7 @@ class t_rr_graph_view { vtr::array_view_id edge_switch_; const std::unordered_map& virtual_clock_network_root_idx_; + vtr::array_view_id node_bend_start_; + vtr::array_view_id node_bend_end_; + }; diff --git a/libs/librrgraph/src/base/rr_graph_view.cpp b/libs/librrgraph/src/base/rr_graph_view.cpp index 0ddc5445d42..f23762c3e7d 100644 --- a/libs/librrgraph/src/base/rr_graph_view.cpp +++ b/libs/librrgraph/src/base/rr_graph_view.cpp @@ -9,7 +9,9 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, const vtr::vector& rr_indexed_data, const std::vector& rr_rc_data, const vtr::vector& rr_segments, - const vtr::vector& rr_switch_inf) + const vtr::vector& rr_switch_inf, + const vtr::vector>& node_in_edges, + const vtr::vector>& node_tileable_track_nums) : node_storage_(node_storage) , node_lookup_(node_lookup) , rr_node_metadata_(rr_node_metadata) @@ -17,5 +19,115 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, , rr_indexed_data_(rr_indexed_data) , rr_rc_data_(rr_rc_data) , rr_segments_(rr_segments) - , rr_switch_inf_(rr_switch_inf) { + , rr_switch_inf_(rr_switch_inf) + , node_in_edges_(node_in_edges) + , node_tileable_track_nums_(node_tileable_track_nums) { } + +std::vector RRGraphView::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +std::vector RRGraphView::node_configurable_in_edges(RRNodeId node) const { + // Note: Should sort edges by configurability when allocating the array! + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (rr_switch_inf_[RRSwitchId(edge_switch(edge))].configurable()) { + ret_edges.push_back(edge); + } + } + return ret_edges; +} + +std::vector RRGraphView::node_non_configurable_in_edges(RRNodeId node) const { + // Note: Should sort edges by configurability when allocating the array! + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (!rr_switch_inf_[RRSwitchId(edge_switch(edge))].configurable()) { + ret_edges.push_back(edge); + } + } + return ret_edges; +} + +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)); + } + } + return edge_list; +} + +RRSegmentId RRGraphView::node_segment(RRNodeId node) const { + RRIndexedDataId cost_index = node_cost_index(node); + return RRSegmentId(rr_indexed_data_[cost_index].seg_index); +} + +size_t RRGraphView::in_edges_count() const { + size_t edge_count = 0; + for (const std::vector& edge_list : node_in_edges_) { + edge_count += edge_list.size(); + } + return edge_count; +} + +bool RRGraphView::validate_in_edges() const { + VTR_ASSERT(node_in_edges_.size() == node_storage_.size()); + size_t num_err = 0; + // For each edge, validate that + // - The source node is in the fan-in edge list of the destination node + // - The sink node is in the fan-out edge list of the source node + for (RRNodeId curr_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + // curr_node ---> des_node + // <-?- check if the incoming edge is correct or not + for (t_edge_size iedge : node_storage_.edges(curr_node)) { + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(curr_node, iedge)); + std::vector des_fanin_nodes; + for (RREdgeId next_edge : node_in_edges(des_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_source_node(next_edge); + des_fanin_nodes.push_back(prev_edge_des_node); + } + if (des_fanin_nodes.end() == std::find(des_fanin_nodes.begin(), des_fanin_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-in edges of Node '%s', while does drive it in its fan-out list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(des_node).c_str()); + num_err++; + } + } + // src_node -?-> curr_node + // <--- check if the fan-out edge is correct or not + for (RREdgeId iedge : node_in_edges(curr_node)) { + RRNodeId src_node = node_storage_.edge_source_node(iedge); + std::vector src_fanout_nodes; + for (t_edge_size prev_edge : node_storage_.edges(src_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, prev_edge)); + src_fanout_nodes.push_back(prev_edge_des_node); + } + if (src_fanout_nodes.end() == std::find(src_fanout_nodes.begin(), src_fanout_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-out edges of Node '%s', while does exist in its fan-in list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(src_node).c_str()); + num_err++; + } + } + } + if (num_err) { + VTR_LOG_ERROR("Found %ld errors when validating incoming edges for routing resource graph\n", num_err); + return false; + } + return true; +} + + diff --git a/libs/librrgraph/src/base/rr_graph_view.h b/libs/librrgraph/src/base/rr_graph_view.h index 05d015ba3bd..b465c528586 100644 --- a/libs/librrgraph/src/base/rr_graph_view.h +++ b/libs/librrgraph/src/base/rr_graph_view.h @@ -45,6 +45,11 @@ * - Timing analyzer * - GUI * + * Note that each client of rr_graph may get a frame view of the object + * The RRGraphView is the complete frame view of the routing resource graph + * - This helps to reduce the memory footprint for each client + * - This avoids massive changes for each client on using the APIs + * as each frame view provides adhoc APIs for each client * \internal * TODO: More compact frame views will be created, such as: * - A mini frame view: Contains only nodes and edges, representing the @@ -72,19 +77,21 @@ class RRGraphView { const vtr::vector& rr_indexed_data, const std::vector& rr_rc_data, const vtr::vector& rr_segments, - const vtr::vector& rr_switch_inf); + const vtr::vector& rr_switch_inf, + const vtr::vector>& node_in_edges, + const vtr::vector>& node_ptc_nums); /* Disable copy constructors and copy assignment operator - * This is to avoid accidental copy because it could be an expensive operation considering that the + * This is to avoid accidental copy because it could be an expensive operation considering that the * memory footprint of the data structure could ~ Gb - * Using the following syntax, we prohibit accidental 'pass-by-value' which can be immediately caught + * Using the following syntax, we prohibit accidental 'pass-by-value' which can be immediately caught * by compiler */ RRGraphView(const RRGraphView&) = delete; void operator=(const RRGraphView&) = delete; /* -- Accessors -- */ - /* TODO: The accessors may be turned into private later if they are replacable by 'questionin' + /* TODO: The accessors may be turned into private later if they are replacable by 'questionin' * kind of accessors */ public: @@ -223,6 +230,24 @@ class RRGraphView { inline short node_layer(RRNodeId node) const { return node_storage_.node_layer(node); } + + /** + * @brief Return the bend start of a specified node. + * @param node The node id + * @return The bend start + */ + inline short node_bend_start(RRNodeId node) const { + return node_storage_.node_bend_start(node); + } + + /** + * @brief Return the bend end of a specified node. + * @param node The node id + * @return The bend end + */ + inline short node_bend_end(RRNodeId node) const { + return node_storage_.node_bend_end(node); + } /** @brief Return the first outgoing edge of a specified node. */ @@ -459,6 +484,24 @@ class RRGraphView { return node_storage_.edge_sink_node(id, iedge); } + /** + * @brief Return the destination node for the specified edge. + * @param edge The edge id + * @return The destination node id + */ + inline RRNodeId edge_sink_node(RREdgeId edge) const { + return node_storage_.edge_sink_node(edge); + } + + /** + * @brief Get the source node for the iedge'th edge from specified RRNodeId. + * @note This method should generally not be used, and instead first_edge and + * last_edge should be used. + */ + inline RRNodeId edge_source_node(RRNodeId id, t_edge_size iedge) const { + return node_storage_.edge_source_node(id, iedge); + } + /** @brief Check if the edge is a configurable edge * @note A configurable edge represents a programmable switch between routing resources, which could be * - a multiplexer @@ -469,6 +512,15 @@ class RRGraphView { return node_storage_.edge_is_configurable(id, iedge, rr_switch_inf_); } + /** + * @brief Check if the edge is a configurable edge + * @param edge The edge id + * @return True if the edge is configurable, false otherwise + */ + inline bool edge_is_configurable(RREdgeId edge) const { + return node_storage_.edge_is_configurable(edge, rr_switch_inf_); + } + /** @brief Return the number of configurable edges. */ inline t_edge_size num_configurable_edges(RRNodeId node) const { @@ -481,12 +533,23 @@ class RRGraphView { return node_storage_.num_non_configurable_edges(node, rr_switch_inf_); } - /** @brief Return ID range for configurable edges. - */ + /** + * @brief A configurable edge represents a programmable switch between routing resources, which could be + * - a multiplexer + * - a tri-state buffer + * - a pass gate + * This API gets ID range for configurable edges. This function is inlined for runtime optimization. */ inline edge_idx_range configurable_edges(RRNodeId node) const { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(node_storage_.num_edges(node) - num_non_configurable_edges(node))); } + /** + * @brief Return ID range for configurable outgoing edges. + */ + inline edge_idx_range node_configurable_out_edges(RRNodeId node) const { + return configurable_edges(node); + } + /** @brief Return ID range for non-configurable edges. * @note A non-configurable edge represents a hard-wired connection between routing resources, which could be * - a non-configurable buffer that can not be turned off @@ -496,6 +559,13 @@ class RRGraphView { return vtr::make_range(edge_idx_iterator(node_storage_.num_edges(node) - num_non_configurable_edges(node)), edge_idx_iterator(num_edges(node))); } + /** + * @brief Return ID range for non-configurable outgoing edges. + */ + inline edge_idx_range node_non_configurable_out_edges(RRNodeId node) const { + return non_configurable_edges(node); + } + /** * @brief Retrieve the outgoing edges for a specified node. This API is designed to facilitate range-based loops for traversing the outgoing edges of a node. * @param id the id of the node @@ -511,6 +581,16 @@ class RRGraphView { return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); } + /** + * @brief Return ID range for outgoing edges. + */ + inline edge_idx_range node_out_edges(const RRNodeId& id) const { + return vtr::make_range(edge_idx_iterator(0), edge_idx_iterator(num_edges(id))); + } + + /** @brief find the edges between two nodes */ + std::vector find_edges(RRNodeId src_node, RRNodeId des_node) const; + /** @brief Return the number of edges. */ inline t_edge_size num_edges(RRNodeId node) const { @@ -560,6 +640,27 @@ class RRGraphView { return node_storage_.node_cost_index(node); } + /** @brief Get the segment id which a routing resource node represents. Only applicable to nodes whose type is CHANX or CHANY */ + RRSegmentId node_segment(RRNodeId node) const; + + /** + * @brief Return incoming edges for a given routing resource node + * Requires build_in_edges() to be called first + */ + std::vector node_in_edges(RRNodeId node) const; + + /** + * @brief Return configurable incoming edges for a given routing resource node + * Requires build_in_edges() to be called first + */ + std::vector node_configurable_in_edges(RRNodeId node) const; + + /** + * @brief Return non-configurable incoming edges for a given routing resource node + * Requires build_in_edges() to be called first + */ + std::vector node_non_configurable_in_edges(RRNodeId node) const; + /** @brief Return detailed routing segment information of a specified segment * @note The routing segments here may not be exactly same as those defined in architecture file. They have been * adapted to fit the context of routing resource graphs. @@ -634,6 +735,26 @@ class RRGraphView { return node_storage_.validate_node(node_id, rr_switch_inf_); } + /** @brief Check if the node id is a valid one in storage */ + inline bool valid_node(RRNodeId node_id) const { + return size_t(node_id) < node_storage_.size(); + } + + /** @brief Check if the switch is a valid one in storage */ + inline bool valid_switch(RRSwitchId switch_id) const { + return (size_t(switch_id) < rr_switch_inf_.size()); + } + + /** @brief Validate if all the fan-in edge lists are valid. This + * function should be called only if build_in_edges() is called before. + */ + bool validate_in_edges() const; + + /** @brief Count the number of incoming edges for all the nodes. This + * function should be called only if build_in_edges() is called before. + */ + size_t in_edges_count() const; + /* -- Internal data storage -- */ /* Note: only read-only object or data structures are allowed!!! */ private: @@ -676,4 +797,12 @@ class RRGraphView { const vtr::vector& rr_segments_; /// switch info for rr nodes const vtr::vector& rr_switch_inf_; + + /// A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. + /// By default, it is empty. Call build_in_edges() to construct it. + const vtr::vector>& node_in_edges_; + + /// A list of extra ptc numbers for each routing resource node. This is only used for tileable architecture. + /// See details in RRGraphBuilder class + const vtr::vector>& node_tileable_track_nums_; }; diff --git a/libs/librrgraph/src/base/rr_node_types.h b/libs/librrgraph/src/base/rr_node_types.h index 6509a3d231c..1cce4542f18 100644 --- a/libs/librrgraph/src/base/rr_node_types.h +++ b/libs/librrgraph/src/base/rr_node_types.h @@ -29,6 +29,8 @@ enum class e_rr_type : unsigned char { OPIN, /// RR_TYPES = {{e_rr_type::SOURCE, e_rr_type::SINK, e_rr_type::IPIN, e_rr_type::OPIN, - e_rr_type::CHANX, e_rr_type::CHANY, e_rr_type::CHANZ}}; + e_rr_type::CHANX, e_rr_type::CHANY, e_rr_type::CHANZ, + e_rr_type::MUX}}; /** * @brief Lookup for the string representation of the given node type. This is useful @@ -50,7 +53,8 @@ constexpr std::array RR_TYPES = {{e_ */ constexpr vtr::array rr_node_typename {"SOURCE", "SINK", "IPIN", "OPIN", - "CHANX", "CHANY", "CHANZ"}; + "CHANX", "CHANY", "CHANZ", + "MUX"}; /** * @enum Direction diff --git a/libs/librrgraph/src/base/rr_spatial_lookup.cpp b/libs/librrgraph/src/base/rr_spatial_lookup.cpp index 7ea9dd3c81e..48c81e40f1f 100644 --- a/libs/librrgraph/src/base/rr_spatial_lookup.cpp +++ b/libs/librrgraph/src/base/rr_spatial_lookup.cpp @@ -190,8 +190,8 @@ std::vector RRSpatialLookup::find_grid_nodes_at_all_sides(int layer, int x, int y, e_rr_type rr_type) const { - VTR_ASSERT(rr_type == e_rr_type::SOURCE || rr_type == e_rr_type::OPIN || rr_type == e_rr_type::IPIN || rr_type == e_rr_type::SINK); - if (rr_type == e_rr_type::SOURCE || rr_type == e_rr_type::SINK) { + VTR_ASSERT(rr_type == e_rr_type::SOURCE || rr_type == e_rr_type::OPIN || rr_type == e_rr_type::IPIN || rr_type == e_rr_type::SINK || rr_type == e_rr_type::MUX); + if (rr_type == e_rr_type::SOURCE || rr_type == e_rr_type::SINK || rr_type == e_rr_type::MUX) { return find_nodes(layer,x, y, rr_type); } @@ -199,7 +199,7 @@ std::vector RRSpatialLookup::find_grid_nodes_at_all_sides(int layer, // Reserve space to avoid memory fragmentation size_t num_nodes = 0; for (e_side node_side : TOTAL_2D_SIDES) { - num_nodes += find_nodes(layer,x, y, rr_type, node_side).size(); + num_nodes += find_nodes(layer, x, y, rr_type, node_side).size(); } nodes.reserve(num_nodes); @@ -290,7 +290,7 @@ void RRSpatialLookup::mirror_nodes(const int layer, const vtr::Point& des_coord, e_rr_type type, e_side side) { - VTR_ASSERT(e_rr_type::SOURCE == type); + VTR_ASSERT(e_rr_type::SOURCE == type || e_rr_type::SINK == type); resize_nodes(layer, des_coord.x(), des_coord.y(), type, side); rr_node_indices_[type][layer][des_coord.x()][des_coord.y()][side] = rr_node_indices_[type][layer][src_coord.x()][src_coord.y()][side]; } diff --git a/libs/librrgraph/src/base/rr_spatial_lookup.h b/libs/librrgraph/src/base/rr_spatial_lookup.h index 733aa413b86..7f9f2414233 100644 --- a/libs/librrgraph/src/base/rr_spatial_lookup.h +++ b/libs/librrgraph/src/base/rr_spatial_lookup.h @@ -132,7 +132,7 @@ class RRSpatialLookup { /** * @brief Returns all matching nodes on all the sides at a specific grid tile (layer,x,y) location. * - * As this is applicable to grid pins, the type of nodes are limited to SOURCE/SINK/IPIN/OPIN + * As this is applicable to grid pins, the type of nodes are limited to SOURCE/SINK/IPIN/OPIN/MUX */ std::vector find_grid_nodes_at_all_sides(int layer, int x, diff --git a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h index 2f760f64dc1..e6d3e3ff467 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx.h @@ -87,7 +87,7 @@ template inline void load_grid_locs(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); template inline void load_node_loc(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); -inline void load_node_loc_required_attributes(const pugi::xml_node &root, int * ptc, int * xhigh, int * xlow, int * yhigh, int * ylow, const std::function * report_error); +inline void load_node_loc_required_attributes(const pugi::xml_node &root, int * xhigh, int * xlow, int * yhigh, int * ylow, const std::function * report_error); template inline void load_node_timing(const pugi::xml_node &root, T &out, Context &context, const std::function *report_error, ptrdiff_t *offset_debug); inline void load_node_timing_required_attributes(const pugi::xml_node &root, float * C, float * R, const std::function * report_error); @@ -1611,7 +1611,7 @@ template constexpr const char *lookup_switch_type[] = {"UXSD_INVALID", "mux", "tristate", "pass_gate", "short", "buffer"}; constexpr const char *lookup_segment_res_type[] = {"UXSD_INVALID", "GENERAL", "GCLK"}; constexpr const char *lookup_pin_type[] = {"UXSD_INVALID", "OPEN", "OUTPUT", "INPUT"}; -constexpr const char *lookup_node_type[] = {"UXSD_INVALID", "CHANX", "CHANY", "CHANZ", "SOURCE", "SINK", "OPIN", "IPIN"}; +constexpr const char *lookup_node_type[] = {"UXSD_INVALID", "CHANX", "CHANY", "CHANZ", "SOURCE", "SINK", "OPIN", "IPIN", "MUX"}; constexpr const char *lookup_node_direction[] = {"UXSD_INVALID", "INC_DIR", "DEC_DIR", "BI_DIR", "NONE"}; constexpr const char *lookup_node_clk_res_type[] = {"UXSD_INVALID", "VIRTUAL_SINK"}; constexpr const char *lookup_loc_side[] = {"UXSD_INVALID", "LEFT", "RIGHT", "TOP", "BOTTOM", "RIGHT_LEFT", "RIGHT_BOTTOM", "RIGHT_BOTTOM_LEFT", "TOP_RIGHT", "TOP_BOTTOM", "TOP_LEFT", "TOP_RIGHT_BOTTOM", "TOP_RIGHT_LEFT", "TOP_BOTTOM_LEFT", "TOP_RIGHT_BOTTOM_LEFT", "BOTTOM_LEFT"}; @@ -2463,7 +2463,7 @@ inline void load_grid_loc_required_attributes(const pugi::xml_node &root, int * if(!test_astate.all()) attr_error(test_astate, atok_lookup_t_grid_loc, report_error); } -inline void load_node_loc_required_attributes(const pugi::xml_node &root, int * ptc, int * xhigh, int * xlow, int * yhigh, int * ylow, const std::function * report_error){ +inline void load_node_loc_required_attributes(const pugi::xml_node &root, int * xhigh, int * xlow, int * yhigh, int * ylow, const std::function * report_error){ std::bitset<7> astate = 0; for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){ atok_t_node_loc in = lex_attr_t_node_loc(attr.name(), report_error); @@ -2474,7 +2474,7 @@ inline void load_node_loc_required_attributes(const pugi::xml_node &root, int * /* Attribute layer set after element init */ break; case atok_t_node_loc::PTC: - *ptc = load_int(attr.value(), report_error); + /* Attribute ptc set after element init */ break; case atok_t_node_loc::SIDE: /* Attribute side set after element init */ @@ -3413,7 +3413,7 @@ inline void load_node_loc(const pugi::xml_node &root, T &out, Context &context, out.set_node_loc_layer(load_int(attr.value(), report_error), context); break; case atok_t_node_loc::PTC: - /* Attribute ptc is already set */ + out.set_node_loc_ptc(attr.value(), context); break; case atok_t_node_loc::SIDE: out.set_node_loc_side(lex_enum_loc_side(attr.value(), true, report_error), context); @@ -3599,8 +3599,6 @@ inline void load_node(const pugi::xml_node &root, T &out, Context &context, cons switch(in){ case gtok_t_node::LOC: { - int node_loc_ptc; - memset(&node_loc_ptc, 0, sizeof(node_loc_ptc)); int node_loc_xhigh; memset(&node_loc_xhigh, 0, sizeof(node_loc_xhigh)); int node_loc_xlow; @@ -3609,8 +3607,8 @@ inline void load_node(const pugi::xml_node &root, T &out, Context &context, cons memset(&node_loc_yhigh, 0, sizeof(node_loc_yhigh)); int node_loc_ylow; memset(&node_loc_ylow, 0, sizeof(node_loc_ylow)); - load_node_loc_required_attributes(node, &node_loc_ptc, &node_loc_xhigh, &node_loc_xlow, &node_loc_yhigh, &node_loc_ylow, report_error); - auto child_context = out.init_node_loc(context, node_loc_ptc, node_loc_xhigh, node_loc_xlow, node_loc_yhigh, node_loc_ylow); + load_node_loc_required_attributes(node, &node_loc_xhigh, &node_loc_xlow, &node_loc_yhigh, &node_loc_ylow, report_error); + auto child_context = out.init_node_loc(context, node_loc_xhigh, node_loc_xlow, node_loc_yhigh, node_loc_ylow); load_node_loc(node, out, child_context, report_error, offset_debug); out.finish_node_loc(child_context); } 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 9fb505d1374..3c3fd4d47c3 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h @@ -766,6 +766,7 @@ inline void load_node_loc_capnp_type(const ucap::NodeLoc::Reader &root, T &out, (void)stack; out.set_node_loc_layer(root.getLayer(), context); + out.set_node_loc_ptc(root.getPtc().cStr(), context); out.set_node_loc_side(conv_enum_loc_side(root.getSide(), report_error), context); } @@ -839,7 +840,7 @@ inline void load_node_capnp_type(const ucap::Node::Reader &root, T &out, Context stack->push_back(std::make_pair("getLoc", 0)); if (root.hasLoc()) { auto child_el = root.getLoc(); - auto child_context = out.init_node_loc(context, child_el.getPtc(), child_el.getXhigh(), child_el.getXlow(), child_el.getYhigh(), child_el.getYlow()); + auto child_context = out.init_node_loc(context, child_el.getXhigh(), child_el.getXlow(), child_el.getYhigh(), child_el.getYlow()); load_node_loc_capnp_type(child_el, out, child_context, report_error, stack); out.finish_node_loc(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 425f1e706c6..7050b45c9cd 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_interface.h @@ -27,7 +27,7 @@ enum class enum_segment_res_type {UXSD_INVALID = 0, GENERAL, GCLK}; enum class enum_pin_type {UXSD_INVALID = 0, OPEN, OUTPUT, INPUT}; -enum class enum_node_type {UXSD_INVALID = 0, CHANX, CHANY, CHANZ, SOURCE, SINK, OPIN, IPIN}; +enum class enum_node_type {UXSD_INVALID = 0, CHANX, CHANY, CHANZ, SOURCE, SINK, OPIN, IPIN, MUX}; enum class enum_node_direction {UXSD_INVALID = 0, INC_DIR, DEC_DIR, BI_DIR, NONE}; @@ -377,12 +377,13 @@ class RrGraphBase { * * * - * + * * */ virtual inline int get_node_loc_layer(typename ContextTypes::NodeLocReadContext &ctx) = 0; virtual inline void set_node_loc_layer(int layer, typename ContextTypes::NodeLocWriteContext &ctx) = 0; - virtual inline int get_node_loc_ptc(typename ContextTypes::NodeLocReadContext &ctx) = 0; + virtual inline const char * get_node_loc_ptc(typename ContextTypes::NodeLocReadContext &ctx) = 0; + virtual inline void set_node_loc_ptc(const char * ptc, typename ContextTypes::NodeLocWriteContext &ctx) = 0; virtual inline enum_loc_side get_node_loc_side(typename ContextTypes::NodeLocReadContext &ctx) = 0; virtual inline void set_node_loc_side(enum_loc_side side, typename ContextTypes::NodeLocWriteContext &ctx) = 0; virtual inline int get_node_loc_xhigh(typename ContextTypes::NodeLocReadContext &ctx) = 0; @@ -458,7 +459,7 @@ class RrGraphBase { virtual inline const char * get_node_name(typename ContextTypes::NodeReadContext &ctx) = 0; virtual inline void set_node_name(const char * name, typename ContextTypes::NodeWriteContext &ctx) = 0; virtual inline enum_node_type get_node_type(typename ContextTypes::NodeReadContext &ctx) = 0; - virtual inline typename ContextTypes::NodeLocWriteContext init_node_loc(typename ContextTypes::NodeWriteContext &ctx, int ptc, int xhigh, int xlow, int yhigh, int ylow) = 0; + virtual inline typename ContextTypes::NodeLocWriteContext init_node_loc(typename ContextTypes::NodeWriteContext &ctx, int xhigh, int xlow, int yhigh, int ylow) = 0; virtual inline void finish_node_loc(typename ContextTypes::NodeLocWriteContext &ctx) = 0; virtual inline typename ContextTypes::NodeLocReadContext get_node_loc(typename ContextTypes::NodeReadContext &ctx) = 0; virtual inline typename ContextTypes::NodeTimingWriteContext init_node_timing(typename ContextTypes::NodeWriteContext &ctx, float C, float R) = 0; diff --git a/libs/librrgraph/src/io/rr_graph.xsd b/libs/librrgraph/src/io/rr_graph.xsd index c6b214fd638..bff9f16c3f4 100644 --- a/libs/librrgraph/src/io/rr_graph.xsd +++ b/libs/librrgraph/src/io/rr_graph.xsd @@ -234,6 +234,7 @@ + @@ -282,7 +283,7 @@ - + diff --git a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h index 9ca8279f473..13a223b627f 100644 --- a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h +++ b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h @@ -406,8 +406,8 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { } t_unified_to_parallel_seg_index seg_index_map; - segment_inf_x_ = get_parallel_segs(rr_segs, seg_index_map, X_AXIS); - segment_inf_y_ = get_parallel_segs(rr_segs, seg_index_map, Y_AXIS); + segment_inf_x_ = get_parallel_segs(rr_segs, seg_index_map, e_parallel_axis::X_AXIS); + segment_inf_y_ = get_parallel_segs(rr_segs, seg_index_map, e_parallel_axis::Y_AXIS); } @@ -421,7 +421,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { int find_segment_index_along_axis(int segment_id, e_parallel_axis axis) const { const std::vector* segment_inf_vec_ptr; - if (axis == X_AXIS) + if (axis == e_parallel_axis::X_AXIS) segment_inf_vec_ptr = &segment_inf_x_; else segment_inf_vec_ptr = &segment_inf_y_; @@ -431,7 +431,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return static_cast(i); } - if (axis == X_AXIS) + if (axis == e_parallel_axis::X_AXIS) VTR_LOG_ERROR("Segment ID %d not found in the list of segments along X axis.\n", segment_id); else VTR_LOG_ERROR("Segment ID %d not found in the list of segments along Y axis.\n", segment_id); @@ -687,27 +687,33 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { * * * - * + * * */ - inline int init_node_loc(int& inode, int ptc, int xhigh, int xlow, int yhigh, int ylow) final { + inline int init_node_loc(int& inode, int xhigh, int xlow, int yhigh, int ylow) final { auto node = (*rr_nodes_)[inode]; RRNodeId node_id = node.id(); rr_graph_builder_->set_node_coordinates(node_id, xlow, ylow, xhigh, yhigh); // We set the layer num 0 - If it is specified in the XML, it will be overwritten rr_graph_builder_->set_node_layer(node_id, 0); - rr_graph_builder_->set_node_ptc_num(node_id, ptc); + return inode; } inline void finish_node_loc(int& /*inode*/) final {} inline const t_rr_node get_node_loc(const t_rr_node& node) final { return node; } + inline void set_node_loc_ptc(const char* ptc, int& inode) final { + t_rr_node node = (*rr_nodes_)[inode]; + RRNodeId node_id = node.id(); + return rr_graph_builder_->set_node_ptc_nums(node_id, std::string(ptc)); + } - inline int get_node_loc_ptc(const t_rr_node& node) final { - return rr_graph_->node_ptc_num(node.id()); + inline const char* get_node_loc_ptc(const t_rr_node& node) final { + temp_string_ = rr_graph_builder_->node_ptc_nums_to_string(node.id()); + return temp_string_.c_str(); } inline int get_node_loc_layer(const t_rr_node& node) final { return rr_graph_->node_layer(node.id()); @@ -817,16 +823,16 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { if (e_graph_type::GLOBAL == graph_type_) { rr_graph_builder_->set_node_cost_index(node_id, RRIndexedDataId(0)); } else if (rr_graph.node_type(node.id()) == e_rr_type::CHANX) { - int seg_ind_x = find_segment_index_along_axis(segment_id, X_AXIS); + int seg_ind_x = find_segment_index_along_axis(segment_id, e_parallel_axis::X_AXIS); rr_graph_builder_->set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + seg_ind_x)); seg_index_[rr_graph.node_cost_index(node.id())] = segment_id; } else if (rr_graph.node_type(node.id()) == e_rr_type::CHANY) { - int seg_ind_y = find_segment_index_along_axis(segment_id, Y_AXIS); + int seg_ind_y = find_segment_index_along_axis(segment_id, e_parallel_axis::Y_AXIS); rr_graph_builder_->set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + segment_inf_x_.size() + seg_ind_y)); seg_index_[rr_graph.node_cost_index(node.id())] = segment_id; } else if (rr_graph.node_type(node.id()) == e_rr_type::CHANZ) { // TODO: Don't use CHANX info - int seg_ind_z = find_segment_index_along_axis(segment_id, X_AXIS); + int seg_ind_z = find_segment_index_along_axis(segment_id, e_parallel_axis::X_AXIS); rr_graph_builder_->set_node_cost_index(node_id, RRIndexedDataId(CHANX_COST_INDEX_START + seg_ind_z)); seg_index_[rr_graph.node_cost_index(node.id())] = segment_id; } @@ -870,6 +876,7 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { */ inline void preallocate_rr_nodes_node(void*& /*ctx*/, size_t size) final { rr_graph_builder_->reserve_nodes(size); + rr_graph_builder_->resize_node_ptc_nums(size); } inline int add_rr_nodes_node(void*& /*ctx*/, unsigned int capacity, unsigned int id, uxsd::enum_node_type type) final { // make_room_in_vector will not allocate if preallocate_rr_nodes_node @@ -1832,10 +1839,12 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { loaded_rr_graph_filename_->assign(read_rr_graph_name_); if (do_check_rr_graph_) { + const VibDeviceGrid vib_grid_; check_rr_graph(*rr_graph_, physical_tile_types_, *rr_indexed_data_, grid_, + vib_grid_, *chan_width_, graph_type_, is_flat_); @@ -1848,14 +1857,21 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { void process_rr_node_indices() { auto& rr_graph_builder = (*rr_graph_builder_); - /* Alloc the lookup table */ + // Alloc the lookup table for (e_rr_type rr_type : RR_TYPES) { rr_graph_builder.node_lookup().resize_nodes(grid_.get_num_layers(), grid_.width(), grid_.height(), rr_type, NUM_2D_SIDES); } - /* Add the correct node into the vector */ + // Add the correct node into the vector for (const t_rr_node& node : *rr_nodes_) { - rr_graph_builder.add_node_to_all_locs(node.id()); + // Set track numbers as a node may have multiple ptc + if (rr_graph_builder.node_contain_multiple_ptc(node.id())) { + if (rr_graph_->node_type(node.id()) == e_rr_type::CHANX || rr_graph_->node_type(node.id()) == e_rr_type::CHANY) { + rr_graph_builder.add_track_node_to_lookup(node.id()); + } + } else { + rr_graph_builder.add_node_to_all_locs(node.id()); + } } } @@ -2017,6 +2033,8 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return e_rr_type::OPIN; case uxsd::enum_node_type::IPIN: return e_rr_type::IPIN; + case uxsd::enum_node_type::MUX: + return e_rr_type::MUX; default: report_error( "Invalid node type %d", @@ -2039,6 +2057,8 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { return uxsd::enum_node_type::OPIN; case e_rr_type::IPIN: return uxsd::enum_node_type::IPIN; + case e_rr_type::MUX: + return uxsd::enum_node_type::MUX; default: report_error( "Invalid type %d", type); diff --git a/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp b/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp index 9c68e2eb455..44568f7e9ec 100644 --- a/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp +++ b/libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp @@ -82,7 +82,7 @@ void alloc_and_load_rr_indexed_data(const RRGraphView& rr_graph, //TODO: SM: IPIN t_linear assumes wire_to_ipin_switch which corresponds to within die switch connection rr_indexed_data[RRIndexedDataId(IPIN_COST_INDEX)].T_linear = rr_graph.rr_switch_inf(RRSwitchId(wire_to_ipin_switch)).Tdel; - std::vector ortho_costs = find_ortho_cost_index(rr_graph, segment_inf_x, segment_inf_y, X_AXIS); + std::vector ortho_costs = find_ortho_cost_index(rr_graph, segment_inf_x, segment_inf_y, e_parallel_axis::X_AXIS); /* AA: The code below should replace find_ortho_cost_index call once we deprecate the CLASSIC lookahead as it is the only lookahead * that actively uses the orthogonal cost indices. To avoid complicated dependencies with the rr_graph reader, regardless of the lookahead, @@ -262,7 +262,7 @@ std::vector find_ortho_cost_index(const RRGraphView& rr_graph, /* The compartor behaves as operator< mostly, so the first element in the * sorted vector will have the lowest cost difference from segment. */ ortho_costs_indices[seg_index] = segment_inf_perp[0].seg_index + start_channel_cost; - ortho_costs_indices[seg_index] = parallel_axis == X_AXIS ? ortho_costs_indices[seg_index] + num_segments : ortho_costs_indices[seg_index]; + ortho_costs_indices[seg_index] = parallel_axis == e_parallel_axis::X_AXIS ? ortho_costs_indices[seg_index] + num_segments : ortho_costs_indices[seg_index]; } /*Pertubate indices to make sure all perp seg types have a corresponding perp segment.*/ @@ -283,7 +283,7 @@ std::vector find_ortho_cost_index(const RRGraphView& rr_graph, perp_segments.resize(segment_inf_perp.size(), 0); for (int i = 0; i < num_segments; ++i) { - int index = parallel_axis == X_AXIS ? ortho_costs_indices[i] - num_segments - start_channel_cost : ortho_costs_indices[i] - start_channel_cost; + int index = parallel_axis == e_parallel_axis::X_AXIS ? ortho_costs_indices[i] - num_segments - start_channel_cost : ortho_costs_indices[i] - start_channel_cost; indices_map.insert(std::make_pair(index, i)); perp_segments[index]++; } @@ -309,7 +309,7 @@ std::vector find_ortho_cost_index(const RRGraphView& rr_graph, auto itr_to_change = indices_map.find(g_index_pair.first); VTR_ASSERT(itr_to_change != indices_map.end()); int index = l_index_pair.first + start_channel_cost; - index = parallel_axis == X_AXIS ? index + num_segments : index; + index = parallel_axis == e_parallel_axis::X_AXIS ? index + num_segments : index; ortho_costs_indices[itr_to_change->second] = index; indices_map.erase(itr_to_change); @@ -344,6 +344,7 @@ static void load_rr_indexed_data_base_costs(const RRGraphView& rr_graph, rr_indexed_data[RRIndexedDataId(SOURCE_COST_INDEX)].base_cost = delay_normalization_fac; rr_indexed_data[RRIndexedDataId(SINK_COST_INDEX)].base_cost = 0.; + rr_indexed_data[RRIndexedDataId(MUX_COST_INDEX)].base_cost = delay_normalization_fac; rr_indexed_data[RRIndexedDataId(OPIN_COST_INDEX)].base_cost = delay_normalization_fac; // The IPIN_COST_INDEX base cost is changed from 0.95 to 0.875 so it is perfectly representable in binary format (this change is made for SPEC benchmark). // This number is perfectly representable in a binary mantissa (without round-off) so we can get the same routing result on different platforms. @@ -739,6 +740,8 @@ static void print_rr_index_info(const vtr::vector +#include + namespace vtr { /** @@ -24,7 +27,10 @@ enum class LogicValue { FALSE = 0, TRUE = 1, DONT_CARE = 2, - UNKOWN = 3 + UNKOWN = 3, + NUM_LOGIC_VALUE_TYPES = 4 }; +constexpr std::array LOGIC_VALUE_STRING = {"false", "true", "don't care", "unknown"}; + } // namespace vtr diff --git a/libs/libvtrutil/src/vtr_ndmatrix.h b/libs/libvtrutil/src/vtr_ndmatrix.h index 45493c7e32f..ec235923eec 100644 --- a/libs/libvtrutil/src/vtr_ndmatrix.h +++ b/libs/libvtrutil/src/vtr_ndmatrix.h @@ -248,7 +248,9 @@ class NdMatrixBase { public: //Mutators ///@brief Set all elements to 'value' void fill(T value) { - std::fill(data_.get(), data_.get() + size(), value); + if (size() > 0) { + std::fill(data_.get(), data_.get() + size(), value); + } } /** diff --git a/libs/libvtrutil/src/vtr_path.cpp b/libs/libvtrutil/src/vtr_path.cpp index e6bf293d753..3f2a6a0fd01 100644 --- a/libs/libvtrutil/src/vtr_path.cpp +++ b/libs/libvtrutil/src/vtr_path.cpp @@ -29,7 +29,7 @@ std::array split_ext(const std::string& filename) { } std::string basename(const std::string& path) { - auto elements = split(path, PATH_DELIM); + std::vector elements = StringToken(path).split(PATH_DELIM); std::string str; if (elements.size() > 0) { @@ -41,7 +41,7 @@ std::string basename(const std::string& path) { } std::string dirname(const std::string& path) { - auto elements = split(path, PATH_DELIM); + std::vector elements = StringToken(path).split(PATH_DELIM); std::string str; if (elements.size() > 0) { diff --git a/libs/libvtrutil/src/vtr_util.cpp b/libs/libvtrutil/src/vtr_util.cpp index 4b1a1e7be82..c7223207cc5 100644 --- a/libs/libvtrutil/src/vtr_util.cpp +++ b/libs/libvtrutil/src/vtr_util.cpp @@ -17,61 +17,15 @@ namespace vtr { -std::string out_file_prefix; /* used by fopen */ -static int file_line_number = 0; /* file in line number being parsed (used by fgets) */ -static int cont; /* line continued? (used by strtok)*/ +/// @brief used by fopen +std::string out_file_prefix; -/** - * @brief Splits the c-style string 'text' along the specified delimiter characters in 'delims' - * - * The split strings (excluding the delimiters) are returned - */ -std::vector split(const char* text, std::string_view delims) { - if (text) { - std::string text_str(text); - return split(text_str, delims); - } - return {}; -} +/// @brief file in line number being parsed (used by fgets) +static int file_line_number = 0; -/** - * @brief Splits the string 'text' along the specified delimiter characters in 'delims' - * - * The split strings (excluding the delimiters) are returned - */ -std::vector split(std::string_view text, std::string_view delims) { - std::vector tokens; - - std::string curr_tok; - for (char c : text) { - if (delims.find(c) != std::string::npos) { - //Delimiter character - if (!curr_tok.empty()) { - //At the end of the token +/// @brief line continued? (used by strtok) +static int cont; - //Save it - tokens.push_back(curr_tok); - - //Reset token - curr_tok.clear(); - } else { - //Pass - } - } else { - //Non-delimiter append to token - curr_tok += c; - } - } - - //Add last token - if (!curr_tok.empty()) { - //Save it - tokens.push_back(curr_tok); - } - return tokens; -} - -///@brief Returns 'input' with the first instance of 'search' replaced with 'replace' std::string replace_first(std::string_view input, std::string_view search, std::string_view replace) { auto pos = input.find(search); @@ -82,7 +36,6 @@ std::string replace_first(std::string_view input, std::string_view search, std:: return output; } -///@brief Returns 'input' with all instances of 'search' replaced with 'replace' std::string replace_all(std::string_view input, std::string_view search, std::string_view replace) { std::string output; @@ -101,12 +54,10 @@ std::string replace_all(std::string_view input, std::string_view search, std::st return output; } -///@brief Returns true if str starts with prefix bool starts_with(const std::string& str, std::string_view prefix) { return str.find(prefix) == 0; } -///@brief Returns a std::string formatted using a printf-style format string std::string string_fmt(const char* fmt, ...) { // Make a variable argument list va_list va_args; @@ -123,7 +74,6 @@ std::string string_fmt(const char* fmt, ...) { return str; } -///@brief Returns a std::string formatted using a printf-style format string taking an explicit va_list std::string vstring_fmt(const char* fmt, va_list args) { // We need to copy the args so we don't change them before the true formatting va_list va_args_copy; @@ -153,30 +103,23 @@ std::string vstring_fmt(const char* fmt, va_list args) { return std::string(buf.get(), len); } -///@brief An alternate for strncpy since strncpy doesn't work as most people would expect. This ensures null termination char* strncpy(char* dest, const char* src, size_t size) { - /* Find string's length */ + // Find string's length size_t len = std::strlen(src); - /* Cap length at (num - 1) to leave room for \0 */ + // Cap length at (num - 1) to leave room for \0 if (size <= len) len = (size - 1); - /* Copy as much of string as we can fit */ + // Copy as much of string as we can fit std::memcpy(dest, src, len); - /* explicit null termination */ + // explicit null termination dest[len] = '\0'; return dest; } -/** - * @brief Legacy c-style function replacements. - * - * Typically these add extra error checking - * and/or correct 'unexpected' behaviour of the standard c-functions - */ char* strdup(const char* str) { if (str == nullptr) { return nullptr; @@ -214,57 +157,22 @@ T atoT(const std::string& value, std::string_view type_name) { return val; } -/** - * @brief Legacy c-style function replacements. - * - * Typically these add extra error checking - * and/or correct 'unexpected' behaviour of the standard c-functions - */ int atoi(const std::string& value) { return atoT(value, "int"); } -/** - * @brief Legacy c-style function replacements. - * - * Typically these add extra error checking - * and/or correct 'unexpected' behaviour of the standard c-functions - */ double atod(const std::string& value) { return atoT(value, "double"); } -/** - * @brief Legacy c-style function replacements. - * - * Typically these add extra error checking - * and/or correct 'unexpected' behaviour of the standard c-functions - */ float atof(const std::string& value) { return atoT(value, "float"); } -/** - * @brief Legacy c-style function replacements. - * - * Typically these add extra error checking - * and/or correct 'unexpected' behaviour of the standard c-functions - */ unsigned atou(const std::string& value) { return atoT(value, "unsigned int"); } -/** - * @brief Get next token, and wrap to next line if \ at end of line. - * - * There is a bit of a "gotcha" in strtok. It does not make a * - * copy of the character array which you pass by pointer on the - * first call. Thus, you must make sure this array exists for - * as long as you are using strtok to parse that line. Don't - * use local buffers in a bunch of subroutines calling each - * other; the local buffer may be overwritten when the stack is - * restored after return from the subroutine. - */ char* strtok(char* ptr, const char* tokens, FILE* fp, char* buf) { char* val; @@ -273,7 +181,7 @@ char* strtok(char* ptr, const char* tokens, FILE* fp, char* buf) { if (val != nullptr || cont == 0) return (val); - /* return unless we have a null value and a continuation line */ + // return unless we have a null value and a continuation line if (vtr::fgets(buf, bufsize, fp) == nullptr) return (nullptr); @@ -281,17 +189,16 @@ char* strtok(char* ptr, const char* tokens, FILE* fp, char* buf) { } } -///@brief The legacy fopen function with extra error checking FILE* fopen(const char* fname, const char* flag) { FILE* fp; size_t Len; char* new_fname = nullptr; file_line_number = 0; - /* Appends a prefix string for output files */ + // Appends a prefix string for output files if (!out_file_prefix.empty()) { if (std::strchr(flag, 'w')) { - Len = 1; /* NULL char */ + Len = 1; // NULL char Len += std::strlen(out_file_prefix.c_str()); Len += std::strlen(fname); new_fname = (char*)vtr::malloc(Len * sizeof(char)); @@ -311,51 +218,41 @@ FILE* fopen(const char* fname, const char* flag) { return (fp); } -///@brief The legacy fclose function int fclose(FILE* f) { return std::fclose(f); } -/** - * @brief Get an input line, update the line number and cut off any comment part. - * - * A \ at the end of a line with no comment part (#) means continue. - * vtr::fgets should give - * identical results for Windows (\r\n) and Linux (\n) - * newlines, since it replaces each carriage return \r - * by a newline character \n. Returns NULL after EOF. - */ char* fgets(char* buf, int max_size, FILE* fp) { int ch; int i; - cont = 0; /* line continued? */ - file_line_number++; /* global variable */ + cont = 0; // line continued? + file_line_number++; // global variable - for (i = 0; i < max_size - 1; i++) { /* Keep going until the line finishes or the buffer is full */ + for (i = 0; i < max_size - 1; i++) { // Keep going until the line finishes or the buffer is full ch = std::fgetc(fp); - if (std::feof(fp)) { /* end of file */ + if (std::feof(fp)) { // end of file if (i == 0) { - return nullptr; /* required so we can write while (vtr::fgets(...) != NULL) */ - } else { /* no newline before end of file - last line must be returned */ + return nullptr; // required so we can write while (vtr::fgets(...) != NULL) + } else { // no newline before end of file - last line must be returned buf[i] = '\0'; return buf; } } - if (ch == '#') { /* comment */ + if (ch == '#') { // comment buf[i] = '\0'; while ((ch = std::fgetc(fp)) != '\n' && !std::feof(fp)) - ; /* skip the rest of the line */ + ; // skip the rest of the line return buf; } - if (ch == '\r' || ch == '\n') { /* newline (cross-platform) */ - if (i != 0 && buf[i - 1] == '\\') { /* if \ at end of line, line continued */ + if (ch == '\r' || ch == '\n') { // newline (cross-platform) + if (i != 0 && buf[i - 1] == '\\') { // if \ at end of line, line continued cont = 1; - buf[i - 1] = '\n'; /* May need this for tokens */ + buf[i - 1] = '\n'; // May need this for tokens buf[i] = '\0'; } else { buf[i] = '\n'; @@ -364,10 +261,10 @@ char* fgets(char* buf, int max_size, FILE* fp) { return buf; } - buf[i] = ch; /* copy character into the buffer */ + buf[i] = ch; // copy character into the buffer } - /* Buffer is full but line has not terminated, so error */ + // Buffer is full but line has not terminated, so error throw VtrError(string_fmt("Error on line %d -- line is too long for input buffer.\n" "All lines must be at most %d characters long.\n", bufsize - 2), @@ -375,7 +272,6 @@ char* fgets(char* buf, int max_size, FILE* fp) { return nullptr; } -///@brief Returns line number of last opened and read file int get_file_line_number_of_last_opened_file() { return file_line_number; } @@ -395,20 +291,12 @@ bool file_exists(const char* filename) { return false; } -/** - * @brief Checks the file extension of an file to ensure correct file format. - * - * Returns true if the extension is correct, and false otherwise. - */ bool check_file_name_extension(std::string_view file_name, std::string_view file_extension) { auto ext = std::filesystem::path(file_name).extension(); return ext == file_extension; } -/** - * @brief Legacy ReadLine Tokening - */ std::vector ReadLineTokens(FILE* InFile, int* LineNum) { std::unique_ptr buf(new char[vtr::bufsize]); @@ -416,10 +304,9 @@ std::vector ReadLineTokens(FILE* InFile, int* LineNum) { ++(*LineNum); - return vtr::split(line); + return vtr::StringToken(line).split(" \t\n"); } -///@brief Returns pid if os is unix, -1 otherwise. int get_pid() { #if defined(__unix__) return getpid(); @@ -428,4 +315,158 @@ int get_pid() { #endif } +/************************************************************************ + * Constructors + ***********************************************************************/ +StringToken::StringToken(const std::string& data) { + set_data(data); +} + +StringToken::StringToken(std::string_view data) { + set_data(std::string(data)); +} + +StringToken::StringToken(const char* data) { + set_data(std::string(data)); +} + +/************************************************************************ + * Public Accessors + ***********************************************************************/ +std::string StringToken::data() const { + return data_; +} + +std::vector StringToken::split(const std::string& delims) const { + // Return vector + std::vector ret; + + // Get a writable char array + char* tmp = new char[data_.size() + 1]; + std::copy(data_.begin(), data_.end(), tmp); + tmp[data_.size()] = '\0'; + // Split using strtok + char* result = std::strtok(tmp, delims.c_str()); + while (nullptr != result) { + std::string result_str(result); + // Store the token + ret.push_back(result_str); + // Got to next + result = std::strtok(nullptr, delims.c_str()); + } + + // Free the tmp + delete[] tmp; + + return ret; +} + +std::vector StringToken::split(const char& delim) const { + // Create delims + std::string delims(1, delim); + + return split(delims); +} + +std::vector StringToken::split(const char* delim) const { + // Create delims + std::string delims(delim); + + return split(delims); +} + +std::vector StringToken::split( + const std::vector& delims) const { + // Create delims + std::string delims_str; + for (const auto& delim : delims) { + delims_str.push_back(delim); + } + + return split(delims_str); +} + +std::vector StringToken::split() { + // Add a default delim + if (true == delims_.empty()) { + add_default_delim(); + } + // Create delims + std::string delims; + for (const auto& delim : delims_) { + delims.push_back(delim); + } + + return split(delims); +} + +std::vector StringToken::find_positions(const char& delim) const { + std::vector anchors; + size_t found = data_.find(delim); + while (std::string::npos != found) { + anchors.push_back(found); + found = data_.find(delim, found + 1); + } + return anchors; +} + +std::vector StringToken::split_by_chunks( + const char& chunk_delim, + const bool& split_odd_chunk) const { + size_t chunk_idx_mod = 0; + if (split_odd_chunk) { + chunk_idx_mod = 1; + } + std::vector tokens; + // There are pairs of quotes, identify the chunk which should be split + std::vector token_chunks = split(chunk_delim); + for (size_t ichunk = 0; ichunk < token_chunks.size(); ichunk++) { + // Chunk with even index (including the first) is always out of two quote -> + // Split! Chunk with odd index is always between two quotes -> Do not split! + if (ichunk % 2 == chunk_idx_mod) { + StringToken chunk_tokenizer(token_chunks[ichunk]); + for (std::string curr_token : chunk_tokenizer.split()) { + tokens.push_back(curr_token); + } + } else { + tokens.push_back(token_chunks[ichunk]); + } + } + return tokens; +} + +/************************************************************************ + * Public Mutators + ***********************************************************************/ +void StringToken::set_data(const std::string& data) { + data_ = data; +} + +void StringToken::add_delim(const char& delim) { + delims_.push_back(delim); +} + +void StringToken::ltrim(const std::string& sensitive_word) { + size_t start = data_.find_first_not_of(sensitive_word); + data_ = (start == std::string::npos) ? "" : data_.substr(start); +} + +void StringToken::rtrim(const std::string& sensitive_word) { + size_t end = data_.find_last_not_of(sensitive_word); + data_ = (end == std::string::npos) ? "" : data_.substr(0, end + 1); +} + +void StringToken::trim() { + rtrim(" "); + ltrim(" "); +} + +/************************************************************************ + * Internal Mutators + ***********************************************************************/ +void StringToken::add_default_delim() { + VTR_ASSERT_SAFE(true == delims_.empty()); + delims_.push_back(' '); +} + } // namespace vtr diff --git a/libs/libvtrutil/src/vtr_util.h b/libs/libvtrutil/src/vtr_util.h index b9a08393c82..43f2c16f3e1 100644 --- a/libs/libvtrutil/src/vtr_util.h +++ b/libs/libvtrutil/src/vtr_util.h @@ -9,14 +9,6 @@ namespace vtr { -/** - * @brief Splits the string 'text' along the specified delimiter characters in 'delims' - * - * The split strings (excluding the delimiters) are returned - */ -std::vector split(const char* text, std::string_view string_view = " \t\n"); -std::vector split(std::string_view text, std::string_view delims = " \t\n"); - ///@brief Returns 'input' with the first instance of 'search' replaced with 'replace' std::string replace_first(std::string_view input, std::string_view search, std::string_view replace); @@ -70,24 +62,103 @@ template void uniquify(Container container); constexpr size_t bufsize = 32768; /* Maximum line length for various parsing proc. */ + +/** + * @brief An alternate for strncpy since strncpy doesn't work as most + * people would expect. This ensures null termination + */ char* strncpy(char* dest, const char* src, size_t size); + +/** + * @brief Legacy c-style function replacements. + * @details Typically these add extra error checking + * and/or correct 'unexpected' behaviour of the standard c-functions + */ char* strdup(const char* str); + +/** + * @brief Get next token, and wrap to next line if \ at end of line. + * + * @details There is a bit of a "gotcha" in strtok. It does not make a + * copy of the character array which you pass by pointer on the + * first call. Thus, you must make sure this array exists for + * as long as you are using strtok to parse that line. Don't + * use local buffers in a bunch of subroutines calling each + * other; the local buffer may be overwritten when the stack is + * restored after return from the subroutine. + */ char* strtok(char* ptr, const char* tokens, FILE* fp, char* buf); + +/** + * @brief The legacy fopen function with extra error checking + */ FILE* fopen(const char* fname, const char* flag); + +/** + * @brief The legacy fclose function + */ int fclose(FILE* f); + +/** + * @brief Get an input line, update the line number and cut off any comment part. + * + * @details A \ at the end of a line with no comment part (#) means continue. + * vtr::fgets should give + * identical results for Windows (\r\n) and Linux (\n) + * newlines, since it replaces each carriage return \r + * by a newline character \n. Returns NULL after EOF. + */ char* fgets(char* buf, int max_size, FILE* fp); + char* getline(char*& _lineptr, FILE* _stream); +/** + * @brief Legacy c-style function replacements. + * + * Typically these add extra error checking + * and/or correct 'unexpected' behaviour of the standard c-functions + */ int atoi(const std::string& value); + +/** + * @brief Legacy c-style function replacements. + * + * Typically these add extra error checking + * and/or correct 'unexpected' behaviour of the standard c-functions + */ unsigned atou(const std::string& value); + +/** + * @brief Legacy c-style function replacements. + * + * Typically these add extra error checking + * and/or correct 'unexpected' behaviour of the standard c-functions + */ float atof(const std::string& value); + +/** + * @brief Legacy c-style function replacements. + * + * Typically these add extra error checking + * and/or correct 'unexpected' behaviour of the standard c-functions + */ double atod(const std::string& value); /** * @brief File utilities */ + +/** + * @brief Returns line number of last opened and read file + */ int get_file_line_number_of_last_opened_file(); bool file_exists(const char* filename); + +/** + * @brief Checks the file extension of an file to ensure correct file format. + * + * @return true if the extension is correct, and false otherwise. + */ bool check_file_name_extension(std::string_view file_name, std::string_view file_extension); extern std::string out_file_prefix; @@ -146,6 +217,94 @@ bool exactly_k_conditions(int k, Conditions... conditions) { return count == k; } +/** + * @brief Returns pid if os is unix, -1 otherwise. + */ int get_pid(); +/** + * @brief A tokenizer for string objects. It splits a string + * with given delimiter and return a vector of tokens + * It can accept different delimiters in splitting strings + */ +class StringToken { + public: /* Constructors*/ + StringToken(const std::string& data); + + StringToken(std::string_view data); + + StringToken(const char* data); + + public: /* Public Accessors */ + /** + * @brief Returns the string data + */ + std::string data() const; + + /** + * @brief Splits the string 'text' along the specified delimiter characters in 'delims' + * + * The split strings (excluding the delimiters) are returned + */ + std::vector split(const std::string& delims) const; + std::vector split(const char& delim) const; + std::vector split(const char* delim) const; + std::vector split(const std::vector& delim) const; + std::vector split(); + /** + * @brief Find the position (i-th charactor) in a string for a given + * delimiter, it will return a list of positions For example, to find the + * position of all quotes (") in a string: "we" are good The following code is + * suggested: StringToken tokenizer("\"we\" are good"); std::vector + * anchors = tokenizer.find_positions('\"') The following vector will be + * returned: [0, 3] + */ + std::vector find_positions(const char& delim) const; + + /** + * @brief split the string for each chunk. This is useful where there are + * chunks of substring should not be splitted by the given delimiter For + * example, to split the string with quotes (") in a string: source "cmdA + * --opt1 val1;cmdB --opt2 val2" --verbose where the string between the two + * quotes should not be splitted The following code is suggested: StringToken + * tokenizer("source \"cmdA --opt1 val1;cmdB --opt2 val2\" --verbose"); + * std::vector tokenizer.split_by_chunks('\"', true); + * The following vector will be returned: + * ["source" "cmdA --opt1 val1;cmdB --opt2 val2" "--verbose"] + * + * .. note:: The option ``split_odd_chunk`` is useful when the chunk delimiter + * appears at the beginning of the string. + */ + std::vector split_by_chunks( + const char& chunk_delim, + const bool& split_odd_chunk = false) const; + + public: /* Public Mutators */ + void set_data(const std::string& data); + + /** + * @brief Add a delima to the list + */ + void add_delim(const char& delim); + + /** + * @brief Remove the string repeated at the beginning of string + */ + void ltrim(const std::string& sensitive_word); + + /** + * @brief Remove the string repeated at the end of string + */ + void rtrim(const std::string& sensitive_word); + + void trim(); + + private: /* Private Mutators */ + void add_default_delim(); + + private: /* Internal data */ + std::string data_; /* Lines to be splited */ + std::vector delims_; +}; + } // namespace vtr diff --git a/utils/fasm/src/fasm.cpp b/utils/fasm/src/fasm.cpp index 3e70578ee6c..829ae98af27 100644 --- a/utils/fasm/src/fasm.cpp +++ b/utils/fasm/src/fasm.cpp @@ -54,7 +54,7 @@ void FasmWriterVisitor::visit_clb_impl(ClusterBlockId blk_id, const t_pb* clb) { int sub_tile = block_locs[blk_id].loc.sub_tile; physical_tile_ = device_ctx.grid.get_physical_type({x, y, layer_num}); logical_block_ = cluster_ctx.clb_nlist.block_type(blk_id); - const auto& grid_meta = device_ctx.grid.get_metadata({x, y, layer_num}); + const t_metadata_dict* grid_meta = device_ctx.grid.get_metadata({x, y, layer_num}); blk_prefix_ = ""; clb_prefix_ = ""; @@ -63,13 +63,13 @@ void FasmWriterVisitor::visit_clb_impl(ClusterBlockId blk_id, const t_pb* clb) { // Get placeholder list (if provided) tags_.clear(); if(grid_meta != nullptr && grid_meta->has(fasm_placeholders)) { - auto* value = grid_meta->get(fasm_placeholders); + const std::vector* value = grid_meta->get(fasm_placeholders); VTR_ASSERT(value != nullptr); // Parse placeholder definition - std::vector tag_defs = vtr::split(value->front().as_string().get(strings_), "\n"); - for (auto& tag_def: tag_defs) { - auto parts = split_fasm_entry(tag_def, "=:", "\t "); + std::vector tag_defs = vtr::StringToken(value->front().as_string().get(strings_)).split("\n"); + for (std::string& tag_def: tag_defs) { + std::vector parts = split_fasm_entry(tag_def, "=:", "\t "); if (parts.empty()) { continue; } @@ -90,10 +90,10 @@ void FasmWriterVisitor::visit_clb_impl(ClusterBlockId blk_id, const t_pb* clb) { std::string grid_prefix; if(grid_meta != nullptr && grid_meta->has(fasm_prefix)) { - auto* value = grid_meta->get(fasm_prefix); + const std::vector* value = grid_meta->get(fasm_prefix); VTR_ASSERT(value != nullptr); std::string prefix_unsplit = value->front().as_string().get(strings_); - std::vector fasm_prefixes = vtr::split(prefix_unsplit, " \t\n"); + std::vector fasm_prefixes = vtr::StringToken(prefix_unsplit).split(" \t\n"); if(fasm_prefixes.size() != static_cast(physical_tile_->capacity)) { vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, @@ -152,7 +152,7 @@ std::string FasmWriterVisitor::handle_fasm_prefix(const t_metadata_dict *meta, auto* value = meta->one(fasm_prefix); VTR_ASSERT(value != nullptr); auto fasm_prefix_unsplit = value->as_string().get(strings_); - auto fasm_prefixes = vtr::split(fasm_prefix_unsplit, " \t\n"); + auto fasm_prefixes = vtr::StringToken(fasm_prefix_unsplit).split(" \t\n"); VTR_ASSERT(pb_type->num_pb >= 0); if(fasm_prefixes.size() != static_cast(pb_type->num_pb)) { vpr_throw(VPR_ERROR_OTHER, @@ -201,7 +201,7 @@ std::string FasmWriterVisitor::build_clb_prefix(const t_pb *pb, const t_pb_graph clb_prefix = build_clb_prefix(pb, pb_graph_node->parent_pb_graph_node, is_parent_pb_null); } - const auto *pb_type = pb_graph_node->pb_type; + const t_pb_type* pb_type = pb_graph_node->pb_type; clb_prefix += handle_fasm_prefix(&pb_type->meta, pb_graph_node, pb_type); @@ -316,8 +316,8 @@ void FasmWriterVisitor::visit_all_impl(const t_pb_routes &pb_routes, const t_pb* return; } - t_pb_type *pb_type = pb_graph_node->pb_type; - auto *mode = &pb_type->modes[pb->mode]; + t_pb_type* pb_type = pb_graph_node->pb_type; + t_mode* mode = &pb_type->modes[pb->mode]; check_features(&pb_type->meta); if(mode != nullptr) { @@ -553,7 +553,7 @@ const LutOutputDefinition* FasmWriterVisitor::find_lut(const t_pb_graph_node* pb std::vector> luts; luts.reserve(lut_parts.size()); for(const auto &part : lut_parts) { - auto parts = vtr::split(part, "="); + auto parts = vtr::StringToken(part).split("="); if(parts.size() != 2) { vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, @@ -573,7 +573,7 @@ const LutOutputDefinition* FasmWriterVisitor::find_lut(const t_pb_graph_node* pb } if(iter != lut_definitions_.end()) { - auto string_at_node = vtr::string_fmt("%s[%d]", pb_graph_node->pb_type->name, pb_graph_node->placement_index); + std::string string_at_node = vtr::string_fmt("%s[%d]", pb_graph_node->pb_type->name, pb_graph_node->placement_index); for(const auto &lut : iter->second) { if(lut.first == string_at_node) { return &lut.second; @@ -601,7 +601,7 @@ static const t_pb_routes &find_pb_route(const t_pb* pb) { void FasmWriterVisitor::check_for_param(const t_pb *atom) { auto& atom_ctx = g_vpr_ctx.atom(); - auto atom_blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom); + AtomBlockId atom_blk_id = atom_ctx.lookup().atom_pb_bimap().pb_atom(atom); if (atom_blk_id == AtomBlockId::INVALID()) { return; } @@ -611,7 +611,7 @@ void FasmWriterVisitor::check_for_param(const t_pb *atom) { return; } - const auto *meta = &atom->pb_graph_node->pb_type->meta; + const t_metadata_dict* meta = &atom->pb_graph_node->pb_type->meta; if(!meta->has(fasm_params)) { return; } @@ -624,8 +624,8 @@ void FasmWriterVisitor::check_for_param(const t_pb *atom) { VTR_ASSERT(value != nullptr); std::string fasm_params_str = value->as_string().get(strings_); - for(const auto& param : vtr::split(fasm_params_str, "\n")) { - auto param_parts = split_fasm_entry(param, "=", "\t "); + for(const std::string& param : vtr::StringToken(fasm_params_str).split("\n")) { + std::vector param_parts = split_fasm_entry(param, "=", "\t "); if(param_parts.empty()) { continue; } @@ -681,8 +681,8 @@ void FasmWriterVisitor::visit_atom_impl(const t_pb* atom) { } void FasmWriterVisitor::walk_route_tree(const RRGraphBuilder& rr_graph_builder, const RouteTreeNode& root) { - for(auto& child: root.child_nodes()){ - auto* meta = vpr::rr_edge_metadata(rr_graph_builder, size_t(root.inode), size_t(child.inode), size_t(child.parent_switch), fasm_features); + for(const RouteTreeNode& child: root.child_nodes()){ + const t_metadata_value* meta = vpr::rr_edge_metadata(rr_graph_builder, size_t(root.inode), size_t(child.inode), size_t(child.parent_switch), fasm_features); if(meta != nullptr) { output_fasm_features(meta->as_string().get(strings_), "", ""); @@ -728,11 +728,11 @@ void FasmWriterVisitor::find_clb_prefix(const t_pb_graph_node *node, void FasmWriterVisitor::output_fasm_mux(std::string_view fasm_mux_str, t_interconnect *interconnect, const t_pb_graph_pin *mux_input_pin) { - auto *pb_name = mux_input_pin->parent_node->pb_type->name; - auto pb_index = mux_input_pin->parent_node->placement_index; - auto *port_name = mux_input_pin->port->name; - auto pin_index = mux_input_pin->pin_number; - auto mux_inputs = vtr::split(fasm_mux_str, "\n"); + char* pb_name = mux_input_pin->parent_node->pb_type->name; + int pb_index = mux_input_pin->parent_node->placement_index; + char* port_name = mux_input_pin->port->name; + int pin_index = mux_input_pin->pin_number; + std::vector mux_inputs = vtr::StringToken(fasm_mux_str).split("\n"); bool have_prefix = false; std::string clb_prefix; @@ -751,8 +751,8 @@ void FasmWriterVisitor::output_fasm_mux(std::string_view fasm_mux_str, find_clb_prefix(mux_input_pin->parent_node->parent_pb_graph_node, &have_prefix, &clb_prefix); } - for(const auto &mux_input : mux_inputs) { - auto mux_parts = split_fasm_entry(mux_input, "=:", "\t "); + for(const std::string& mux_input : mux_inputs) { + std::vector mux_parts = split_fasm_entry(mux_input, "=:", "\t "); if(mux_parts.empty()) { // Swallow whitespace. @@ -765,7 +765,7 @@ void FasmWriterVisitor::output_fasm_mux(std::string_view fasm_mux_str, mux_input.c_str(), mux_parts.size()); } - auto vtr_parts = vtr::split(mux_parts[0], "."); + std::vector vtr_parts = vtr::StringToken(mux_parts[0]).split("."); if(vtr_parts.size() != 2) { vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, "fasm_mux line %s does not have 2 parts, has %d parts.\n", @@ -782,7 +782,7 @@ void FasmWriterVisitor::output_fasm_mux(std::string_view fasm_mux_str, bool root_level_connection = interconnect->parent_mode->parent_pb_type == mux_input_pin->parent_node->pb_type; - auto fasm_features_str = vtr::join(vtr::split(mux_parts[1], ","), "\n"); + std::string fasm_features_str = vtr::join(vtr::StringToken(mux_parts[1]).split(","), "\n"); if(root_level_connection) { diff --git a/utils/fasm/src/fasm_utils.cpp b/utils/fasm/src/fasm_utils.cpp index ce7bd5c3759..693e6c77fc1 100644 --- a/utils/fasm/src/fasm_utils.cpp +++ b/utils/fasm/src/fasm_utils.cpp @@ -6,7 +6,7 @@ namespace fasm { void parse_name_with_optional_index(std::string_view in, std::string *name, int *index) { - auto in_parts = vtr::split(in, "[]"); + auto in_parts = vtr::StringToken(in).split("[]"); if(in_parts.size() == 1) { *name = in; @@ -29,7 +29,7 @@ std::vector split_fasm_entry(std::string entry, } } - return vtr::split(entry, delims); + return vtr::StringToken(entry).split(std::string(delims)); } std::vector find_tags_in_feature (std::string_view a_String) { diff --git a/utils/fasm/test/test_fasm.cpp b/utils/fasm/test/test_fasm.cpp index c4cf95e933b..63767ccc1d1 100644 --- a/utils/fasm/test/test_fasm.cpp +++ b/utils/fasm/test/test_fasm.cpp @@ -429,7 +429,7 @@ TEST_CASE("fasm_integration_test", "[fasm]") { // A feature representing block pin used by the router if(line.find("PIN_") != std::string::npos) { - auto parts = vtr::split(line, "_"); + auto parts = vtr::StringToken(line).split("_"); REQUIRE(parts.size() == 6); auto x = vtr::atoi(parts[1]); @@ -558,7 +558,7 @@ TEST_CASE("fasm_integration_test", "[fasm]") { CHECK(FLE_occurrences <= 1); } else { - auto parts = vtr::split(line, "_"); + auto parts = vtr::StringToken(line).split("_"); REQUIRE(parts.size() == 3); auto src_inode = vtr::atoi(parts[0]); auto sink_inode = vtr::atoi(parts[1]); diff --git a/vpr/src/base/load_flat_place.cpp b/vpr/src/base/load_flat_place.cpp index 14de1c6da1f..9a413578db4 100644 --- a/vpr/src/base/load_flat_place.cpp +++ b/vpr/src/base/load_flat_place.cpp @@ -127,7 +127,7 @@ FlatPlacementInfo read_flat_placement(const std::string& read_flat_place_file_pa std::string line; while (std::getline(flat_place_file, line)) { // Split the line into tokens (using spaces, tabs, etc. as delimiters). - std::vector tokens = vtr::split(line); + std::vector tokens = vtr::StringToken(line).split(" \t\n"); // Skip empty lines if (tokens.empty()) continue; diff --git a/vpr/src/base/place_and_route.cpp b/vpr/src/base/place_and_route.cpp index ac61f644781..138d411539d 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -71,19 +71,23 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, e_graph_type graph_type; e_graph_type graph_directionality; - /* We have chosen to pass placer_opts_ref by reference because of its large size. * - * However, since the value is mutated later in the function, we declare a * - * mutable variable called placer_opts equal to placer_opts_ref. */ + // We have chosen to pass placer_opts_ref by reference because of its large size. + // However, since the value is mutated later in the function, we declare a + // mutable variable called placer_opts equal to placer_opts_ref. t_placer_opts placer_opts = placer_opts_ref; - /* Allocate the major routing structures. */ + // Allocate the major routing structures. if (router_opts.route_type == e_route_type::GLOBAL) { graph_type = e_graph_type::GLOBAL; graph_directionality = e_graph_type::BIDIR; } else { graph_type = (det_routing_arch.directionality == BI_DIRECTIONAL ? e_graph_type::BIDIR : e_graph_type::UNIDIR); + // Branch on tileable routing + if (det_routing_arch.directionality == UNI_DIRECTIONAL && det_routing_arch.tileable) { + graph_type = e_graph_type::UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch.directionality == BI_DIRECTIONAL ? e_graph_type::BIDIR : e_graph_type::UNIDIR); } @@ -98,15 +102,15 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, current = router_opts.fixed_channel_width + 5 * udsd_multiplier; low = router_opts.fixed_channel_width - 1 * udsd_multiplier; } else { - /* Initialize binary serach guess */ + // Initialize binary serach guess if (min_chan_width_hint > 0) { - //If the user provided a hint use it as the initial guess + // If the user provided a hint use it as the initial guess VTR_LOG("Initializing minimum channel width search using specified hint\n"); current = min_chan_width_hint; using_minw_hint = true; } else { - //Otherwise base it off the architecture + // Otherwise base it off the architecture VTR_LOG("Initializing minimum channel width search based on maximum CLB pins\n"); int max_pins = max_pins_per_grid_tile(); current = max_pins + max_pins % 2; @@ -115,7 +119,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, low = -1; } - /* Constraints must be checked to not break rr_graph generator */ + // Constraints must be checked to not break rr_graph generator if (det_routing_arch.directionality == UNI_DIRECTIONAL) { if (current % 2 != 0) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, @@ -140,9 +144,8 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, VTR_LOG("Attempting to route at %d channels (binary search bounds: [%d, %d])\n", current, low, high); fflush(stdout); - /* Check if the channel width is huge to avoid overflow. Assume the * - * circuit is unroutable with the current router options if we're * - * going to overflow. */ + // Check if the channel width is huge to avoid overflow. + // Assume the circuit is unroutable with the current router options if we're going to overflow. if (router_opts.fixed_channel_width != NO_FIXED_CHANNEL_WIDTH) { if (current > router_opts.fixed_channel_width * 4) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, @@ -198,31 +201,31 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, if (success && !Fc_clipped) { if (current == high) { - /* Can't go any lower */ + // Can't go any lower final = current; } high = current; - /* If Fc_output is too high, set to full connectivity but warn the user */ + // If Fc_output is too high, set to full connectivity but warn the user if (Fc_clipped) { VTR_LOG_WARN("Fc_output was too high and was clipped to full (maximum) connectivity.\n"); } - /* Save routing in case it is best. */ + // Save routing in case it is best. save_routing(best_routing, route_ctx.clb_opins_used_locally, saved_clb_opins_used_locally); - //If the user gave us a minW hint (and we routed successfully at that width) - //make the initial guess closer to the current value instead of the standard guess. + // If the user gave us a minW hint (and we routed successfully at that width) + // make the initial guess closer to the current value instead of the standard guess. // - //To avoid wasting time at unroutable channel widths we want to determine an un-routable (but close - //to the hint channel width). Picking a value too far below the hint may cause us to waste time - //at an un-routable channel width. Picking a value too close to the hint may cause a spurious - //failure (c.f. verify_binary_search). The scale_factor below seems a reasonable compromise. + // To avoid wasting time at unroutable channel widths we want to determine an un-routable (but close + // to the hint channel width). Picking a value too far below the hint may cause us to waste time + // at an un-routable channel width. Picking a value too close to the hint may cause a spurious + // failure (c.f. verify_binary_search). The scale_factor below seems a reasonable compromise. // - //Note this is only active for only the first re-routing after the initial guess, - //and we use the default scale_factor otherwise + // Note this is only active for only the first re-routing after the initial guess, + // and we use the default scale_factor otherwise if (using_minw_hint && attempt_count == 1) { scale_factor = 1.1; } @@ -245,7 +248,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, VTR_ASSERT(current < high); } - } else { /* last route not successful */ + } else { // last route not successful if (success && Fc_clipped) { VTR_LOG("Routing rejected, Fc_output was too high.\n"); success = false; @@ -259,7 +262,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, current = (high + low) / scale_factor; //Step to midpoint } else { if (router_opts.fixed_channel_width != NO_FIXED_CHANNEL_WIDTH) { - /* FOR Wneed = f(Fs) search */ + // FOR Wneed = f(Fs) search if (low < router_opts.fixed_channel_width + 30) { current = low + 5 * udsd_multiplier; } else { @@ -267,12 +270,12 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, "Aborting: Wneed = f(Fs) search found exceedingly large Wneed (at least %d).\n", low); } } else { - current = low * scale_factor; /* Haven't found upper bound yet */ + current = low * scale_factor; // Haven't found upper bound yet if (std::abs(current - low) < udsd_multiplier) { - //If high and scale_factor are both small, we might have ended - //up with no change in current. - //In that case, ensure we increase current by at least the track multiplier + // If high and scale_factor are both small, we might have ended + // up with no change in current. + // In that case, ensure we increase current by at least the track multiplier current = high + udsd_multiplier; } VTR_ASSERT(current > low); @@ -282,21 +285,21 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, current = current + current % udsd_multiplier; } - /* The binary search above occasionally does not find the minimum * - * routeable channel width. Sometimes a circuit that will not route * - * in 19 channels will route in 18, due to router flukiness. If * - * verify_binary_search is set, the code below will ensure that FPGAs * - * with channel widths of final-2 and final-3 wil not route * - * successfully. If one does route successfully, the router keeps * - * trying smaller channel widths until two in a row (e.g. 8 and 9) * - * fail. */ + // The binary search above occasionally does not find the minimum routeable channel width. + // Sometimes a circuit that will not route in 19 channels will route in 18, due to router flukiness. + // If verify_binary_search is set, the code below will ensure that FPGAs with channel widths of + // final-2 and final-3 wil not route successfully. + // If one does route successfully, the router keeps trying smaller channel widths until two in a + // row (e.g. 8 and 9) fail. if (verify_binary_search) { VTR_LOG("\n"); VTR_LOG("Verifying that binary search found min channel width...\n"); - prev_success = true; /* Actually final - 1 failed, but this makes router */ - /* try final-2 and final-3 even if both fail: safer */ + // Actually final - 1 failed, but this makes router + prev_success = true; + + // try final-2 and final-3 even if both fail: safer prev2_success = true; current = final - 2; @@ -351,14 +354,15 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, prev_success = success; current--; if (det_routing_arch.directionality == UNI_DIRECTIONAL) { - current--; /* width must be even */ + // width must be even + current--; } } } - /* End binary search verification. */ - /* Restore the best placement (if necessary), the best routing, and * - * * the best channel widths for final drawing and statistics output. */ + // End binary search verification. + // Restore the best placement (if necessary), the best routing, and the + // best channel widths for final drawing and statistics output. t_chan_width chan_width = init_chan(final, arch->Chans, graph_directionality); free_rr_graph(); @@ -405,8 +409,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, t_chan_width setup_chan_width(const t_router_opts& router_opts, t_chan_width_dist chan_width_dist) { - /*we give plenty of tracks, this increases routability for the */ - /*lookup table generation */ + // we give plenty of tracks, this increases routability for the lookup table generation e_graph_type graph_directionality; int width_fac; @@ -417,9 +420,9 @@ t_chan_width setup_chan_width(const t_router_opts& router_opts, auto type = find_most_common_tile_type(device_ctx.grid); width_fac = 4 * type->num_pins; - /*this is 2x the value that binary search starts */ - /*this should be enough to allow most pins to */ - /*connect to tracks in the architecture */ + // this is 2x the value that binary search starts + // this should be enough to allow most pins to + // connect to tracks in the architecture } else { width_fac = router_opts.fixed_channel_width; } @@ -457,24 +460,28 @@ t_chan_width init_chan(int cfactor, if (grid.height() > 1) { int num_channels = grid.height() - 1; VTR_ASSERT(num_channels > 0); - float separation = 1.0 / num_channels; /* Norm. distance between two channels. */ + // Norm. distance between two channels. + float separation = 1.0 / num_channels; for (size_t i = 0; i < grid.height(); ++i) { float y = float(i) / num_channels; chan_width.x_list[i] = compute_chan_width(cfactor, chan_x_dist, y, separation, graph_directionality); - chan_width.x_list[i] = std::max(chan_width.x_list[i], 1); //Minimum channel width 1 + // Minimum channel width 1 + chan_width.x_list[i] = std::max(chan_width.x_list[i], 1); } } if (grid.width() > 1) { int num_channels = grid.width() - 1; VTR_ASSERT(num_channels > 0); - float separation = 1.0 / num_channels; /* Norm. distance between two channels. */ + // Norm. distance between two channels. + float separation = 1.0 / num_channels; for (size_t i = 0; i < grid.width(); ++i) { float x = float(i) / num_channels; chan_width.y_list[i] = compute_chan_width(cfactor, chan_y_dist, x, separation, graph_directionality); - chan_width.y_list[i] = std::max(chan_width.y_list[i], 1); //Minimum channel width 1 + // Minimum channel width 1 + chan_width.y_list[i] = std::max(chan_width.y_list[i], 1); } } @@ -589,7 +596,7 @@ static float comp_width(t_chan* chan, float x, float separation) { * This function should only be called once */ void post_place_sync() { - /* Go through each block */ + // Go through each block const auto& cluster_ctx = g_vpr_ctx.clustering(); auto& blk_loc_registry = g_vpr_ctx.mutable_placement().mutable_blk_loc_registry(); diff --git a/vpr/src/base/read_blif.cpp b/vpr/src/base/read_blif.cpp index 653b7bf01db..70d3fdb7e1b 100644 --- a/vpr/src/base/read_blif.cpp +++ b/vpr/src/base/read_blif.cpp @@ -19,6 +19,7 @@ #include #include #include //std::isdigit +#include //std::regex #include "blifparse.hpp" #include "atom_netlist.h" @@ -643,66 +644,63 @@ vtr::LogicValue to_vtr_logic_value(blifparse::LogicValue val) { } bool is_string_param(const std::string& param) { - /* Empty param is considered a string */ + // Empty param is considered a string if (param.empty()) { return true; } - /* There have to be at least 2 characters (the quotes) */ + // There have to be at least 2 characters (the quotes) if (param.length() < 2) { return false; } - /* The first and the last characters must be quotes */ + // The first and the last characters must be quotes size_t len = param.length(); if (param[0] != '"' || param[len - 1] != '"') { return false; } - /* There mustn't be any other quotes except for escaped ones */ + // There mustn't be any other quotes except for escaped ones for (size_t i = 1; i < (len - 1); ++i) { if (param[i] == '"' && param[i - 1] != '\\') { return false; } } - /* This is a string param */ + // This is a string param return true; } bool is_binary_param(const std::string& param) { - /* Must be non-empty */ + // Must be non-empty if (param.empty()) { return false; } - /* The string must contain only '0' and '1' */ + // The string must contain only '0' and '1' for (size_t i = 0; i < param.length(); ++i) { if (param[i] != '0' && param[i] != '1') { return false; } } - /* This is a binary word param */ + // This is a binary word param return true; } bool is_real_param(const std::string& param) { - const std::string chars = "012345678."; - /* Must be non-empty */ if (param.empty()) { return false; } - /* The string mustn't contain any other chars that the expected ones */ - for (size_t i = 0; i < param.length(); ++i) { - if (chars.find(param[i]) == std::string::npos) { - return false; - } + // The string must match the regular expression + const std::regex real_number_expr("[+-]?([0-9]*\\.[0-9]+)|([0-9]+\\.[0-9]*)"); + if (!std::regex_match(param, real_number_expr)) { + return false; } - /* This is a real number param */ + // This is a real number param return true; } diff --git a/vpr/src/base/read_netlist.cpp b/vpr/src/base/read_netlist.cpp index 9d52551ca3c..f149b9abba7 100644 --- a/vpr/src/base/read_netlist.cpp +++ b/vpr/src/base/read_netlist.cpp @@ -165,13 +165,13 @@ ClusteredNetlist read_netlist(const char* net_file, //Collect top level I/Os auto top_inputs = pugiutil::get_single_child(top, "inputs", loc_data); - circuit_inputs = vtr::split(top_inputs.text().get()); + circuit_inputs = vtr::StringToken(top_inputs.text().get()).split(" \t\n"); auto top_outputs = pugiutil::get_single_child(top, "outputs", loc_data); - circuit_outputs = vtr::split(top_outputs.text().get()); + circuit_outputs = vtr::StringToken(top_outputs.text().get()).split(" \t\n"); auto top_clocks = pugiutil::get_single_child(top, "clocks", loc_data); - circuit_clocks = vtr::split(top_clocks.text().get()); + circuit_clocks = vtr::StringToken(top_clocks.text().get()).split(" \t\n"); /* Parse all CLB blocks and all nets*/ @@ -634,7 +634,7 @@ static void processPorts(pugi::xml_node Parent, t_pb* pb, t_pb_routes& pb_route, } //Extract all the pins for this port - pins = vtr::split(Cur.text().get()); + pins = vtr::StringToken(Cur.text().get()).split(" \t\n"); num_tokens = pins.size(); //Check that the number of pins from the netlist file matches the pb port's number of pins @@ -839,7 +839,7 @@ static void processPorts(pugi::xml_node Parent, t_pb* pb, t_pb_routes& pb_route, pb->pb_graph_node->pb_type->name, pb->pb_graph_node->placement_index); } - auto pin_mapping = vtr::split(pin_rot_map.text().get()); + auto pin_mapping = vtr::StringToken(pin_rot_map.text().get()).split(" \t\n"); if (size_t(pb_gport->num_pins) != pin_mapping.size()) { vpr_throw(VPR_ERROR_NET_F, netlist_file_name, loc_data.line(pin_rot_map), diff --git a/vpr/src/base/read_place.cpp b/vpr/src/base/read_place.cpp index 5b07ef33f28..36f4174af51 100644 --- a/vpr/src/base/read_place.cpp +++ b/vpr/src/base/read_place.cpp @@ -99,7 +99,7 @@ static void read_place_header(std::ifstream& placement_file, while (std::getline(placement_file, line) && (!seen_netlist_id || !seen_grid_dimensions)) { //Parse line-by-line ++lineno; - std::vector tokens = vtr::split(line); + std::vector tokens = vtr::StringToken(line).split(" \t\n"); if (tokens.empty()) { continue; //Skip blank lines @@ -226,7 +226,7 @@ static std::string read_place_body(std::ifstream& placement_file, while (std::getline(placement_file, line)) { //Parse line-by-line ++lineno; - std::vector tokens = vtr::split(line); + std::vector tokens = vtr::StringToken(line).split(" \t\n"); if (tokens.empty()) { continue; //Skip blank lines diff --git a/vpr/src/base/read_route.cpp b/vpr/src/base/read_route.cpp index a8a08218d70..691169fd81d 100644 --- a/vpr/src/base/read_route.cpp +++ b/vpr/src/base/read_route.cpp @@ -163,7 +163,7 @@ bool read_route(const char* route_file, std::getline(fp, header_str); ++lineno; - std::vector header = vtr::split(header_str); + std::vector header = vtr::StringToken(header_str).split(" \t\n"); if (header[0] == "Placement_File:" && header[2] == "Placement_ID:" && header[3] != place_ctx.placement_id) { auto msg = vtr::string_fmt( "Placement file %s specified in the routing file" @@ -188,7 +188,7 @@ bool read_route(const char* route_file, std::getline(fp, header_str); ++lineno; header.clear(); - header = vtr::split(header_str); + header = vtr::StringToken(header_str).split(" \t\n"); if (header[0] == "Array" && header[1] == "size:" && (vtr::atou(header[2].c_str()) != device_ctx.grid.width() || vtr::atou(header[4].c_str()) != device_ctx.grid.height())) { vpr_throw(VPR_ERROR_ROUTE, route_file, lineno, "Device dimensions %sx%s specified in the routing file does not match given %dx%d ", @@ -239,7 +239,7 @@ static void process_route(const Netlist<>& net_list, while (std::getline(fp, input)) { ++lineno; tokens.clear(); - tokens = vtr::split(input); + tokens = vtr::StringToken(input).split(" \t\n"); if (tokens.empty()) { continue; //Skip blank lines } else if (tokens[0][0] == '#') { @@ -341,7 +341,7 @@ static void process_nodes(const Netlist<>& net_list, ++lineno; tokens.clear(); - tokens = vtr::split(input); + tokens = vtr::StringToken(input).split(" \t\n"); if (tokens.empty()) { continue; /*Skip blank lines*/ @@ -526,7 +526,7 @@ static void process_global_blocks(const Netlist<>& net_list, std::ifstream& fp, while (std::getline(fp, block)) { ++lineno; tokens.clear(); - tokens = vtr::split(block); + tokens = vtr::StringToken(block).split(" \t\n"); if (tokens.empty()) { continue; /*Skip blank lines*/ @@ -730,6 +730,10 @@ void print_route(const Netlist<>& net_list, } break; + case e_rr_type::MUX: + fprintf(fp, " Index: "); + break; + default: VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "in print_route: Unexpected traceback element type: %d (%s).\n", diff --git a/vpr/src/base/setup_clocks.cpp b/vpr/src/base/setup_clocks.cpp index f1232005a42..94897db1b03 100644 --- a/vpr/src/base/setup_clocks.cpp +++ b/vpr/src/base/setup_clocks.cpp @@ -37,15 +37,15 @@ void setup_clock_network_wires(const t_arch& Arch, FormulaParser& p, std::vector auto& clock_networks_device = device_ctx.clock_networks; auto& grid = device_ctx.grid; - auto& clock_networks_arch = Arch.clock_arch.clock_networks_arch; - auto& clock_metal_layers = Arch.clock_arch.clock_metal_layers; + const std::vector& clock_networks_arch = Arch.clock_arch.clock_networks_arch; + const std::unordered_map& clock_metal_layers = Arch.clock_arch.clock_metal_layers; // TODO: copied over from SetupGrid. Ensure consistency by only assigning in one place t_formula_data vars; vars.set_var_value("W", grid.width()); vars.set_var_value("H", grid.height()); - for (auto clock_network_arch : clock_networks_arch) { + for (const t_clock_network_arch& clock_network_arch : clock_networks_arch) { switch (clock_network_arch.type) { case e_clock_type::SPINE: { std::unique_ptr spine = std::make_unique(); @@ -123,20 +123,20 @@ void setup_clock_connections(const t_arch& Arch, FormulaParser& p) { auto& clock_connections_device = device_ctx.clock_connections; auto& grid = device_ctx.grid; - auto& clock_connections_arch = Arch.clock_arch.clock_connections_arch; + const std::vector& clock_connections_arch = Arch.clock_arch.clock_connections_arch; // TODO: copied over from SetupGrid. Ensure consistency by only assigning in one place t_formula_data vars; vars.set_var_value("W", grid.width()); vars.set_var_value("H", grid.height()); - for (auto clock_connection_arch : clock_connections_arch) { + for (const t_clock_connection_arch& clock_connection_arch : clock_connections_arch) { if (clock_connection_arch.from == "ROUTING") { clock_connections_device.emplace_back(new RoutingToClockConnection); if (RoutingToClockConnection* routing_to_clock = dynamic_cast(clock_connections_device.back().get())) { //TODO: Add error check to check that clock name and tap name exist and that only // two names are returned by the below function - auto names = vtr::split(clock_connection_arch.to, "."); + std::vector names = vtr::StringToken(clock_connection_arch.to).split("."); VTR_ASSERT_MSG(names.size() == 2, "Invalid clock name.\n"); routing_to_clock->set_clock_name_to_connect_to(names[0]); routing_to_clock->set_clock_switch_point_name(names[1]); @@ -153,7 +153,7 @@ void setup_clock_connections(const t_arch& Arch, FormulaParser& p) { if (ClockToPinsConnection* clock_to_pins = dynamic_cast(clock_connections_device.back().get())) { //TODO: Add error check to check that clock name and tap name exist and that only // two names are returned by the below function - auto names = vtr::split(clock_connection_arch.from, "."); + std::vector names = vtr::StringToken(clock_connection_arch.from).split("."); VTR_ASSERT_MSG(names.size() == 2, "Invalid clock name.\n"); clock_to_pins->set_clock_name_to_connect_from(names[0]); clock_to_pins->set_clock_switch_point_name(names[1]); @@ -166,8 +166,8 @@ void setup_clock_connections(const t_arch& Arch, FormulaParser& p) { if (ClockToClockConneciton* clock_to_clock = dynamic_cast(clock_connections_device.back().get())) { //TODO: Add error check to check that clock name and tap name exist and that only // two names are returned by the below function - auto to_names = vtr::split(clock_connection_arch.to, "."); - auto from_names = vtr::split(clock_connection_arch.from, "."); + std::vector to_names = vtr::StringToken(clock_connection_arch.to).split("."); + std::vector from_names = vtr::StringToken(clock_connection_arch.from).split("."); VTR_ASSERT_MSG(to_names.size() == 2, "Invalid clock name.\n"); clock_to_clock->set_to_clock_name(to_names[0]); clock_to_clock->set_to_clock_switch_point_name(to_names[1]); @@ -196,7 +196,7 @@ MetalLayer get_metal_layer_from_name( } // Metal layer was found. Copy over from arch description to proper data type - auto arch_metal_layer = itter->second; + t_metal_layer arch_metal_layer = itter->second; MetalLayer metal_layer; metal_layer.r_metal = arch_metal_layer.r_metal; metal_layer.c_metal = arch_metal_layer.c_metal; diff --git a/vpr/src/base/SetupGrid.cpp b/vpr/src/base/setup_grid.cpp similarity index 99% rename from vpr/src/base/SetupGrid.cpp rename to vpr/src/base/setup_grid.cpp index 3c7c03b1782..fb6b73f982d 100644 --- a/vpr/src/base/SetupGrid.cpp +++ b/vpr/src/base/setup_grid.cpp @@ -22,7 +22,7 @@ #include "vpr_utils.h" #include "globals.h" -#include "SetupGrid.h" +#include "setup_grid.h" #include "vtr_expr_eval.h" #define MAX_SIZE_FACTOR 10000 diff --git a/vpr/src/base/SetupGrid.h b/vpr/src/base/setup_grid.h similarity index 100% rename from vpr/src/base/SetupGrid.h rename to vpr/src/base/setup_grid.h diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/setup_vpr.cpp similarity index 89% rename from vpr/src/base/SetupVPR.cpp rename to vpr/src/base/setup_vpr.cpp index f27c21876c4..4f2cc967bca 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/setup_vpr.cpp @@ -1,7 +1,7 @@ #include #include -#include "SetupVPR.h" +#include "setup_vpr.h" #include "physical_types_util.h" #include "vtr_assert.h" #include "vtr_util.h" @@ -24,29 +24,31 @@ #include "clock_modeling.h" #include "ShowSetup.h" -static void SetupNetlistOpts(const t_options& Options, t_netlist_opts& NetlistOpts); -static void SetupAPOpts(const t_options& options, - t_ap_opts& apOpts); -static void SetupPackerOpts(const t_options& Options, - t_packer_opts* PackerOpts); -static void SetupPlacerOpts(const t_options& Options, - t_placer_opts* PlacerOpts); -static void SetupAnnealSched(const t_options& Options, - t_annealing_sched* AnnealSched); -static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts); -static void SetupNocOpts(const t_options& Options, - t_noc_opts* NocOpts); -static void SetupServerOpts(const t_options& Options, - t_server_opts* ServerOpts); - -static void SetupRoutingArch(const t_arch& Arch, t_det_routing_arch& RoutingArch); - -static void SetupTiming(const t_options& Options, const bool TimingEnabled, t_timing_inf* Timing); -static void SetupSwitches(const t_arch& Arch, - t_det_routing_arch& RoutingArch, - const std::vector& arch_switches); -static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysis_opts); -static void SetupPowerOpts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch); +#include "setup_vib_utils.h" + +static void setup_netlist_opts(const t_options& Options, t_netlist_opts& NetlistOpts); +static void setup_ap_opts(const t_options& options, + t_ap_opts& apOpts); +static void setup_packer_opts(const t_options& Options, + t_packer_opts* PackerOpts); +static void setup_placer_opts(const t_options& Options, + t_placer_opts* PlacerOpts); +static void setup_anneal_sched(const t_options& Options, + t_annealing_sched* AnnealSched); +static void setup_router_opts(const t_options& Options, t_router_opts* RouterOpts); +static void setup_noc_opts(const t_options& Options, + t_noc_opts* NocOpts); +static void setup_server_opts(const t_options& Options, + t_server_opts* ServerOpts); + +static void setup_routing_arch(const t_arch& Arch, t_det_routing_arch& RoutingArch); + +static void setup_timing(const t_options& Options, const bool TimingEnabled, t_timing_inf* Timing); +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_power_opts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch); /** * @brief Identify which switch must be used for *track* to *IPIN* connections based on architecture file specification. @@ -111,7 +113,7 @@ void SetupVPR(const t_options* options, t_vpr_setup* vpr_setup) { using argparse::Provenance; - auto& device_ctx = g_vpr_ctx.mutable_device(); + DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); device_ctx.arch = arch; @@ -143,14 +145,14 @@ void SetupVPR(const t_options* options, fileNameOpts->verify_file_digests = options->verify_file_digests; - SetupNetlistOpts(*options, *netlistOpts); - SetupPlacerOpts(*options, placerOpts); - SetupAnnealSched(*options, &placerOpts->anneal_sched); - SetupRouterOpts(*options, routerOpts); - SetupAnalysisOpts(*options, *analysisOpts); - SetupPowerOpts(*options, powerOpts, arch); - SetupNocOpts(*options, nocOpts); - SetupServerOpts(*options, serverOpts); + setup_netlist_opts(*options, *netlistOpts); + setup_placer_opts(*options, placerOpts); + setup_anneal_sched(*options, &placerOpts->anneal_sched); + setup_router_opts(*options, routerOpts); + setup_analysis_opts(*options, *analysisOpts); + setup_power_opts(*options, powerOpts, arch); + setup_noc_opts(*options, nocOpts); + setup_server_opts(*options, serverOpts); //save the device layout, which is required to parse the architecture file arch->device_layout = options->device_layout; @@ -182,7 +184,7 @@ void SetupVPR(const t_options* options, device_ctx.EMPTY_PHYSICAL_TILE_TYPE = nullptr; int num_inputs = 0; int num_outputs = 0; - for (auto& type : device_ctx.physical_tile_types) { + for (t_physical_tile_type& type : device_ctx.physical_tile_types) { if (type.is_empty()) { VTR_ASSERT(device_ctx.EMPTY_PHYSICAL_TILE_TYPE == nullptr); VTR_ASSERT(type.num_pins == 0); @@ -200,7 +202,7 @@ void SetupVPR(const t_options* options, device_ctx.EMPTY_LOGICAL_BLOCK_TYPE = nullptr; int max_equivalent_tiles = 0; - for (const auto& type : device_ctx.logical_block_types) { + for (const t_logical_block_type& type : device_ctx.logical_block_types) { if (type.is_empty()) { VTR_ASSERT(device_ctx.EMPTY_LOGICAL_BLOCK_TYPE == nullptr); VTR_ASSERT(type.pb_type == nullptr); @@ -228,16 +230,20 @@ void SetupVPR(const t_options* options, segments = arch->Segments; - SetupSwitches(*arch, routingArch, arch->switches); - SetupRoutingArch(*arch, routingArch); - SetupTiming(*options, timingenabled, timing); - SetupPackerOpts(*options, packerOpts); - SetupAPOpts(*options, *apOpts); + setup_switches(*arch, routingArch, arch->switches); + setup_routing_arch(*arch, routingArch); + setup_timing(*options, timingenabled, timing); + setup_packer_opts(*options, packerOpts); + setup_ap_opts(*options, *apOpts); routingArch.write_rr_graph_filename = options->write_rr_graph_file; routingArch.read_rr_graph_filename = options->read_rr_graph_file; routingArch.read_rr_edge_override_filename = options->read_rr_edge_override_file; - for (auto has_global_routing : arch->layer_global_routing) { + if (!arch->vib_infs.empty()) { + setup_vib_inf(device_ctx.physical_tile_types, arch->switches, arch->Segments, arch->vib_infs); + } + + for (bool has_global_routing : arch->layer_global_routing) { device_ctx.inter_cluster_prog_routing_resources.emplace_back(has_global_routing); } @@ -341,7 +347,7 @@ void SetupVPR(const t_options* options, } } -static void SetupTiming(const t_options& Options, const bool TimingEnabled, t_timing_inf* Timing) { +static void setup_timing(const t_options& Options, const bool TimingEnabled, t_timing_inf* Timing) { /* Don't do anything if they don't want timing */ if (!TimingEnabled) { Timing->timing_analysis_enabled = false; @@ -356,10 +362,10 @@ static void SetupTiming(const t_options& Options, const bool TimingEnabled, t_ti * @brief This loads up VPR's arch_switch_inf data by combining the switches * from the arch file with the special switches that VPR needs. */ -static void SetupSwitches(const t_arch& Arch, - t_det_routing_arch& RoutingArch, - const std::vector& arch_switches) { - auto& device_ctx = g_vpr_ctx.mutable_device(); +static void setup_switches(const t_arch& Arch, + t_det_routing_arch& RoutingArch, + const std::vector& arch_switches) { + DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); int switches_to_copy = (int)arch_switches.size(); int num_arch_switches = (int)arch_switches.size(); @@ -413,12 +419,14 @@ static void SetupSwitches(const t_arch& Arch, * * Since checks are already done, this just copies values across */ -static void SetupRoutingArch(const t_arch& Arch, - t_det_routing_arch& RoutingArch) { - RoutingArch.switch_block_type = Arch.SBType; +static void setup_routing_arch(const t_arch& Arch, + t_det_routing_arch& RoutingArch) { + RoutingArch.switch_block_type = Arch.sb_type; + RoutingArch.switch_block_subtype = Arch.sb_sub_type; RoutingArch.R_minW_nmos = Arch.R_minW_nmos; RoutingArch.R_minW_pmos = Arch.R_minW_pmos; RoutingArch.Fs = Arch.Fs; + RoutingArch.sub_fs = Arch.sub_fs; RoutingArch.directionality = BI_DIRECTIONAL; if (!Arch.Segments.empty()) { RoutingArch.directionality = Arch.Segments[0].directionality; @@ -426,9 +434,18 @@ static void SetupRoutingArch(const t_arch& Arch, /* copy over the switch block information */ RoutingArch.switchblocks = Arch.switchblocks; + + /* Copy the tileable routing setting */ + RoutingArch.tileable = Arch.tileable; + RoutingArch.perimeter_cb = Arch.perimeter_cb; + RoutingArch.shrink_boundary = Arch.shrink_boundary; + RoutingArch.through_channel = Arch.through_channel; + RoutingArch.opin2all_sides = Arch.opin2all_sides; + RoutingArch.concat_wire = Arch.concat_wire; + RoutingArch.concat_pass_wire = Arch.concat_pass_wire; } -static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) { +static void setup_router_opts(const t_options& Options, t_router_opts* RouterOpts) { RouterOpts->do_check_rr_graph = Options.check_rr_graph; RouterOpts->astar_fac = Options.astar_fac; RouterOpts->astar_offset = Options.astar_offset; @@ -462,6 +479,7 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->router_algorithm = Options.RouterAlgorithm; RouterOpts->fixed_channel_width = Options.RouteChanWidth; RouterOpts->min_channel_width_hint = Options.min_route_chan_width_hint; + RouterOpts->read_rr_edge_metadata = Options.read_rr_edge_metadata; RouterOpts->reorder_rr_graph_nodes_algorithm = Options.reorder_rr_graph_nodes_algorithm; RouterOpts->reorder_rr_graph_nodes_threshold = Options.reorder_rr_graph_nodes_threshold; @@ -522,8 +540,8 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->generate_router_lookahead_report = Options.generate_router_lookahead_report.value(); } -static void SetupAnnealSched(const t_options& Options, - t_annealing_sched* AnnealSched) { +static void setup_anneal_sched(const t_options& Options, + t_annealing_sched* AnnealSched) { AnnealSched->alpha_t = Options.PlaceAlphaT; if (AnnealSched->alpha_t >= 1 || AnnealSched->alpha_t <= 0) { VPR_FATAL_ERROR(VPR_ERROR_OTHER, "alpha_t must be between 0 and 1 exclusive.\n"); @@ -558,8 +576,8 @@ static void SetupAnnealSched(const t_options& Options, * Error checking, such as checking for conflicting params is assumed * to be done beforehand */ -void SetupAPOpts(const t_options& options, - t_ap_opts& apOpts) { +void setup_ap_opts(const t_options& options, + t_ap_opts& apOpts) { apOpts.analytical_solver_type = options.ap_analytical_solver.value(); apOpts.partial_legalizer_type = options.ap_partial_legalizer.value(); apOpts.full_legalizer_type = options.ap_full_legalizer.value(); @@ -580,8 +598,8 @@ void SetupAPOpts(const t_options& options, * Error checking, such as checking for conflicting params is assumed * to be done beforehand */ -void SetupPackerOpts(const t_options& Options, - t_packer_opts* PackerOpts) { +void setup_packer_opts(const t_options& Options, + t_packer_opts* PackerOpts) { PackerOpts->output_file = Options.NetFile; PackerOpts->circuit_file_name = Options.CircuitFile; @@ -611,7 +629,7 @@ void SetupPackerOpts(const t_options& Options, PackerOpts->timing_update_type = Options.timing_update_type; } -static void SetupNetlistOpts(const t_options& Options, t_netlist_opts& NetlistOpts) { +static void setup_netlist_opts(const t_options& Options, t_netlist_opts& NetlistOpts) { NetlistOpts.const_gen_inference = Options.const_gen_inference; NetlistOpts.absorb_buffer_luts = Options.absorb_buffer_luts; NetlistOpts.sweep_dangling_primary_ios = Options.sweep_dangling_primary_ios; @@ -627,7 +645,7 @@ static void SetupNetlistOpts(const t_options& Options, t_netlist_opts& NetlistOp * Error checking, such as checking for conflicting params * is assumed to be done beforehand */ -static void SetupPlacerOpts(const t_options& Options, t_placer_opts* PlacerOpts) { +static void setup_placer_opts(const t_options& Options, t_placer_opts* PlacerOpts) { if (Options.do_placement) { PlacerOpts->doPlacement = e_stage_action::DO; } @@ -711,7 +729,7 @@ static void SetupPlacerOpts(const t_options& Options, t_placer_opts* PlacerOpts) PlacerOpts->place_auto_init_t_scale = Options.place_auto_init_t_scale.value(); } -static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysis_opts) { +static void setup_analysis_opts(const t_options& Options, t_analysis_opts& analysis_opts) { if (Options.do_analysis) { analysis_opts.doAnalysis = e_stage_action::DO; } @@ -735,8 +753,8 @@ static void SetupAnalysisOpts(const t_options& Options, t_analysis_opts& analysi analysis_opts.generate_net_timing_report = Options.generate_net_timing_report; } -static void SetupPowerOpts(const t_options& Options, t_power_opts* power_opts, t_arch* Arch) { - auto& device_ctx = g_vpr_ctx.mutable_device(); +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(); power_opts->do_power = Options.do_power; @@ -760,7 +778,7 @@ static void SetupPowerOpts(const t_options& Options, t_power_opts* power_opts, t /* * Go through all the NoC options supplied by the user and store them internally. */ -static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts) { +static void setup_noc_opts(const t_options& Options, t_noc_opts* NocOpts) { // assign the noc specific options from the command line NocOpts->noc = Options.noc; NocOpts->noc_flows_file = Options.noc_flows_file; @@ -784,7 +802,7 @@ static void SetupNocOpts(const t_options& Options, t_noc_opts* NocOpts) { NocOpts->noc_placement_file_name = Options.noc_placement_file_name; } -static void SetupServerOpts(const t_options& Options, t_server_opts* ServerOpts) { +static void setup_server_opts(const t_options& Options, t_server_opts* ServerOpts) { ServerOpts->is_server_mode_enabled = Options.is_server_mode_enabled; ServerOpts->port_num = Options.server_port_num; } @@ -816,20 +834,20 @@ static void find_ipin_cblock_switch_index(const t_arch& Arch, int& wire_to_arch_ } static void alloc_and_load_intra_cluster_resources(bool reachability_analysis) { - auto& device_ctx = g_vpr_ctx.mutable_device(); + DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); - for (auto& physical_type : device_ctx.physical_tile_types) { + for (t_physical_tile_type& physical_type : device_ctx.physical_tile_types) { set_root_pin_to_pb_pin_map(&physical_type); // Physical number of pins and classes in the clusters start from the number of pins and classes on the cluster // to avoid collision between intra-cluster pins and classes with root-level ones int physical_pin_offset = physical_type.num_pins; int physical_class_offset = (int)physical_type.class_inf.size(); physical_type.primitive_class_starting_idx = physical_class_offset; - for (auto& sub_tile : physical_type.sub_tiles) { + for (t_sub_tile& sub_tile : physical_type.sub_tiles) { sub_tile.primitive_class_range.resize(sub_tile.capacity.total()); sub_tile.intra_pin_range.resize(sub_tile.capacity.total()); for (int sub_tile_inst = 0; sub_tile_inst < sub_tile.capacity.total(); sub_tile_inst++) { - for (auto logic_block_ptr : sub_tile.equivalent_sites) { + for (t_logical_block_type_ptr logic_block_ptr : sub_tile.equivalent_sites) { int num_classes = (int)logic_block_ptr->primitive_logical_class_inf.size(); int num_pins = (int)logic_block_ptr->pin_logical_num_to_pb_pin_mapping.size(); int logical_block_idx = logic_block_ptr->index; @@ -848,13 +866,13 @@ static void alloc_and_load_intra_cluster_resources(bool reachability_analysis) { // Change the pin numbers in a class pin list from logical number to physical number std::for_each(logical_classes.begin(), logical_classes.end(), [&physical_pin_offset](t_class& l_class) { - for (auto& pin : l_class.pinlist) { + for (int& pin : l_class.pinlist) { pin += physical_pin_offset; } }); int physical_class_num = physical_class_offset; - for (auto& logic_class : logical_classes) { + for (t_class& logic_class : logical_classes) { auto result = physical_type.primitive_class_inf.insert(std::make_pair(physical_class_num, logic_class)); add_primitive_pin_to_physical_tile(logic_class.pinlist, physical_class_num, @@ -880,13 +898,13 @@ static void alloc_and_load_intra_cluster_resources(bool reachability_analysis) { static void set_root_pin_to_pb_pin_map(t_physical_tile_type* physical_type) { for (int sub_tile_idx = 0; sub_tile_idx < (int)physical_type->sub_tiles.size(); sub_tile_idx++) { - auto& sub_tile = physical_type->sub_tiles[sub_tile_idx]; + t_sub_tile& sub_tile = physical_type->sub_tiles[sub_tile_idx]; int inst_num_pin = sub_tile.num_phy_pins / sub_tile.capacity.total(); // Later in the code, I've assumed that pins of a subtile are mapped in a continuous fashion to // the tile pins - Usage case: vpr_utils.cpp:get_pb_pins VTR_ASSERT(sub_tile.sub_tile_to_tile_pin_indices[0] + sub_tile.num_phy_pins - 1 == sub_tile.sub_tile_to_tile_pin_indices[sub_tile.num_phy_pins - 1]); for (int sub_tile_pin_num = 0; sub_tile_pin_num < sub_tile.num_phy_pins; sub_tile_pin_num++) { - for (auto& eq_site : sub_tile.equivalent_sites) { + for (t_logical_block_type_ptr eq_site : sub_tile.equivalent_sites) { t_physical_pin sub_tile_physical_pin = t_physical_pin(sub_tile_pin_num % inst_num_pin); int physical_pin_num = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_num]; auto direct_map = physical_type->tile_block_pin_directs_map.at(eq_site->index).at(sub_tile.index); @@ -922,19 +940,19 @@ static void add_logical_pin_to_physical_tile(int physical_pin_offset, static void add_primitive_pin_to_physical_tile(const std::vector& pin_list, int physical_class_num, t_physical_tile_type* physical_tile) { - for (auto pin_num : pin_list) { + for (int pin_num : pin_list) { physical_tile->primitive_pin_class.insert(std::make_pair(pin_num, physical_class_num)); } } static void add_intra_tile_switches() { - auto& device_ctx = g_vpr_ctx.mutable_device(); + DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); std::unordered_map pb_edge_delays; VTR_ASSERT(device_ctx.all_sw_inf.size() == device_ctx.arch_switch_inf.size()); - for (auto& logical_block : device_ctx.logical_block_types) { + for (t_logical_block_type& logical_block : device_ctx.logical_block_types) { if (logical_block.is_empty()) { continue; } @@ -948,7 +966,7 @@ static void add_intra_tile_switches() { while (!pb_graph_node_q.empty()) { pb_graph_node = pb_graph_node_q.front(); pb_graph_node_q.pop_front(); - auto pb_pins = get_mutable_pb_graph_node_pb_pins(pb_graph_node); + std::vector pb_pins = get_mutable_pb_graph_node_pb_pins(pb_graph_node); for (t_pb_graph_pin* pb_pin : pb_pins) { for (int out_edge_idx = 0; out_edge_idx < pb_pin->num_output_edges; out_edge_idx++) { @@ -1012,10 +1030,10 @@ static void do_reachability_analysis(t_physical_tile_type* physical_tile, // Make sure that we are visiting each pin once. if (insert_res.second) { curr_pb_graph_pin->connected_sinks_ptc.insert(physical_class_num); - auto driving_pins = get_physical_pin_src_pins(physical_tile, - logical_block, - curr_pin_physical_num); - for (auto driving_pin_physical_num : driving_pins) { + std::vector driving_pins = get_physical_pin_src_pins(physical_tile, + logical_block, + curr_pin_physical_num); + for (int driving_pin_physical_num : driving_pins) { // Since we define reachable class as a class which is connected to a pin through a series of IPINs, only IPINs are added to the list if (get_pin_type_from_pin_physical_num(physical_tile, driving_pin_physical_num) == e_pin_type::RECEIVER) { pin_list.push_back(driving_pin_physical_num); diff --git a/vpr/src/base/SetupVPR.h b/vpr/src/base/setup_vpr.h similarity index 100% rename from vpr/src/base/SetupVPR.h rename to vpr/src/base/setup_vpr.h diff --git a/vpr/src/base/vib_grid/setup_vib_grid.cpp b/vpr/src/base/vib_grid/setup_vib_grid.cpp new file mode 100644 index 00000000000..a8c481db705 --- /dev/null +++ b/vpr/src/base/vib_grid/setup_vib_grid.cpp @@ -0,0 +1,388 @@ +#include +#include +#include +#include +#include + +#include "vtr_assert.h" +#include "vtr_math.h" +#include "vtr_log.h" + +#include "vpr_types.h" +#include "vpr_error.h" +#include "vpr_utils.h" + +#include "globals.h" +#include "setup_grid.h" +#include "setup_vib_grid.h" +#include "vtr_expr_eval.h" + +using vtr::FormulaParser; +using vtr::t_formula_data; + +static VibDeviceGrid build_vib_device_grid(const t_vib_grid_def& grid_def, size_t grid_width, size_t grid_height, bool warn_out_of_range = true); + +static void set_vib_grid_block_type(int priority, + const VibInf* type, + int layer_num, + size_t x_root, + size_t y_root, + vtr::NdMatrix& vib_grid, + vtr::NdMatrix& grid_priorities); + +VibDeviceGrid create_vib_device_grid(std::string_view layout_name, const std::vector& vib_grid_layouts) { + if (layout_name == "auto") { + //We do not support auto layout now + VPR_FATAL_ERROR(VPR_ERROR_ARCH, "VIB architecture doesn't support auto layout now\n"); + + } else { + //Use the specified device + + //Find the matching grid definition + auto cmp = [&](const t_vib_grid_def& grid_def) { + return grid_def.name == layout_name; + }; + + auto iter = std::find_if(vib_grid_layouts.begin(), vib_grid_layouts.end(), cmp); + if (iter == vib_grid_layouts.end()) { + //Not found + std::string valid_names; + for (size_t i = 0; i < vib_grid_layouts.size(); ++i) { + if (i != 0) { + valid_names += ", "; + } + valid_names += "'" + vib_grid_layouts[i].name + "'"; + } + std::string error_msg = vtr::string_fmt("Failed to find grid layout named '%s' (valid grid layouts: %s)", layout_name, valid_names.c_str()); + VPR_FATAL_ERROR(VPR_ERROR_ARCH, error_msg.c_str()); + } + + return build_vib_device_grid(*iter, iter->width, iter->height); + } +} + +///@brief Build the specified device grid +static VibDeviceGrid build_vib_device_grid(const t_vib_grid_def& grid_def, size_t grid_width, size_t grid_height, bool warn_out_of_range) { + if (grid_def.grid_type == e_vib_grid_def_type::VIB_FIXED) { + if (grid_def.width != int(grid_width) || grid_def.height != int(grid_height)) { + VPR_FATAL_ERROR(VPR_ERROR_OTHER, + "Requested grid size (%zu%zu) does not match fixed device size (%dx%d)", + grid_width, grid_height, grid_def.width, grid_def.height); + } + } + + auto& device_ctx = g_vpr_ctx.device(); + + //Initialize the grid and each location priority based on available dies in the architecture file + vtr::NdMatrix vib_grid; + vtr::NdMatrix grid_priorities; + int num_layers = (int)grid_def.layers.size(); + vib_grid.resize(std::array{(size_t)num_layers, grid_width, grid_height}); + + //Track the current priority for each grid location + // Note that we initialize it to the lowest (i.e. most negative) possible value, so + // any user-specified priority will override the default empty grid + grid_priorities.resize(std::array{(size_t)num_layers, grid_width, grid_height}, std::numeric_limits::lowest()); + + //Initialize the device to all empty blocks + const VibInf* empty_type = nullptr; + for (int layer = 0; layer < num_layers; ++layer) { + for (size_t x = 0; x < grid_width; ++x) { + for (size_t y = 0; y < grid_height; ++y) { + set_vib_grid_block_type(std::numeric_limits::lowest() + 1, //+1 so it overrides without warning + empty_type, + layer, x, y, + vib_grid, grid_priorities); + } + } + } + + FormulaParser p; + std::set seen_types; + for (int layer = 0; layer < num_layers; layer++) { + for (const t_vib_grid_loc_def& grid_loc_def : grid_def.layers.at(layer).loc_defs) { + //Fill in the block types according to the specification + const VibInf* type = nullptr; + for (size_t vib_type = 0; vib_type < device_ctx.arch->vib_infs.size(); vib_type++) { + if (grid_loc_def.block_type == device_ctx.arch->vib_infs[vib_type].get_name()) { + type = &device_ctx.arch->vib_infs[vib_type]; + break; + } + } + + if (!type) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Failed to find block type '%s' for grid location specification", + grid_loc_def.block_type.c_str()); + } + + seen_types.insert(type); + + t_formula_data vars; + vars.set_var_value("W", grid_width); + vars.set_var_value("H", grid_height); + vars.set_var_value("w", 1); + vars.set_var_value("h", 1); + + //Load the x specification + auto& xspec = grid_loc_def.x; + + VTR_ASSERT_MSG(!xspec.start_expr.empty(), "x start position must be specified"); + VTR_ASSERT_MSG(!xspec.end_expr.empty(), "x end position must be specified"); + VTR_ASSERT_MSG(!xspec.incr_expr.empty(), "x increment must be specified"); + VTR_ASSERT_MSG(!xspec.repeat_expr.empty(), "x repeat must be specified"); + + size_t startx = p.parse_formula(xspec.start_expr, vars); + size_t endx = p.parse_formula(xspec.end_expr, vars); + size_t incrx = p.parse_formula(xspec.incr_expr, vars); + size_t repeatx = p.parse_formula(xspec.repeat_expr, vars); + + //Load the y specification + auto& yspec = grid_loc_def.y; + + VTR_ASSERT_MSG(!yspec.start_expr.empty(), "y start position must be specified"); + VTR_ASSERT_MSG(!yspec.end_expr.empty(), "y end position must be specified"); + VTR_ASSERT_MSG(!yspec.incr_expr.empty(), "y increment must be specified"); + VTR_ASSERT_MSG(!yspec.repeat_expr.empty(), "y repeat must be specified"); + + size_t starty = p.parse_formula(yspec.start_expr, vars); + size_t endy = p.parse_formula(yspec.end_expr, vars); + size_t incry = p.parse_formula(yspec.incr_expr, vars); + size_t repeaty = p.parse_formula(yspec.repeat_expr, vars); + + //Check start against the device dimensions + // Start locations outside the device will never create block instances + if (startx > grid_width - 1) { + if (warn_out_of_range) { + VTR_LOG_WARN("Block type '%s' grid location specification startx (%s = %d) falls outside device horizontal range [%d,%d]\n", + type->get_name().c_str(), xspec.start_expr.c_str(), startx, 0, grid_width - 1); + } + continue; //No instances will be created + } + + if (starty > grid_height - 1) { + if (warn_out_of_range) { + VTR_LOG_WARN("Block type '%s' grid location specification starty (%s = %d) falls outside device vertical range [%d,%d]\n", + type->get_name().c_str(), yspec.start_expr.c_str(), starty, 0, grid_height - 1); + } + continue; //No instances will be created + } + + //Check end against the device dimensions + if (endx > grid_width - 1) { + if (warn_out_of_range) { + VTR_LOG_WARN("Block type '%s' grid location specification endx (%s = %d) falls outside device horizontal range [%d,%d]\n", + type->get_name().c_str(), xspec.end_expr.c_str(), endx, 0, grid_width - 1); + } + } + + if (endy > grid_height - 1) { + if (warn_out_of_range) { + VTR_LOG_WARN("Block type '%s' grid location specification endy (%s = %d) falls outside device vertical range [%d,%d]\n", + type->get_name().c_str(), yspec.end_expr.c_str(), endy, 0, grid_height - 1); + } + } + + //The end must fall after (or equal) to the start + if (endx < startx) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Grid location specification endx (%s = %d) can not come before startx (%s = %d) for block type '%s'", + xspec.end_expr.c_str(), endx, xspec.start_expr.c_str(), startx, type->get_name().c_str()); + } + + if (endy < starty) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Grid location specification endy (%s = %d) can not come before starty (%s = %d) for block type '%s'", + yspec.end_expr.c_str(), endy, yspec.start_expr.c_str(), starty, type->get_name().c_str()); + } + + //The minimum increment is the block dimension + if (incrx < 1 /*size_t(type->width)*/) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Grid location specification incrx for block type '%s' must be at least" + " block width (%d) to avoid overlapping instances (was %s = %d)", + type->get_name().c_str(), 1, xspec.incr_expr.c_str(), incrx); + } + + if (incry < 1 /*size_t(type->height)*/) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Grid location specification incry for block type '%s' must be at least" + " block height (%d) to avoid overlapping instances (was %s = %d)", + type->get_name().c_str(), 1, yspec.incr_expr.c_str(), incry); + } + + //The minimum repeat is the region dimension + size_t region_width = endx - startx + 1; //+1 since start/end are both inclusive + if (repeatx < region_width) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Grid location specification repeatx for block type '%s' must be at least" + " the region width (%d) to avoid overlapping instances (was %s = %d)", + type->get_name().c_str(), region_width, xspec.repeat_expr.c_str(), repeatx); + } + + size_t region_height = endy - starty + 1; //+1 since start/end are both inclusive + if (repeaty < region_height) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Grid location specification repeaty for block type '%s' must be at least" + " the region height (%d) to avoid overlapping instances (was %s = %d)", + type->get_name().c_str(), region_height, xspec.repeat_expr.c_str(), repeaty); + } + + size_t x_end = 0; + for (size_t kx = 0; x_end < grid_width; ++kx) { //Repeat in x direction + size_t x_start = startx + kx * repeatx; + x_end = endx + kx * repeatx; + + size_t y_end = 0; + for (size_t ky = 0; y_end < grid_height; ++ky) { //Repeat in y direction + size_t y_start = starty + ky * repeaty; + y_end = endy + ky * repeaty; + + size_t x_max = std::min(x_end, grid_width - 1); + size_t y_max = std::min(y_end, grid_height - 1); + + //Fill in the region + for (size_t x = x_start; x <= x_max; x += incrx) { + for (size_t y = y_start; y <= y_max; y += incry) { + set_vib_grid_block_type(grid_loc_def.priority, + type, + layer, x, y, + vib_grid, grid_priorities); + } + } + } + } + } + } + + auto vib_device_grid = VibDeviceGrid(grid_def.name, vib_grid); + + return vib_device_grid; +} + +static void set_vib_grid_block_type(int priority, + const VibInf* type, + int layer_num, + size_t x_root, + size_t y_root, + vtr::NdMatrix& vib_grid, + vtr::NdMatrix& grid_priorities) { + struct TypeLocation { + TypeLocation(size_t x_val, size_t y_val, const VibInf* type_val, int priority_val) + : x(x_val) + , y(y_val) + , type(type_val) + , priority(priority_val) {} + size_t x; + size_t y; + const VibInf* type; + int priority; + + bool operator<(const TypeLocation& rhs) const { + return x < rhs.x || y < rhs.y || type < rhs.type; + } + }; + + //Collect locations effected by this block + std::set target_locations; + for (size_t x = x_root; x < x_root + 1; ++x) { + for (size_t y = y_root; y < y_root + 1; ++y) { + target_locations.insert(TypeLocation(x, y, vib_grid[layer_num][x][y], grid_priorities[layer_num][x][y])); + } + } + + //Record the highest priority of all effected locations + auto iter = target_locations.begin(); + TypeLocation max_priority_type_loc = *iter; + for (; iter != target_locations.end(); ++iter) { + if (iter->priority > max_priority_type_loc.priority) { + max_priority_type_loc = *iter; + } + } + + if (priority < max_priority_type_loc.priority) { + //Lower priority, do not override + std::string type_name = (type == nullptr) ? "nullptr" : type->get_name(); + std::string msg = vtr::string_fmt("Not creating block '%s' at (%zu,%zu) since overlaps block '%s' at (%zu,%zu) with higher priority (%d > %d)\n", + type_name.c_str(), x_root, y_root, max_priority_type_loc.type->get_name().c_str(), max_priority_type_loc.x, max_priority_type_loc.y, + max_priority_type_loc.priority, priority); + VTR_LOG_DEBUG(msg.c_str()); + return; + } + + if (priority == max_priority_type_loc.priority) { + //Ambiguous case where current grid block and new specification have equal priority + // + //We arbitrarily decide to take the 'last applied' wins approach, and warn the user + //about the potential ambiguity + std::string type_name = (type == nullptr) ? "nullptr" : type->get_name(); + VTR_LOG_WARN( + "Ambiguous block type specification at grid location (%zu,%zu)." + " Existing block type '%s' at (%zu,%zu) has the same priority (%d) as new overlapping type '%s'." + " The last specification will apply.\n", + x_root, y_root, + max_priority_type_loc.type->get_name().c_str(), max_priority_type_loc.x, max_priority_type_loc.y, + priority, type_name.c_str()); + } + + //Mark all the grid tiles 'covered' by this block with the appropriate type + //and width/height offsets + std::set root_blocks_to_rip_up; + for (size_t x = x_root; x < x_root + 1; ++x) { + VTR_ASSERT(x < vib_grid.end_index(1)); + + //size_t x_offset = x - x_root; + for (size_t y = y_root; y < y_root + 1; ++y) { + VTR_ASSERT(y < vib_grid.end_index(2)); + //size_t y_offset = y - y_root; + + auto& grid_tile = vib_grid[layer_num][x][y]; + VTR_ASSERT(grid_priorities[layer_num][x][y] <= priority); + + if (grid_tile != nullptr + //&& grid_tile.type != device_ctx.EMPTY_PHYSICAL_TILE_TYPE + ) { + //We are overriding a non-empty block, we need to be careful + //to ensure we remove any blocks which will be invalidated when we + //overwrite part of their locations + + size_t orig_root_x = x; + size_t orig_root_y = y; + + root_blocks_to_rip_up.insert(TypeLocation(orig_root_x, + orig_root_y, + vib_grid[layer_num][x][y], + grid_priorities[layer_num][x][y])); + } + + vib_grid[layer_num][x][y] = type; + grid_priorities[layer_num][x][y] = priority; + } + } + + //Rip-up any invalidated blocks + for (auto invalidated_root : root_blocks_to_rip_up) { + //Mark all the grid locations used by this root block as empty + for (size_t x = invalidated_root.x; x < invalidated_root.x + 1; ++x) { + int x_offset = x - invalidated_root.x; + for (size_t y = invalidated_root.y; y < invalidated_root.y + 1; ++y) { + int y_offset = y - invalidated_root.y; + + if (vib_grid[layer_num][x][y] == invalidated_root.type + && 0 == x_offset + && 0 == y_offset) { + +#ifdef VERBOSE + VTR_LOG("Ripping up block '%s' at (%d,%d) offset (%d,%d). Overlapped by '%s' at (%d,%d)\n", + invalidated_root.type->name.c_str(), invalidated_root.x, invalidated_root.y, + x_offset, y_offset, + type->name.c_str(), x_root, y_root); +#endif + + vib_grid[layer_num][x][y] = nullptr; + grid_priorities[layer_num][x][y] = std::numeric_limits::lowest(); + } + } + } + } +} diff --git a/vpr/src/base/vib_grid/setup_vib_grid.h b/vpr/src/base/vib_grid/setup_vib_grid.h new file mode 100644 index 00000000000..b9d6f5cc1d6 --- /dev/null +++ b/vpr/src/base/vib_grid/setup_vib_grid.h @@ -0,0 +1,25 @@ +#pragma once + +/** + * @file setup_vib_grid.h + * @brief This file contains the functions to create a grid for device where + * Versatile Interconnect Block (VIB) is used. For further details, please refer to + * the following paper: + * https://doi.org/10.1109/ICFPT59805.2023.00014 + * + */ + +#include +#include + +#include "physical_types.h" + +/** + * @brief Create a VIB device grid. This function is called when the + * architecture described in the architecture file use versatile interconnect block (VIB). + * + * @param layout_name The name of the VIB device grid layout + * @param vib_grid_layouts The list of VIB device grid layouts + * @return The VIB device grid + */ +VibDeviceGrid create_vib_device_grid(std::string_view layout_name, const std::vector& vib_grid_layouts); diff --git a/vpr/src/base/vib_grid/setup_vib_utils.cpp b/vpr/src/base/vib_grid/setup_vib_utils.cpp new file mode 100644 index 00000000000..344d3ae1613 --- /dev/null +++ b/vpr/src/base/vib_grid/setup_vib_utils.cpp @@ -0,0 +1,243 @@ +#include "setup_vib_utils.h" + +#include "vpr_error.h" + +static void process_from_or_to_tokens(const std::vector tokens, + const std::vector& physical_tile_types, + const std::vector segments, + std::vector& froms); + +static void parse_pin_name(const char* src_string, + int* start_pin_index, + int* end_pin_index, + char* pb_type_name, + char* port_name); + +static void process_from_or_to_tokens(const std::vector tokens, + const std::vector& physical_tile_types, + const std::vector segments, + std::vector& froms) { + for (int i_token = 0; i_token < (int)tokens.size(); i_token++) { + std::string curr_token = tokens[i_token]; + const char* Token_char = curr_token.c_str(); + std::vector splitted_tokens = vtr::StringToken(curr_token).split("."); + if (splitted_tokens.size() == 1) { + t_from_or_to_inf from_inf; + from_inf.type_name = splitted_tokens[0]; + from_inf.from_type = e_multistage_mux_from_or_to_type::MUX; + froms.push_back(from_inf); + } else if (splitted_tokens.size() == 2) { + std::string from_type_name = splitted_tokens[0]; + e_multistage_mux_from_or_to_type from_type; + for (int i_phy_type = 0; i_phy_type < (int)physical_tile_types.size(); i_phy_type++) { + if (from_type_name == physical_tile_types[i_phy_type].name) { + from_type = e_multistage_mux_from_or_to_type::PB; + int start_pin_index, end_pin_index; + char *pb_type_name, *port_name; + pb_type_name = nullptr; + port_name = nullptr; + pb_type_name = new char[strlen(Token_char)]; + port_name = new char[strlen(Token_char)]; + parse_pin_name(Token_char, &start_pin_index, &end_pin_index, pb_type_name, port_name); + + std::vector all_sub_tile_to_tile_pin_indices; + for (const t_sub_tile& sub_tile : physical_tile_types[i_phy_type].sub_tiles) { + int sub_tile_capacity = sub_tile.capacity.total(); + + int start = 0; + int end = 0; + int i_port = 0; + for (; i_port < (int)sub_tile.ports.size(); ++i_port) { + if (!strcmp(sub_tile.ports[i_port].name, port_name)) { + start = sub_tile.ports[i_port].absolute_first_pin_index; + end = start + sub_tile.ports[i_port].num_pins - 1; + break; + } + } + if (i_port == (int)sub_tile.ports.size()) { + continue; + } + for (int pin_num = start; pin_num <= end; ++pin_num) { + VTR_ASSERT(pin_num < (int)sub_tile.sub_tile_to_tile_pin_indices.size() / sub_tile_capacity); + for (int capacity = 0; capacity < sub_tile_capacity; ++capacity) { + int sub_tile_pin_index = pin_num + capacity * sub_tile.num_phy_pins / sub_tile_capacity; + int physical_pin_index = sub_tile.sub_tile_to_tile_pin_indices[sub_tile_pin_index]; + all_sub_tile_to_tile_pin_indices.push_back(physical_pin_index); + } + } + } + + if (start_pin_index == end_pin_index && start_pin_index < 0) { + start_pin_index = 0; + end_pin_index = all_sub_tile_to_tile_pin_indices.size() - 1; + } + + if ((int)all_sub_tile_to_tile_pin_indices.size() <= start_pin_index || (int)all_sub_tile_to_tile_pin_indices.size() <= end_pin_index) { + VTR_LOGF_ERROR(__FILE__, __LINE__, + "The index of pbtype %s : port %s exceeds its total number!\n", pb_type_name, port_name); + } + + for (int i = start_pin_index; i <= end_pin_index; i++) { + t_from_or_to_inf from_inf; + from_inf.type_name = from_type_name; + from_inf.from_type = from_type; + from_inf.type_index = i_phy_type; + from_inf.phy_pin_index = all_sub_tile_to_tile_pin_indices[i]; + froms.push_back(from_inf); + } + } + } + for (int i_seg_type = 0; i_seg_type < (int)segments.size(); i_seg_type++) { + if (from_type_name == segments[i_seg_type].name) { + from_type = e_multistage_mux_from_or_to_type::SEGMENT; + std::string from_detail = splitted_tokens[1]; + if (from_detail.length() >= 2) { + char dir = from_detail.c_str()[0]; + from_detail.erase(from_detail.begin()); + int seg_index = std::stoi(from_detail); + + t_from_or_to_inf from_inf; + from_inf.type_name = from_type_name; + from_inf.from_type = from_type; + from_inf.type_index = i_seg_type; + from_inf.seg_dir = dir; + from_inf.seg_index = seg_index; + froms.push_back(from_inf); + } + + break; + } + } + VTR_ASSERT(from_type == e_multistage_mux_from_or_to_type::PB + || from_type == e_multistage_mux_from_or_to_type::SEGMENT); + + } else { + std::string msg = vtr::string_fmt("Failed to parse vib mux from information '%s'", curr_token.c_str()); + VTR_LOGF_ERROR(__FILE__, __LINE__, msg.c_str()); + } + } +} + +static void parse_pin_name(const char* src_string, int* start_pin_index, int* end_pin_index, char* pb_type_name, char* port_name) { + // Parses out the pb_type_name and port_name + // If the start_pin_index and end_pin_index is specified, parse them too. + // Return the values parsed by reference. + + std::string source_string; + + // parse out the pb_type and port name, possibly pin_indices + const char* find_format = strstr(src_string, "["); + if (find_format == nullptr) { + /* Format "pb_type_name.port_name" */ + *start_pin_index = *end_pin_index = -1; + + source_string = src_string; + + for (size_t ichar = 0; ichar < source_string.size(); ichar++) { + if (source_string[ichar] == '.') + source_string[ichar] = ' '; + } + + int match_count = sscanf(source_string.c_str(), "%s %s", pb_type_name, port_name); + if (match_count != 2) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Invalid pin - %s, name should be in the format " + "\"pb_type_name\".\"port_name\" or \"pb_type_name\".\"port_name[end_pin_index:start_pin_index]\". " + "The end_pin_index and start_pin_index can be the same.\n", + src_string); + } + } else { + /* Format "pb_type_name.port_name[end_pin_index:start_pin_index]" */ + source_string = src_string; + for (size_t ichar = 0; ichar < source_string.size(); ichar++) { + //Need white space between the components when using %s with + //sscanf + if (source_string[ichar] == '.') + source_string[ichar] = ' '; + if (source_string[ichar] == '[') + source_string[ichar] = ' '; + } + + int match_count = sscanf(source_string.c_str(), "%s %s %d:%d]", + pb_type_name, port_name, + end_pin_index, start_pin_index); + if (match_count != 4) { + match_count = sscanf(source_string.c_str(), "%s %s %d]", + pb_type_name, port_name, + end_pin_index); + *start_pin_index = *end_pin_index; + if (match_count != 3) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Invalid pin - %s, name should be in the format " + "\"pb_type_name\".\"port_name\" or \"pb_type_name\".\"port_name[end_pin_index:start_pin_index]\". " + "The end_pin_index and start_pin_index can be the same.\n", + src_string); + } + } + if (*end_pin_index < 0 || *start_pin_index < 0) { + VPR_FATAL_ERROR(VPR_ERROR_ARCH, + "Invalid pin - %s, the pin_index in " + "[end_pin_index:start_pin_index] should not be a negative value.\n", + src_string); + } + if (*end_pin_index < *start_pin_index) { + int temp; + temp = *end_pin_index; + *end_pin_index = *start_pin_index; + *start_pin_index = temp; + } + } +} + +void setup_vib_inf(const std::vector& physical_tile_types, + const std::vector& switches, + const std::vector& segments, + std::vector& vib_infs) { + VTR_ASSERT(!vib_infs.empty()); + for (VibInf& vib_inf : vib_infs) { + for (size_t i_switch = 0; i_switch < switches.size(); i_switch++) { + if (vib_inf.get_switch_name() == switches[i_switch].name) { + vib_inf.set_switch_idx(i_switch); + break; + } + } + + std::vector seg_groups = vib_inf.get_seg_groups(); + for (t_seg_group& seg_group : seg_groups) { + for (int i_seg = 0; i_seg < (int)segments.size(); i_seg++) { + if (segments[i_seg].name == seg_group.name) { + seg_group.seg_index = i_seg; + break; + } + } + } + vib_inf.set_seg_groups(seg_groups); + + std::vector first_stages = vib_inf.get_first_stages(); + for (t_first_stage_mux_inf& first_stage : first_stages) { + std::vector>& from_tokens = first_stage.from_tokens; + for (const std::vector& from_token : from_tokens) { + process_from_or_to_tokens(from_token, physical_tile_types, segments, first_stage.froms); + } + } + vib_inf.set_first_stages(first_stages); + + std::vector second_stages = vib_inf.get_second_stages(); + for (t_second_stage_mux_inf& second_stage : second_stages) { + std::vector tos; + + process_from_or_to_tokens(second_stage.to_tokens, physical_tile_types, segments, tos); + for (t_from_or_to_inf& to : tos) { + VTR_ASSERT(to.from_type == e_multistage_mux_from_or_to_type::SEGMENT + || to.from_type == e_multistage_mux_from_or_to_type::PB); + second_stage.to.push_back(to); + } + + std::vector> from_tokens = second_stage.from_tokens; + for (const std::vector& from_token : from_tokens) { + process_from_or_to_tokens(from_token, physical_tile_types, segments, second_stage.froms); + } + } + vib_inf.set_second_stages(second_stages); + } +} diff --git a/vpr/src/base/vib_grid/setup_vib_utils.h b/vpr/src/base/vib_grid/setup_vib_utils.h new file mode 100644 index 00000000000..eb6d974176d --- /dev/null +++ b/vpr/src/base/vib_grid/setup_vib_utils.h @@ -0,0 +1,25 @@ +#pragma once + +/** + * @file setup_vib_utils.h + * @brief This file contains the utility functions to build device grid + * when the architecture described in the architecture file use versatile interconnect block (VIB). + */ + +#include + +#include "vpr_types.h" + +/** + * @brief Setup the VIB information. This function is called when the + * architecture described in the architecture file use versatile interconnect block (VIB). + * + * @param physical_tile_types Physical tile types defined in the architecture file + * @param switches Switches defined in the architecture file + * @param segments The segments defined in the architecture file + * @param vib_infs vector containing the VIB information which will be populated by this function + */ +void setup_vib_inf(const std::vector& physical_tile_types, + const std::vector& switches, + const std::vector& segments, + std::vector& vib_infs); diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 7b2e7928b1c..dcd0d2394c9 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -39,7 +39,8 @@ #include "place_and_route.h" #include "pack.h" #include "place.h" -#include "SetupGrid.h" +#include "setup_grid.h" +#include "setup_vib_grid.h" #include "setup_clocks.h" #include "setup_noc.h" #include "read_xml_noc_traffic_flows_file.h" @@ -47,7 +48,7 @@ #include "stats.h" #include "read_options.h" #include "echo_files.h" -#include "SetupVPR.h" +#include "setup_vpr.h" #include "ShowSetup.h" #include "CheckArch.h" #include "CheckSetup.h" @@ -259,7 +260,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a * Initialize the functions names for which VPR_ERRORs * are demoted to VTR_LOG_WARNs */ - for (const std::string& func_name : vtr::split(options->disable_errors.value(), ":")) { + for (const std::string& func_name : vtr::StringToken(options->disable_errors.value()).split(":")) { map_error_activation_status(func_name); } @@ -267,7 +268,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a * Initialize the functions names for which * warnings are being suppressed */ - std::vector split_warning_option = vtr::split(options->suppress_warnings.value(), ","); + std::vector split_warning_option = vtr::StringToken(options->suppress_warnings.value()).split(","); std::string warn_log_file; std::string warn_functions; // If no log file name is provided, the specified warning @@ -280,7 +281,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a } set_noisy_warn_log_file(warn_log_file); - for (const std::string& func_name : vtr::split(warn_functions, std::string(":"))) { + for (const std::string& func_name : vtr::StringToken(warn_functions).split(":")) { add_warnings_to_suppress(func_name); } @@ -570,6 +571,9 @@ void vpr_create_device_grid(const t_vpr_setup& vpr_setup, const t_arch& Arch) { //Build the device float target_device_utilization = vpr_setup.PackerOpts.target_device_utilization; device_ctx.grid = create_device_grid(vpr_setup.device_layout, Arch.grid_layouts, num_type_instances, target_device_utilization); + if (!Arch.vib_infs.empty()) { + device_ctx.vib_grid = create_vib_device_grid(vpr_setup.device_layout, Arch.vib_grid_layouts); + } VTR_ASSERT_MSG(device_ctx.grid.get_num_layers() <= MAX_NUM_LAYERS, "Number of layers should be less than MAX_NUM_LAYERS. " @@ -1180,6 +1184,9 @@ void vpr_create_rr_graph(t_vpr_setup& vpr_setup, const t_arch& arch, int chan_wi graph_directionality = e_graph_type::BIDIR; } else { graph_type = (det_routing_arch.directionality == BI_DIRECTIONAL ? e_graph_type::BIDIR : e_graph_type::UNIDIR); + if (det_routing_arch.directionality == UNI_DIRECTIONAL && det_routing_arch.tileable) { + graph_type = e_graph_type::UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch.directionality == BI_DIRECTIONAL ? e_graph_type::BIDIR : e_graph_type::UNIDIR); } diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 89775e23b92..5518b43bc7c 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -177,6 +177,13 @@ struct DeviceContext : public Context { * in this data structure should be used. */ DeviceGrid grid; + + /** + * @brief The VIB device grid. For details about this layout + * refer to https://doi.org/10.1109/ICFPT59805.2023.00014 + */ + VibDeviceGrid vib_grid; + /* * Empty types */ @@ -236,7 +243,20 @@ struct DeviceContext : public Context { /* A read-only view of routing resource graph to be the ONLY database * for client functions: GUI, placer, router, timing analyzer etc. */ - RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch()}; + RRGraphView rr_graph{rr_graph_builder.rr_nodes(), + rr_graph_builder.node_lookup(), + rr_graph_builder.rr_node_metadata(), + rr_graph_builder.rr_edge_metadata(), + rr_indexed_data, + rr_rc_data, + rr_graph_builder.rr_segments(), + rr_graph_builder.rr_switch(), + rr_graph_builder.node_in_edge_storage(), + rr_graph_builder.node_ptc_storage()}; + + ///@brief Track ids for each rr_node in the rr_graph. This is used by drawer for tileable routing resource graph + std::map> rr_node_track_ids; + std::vector arch_switch_inf; // [0..(num_arch_switches-1)] std::map all_sw_inf; diff --git a/vpr/src/base/vpr_types.cpp b/vpr/src/base/vpr_types.cpp index 84437530f1c..f1cd81df6d1 100644 --- a/vpr/src/base/vpr_types.cpp +++ b/vpr/src/base/vpr_types.cpp @@ -58,7 +58,7 @@ t_ext_pin_util_targets::t_ext_pin_util_targets(const std::vector& s for (const auto& spec : specs) { t_ext_pin_util target_ext_pin_util(1., 1.); - auto block_values = vtr::split(spec, ":"); + auto block_values = vtr::StringToken(spec).split(":"); std::string block_type; std::string values; if (block_values.size() == 2) { @@ -72,7 +72,7 @@ t_ext_pin_util_targets::t_ext_pin_util_targets(const std::vector& s VPR_FATAL_ERROR(VPR_ERROR_PACK, msg.str().c_str()); } - auto elements = vtr::split(values, ","); + auto elements = vtr::StringToken(values).split(","); if (elements.size() == 1) { target_ext_pin_util.input_pin_util = vtr::atof(elements[0]); } else if (elements.size() == 2) { @@ -196,7 +196,7 @@ t_pack_high_fanout_thresholds::t_pack_high_fanout_thresholds(const std::vector seen_block_types; for (const auto& spec : specs) { - auto block_values = vtr::split(spec, ":"); + auto block_values = vtr::StringToken(spec).split(":"); std::string block_type; std::string value; if (block_values.size() == 1) { diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 625a7dd4791..41da9a6d085 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1426,6 +1426,40 @@ struct t_det_routing_arch { /// the CUSTOM switch block type. See comment at top of SRC/route/build_switchblocks.c std::vector switchblocks; + // Following options are used only for tileable routing architecture + + /// Whether the routing architecture is tileable + bool tileable; + + /// Sub type and Fs are applied to pass tracks + int sub_fs; + + /// Subtype of switch blocks. + enum e_switch_block_type switch_block_subtype; + + /// Allow connection blocks to appear around the perimeter programmable block (mainly I/Os) + bool perimeter_cb; + + /// Remove all the routing wires in empty regions + bool shrink_boundary; + + /// Allow routing channels to pass through multi-width and multi-height programmable blocks. + bool through_channel; + + /// Allow each output pin of a programmable block to drive the routing tracks on all the + /// sides of its adjacent switch block + bool opin2all_sides; + + ///In each switch block, allow each routing track which ends to drive another + /// routing track on the opposite side + bool concat_wire; + + /// In each switch block, allow each routing track which passes to drive + /// another routing track on the opposite side + bool concat_pass_wire; + + // End of tileable routing architecture-specific options + short global_route_switch; /// Index of a zero delay switch (used to connect things that should have no delay). diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 8a63c002854..2427ac7a2db 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -1227,8 +1227,8 @@ static void run_graphics_commands(const std::string& commands) { t_draw_state backup_draw_state = *draw_state; std::vector> cmds; - for (std::string raw_cmd : vtr::split(commands, ";")) { - cmds.push_back(vtr::split(raw_cmd)); + for (const std::string& raw_cmd : vtr::StringToken(commands).split(";")) { + cmds.push_back(vtr::StringToken(raw_cmd).split(" \t\n")); } for (auto& cmd : cmds) { diff --git a/vpr/src/pack/greedy_clusterer.cpp b/vpr/src/pack/greedy_clusterer.cpp index 91cfd532a93..2a6365b4de4 100644 --- a/vpr/src/pack/greedy_clusterer.cpp +++ b/vpr/src/pack/greedy_clusterer.cpp @@ -42,7 +42,7 @@ #include #include #include "appack_context.h" -#include "SetupGrid.h" +#include "setup_grid.h" #include "atom_netlist.h" #include "attraction_groups.h" #include "cluster_legalizer.h" diff --git a/vpr/src/pack/pack.cpp b/vpr/src/pack/pack.cpp index 7914f97f4ae..e825bdcc0dc 100644 --- a/vpr/src/pack/pack.cpp +++ b/vpr/src/pack/pack.cpp @@ -3,7 +3,7 @@ #include #include "PreClusterTimingManager.h" -#include "SetupGrid.h" +#include "setup_grid.h" #include "appack_context.h" #include "attraction_groups.h" #include "cluster_legalizer.h" diff --git a/vpr/src/place/delay_model/compute_delta_delays_utils.cpp b/vpr/src/place/delay_model/compute_delta_delays_utils.cpp index 17b866c6330..dbcdbc05f91 100644 --- a/vpr/src/place/delay_model/compute_delta_delays_utils.cpp +++ b/vpr/src/place/delay_model/compute_delta_delays_utils.cpp @@ -197,7 +197,7 @@ static vtr::NdMatrix compute_delta_delays(RouterDelayProfiler& route_p std::set allowed_types; if (!placer_opts.allowed_tiles_for_delay_model.empty()) { - std::vector allowed_types_vector = vtr::split(placer_opts.allowed_tiles_for_delay_model, ","); + std::vector allowed_types_vector = vtr::StringToken(placer_opts.allowed_tiles_for_delay_model).split(","); allowed_types = std::set(allowed_types_vector.begin(), allowed_types_vector.end()); } diff --git a/vpr/src/route/check_route.cpp b/vpr/src/route/check_route.cpp index d4f9a07ee63..d464bfff6c2 100644 --- a/vpr/src/route/check_route.cpp +++ b/vpr/src/route/check_route.cpp @@ -364,7 +364,7 @@ static bool check_adjacent(RRNodeId from_node, RRNodeId to_node, bool is_flat) { case e_rr_type::OPIN: from_grid_type = device_ctx.grid.get_physical_type({from_xlow, from_ylow, from_layer}); - if (to_type == e_rr_type::CHANX || to_type == e_rr_type::CHANY) { + if (to_type == e_rr_type::CHANX || to_type == e_rr_type::CHANY || to_type == e_rr_type::MUX) { num_adj += 1; //adjacent } else if (is_flat) { VTR_ASSERT(to_type == e_rr_type::OPIN || to_type == e_rr_type::IPIN); // If pin is located inside a cluster @@ -417,6 +417,8 @@ static bool check_adjacent(RRNodeId from_node, RRNodeId to_node, bool is_flat) { num_adj += 1; // adjacent } else if (to_type == e_rr_type::CHANX || to_type == e_rr_type::CHANY || to_type == e_rr_type::CHANZ) { num_adj += rr_graph.chan_nodes_are_adjacent(from_node, to_node); + } else if (to_type == e_rr_type::MUX) { + num_adj += 1; // adjacent } else { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "in check_adjacent: %d and %d are not adjacent", from_node, to_node); @@ -428,6 +430,8 @@ static bool check_adjacent(RRNodeId from_node, RRNodeId to_node, bool is_flat) { num_adj += 1; // adjacent } else if (to_type == e_rr_type::CHANX || to_type == e_rr_type::CHANY || to_type == e_rr_type::CHANZ) { num_adj += rr_graph.chan_nodes_are_adjacent(from_node, to_node); + } else if (to_type == e_rr_type::MUX) { + num_adj += 1; // adjacent } else { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "in check_adjacent: %d and %d are not adjacent", from_node, to_node); @@ -443,6 +447,19 @@ static bool check_adjacent(RRNodeId from_node, RRNodeId to_node, bool is_flat) { } break; + case e_rr_type::MUX: + if (to_type == e_rr_type::CHANX || to_type == e_rr_type::CHANY || to_type == e_rr_type::MUX) { + num_adj += 1; //adjacent + } else if (is_flat) { + VTR_ASSERT(to_type == e_rr_type::OPIN || to_type == e_rr_type::IPIN); // If pin is located inside a cluster + return true; + } else { + VTR_ASSERT(to_type == e_rr_type::IPIN); + num_adj += 1; + } + + break; + default: VPR_ERROR(VPR_ERROR_ROUTE, "in check_adjacent: Unknown RR type.\n"); @@ -568,6 +585,7 @@ static void check_node_and_range(RRNodeId inode, check_rr_node(device_ctx.rr_graph, device_ctx.rr_indexed_data, device_ctx.grid, + device_ctx.vib_grid, device_ctx.chan_width, route_type, size_t(inode), diff --git a/vpr/src/route/route.cpp b/vpr/src/route/route.cpp index 912ae4fe55f..4a9577b01f7 100644 --- a/vpr/src/route/route.cpp +++ b/vpr/src/route/route.cpp @@ -42,6 +42,9 @@ bool route(const Netlist<>& net_list, graph_directionality = e_graph_type::BIDIR; } else { graph_type = (det_routing_arch.directionality == BI_DIRECTIONAL ? e_graph_type::BIDIR : e_graph_type::UNIDIR); + if (det_routing_arch.directionality == UNI_DIRECTIONAL && det_routing_arch.tileable) { + graph_type = e_graph_type::UNIDIR_TILEABLE; + } graph_directionality = (det_routing_arch.directionality == BI_DIRECTIONAL ? e_graph_type::BIDIR : e_graph_type::UNIDIR); } diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index 66a8e8e13fa..d146757b77c 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -248,6 +248,10 @@ void alloc_routing_structs(const t_chan_width& chan_width, graph_type = e_graph_type::GLOBAL; } else { graph_type = (det_routing_arch.directionality == BI_DIRECTIONAL ? e_graph_type::BIDIR : e_graph_type::UNIDIR); + /* Branch on tileable routing */ + if (det_routing_arch.directionality == UNI_DIRECTIONAL && det_routing_arch.tileable) { + graph_type = e_graph_type::UNIDIR_TILEABLE; + } } create_rr_graph(graph_type, diff --git a/vpr/src/route/router_lookahead/router_lookahead_compressed_map.cpp b/vpr/src/route/router_lookahead/router_lookahead_compressed_map.cpp index 6d6bbb42ec4..4781e97e5f4 100644 --- a/vpr/src/route/router_lookahead/router_lookahead_compressed_map.cpp +++ b/vpr/src/route/router_lookahead/router_lookahead_compressed_map.cpp @@ -137,9 +137,9 @@ static void compute_router_wire_compressed_lookahead(const std::vector> sample_nodes; std::vector chan_types; - if (segment_inf.parallel_axis == X_AXIS) + if (segment_inf.parallel_axis == e_parallel_axis::X_AXIS) chan_types.push_back(e_rr_type::CHANX); - else if (segment_inf.parallel_axis == Y_AXIS) + else if (segment_inf.parallel_axis == e_parallel_axis::Y_AXIS) chan_types.push_back(e_rr_type::CHANY); else //Both for BOTH_AXIS segments and special segments such as clock_networks we want to search in both directions. chan_types.insert(chan_types.end(), {e_rr_type::CHANX, e_rr_type::CHANY}); diff --git a/vpr/src/route/router_lookahead/router_lookahead_map.cpp b/vpr/src/route/router_lookahead/router_lookahead_map.cpp index 539be31e378..4df9fcefa07 100644 --- a/vpr/src/route/router_lookahead/router_lookahead_map.cpp +++ b/vpr/src/route/router_lookahead/router_lookahead_map.cpp @@ -517,9 +517,9 @@ static void compute_router_wire_lookahead(const std::vector& segm for (int from_layer_num = 0; from_layer_num < grid.get_num_layers(); from_layer_num++) { for (const auto& segment_inf : segment_inf_vec) { std::vector chan_types; - if (segment_inf.parallel_axis == X_AXIS) + if (segment_inf.parallel_axis == e_parallel_axis::X_AXIS) chan_types.push_back(e_rr_type::CHANX); - else if (segment_inf.parallel_axis == Y_AXIS) + else if (segment_inf.parallel_axis == e_parallel_axis::Y_AXIS) chan_types.push_back(e_rr_type::CHANY); else //Both for BOTH_AXIS segments and special segments such as clock_networks we want to search in both directions. chan_types.insert(chan_types.end(), {e_rr_type::CHANX, e_rr_type::CHANY}); @@ -728,7 +728,9 @@ static void compute_tile_lookahead(std::unordered_map& rr_nodes_at_loc = device_ctx.rr_graph.node_lookup().find_grid_nodes_at_all_sides(sample_loc.layer_num, sample_loc.x, sample_loc.y, rr_type); for (RRNodeId node_id : rr_nodes_at_loc) { int ptc = rr_graph.node_ptc_num(node_id); - // For the time being, we decide to not let the lookahead explore the node inside the clusters if (!is_inter_cluster_node(rr_graph, node_id)) { continue; } @@ -661,7 +659,18 @@ std::pair get_xy_deltas(RRNodeId from_node, RRNodeId to_node) { Direction from_dir = rr_graph.node_direction(from_node); if (is_chanxy(from_type) && ((to_seg < from_seg_low && from_dir == Direction::INC) || (to_seg > from_seg_high && from_dir == Direction::DEC))) { - delta_seg++; + // If the routing channel starts from the perimeter of the grid, + // and it is heading towards the outside of the grid, we should + // not increment the delta_seg by 1. + int max_seg_index = -1; + if (from_type == e_rr_type::CHANX) { + max_seg_index = static_cast(device_ctx.grid.width()) - 1; + } else { + max_seg_index = static_cast(device_ctx.grid.height()) - 1; + } + if (!((from_seg_low == 0 && from_dir == Direction::DEC) || (from_seg_low == max_seg_index && from_dir == Direction::INC))) { + delta_seg++; + } } if (from_type == e_rr_type::CHANY) { @@ -720,9 +729,9 @@ t_routing_cost_map get_routing_cost_map(int longest_seg_length, //First try to pick good representative sample locations for each type std::vector sample_nodes; std::vector chan_types; - if (segment_inf.parallel_axis == X_AXIS) + if (segment_inf.parallel_axis == e_parallel_axis::X_AXIS) chan_types.push_back(e_rr_type::CHANX); - else if (segment_inf.parallel_axis == Y_AXIS) + else if (segment_inf.parallel_axis == e_parallel_axis::Y_AXIS) chan_types.push_back(e_rr_type::CHANY); else //Both for BOTH_AXIS segments and special segments such as clock_networks we want to search in both directions. chan_types.insert(chan_types.end(), {e_rr_type::CHANX, e_rr_type::CHANY}); @@ -1010,7 +1019,7 @@ static void dijkstra_flood_to_wires(int itile, src_opin_delays[root_layer_num][itile][ptc][curr_layer_num][seg_index].congestion = curr.congestion; } - } else if (curr_rr_type == e_rr_type::SOURCE || curr_rr_type == e_rr_type::OPIN || curr_rr_type == e_rr_type::IPIN) { + } else if (curr_rr_type == e_rr_type::SOURCE || curr_rr_type == e_rr_type::OPIN || curr_rr_type == e_rr_type::IPIN || curr_rr_type == e_rr_type::MUX) { //We allow expansion through SOURCE/OPIN/IPIN types auto cost_index = rr_graph.node_cost_index(curr.node); float incr_cong = device_ctx.rr_indexed_data[cost_index].base_cost; //Current nodes congestion cost @@ -1021,7 +1030,6 @@ static void dijkstra_flood_to_wires(int itile, RRNodeId next_node = rr_graph.rr_nodes().edge_sink_node(edge); // For the time being, we decide to not let the lookahead explore the node inside the clusters - if (!is_inter_cluster_node(rr_graph, next_node)) { // Don't go inside the clusters continue; @@ -1376,8 +1384,8 @@ static void expand_dijkstra_neighbours(util::PQ_Entry parent_entry, for (t_edge_size edge : rr_graph.edges(parent)) { RRNodeId child_node = rr_graph.edge_sink_node(parent, edge); - // For the time being, we decide to not let the lookahead explore the node inside the clusters - + // Don't expand the nodes inside the clusters since the intra-cluster lookahead + // is computed separately. if (!is_inter_cluster_node(rr_graph, child_node)) { continue; } diff --git a/vpr/src/route/rr_graph_generation/clb2clb_directs.cpp b/vpr/src/route/rr_graph_generation/clb2clb_directs.cpp new file mode 100644 index 00000000000..ceedb7467f2 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/clb2clb_directs.cpp @@ -0,0 +1,88 @@ +#include "clb2clb_directs.h" + +#include "globals.h" +#include "vpr_utils.h" +#include "physical_types_util.h" + +std::vector alloc_and_load_clb_to_clb_directs(const std::vector& directs, int delayless_switch) { + // TODO: The function that does this parsing in placement is poorly done because it lacks generality on heterogeniety, should replace with this one + + auto& device_ctx = g_vpr_ctx.device(); + + const int num_directs = directs.size(); + std::vector clb_to_clb_directs(num_directs); + + for (int i = 0; i < num_directs; i++) { + //clb_to_clb_directs[i].from_clb_type; + clb_to_clb_directs[i].from_clb_pin_start_index = 0; + clb_to_clb_directs[i].from_clb_pin_end_index = 0; + //clb_to_clb_directs[i].to_clb_type; + clb_to_clb_directs[i].to_clb_pin_start_index = 0; + clb_to_clb_directs[i].to_clb_pin_end_index = 0; + clb_to_clb_directs[i].switch_index = 0; + + // Load from pins + // Parse out the pb_type name, port name, and pin range + auto [start_pin_index, end_pin_index, tile_name, port_name] = parse_direct_pin_name(directs[i].from_pin, directs[i].line); + + // Figure out which type, port, and pin is used + t_physical_tile_type_ptr physical_tile = find_tile_type_by_name(tile_name, device_ctx.physical_tile_types); + if (physical_tile == nullptr) { + VPR_THROW(VPR_ERROR_ARCH, "Unable to find block %s.\n", tile_name.c_str()); + } + clb_to_clb_directs[i].from_clb_type = physical_tile; + + t_physical_tile_port tile_port = find_tile_port_by_name(physical_tile, port_name); + + if (start_pin_index == OPEN) { + VTR_ASSERT(start_pin_index == end_pin_index); + start_pin_index = 0; + end_pin_index = tile_port.num_pins - 1; + } + + // Add clb directs start/end pin indices based on the absolute pin position + // of the port defined in the direct connection. The CLB is the source one. + clb_to_clb_directs[i].from_clb_pin_start_index = tile_port.absolute_first_pin_index + start_pin_index; + clb_to_clb_directs[i].from_clb_pin_end_index = tile_port.absolute_first_pin_index + end_pin_index; + + // Load to pins + // Parse out the pb_type name, port name, and pin range + std::tie(start_pin_index, end_pin_index, tile_name, port_name) = parse_direct_pin_name(directs[i].to_pin, directs[i].line); + + // Figure out which type, port, and pin is used + physical_tile = find_tile_type_by_name(tile_name, device_ctx.physical_tile_types); + if (physical_tile == nullptr) { + VPR_THROW(VPR_ERROR_ARCH, "Unable to find block %s.\n", tile_name.c_str()); + } + clb_to_clb_directs[i].to_clb_type = physical_tile; + + tile_port = find_tile_port_by_name(physical_tile, port_name); + + if (start_pin_index == OPEN) { + VTR_ASSERT(start_pin_index == end_pin_index); + start_pin_index = 0; + end_pin_index = tile_port.num_pins - 1; + } + + // Add clb directs start/end pin indices based on the absolute pin position + // of the port defined in the direct connection. The CLB is the destination one. + clb_to_clb_directs[i].to_clb_pin_start_index = tile_port.absolute_first_pin_index + start_pin_index; + clb_to_clb_directs[i].to_clb_pin_end_index = tile_port.absolute_first_pin_index + end_pin_index; + + if (abs(clb_to_clb_directs[i].from_clb_pin_start_index - clb_to_clb_directs[i].from_clb_pin_end_index) != abs(clb_to_clb_directs[i].to_clb_pin_start_index - clb_to_clb_directs[i].to_clb_pin_end_index)) { + vpr_throw(VPR_ERROR_ARCH, get_arch_file_name(), directs[i].line, + "Range mismatch from %s to %s.\n", directs[i].from_pin.c_str(), directs[i].to_pin.c_str()); + } + + //Set the switch index + if (directs[i].switch_type > 0) { + //Use the specified switch + clb_to_clb_directs[i].switch_index = directs[i].switch_type; + } else { + //Use the delayless switch by default + clb_to_clb_directs[i].switch_index = delayless_switch; + } + } + + return clb_to_clb_directs; +} diff --git a/vpr/src/route/rr_graph_generation/clb2clb_directs.h b/vpr/src/route/rr_graph_generation/clb2clb_directs.h new file mode 100644 index 00000000000..c9435e6fb91 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/clb2clb_directs.h @@ -0,0 +1,32 @@ +#pragma once + +/** + * @file clb2clb_directs.h + * @brief This file contains the functions to parse the direct connections between two CLBs + * and store them in a clb_to_clb_directs data structure. + */ + +#include "physical_types.h" + +/** + * @brief A structure to store the direct connection between two CLBs + */ +struct t_clb_to_clb_directs { + t_physical_tile_type_ptr from_clb_type; + int from_clb_pin_start_index; + int from_clb_pin_end_index; + t_physical_tile_type_ptr to_clb_type; + int to_clb_pin_start_index; + int to_clb_pin_end_index; + int switch_index; //The switch type used by this direct connection +}; + +/** + * @brief Parse out which CLB pins should connect directly to which other CLB pins then store that in a clb_to_clb_directs data structure + * This data structure supplements the the info in the "directs" data structure + * + * @param directs The direct connections to parse + * @param delayless_switch The switch index to use for delayless connections + * @return A vector of clb_to_clb_directs structures + */ +std::vector alloc_and_load_clb_to_clb_directs(const std::vector& directs, const int delayless_switch); diff --git a/vpr/src/route/rr_graph_generation/clock_network_builders.cpp b/vpr/src/route/rr_graph_generation/clock_network_builders.cpp index 73932d99f3a..14d2b57bc20 100644 --- a/vpr/src/route/rr_graph_generation/clock_network_builders.cpp +++ b/vpr/src/route/rr_graph_generation/clock_network_builders.cpp @@ -167,7 +167,7 @@ void ClockRib::create_segments(std::vector& segment_inf) { /*AA: ClockRibs are assumed to be horizontal currently. */ - populate_segment_values(index, name, length, x_chan_wire.layer, segment_inf, X_AXIS); + populate_segment_values(index, name, length, x_chan_wire.layer, segment_inf, e_parallel_axis::X_AXIS); // Segment to the right of the drive point segment_inf.emplace_back(); @@ -177,7 +177,7 @@ void ClockRib::create_segments(std::vector& segment_inf) { name = clock_name_ + "_right"; length = (x_chan_wire.length - drive.offset) - 1; - populate_segment_values(index, name, length, x_chan_wire.layer, segment_inf, X_AXIS); + populate_segment_values(index, name, length, x_chan_wire.layer, segment_inf, e_parallel_axis::X_AXIS); // Segment to the left of the drive point segment_inf.emplace_back(); @@ -187,7 +187,7 @@ void ClockRib::create_segments(std::vector& segment_inf) { name = clock_name_ + "_left"; length = drive.offset - 1; - populate_segment_values(index, name, length, x_chan_wire.layer, segment_inf, X_AXIS); + populate_segment_values(index, name, length, x_chan_wire.layer, segment_inf, e_parallel_axis::X_AXIS); } size_t ClockRib::estimate_additional_nodes(const DeviceGrid& grid) { @@ -394,7 +394,7 @@ void ClockRib::record_tap_locations(unsigned x_start, } } -static void get_parallel_seg_index(int& unified_seg_index, enum e_parallel_axis axis, const t_unified_to_parallel_seg_index& indices_map) { +static void get_parallel_seg_index(int& unified_seg_index, e_parallel_axis axis, const t_unified_to_parallel_seg_index& indices_map) { auto itr_pair = indices_map.equal_range(unified_seg_index); for (auto itr = itr_pair.first; itr != itr_pair.second; ++itr) { @@ -409,11 +409,11 @@ static void get_parallel_seg_index(int& unified_seg_index, enum e_parallel_axis void ClockRib::map_relative_seg_indices(const t_unified_to_parallel_seg_index& indices_map) { /*We have horizontal segments in clock-ribs so we search for X_AXIS*/ - get_parallel_seg_index(drive_seg_idx, X_AXIS, indices_map); + get_parallel_seg_index(drive_seg_idx, e_parallel_axis::X_AXIS, indices_map); - get_parallel_seg_index(left_seg_idx, X_AXIS, indices_map); + get_parallel_seg_index(left_seg_idx, e_parallel_axis::X_AXIS, indices_map); - get_parallel_seg_index(right_seg_idx, X_AXIS, indices_map); + get_parallel_seg_index(right_seg_idx, e_parallel_axis::X_AXIS, indices_map); } /********************************************************************************* @@ -506,7 +506,7 @@ void ClockSpine::create_segments(std::vector& segment_inf) { length = 1; // Since drive segment has one length, the left and right segments have length - 1 /* AA: ClockSpines are assumed to be vertical currently. */ - populate_segment_values(index, name, length, y_chan_wire.layer, segment_inf, Y_AXIS); + populate_segment_values(index, name, length, y_chan_wire.layer, segment_inf, e_parallel_axis::Y_AXIS); // Segment to the right of the drive point segment_inf.emplace_back(); @@ -516,7 +516,7 @@ void ClockSpine::create_segments(std::vector& segment_inf) { name = clock_name_ + "_right"; length = (y_chan_wire.length - drive.offset) - 1; - populate_segment_values(index, name, length, y_chan_wire.layer, segment_inf, Y_AXIS); + populate_segment_values(index, name, length, y_chan_wire.layer, segment_inf, e_parallel_axis::Y_AXIS); // Segment to the left of the drive point segment_inf.emplace_back(); @@ -526,7 +526,7 @@ void ClockSpine::create_segments(std::vector& segment_inf) { name = clock_name_ + "_left"; length = drive.offset - 1; - populate_segment_values(index, name, length, y_chan_wire.layer, segment_inf, Y_AXIS); + populate_segment_values(index, name, length, y_chan_wire.layer, segment_inf, e_parallel_axis::Y_AXIS); } size_t ClockSpine::estimate_additional_nodes(const DeviceGrid& grid) { @@ -739,11 +739,11 @@ void ClockSpine::record_tap_locations(unsigned y_start, void ClockSpine::map_relative_seg_indices(const t_unified_to_parallel_seg_index& indices_map) { /*We have vertical segments in clock-spines so we search for Y_AXIS*/ - get_parallel_seg_index(drive_seg_idx, Y_AXIS, indices_map); + get_parallel_seg_index(drive_seg_idx, e_parallel_axis::Y_AXIS, indices_map); - get_parallel_seg_index(left_seg_idx, Y_AXIS, indices_map); + get_parallel_seg_index(left_seg_idx, e_parallel_axis::Y_AXIS, indices_map); - get_parallel_seg_index(right_seg_idx, Y_AXIS, indices_map); + get_parallel_seg_index(right_seg_idx, e_parallel_axis::Y_AXIS, indices_map); } /********************************************************************************* diff --git a/vpr/src/route/rr_graph_generation/rr_graph.cpp b/vpr/src/route/rr_graph_generation/rr_graph.cpp index c67a1b4163a..ec4676190d8 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph.cpp +++ b/vpr/src/route/rr_graph_generation/rr_graph.cpp @@ -33,22 +33,14 @@ #include "rr_graph_clock.h" #include "edge_groups.h" #include "rr_graph_builder.h" +#include "tileable_rr_graph_builder.h" + #include "rr_types.h" #include "rr_node_indices.h" //#define VERBOSE //used for getting the exact count of each edge type and printing it to std out. -struct t_clb_to_clb_directs { - t_physical_tile_type_ptr from_clb_type; - int from_clb_pin_start_index; - int from_clb_pin_end_index; - t_physical_tile_type_ptr to_clb_type; - int to_clb_pin_start_index; - int to_clb_pin_end_index; - int switch_index; //The switch type used by this direct connection -}; - struct t_pin_loc { int pin_index; int width_offset; @@ -339,6 +331,17 @@ static void load_perturbed_connection_block_pattern(vtr::NdMatrix& track const int seg_index, const enum e_directionality directionality); +/** + * @brief Determines whether the output pins of the specified block type should be perturbed. + * This is to prevent pathological cases where the output pin connections are + * spaced such that the connection pattern always skips some types of wire (w.r.t. + * starting points) + * + * @param type The block type to check + * @param Fc_out The Fc values for the output pins + * @param max_chan_width The maximum channel width + * @param segment_inf The segment information + */ static std::vector alloc_and_load_perturb_opins(const t_physical_tile_type_ptr type, const vtr::Matrix& Fc_out, const int max_chan_width, const std::vector& segment_inf); #ifdef ENABLE_CHECK_ALL_TRACKS @@ -521,14 +524,6 @@ void uniquify_edges(t_rr_edge_info_set& rr_edges_to_create); void alloc_and_load_edges(RRGraphBuilder& rr_graph_builder, const t_rr_edge_info_set& rr_edges_to_create); -static void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, - std::vector>& switch_fanin_remap, - const std::map& arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch); - static void remap_rr_node_switch_indices(RRGraphBuilder& rr_graph_builder, const t_arch_switch_fanin& switch_fanin); @@ -543,27 +538,8 @@ static void alloc_rr_switch_inf(RRGraphBuilder& rr_graph_builder, t_arch_switch_fanin& arch_switch_fanins, const std::map& arch_sw_map); -static void rr_graph_externals(const std::vector& segment_inf, - const std::vector& segment_inf_x, - const std::vector& segment_inf_y, - int wire_to_rr_ipin_switch, - enum e_base_cost_type base_cost_type); - -static std::vector alloc_and_load_clb_to_clb_directs(const std::vector& directs, - int delayless_switch); - static std::vector alloc_and_load_global_route_seg_details(const int global_route_switch); -static std::vector> alloc_and_load_actual_fc(const std::vector& types, - const int max_pins, - const std::vector& segment_inf, - const std::vector& sets_per_seg_type, - const t_chan_width* nodes_per_chan, - const e_fc_type fc_type, - const enum e_directionality directionality, - bool* Fc_clipped, - bool is_flat); - static RRNodeId pick_best_direct_connect_target_rr_node(const RRGraphView& rr_graph, RRNodeId from_rr, const std::vector& candidate_rr_nodes); @@ -740,28 +716,54 @@ void create_rr_graph(e_graph_type graph_type, } } else { free_rr_graph(); - build_rr_graph(graph_type, - block_types, - grid, - nodes_per_chan, - det_routing_arch.switch_block_type, - det_routing_arch.Fs, - det_routing_arch.switchblocks, - segment_inf, - det_routing_arch.global_route_switch, - det_routing_arch.wire_to_arch_ipin_switch, - det_routing_arch.wire_to_arch_ipin_switch_between_dice, - router_opts.custom_3d_sb_fanin_fanout, - det_routing_arch.delayless_switch, - det_routing_arch.R_minW_nmos, - det_routing_arch.R_minW_pmos, - router_opts.base_cost_type, - router_opts.clock_modeling, - directs, - &det_routing_arch.wire_to_rr_ipin_switch, - is_flat, - Warnings, - router_opts.route_verbosity); + if (e_graph_type::UNIDIR_TILEABLE != graph_type) { + build_rr_graph(graph_type, + block_types, + grid, + nodes_per_chan, + det_routing_arch.switch_block_type, + det_routing_arch.Fs, + det_routing_arch.switchblocks, + segment_inf, + det_routing_arch.global_route_switch, + det_routing_arch.wire_to_arch_ipin_switch, + det_routing_arch.wire_to_arch_ipin_switch_between_dice, + router_opts.custom_3d_sb_fanin_fanout, + det_routing_arch.delayless_switch, + det_routing_arch.R_minW_nmos, + det_routing_arch.R_minW_pmos, + router_opts.base_cost_type, + router_opts.clock_modeling, + directs, + &det_routing_arch.wire_to_rr_ipin_switch, + is_flat, + Warnings, + router_opts.route_verbosity); + } else { + // Note: We do not support dedicated network for clocks in tileable rr_graph generation + build_tileable_unidir_rr_graph(block_types, + grid, + nodes_per_chan, + det_routing_arch.switch_block_type, + det_routing_arch.Fs, + det_routing_arch.switch_block_subtype, + det_routing_arch.sub_fs, + segment_inf, + det_routing_arch.delayless_switch, + det_routing_arch.wire_to_arch_ipin_switch, + det_routing_arch.R_minW_nmos, + det_routing_arch.R_minW_pmos, + router_opts.base_cost_type, + directs, + &det_routing_arch.wire_to_rr_ipin_switch, + det_routing_arch.shrink_boundary, // Shrink to the smallest boundary, no routing wires for empty zone + det_routing_arch.perimeter_cb, // Now I/O or any programmable blocks on perimeter can have full cb access (both cbx and cby) + det_routing_arch.through_channel, // Allow/Prohibit through tracks across multi-height and multi-width grids + 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 + Warnings); + } } // Check if there is an edge override file to read and that it is not already loaded. @@ -791,6 +793,7 @@ void create_rr_graph(e_graph_type graph_type, is_flat, load_rr_graph); + // Reorder nodes upon needs in algorithms and router options if (router_opts.reorder_rr_graph_nodes_algorithm != DONT_REORDER) { mutable_device_ctx.rr_graph_builder.reorder_nodes(router_opts.reorder_rr_graph_nodes_algorithm, router_opts.reorder_rr_graph_nodes_threshold, @@ -843,7 +846,7 @@ static void add_intra_cluster_edges_rr_graph(RRGraphBuilder& rr_graph_builder, bool is_flat, bool load_rr_graph) { VTR_ASSERT(is_flat); - /* This function should be called if placement is done! */ + // This function should be called if placement is done! auto& block_locs = g_vpr_ctx.placement().block_locs(); auto& cluster_net_list = g_vpr_ctx.clustering().clb_nlist; @@ -1011,7 +1014,7 @@ static void build_rr_graph(e_graph_type graph_type, directionality = BI_DIRECTIONAL; } - /* Global routing uses a single longwire track */ + // Global routing uses a single longwire track int max_chan_width = nodes_per_chan.max = (is_global_graph ? 1 : nodes_per_chan.max); int max_chan_width_x = nodes_per_chan.x_max = (is_global_graph ? 1 : nodes_per_chan.x_max); @@ -1024,7 +1027,7 @@ static void build_rr_graph(e_graph_type graph_type, std::vector clb_to_clb_directs = alloc_and_load_clb_to_clb_directs(directs, delayless_switch); - /* START SEG_DETAILS */ + // START SEG_DETAILS const size_t num_segments = segment_inf.size(); device_ctx.rr_graph_builder.reserve_segments(num_segments); for (size_t iseg = 0; iseg < num_segments; ++iseg) { @@ -1032,8 +1035,8 @@ static void build_rr_graph(e_graph_type graph_type, } t_unified_to_parallel_seg_index segment_index_map; - std::vector segment_inf_x = get_parallel_segs(segment_inf, segment_index_map, X_AXIS); - std::vector segment_inf_y = get_parallel_segs(segment_inf, segment_index_map, Y_AXIS); + std::vector segment_inf_x = get_parallel_segs(segment_inf, segment_index_map, e_parallel_axis::X_AXIS); + std::vector segment_inf_y = get_parallel_segs(segment_inf, segment_index_map, e_parallel_axis::Y_AXIS); std::vector seg_details_x; std::vector seg_details_y; @@ -1044,12 +1047,12 @@ static void build_rr_graph(e_graph_type graph_type, seg_details_y = alloc_and_load_global_route_seg_details(global_route_switch); } else { - /* Setup segments including distributing tracks and staggering. - * If use_full_seg_groups is specified, max_chan_width may be - * changed. Warning should be singled to caller if this happens. */ + // Setup segments including distributing tracks and staggering. + // If use_full_seg_groups is specified, max_chan_width may be + // changed. Warning should be singled to caller if this happens. - /* Need to setup segments along x & y axes separately, due to different - * max_channel_widths and segment specifications. */ + // Need to setup segments along x & y axes separately, due to different + // max_channel_widths and segment specifications. size_t max_dim = std::max(grid.width(), grid.height()) - 2; //-2 for no perim channels @@ -1071,20 +1074,20 @@ static void build_rr_graph(e_graph_type graph_type, *Warnings |= RR_GRAPH_WARN_CHAN_Y_WIDTH_CHANGED; } - //TODO: Fix - //if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_SEG_DETAILS)) { - //dump_seg_details(seg_details, max_chan_width, - //getEchoFileName(E_ECHO_SEG_DETAILS)); - //} + // TODO: Fix + // if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_SEG_DETAILS)) { + // dump_seg_details(seg_details, max_chan_width, + // getEchoFileName(E_ECHO_SEG_DETAILS)); + // } } // Map the internal segment indices of the networks if (clock_modeling == DEDICATED_NETWORK) { ClockRRGraphBuilder::map_relative_seg_indices(segment_index_map); } - /* END SEG_DETAILS */ + // END SEG_DETAILS - /* START CHAN_DETAILS */ + // START CHAN_DETAILS t_chan_details chan_details_x; t_chan_details chan_details_y; @@ -1097,9 +1100,9 @@ static void build_rr_graph(e_graph_type graph_type, dump_chan_details(chan_details_x, chan_details_y, &nodes_per_chan, grid, getEchoFileName(E_ECHO_CHAN_DETAILS)); } - /* END CHAN_DETAILS */ + // END CHAN_DETAILS - /* START FC */ + // START FC // Determine the actual value of Fc std::vector> Fc_in; // [0..device_ctx.num_block_types-1][0..num_pins-1][0..num_segments-1] std::vector> Fc_out; // [0..device_ctx.num_block_types-1][0..num_pins-1][0..num_segments-1] @@ -1153,7 +1156,7 @@ static void build_rr_graph(e_graph_type graph_type, for (const t_physical_tile_type& type : types) { int i = type.index; - /* Skip "EMPTY" */ + // Skip "EMPTY" if (is_empty_type(&type)) { continue; } @@ -1181,9 +1184,9 @@ static void build_rr_graph(e_graph_type graph_type, auto perturb_ipins = alloc_and_load_perturb_ipins(types.size(), segment_inf.size(), sets_per_seg_type, Fc_in, Fc_out, directionality); - /* END FC */ + // END FC - /* Alloc node lookups, count nodes, alloc rr nodes */ + // Alloc node lookups, count nodes, alloc rr nodes int num_rr_nodes = 0; // Add routing resources to rr_graph lookup table @@ -1201,9 +1204,9 @@ static void build_rr_graph(e_graph_type graph_type, } device_ctx.rr_graph_builder.resize_nodes(num_rr_nodes); - /* These are data structures used by the unidir opin mapping. They are used - * to spread connections evenly for each segment type among the available - * wire start points */ + // These are data structures used by the unidir opin mapping. They are used + // to spread connections evenly for each segment type among the available + // wire start points vtr::NdMatrix Fc_xofs({grid.height() - 1, grid.width() - 1, segment_inf_x.size()}, @@ -1213,8 +1216,8 @@ static void build_rr_graph(e_graph_type graph_type, segment_inf_y.size()}, 0); //[0..grid.width()-2][0..grid.height()-2][0..num_seg_types_y-1] - /* START SB LOOKUP */ - /* Alloc and load the switch block lookup */ + // START SB LOOKUP + // Alloc and load the switch block lookup vtr::NdMatrix, 3> switch_block_conn; t_sblock_pattern unidir_sb_pattern; t_sb_connection_map* sb_conn_map = nullptr; //for custom switch blocks @@ -1248,7 +1251,7 @@ static void build_rr_graph(e_graph_type graph_type, switchblocks, nodes_per_chan, directionality, switchpoint_rng); } else { - /* it looks like we get unbalanced muxing from this switch block code with Fs > 3 */ + // it looks like we get unbalanced muxing from this switch block code with Fs > 3 VTR_ASSERT(Fs == 3); unidir_sb_pattern = alloc_sblock_pattern_lookup(grid, nodes_per_chan); @@ -1266,7 +1269,7 @@ static void build_rr_graph(e_graph_type graph_type, } } } - /* END SB LOOKUP */ + // END SB LOOKUP // Check whether RR graph need to allocate new nodes for 3D custom switch blocks. // To avoid wasting memory, the data structures are only allocated if a custom switch block @@ -1279,11 +1282,11 @@ static void build_rr_graph(e_graph_type graph_type, device_ctx.rr_graph_builder.resize_nodes(num_rr_nodes); } - /* START IPIN MAP */ - /* Create ipin map lookups */ + // START IPIN MAP + // Create ipin map lookups - t_pin_to_track_lookup ipin_to_track_map_x(types.size()); /* [0..device_ctx.physical_tile_types.size()-1][0..num_pins-1][0..width-1][0..height-1][0..layers-1][0..sides-1][0..Fc-1] */ - t_pin_to_track_lookup ipin_to_track_map_y(types.size()); /* [0..device_ctx.physical_tile_types.size()-1][0..num_pins-1][0..width-1][0..height-1][0..layers-1][0..sides-1][0..Fc-1] */ + t_pin_to_track_lookup ipin_to_track_map_x(types.size()); // [0..device_ctx.physical_tile_types.size()-1][0..num_pins-1][0..width-1][0..height-1][0..layers-1][0..sides-1][0..Fc-1] + t_pin_to_track_lookup ipin_to_track_map_y(types.size()); // [0..device_ctx.physical_tile_types.size()-1][0..num_pins-1][0..width-1][0..height-1][0..layers-1][0..sides-1][0..Fc-1] t_track_to_pin_lookup track_to_pin_lookup_x(types.size()); t_track_to_pin_lookup track_to_pin_lookup_y(types.size()); @@ -1324,11 +1327,11 @@ static void build_rr_graph(e_graph_type graph_type, dump_track_to_pin_map(track_to_pin_lookup_x, types, nodes_per_chan.x_max, fp); fclose(fp); } - /* END IPIN MAP */ + // END IPIN MAP - /* START OPIN MAP */ - /* Create opin map lookups */ - t_pin_to_track_lookup opin_to_track_map(types.size()); /* [0..device_ctx.physical_tile_types.size()-1][0..num_pins-1][0..width][0..height][0..3][0..Fc-1] */ + // START OPIN MAP + // Create opin map lookups + t_pin_to_track_lookup opin_to_track_map(types.size()); // [0..device_ctx.physical_tile_types.size()-1][0..num_pins-1][0..width][0..height][0..3][0..Fc-1] if (BI_DIRECTIONAL == directionality) { for (unsigned int itype = 0; itype < types.size(); ++itype) { auto type_layer = get_layers_of_physical_types(&types[itype]); @@ -1339,15 +1342,14 @@ static void build_rr_graph(e_graph_type graph_type, segment_inf, sets_per_seg_type); } } - /* END OPIN MAP */ + // END OPIN MAP bool Fc_clipped = false; - /* Draft the switches as internal data of RRGraph object - * These are temporary switches copied from arch switches - * We use them to build the edges - * We will reset all the switches in the function - * alloc_and_load_rr_switch_inf() - */ + // Draft the switches as internal data of RRGraph object + // These are temporary switches copied from arch switches + // We use them to build the edges + // We will reset all the switches in the function + // alloc_and_load_rr_switch_inf() device_ctx.rr_graph_builder.reserve_switches(device_ctx.all_sw_inf.size()); // Create the switches for (const auto& sw_pair : device_ctx.all_sw_inf) { @@ -1384,14 +1386,14 @@ static void build_rr_graph(e_graph_type graph_type, // Verify no incremental node allocation. // AA: Note that in the case of dedicated networks, we are currently underestimating the additional node count due to the clock networks. - /* For now, the node count comparison is being skipped in the presence of clock networks. - * TODO: The node estimation needs to be fixed for dedicated clock networks. */ + // For now, the node count comparison is being skipped in the presence of clock networks. + // TODO: The node estimation needs to be fixed for dedicated clock networks. if (rr_graph.num_nodes() > expected_node_count && clock_modeling != DEDICATED_NETWORK) { VTR_LOG_ERROR("Expected no more than %zu nodes, have %zu nodes\n", expected_node_count, rr_graph.num_nodes()); } - /* Update rr_nodes capacities if global routing */ + // Update rr_nodes capacities if global routing if (graph_type == e_graph_type::GLOBAL) { // Using num_rr_nodes here over device_ctx.rr_nodes.size() because // clock_modeling::DEDICATED_NETWORK will append some rr nodes after @@ -1410,8 +1412,8 @@ static void build_rr_graph(e_graph_type graph_type, update_chan_width(&nodes_per_chan); - /* 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 */ + // 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 alloc_and_load_rr_switch_inf(g_vpr_ctx.mutable_device().rr_graph_builder, g_vpr_ctx.mutable_device().switch_fanin_remap, device_ctx.all_sw_inf, @@ -1420,8 +1422,8 @@ static void build_rr_graph(e_graph_type graph_type, wire_to_arch_ipin_switch, wire_to_rr_ipin_switch); - //Partition the rr graph edges for efficient access to configurable/non-configurable - //edge subsets. Must be done after RR switches have been allocated + // Partition the rr graph edges for efficient access to configurable/non-configurable + // edge subsets. Must be done after RR switches have been allocated device_ctx.rr_graph_builder.partition_edges(); //Save the channel widths for the newly constructed graph @@ -1429,10 +1431,12 @@ static void build_rr_graph(e_graph_type graph_type, rr_graph_externals(segment_inf, segment_inf_x, segment_inf_y, *wire_to_rr_ipin_switch, base_cost_type); + const VibDeviceGrid vib_grid; check_rr_graph(device_ctx.rr_graph, types, device_ctx.rr_indexed_data, grid, + vib_grid, device_ctx.chan_width, graph_type, is_flat); @@ -1506,10 +1510,12 @@ static void build_intra_cluster_rr_graph(e_graph_type graph_type, rr_graph_builder.clear_temp_storage(); + const VibDeviceGrid vib_grid; check_rr_graph(device_ctx.rr_graph, types, device_ctx.rr_indexed_data, grid, + vib_grid, device_ctx.chan_width, graph_type, is_flat); @@ -1590,30 +1596,29 @@ void build_tile_rr_graph(RRGraphBuilder& rr_graph_builder, * and count how many different fan-ins exist for each arch switch. * Then we create these rr switches and update the switch indices * of rr_nodes to index into the rr_switch_inf array. */ -static void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, - std::vector>& switch_fanin_remap, - const std::map& arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch) { - /* we will potentially be creating a couple of versions of each arch switch where - * each version corresponds to a different fan-in. We will need to fill device_ctx.rr_switch_inf - * with this expanded list of switches. - * - * To do this we will use arch_switch_fanins, which is indexed as: - * arch_switch_fanins[i_arch_switch][fanin] -> new_switch_id - */ +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map& arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch) { + // we will potentially be creating a couple of versions of each arch switch where + // each version corresponds to a different fan-in. We will need to fill device_ctx.rr_switch_inf + // with this expanded list of switches. + // + // To do this we will use arch_switch_fanins, which is indexed as: + // arch_switch_fanins[i_arch_switch][fanin] -> new_switch_id t_arch_switch_fanin arch_switch_fanins(arch_sw_inf.size()); - /* Determine what the different fan-ins are for each arch switch, and also - * how many entries the rr_switch_inf array should have */ + // Determine what the different fan-ins are for each arch switch, and also + // how many entries the rr_switch_inf array should have alloc_rr_switch_inf(rr_graph_builder, arch_switch_fanins, arch_sw_inf); - /* create the rr switches. also keep track of, for each arch switch, what index of the rr_switch_inf - * array each version of its fanin has been mapped to */ + // create the rr switches. also keep track of, for each arch switch, what index of the rr_switch_inf + // array each version of its fanin has been mapped to load_rr_switch_inf(rr_graph_builder, switch_fanin_remap, arch_sw_inf, @@ -1621,16 +1626,16 @@ static void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, R_minW_pmos, arch_switch_fanins); - /* next, walk through rr nodes again and remap their switch indices to rr_switch_inf */ + // next, walk through rr nodes again and remap their switch indices to rr_switch_inf remap_rr_node_switch_indices(rr_graph_builder, arch_switch_fanins); - /* now we need to set the wire_to_rr_ipin_switch variable which points the detailed routing architecture - * to the representative ipin cblock switch. currently we're not allowing the specification of an ipin cblock switch - * with multiple fan-ins, so right now there's just one. May change in the future, in which case we'd need to - * return a representative switch */ + // now we need to set the wire_to_rr_ipin_switch variable which points the detailed routing architecture + // to the representative ipin cblock switch. currently we're not allowing the specification of an ipin cblock switch + // with multiple fan-ins, so right now there's just one. May change in the future, in which case we'd need to + // return a representative switch if (arch_switch_fanins[wire_to_arch_ipin_switch].count(UNDEFINED)) { - /* only have one ipin cblock switch. OK. */ + // only have one ipin cblock switch. OK. (*wire_to_rr_ipin_switch) = arch_switch_fanins[wire_to_arch_ipin_switch][UNDEFINED]; } else if (arch_switch_fanins[wire_to_arch_ipin_switch].size() != 0) { VPR_FATAL_ERROR(VPR_ERROR_ARCH, @@ -1680,8 +1685,8 @@ static void load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, int arch_sw_id = arch_sw_pair.first; std::map::iterator it; for (auto fanin_rrswitch : arch_switch_fanins[arch_sw_id]) { - /* the fanin value is in it->first, and we'll need to set what index this i_arch_switch/fanin - * combination maps to (within rr_switch_inf) in it->second) */ + // the fanin value is in it->first, and we'll need to set what index this i_arch_switch/fanin + // combination maps to (within rr_switch_inf) in it->second) int fanin; int i_rr_switch; std::tie(fanin, i_rr_switch) = fanin_rrswitch; @@ -1709,11 +1714,11 @@ t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_ const float R_minW_pmos) { t_rr_switch_inf rr_switch_inf; - /* figure out, by looking at the arch switch's Tdel map, what the delay of the new - * rr switch should be */ + // figure out, by looking at the arch switch's Tdel map, what the delay of the new + // rr switch should be double rr_switch_Tdel = arch_sw_inf.Tdel(0); - /* copy over the arch switch to rr_switch_inf[rr_switch_idx], but with the changed Tdel value */ + // copy over the arch switch to rr_switch_inf[rr_switch_idx], but with the changed Tdel value rr_switch_inf.set_type(arch_sw_inf.type()); rr_switch_inf.R = arch_sw_inf.R; rr_switch_inf.Cin = arch_sw_inf.Cin; @@ -1737,6 +1742,7 @@ t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_ return rr_switch_inf; } + /* This function is same as create_rr_switch_from_arch_switch() in terms of functionality. It is tuned for clients functions in routing resource graph builder */ void load_rr_switch_from_arch_switch(RRGraphBuilder& rr_graph_builder, const std::map& arch_sw_inf, @@ -1745,11 +1751,11 @@ void load_rr_switch_from_arch_switch(RRGraphBuilder& rr_graph_builder, int fanin, const float R_minW_nmos, const float R_minW_pmos) { - /* figure out, by looking at the arch switch's Tdel map, what the delay of the new - * rr switch should be */ + // figure out, by looking at the arch switch's Tdel map, what the delay of the new + // rr switch should be double rr_switch_Tdel = arch_sw_inf.at(arch_switch_idx).Tdel(fanin); - /* copy over the arch switch to rr_switch_inf[rr_switch_idx], but with the changed Tdel value */ + // copy over the arch switch to rr_switch_inf[rr_switch_idx], but with the changed Tdel value rr_graph_builder.rr_switch()[RRSwitchId(rr_switch_idx)].set_type(arch_sw_inf.at(arch_switch_idx).type()); rr_graph_builder.rr_switch()[RRSwitchId(rr_switch_idx)].R = arch_sw_inf.at(arch_switch_idx).R; rr_graph_builder.rr_switch()[RRSwitchId(rr_switch_idx)].Cin = arch_sw_inf.at(arch_switch_idx).Cin; @@ -1778,11 +1784,11 @@ static void remap_rr_node_switch_indices(RRGraphBuilder& rr_graph_builder, rr_graph_builder.remap_rr_node_switch_indices(switch_fanin); } -static void rr_graph_externals(const std::vector& segment_inf, - const std::vector& segment_inf_x, - const std::vector& segment_inf_y, - int wire_to_rr_ipin_switch, - enum e_base_cost_type base_cost_type) { +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; const auto& grid = device_ctx.grid; @@ -1872,15 +1878,15 @@ static std::vector alloc_and_load_global_route_seg_details(const } /* Calculates the number of track connections from each block pin to each segment type */ -static std::vector> alloc_and_load_actual_fc(const std::vector& types, - const int max_pins, - const std::vector& segment_inf, - const std::vector& sets_per_seg_type, - const t_chan_width* nodes_per_chan, - const e_fc_type fc_type, - const enum e_directionality directionality, - bool* Fc_clipped, - bool is_flat) { +std::vector> alloc_and_load_actual_fc(const std::vector& types, + const int max_pins, + const std::vector& segment_inf, + const std::vector& sets_per_seg_type, + const t_chan_width* nodes_per_chan, + const e_fc_type fc_type, + const enum e_directionality directionality, + bool* Fc_clipped, + bool is_flat) { // Initialize Fc of all blocks to zero auto zeros = vtr::Matrix({size_t(max_pins), segment_inf.size()}, 0); std::vector> Fc(types.size(), zeros); @@ -2019,30 +2025,30 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder const enum e_clock_modeling clock_modeling, bool /*is_flat*/, const int route_verbosity) { - //We take special care when creating RR graph edges (there are typically many more - //edges than nodes in an RR graph). + // We take special care when creating RR graph edges (there are typically many more + // edges than nodes in an RR graph). // - //In particular, all the following build_*() functions do not create the edges, but - //instead record the edges they wish to create in rr_edges_to_create. + // In particular, all the following build_*() functions do not create the edges, but + // instead record the edges they wish to create in rr_edges_to_create. // - //We uniquify the edges to be created (avoiding any duplicates), and create - //the edges in alloc_and_load_edges(). + // We uniquify the edges to be created (avoiding any duplicates), and create + // the edges in alloc_and_load_edges(). // - //By doing things in this manner we ensure we know exactly how many edges leave each RR - //node, which avoids resizing the RR edge arrays (which can cause significant memory - //fragmentation, and significantly increasing peak memory usage). This is important since - //RR graph creation is the high-watermark of VPR's memory use. + // By doing things in this manner we ensure we know exactly how many edges leave each RR + // node, which avoids resizing the RR edge arrays (which can cause significant memory + // fragmentation, and significantly increasing peak memory usage). This is important since + // RR graph creation is the high-watermark of VPR's memory use. t_rr_edge_info_set rr_edges_to_create; - /* If Fc gets clipped, this will be flagged to true */ + // If Fc gets clipped, this will be flagged to true *Fc_clipped = false; - /* This function is called to build the general routing graph resources. Thus, - * the edges are not remapped yet.*/ + // This function is called to build the general routing graph resources. Thus, + // the edges are not remapped yet. bool switches_remapped = false; int num_edges = 0; - /* Connection SINKS and SOURCES to their pins - Initializing IPINs/OPINs. */ + // Connection SINKS and SOURCES to their pins - Initializing IPINs/OPINs. for (int layer = 0; layer < grid.get_num_layers(); ++layer) { for (int i = 0; i < (int)grid.width(); ++i) { for (int j = 0; j < (int)grid.height(); ++j) { @@ -2087,7 +2093,7 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder VTR_LOGV(route_verbosity > 1, "SOURCE->OPIN and IPIN->SINK edge count:%d\n", num_edges); num_edges = 0; - /* Build opins */ + // Build opins int rr_edges_before_directs = 0; for (int layer = 0; layer < grid.get_num_layers(); layer++) { for (size_t i = 0; i < grid.width(); ++i) { @@ -2126,7 +2132,7 @@ static std::function alloc_and_load_rr_graph(RRGraphBuilder VTR_LOGV(route_verbosity > 1, "OPIN->CHANX/CHANY edge count after creating direct connections: %d\n", num_edges); num_edges = 0; - /* Build channels */ + // Build channels VTR_ASSERT(Fs % 3 == 0); // In case of multi-die FPGA and a custom 3D SB, we keep track of how many 3D connections have been already made for each x,y location @@ -2429,11 +2435,11 @@ static void add_pins_rr_graph(RRGraphBuilder& rr_graph_builder, float C = 0.; rr_graph_builder.set_node_rc_index(node_id, NodeRCIndex(find_create_rr_rc_data(R, C, mutable_device_ctx.rr_rc_data))); rr_graph_builder.set_node_pin_num(node_id, pin_num); - //Note that we store the grid tile location and side where the pin is located, - //which greatly simplifies the drawing code - //For those pins located on multiple sides, we save the rr node index - //for the pin on all sides at which it exists - //As such, multiple driver problem can be avoided. + // Note that we store the grid tile location and side where the pin is located, + // which greatly simplifies the drawing code + // For those pins located on multiple sides, we save the rr node index + // for the pin on all sides at which it exists + // As such, multiple driver problem can be avoided. rr_graph_builder.set_node_coordinates(node_id, i + x_offset, j + y_offset, @@ -2460,8 +2466,6 @@ static void connect_tile_src_sink_to_pins(RRGraphBuilder& rr_graph_builder, auto class_type = get_class_type_from_class_physical_num(physical_type_ptr, class_num); RRNodeId class_rr_node_id = get_class_rr_node_id(rr_graph_builder.node_lookup(), physical_type_ptr, layer, i, j, class_num); VTR_ASSERT(class_rr_node_id != RRNodeId::INVALID()); - //bool is_primitive = is_primitive_pin(physical_type_ptr, pin_list[0]); - //t_logical_block_type_ptr logical_block = is_primitive ? get_logical_block_from_pin_physical_num(physical_type_ptr, pin_list[0]) : nullptr; for (auto pin_num : pin_list) { RRNodeId pin_rr_node_id = get_pin_rr_node_id(rr_graph_builder.node_lookup(), physical_type_ptr, layer, i, j, pin_num); if (pin_rr_node_id == RRNodeId::INVALID()) { @@ -2598,7 +2602,7 @@ static void build_bidir_rr_opins(RRGraphBuilder& rr_graph_builder, const std::vector& directs, const std::vector& clb_to_clb_directs, const int num_seg_types) { - //Don't connect pins which are not adjacent to channels around the perimeter + // Don't connect pins which are not adjacent to channels around the perimeter if ((i == 0 && side != RIGHT) || (i == int(grid.width() - 1) && side != LEFT) || (j == 0 && side != TOP) @@ -2613,17 +2617,17 @@ static void build_bidir_rr_opins(RRGraphBuilder& rr_graph_builder, const vtr::Matrix& Fc = Fc_out[type->index]; for (int pin_index = 0; pin_index < type->num_pins; ++pin_index) { - /* We only are working with opins so skip non-drivers */ + // We only are working with opins so skip non-drivers if (get_pin_type_from_pin_physical_num(type, pin_index) != DRIVER) { continue; } - /* Can't do anything if pin isn't at this location */ + // Can't do anything if pin isn't at this location if (0 == type->pinloc[width_offset][height_offset][side][pin_index]) { continue; } - /* get number of tracks that this pin connects to */ + // get number of tracks that this pin connects to int total_pin_Fc = 0; for (int iseg = 0; iseg < num_seg_types; iseg++) { total_pin_Fc += Fc[pin_index][iseg]; @@ -2641,7 +2645,7 @@ static void build_bidir_rr_opins(RRGraphBuilder& rr_graph_builder, } } - /* Add in direct connections */ + // Add in direct connections get_opin_direct_connections(rr_graph_builder, rr_graph, layer, i, j, side, pin_index, node_index, rr_edges_to_create, directs, clb_to_clb_directs); @@ -2649,14 +2653,11 @@ static void build_bidir_rr_opins(RRGraphBuilder& rr_graph_builder, } void free_rr_graph() { - /* Frees all the routing graph data structures, if they have been * - * allocated. I use rr_mem_chunk_list_head as a flag to indicate * - * whether or not the graph has been allocated -- if it is not NULL, * - * a routing graph exists and can be freed. Hence, you can call this * - * routine even if you're not sure of whether a rr_graph exists or not. */ - - /* Before adding any more free calls here, be sure the data is NOT chunk * - * allocated, as ALL the chunk allocated data is already free! */ + // Frees all the routing graph data structures, if they have been allocated. + // I use rr_mem_chunk_list_head as a flag to indicate whether or not the graph has been allocated -- if it is not NULL, + // a routing graph exists and can be freed. Hence, you can call this routine even if you're not sure of whether a rr_graph exists or not. + + // Before adding any more free calls here, be sure the data is NOT chunk allocated, as ALL the chunk allocated data is already free! auto& device_ctx = g_vpr_ctx.mutable_device(); device_ctx.loaded_rr_graph_filename.clear(); @@ -2664,6 +2665,8 @@ void free_rr_graph() { device_ctx.rr_graph_builder.clear(); + device_ctx.rr_node_track_ids.clear(); + device_ctx.rr_indexed_data.clear(); device_ctx.switch_fanin_remap.clear(); @@ -2688,7 +2691,7 @@ static void build_cluster_internal_edges(RRGraphBuilder& rr_graph_builder, bool is_flat, bool load_rr_graph) { VTR_ASSERT(is_flat); - /* Internal edges are added from the start tile */ + // Internal edges are added from the start tile int width_offset = grid.get_width_offset({i, j, layer}); int height_offset = grid.get_height_offset({i, j, layer}); VTR_ASSERT(width_offset == 0 && height_offset == 0); @@ -3066,8 +3069,7 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, const int custom_3d_sb_fanin_fanout, const int delayless_switch, const enum e_directionality directionality) { - /* this function builds both x and y-directed channel segments, so set up our - * coordinates based on channel type */ + // this function builds both x and y-directed channel segments, so set up our coordinates based on channel type const auto& device_ctx = g_vpr_ctx.device(); auto& mutable_device_ctx = g_vpr_ctx.mutable_device(); @@ -3089,22 +3091,21 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, const t_chan_seg_details* seg_details = from_chan_details[x_coord][y_coord].data(); - /* figure out if we're generating switch block edges based on a custom switch block - * description */ + // figure out if we're generating switch block edges based on a custom switch block description bool custom_switch_block = false; if (sb_conn_map != nullptr) { VTR_ASSERT(sblock_pattern.empty() && switch_block_conn.empty()); custom_switch_block = true; } - /* Loads up all the routing resource nodes in the current channel segment */ + // Loads up all the routing resource nodes in the current channel segment for (int track = 0; track < tracks_per_chan; ++track) { if (seg_details[track].length() == 0) continue; - //Start and end coordinates of this segment along the length of the channel - //Note that these values are in the VPR coordinate system (and do not consider - //wire directionality), so start correspond to left/bottom and end corresponds to right/top + // Start and end coordinates of this segment along the length of the channel + // Note that these values are in the VPR coordinate system (and do not consider + // wire directionality), so start correspond to left/bottom and end corresponds to right/top int start = get_seg_start(seg_details, track, chan_coord, seg_coord); int end = get_seg_end(seg_details, track, start, chan_coord, seg_dimension); @@ -3173,20 +3174,19 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, } } - /* walk over the switch blocks along the source track and implement edges from this track to other tracks - * in the same channel (i.e. straight-through connections) */ + // walk over the switch blocks along the source track and implement edges from this track to other tracks in the same channel (i.e. straight-through connections) for (int target_seg = start - 1; target_seg <= end + 1; target_seg++) { if (target_seg != start - 1 && target_seg != end + 1) { - /* skip straight-through connections from midpoint if non-custom switch block. - * currently non-custom switch blocks don't properly describe connections from the mid-point of a wire segment - * to other segments in the same channel (i.e. straight-through connections) */ + // skip straight-through connections from midpoint if non-custom switch block. + // currently non-custom switch blocks don't properly describe connections from the mid-point of a wire segment + // to other segments in the same channel (i.e. straight-through connections) if (!custom_switch_block) { continue; } } if (target_seg > 0 && target_seg < seg_dimension + 1) { const t_chan_seg_details* to_seg_details; - /* AA: Same channel width for straight through connections assuming uniform width distributions along the axis*/ + // AA: Same channel width for straight through connections assuming uniform width distributions along the axis int max_chan_width = 0; if (chan_type == e_rr_type::CHANX) { to_seg_details = chan_details_x[target_seg][y_coord].data(); @@ -3207,14 +3207,12 @@ static void build_rr_chan(RRGraphBuilder& rr_graph_builder, } } - /* Edge arrays have now been built up. Do everything else. */ - /* AA: The cost_index should be w.r.t the index of the segment to its **parallel** - * segment_inf vector. Note that when building channels, we use the indices - * w.r.t segment_inf_x and segment_inf_y as computed earlier in - * build_rr_graph so it's fine to use .index() for to get the correct index. - */ + // Edge arrays have now been built up. Do everything else. + // AA: The cost_index should be w.r.t the index of the segment to its **parallel** segment_inf vector. + // Note that when building channels, we use the indices w.r.t segment_inf_x and segment_inf_y as + // computed earlier in build_rr_graph so it's fine to use .index() for to get the correct index. rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(cost_index_offset + seg_details[track].index())); - rr_graph_builder.set_node_capacity(node, 1); /* GLOBAL routing handled elsewhere */ + rr_graph_builder.set_node_capacity(node, 1); // GLOBAL routing handled elsewhere if (chan_type == e_rr_type::CHANX) { rr_graph_builder.set_node_coordinates(node, start, y_coord, end, y_coord); @@ -3303,8 +3301,8 @@ static vtr::NdMatrix, 5> alloc_and_load_pin_to_track_map(const const e_directionality directionality, const std::vector& seg_inf, const std::vector& sets_per_seg_type) { - /* allocate 'result' matrix and initialize entries to OPEN. also allocate and intialize matrix which will be - * used to index into the correct entries when loading up 'result' */ + // allocate 'result' matrix and initialize entries to OPEN. also allocate and intialize matrix which will be used + // to index into the correct entries when loading up 'result' auto& grid = g_vpr_ctx.device().grid; auto result = vtr::NdMatrix, 5>({ size_t(tile_type->num_pins), //[0..num_pins-1] @@ -3314,19 +3312,19 @@ static vtr::NdMatrix, 5> alloc_and_load_pin_to_track_map(const 4, //[0..sides-1] }); - /* multiplier for unidirectional vs bidirectional architectures */ + // multiplier for unidirectional vs bidirectional architectures int fac = 1; if (directionality == UNI_DIRECTIONAL) { fac = 2; } - /* load the pin to track matrix by looking at each segment type in turn */ + // load the pin to track matrix by looking at each segment type in turn int num_parallel_seg_types = seg_inf.size(); int seg_type_start_track = 0; for (int iseg = 0; iseg < num_parallel_seg_types; iseg++) { int num_seg_type_tracks = fac * sets_per_seg_type[iseg]; - /* determine the maximum Fc to this segment type across all pins */ + // determine the maximum Fc to this segment type across all pins int max_Fc = 0; for (int pin_index = 0; pin_index < tile_type->num_pins; ++pin_index) { int pin_class = tile_type->pin_class[pin_index]; @@ -3335,12 +3333,11 @@ static vtr::NdMatrix, 5> alloc_and_load_pin_to_track_map(const } } - /* get pin connections to tracks of the current segment type */ + // get pin connections to tracks of the current segment type auto pin_to_seg_type_map = alloc_and_load_pin_to_seg_type(pin_type, Fc, num_seg_type_tracks, seg_inf[iseg].seg_index, max_Fc, tile_type, type_layer, perturb_switch_pattern[seg_inf[iseg].seg_index], directionality); - /* connections in pin_to_seg_type_map are within that seg type -- i.e. in the [0,num_seg_type_tracks-1] range. - * now load up 'result' array with these connections, but offset them so they are relative to the channel - * as a whole */ + // connections in pin_to_seg_type_map are within that seg type -- i.e. in the [0,num_seg_type_tracks-1] range. + // now load up 'result' array with these connections, but offset them so they are relative to the channel as a whole for (auto type_layer_index : type_layer) { for (int ipin = 0; ipin < tile_type->num_pins; ipin++) { int cur_Fc = Fc[ipin][seg_inf[iseg].seg_index]; @@ -3366,7 +3363,7 @@ static vtr::NdMatrix, 5> alloc_and_load_pin_to_track_map(const } } - /* next seg type will start at this track index */ + // next seg type will start at this track index seg_type_start_track += num_seg_type_tracks; } @@ -3382,15 +3379,14 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin const std::set& type_layer, const bool perturb_switch_pattern, const e_directionality directionality) { - /* Note: currently a single value of Fc is used across each pin. In the future - * the looping below will have to be modified if we want to account for pin-based - * Fc values */ + // Note: currently a single value of Fc is used across each pin. In the future the looping below will + // have to be modified if we want to account for pin-based Fc values - /* NB: This wastes some space. Could set tracks_..._pin[ipin][ioff][iside] = - * NULL if there is no pin on that side, or that pin is of the wrong type. - * Probably not enough memory to worry about, esp. as it's temporary. - * If pin ipin on side iside does not exist or is of the wrong type, - * tracks_connected_to_pin[ipin][iside][0] = OPEN. */ + // NB: This wastes some space. Could set tracks_..._pin[ipin][ioff][iside] = NULL if there is + // no pin on that side, or that pin is of the wrong type. + // Probably not enough memory to worry about, esp. as it's temporary. + // If pin ipin on side iside does not exist or is of the wrong type, + // tracks_connected_to_pin[ipin][iside][0] = OPEN. auto& grid = g_vpr_ctx.device().grid; @@ -3399,51 +3395,51 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin } auto tracks_connected_to_pin = vtr::NdMatrix({ - size_t(tile_type->num_pins), //[0..num_pins-1] - size_t(tile_type->width), //[0..width-1] - size_t(tile_type->height), //[0..height-1] - size_t(grid.get_num_layers()), //[0..layer-1] - NUM_2D_SIDES, //[0..NUM_2D_SIDES-1] - size_t(max_Fc) //[0..Fc-1] + size_t(tile_type->num_pins), // [0..num_pins-1] + size_t(tile_type->width), // [0..width-1] + size_t(tile_type->height), // [0..height-1] + size_t(grid.get_num_layers()), // [0..layer-1] + NUM_2D_SIDES, // [0..NUM_2D_SIDES-1] + size_t(max_Fc) // [0..Fc-1] }, - OPEN); //Unconnected + OPEN); // Unconnected - //Number of *physical* pins on each side. - //Note that his may be more than the logical number of pins (i.e. - //Type->num_pins) if a logical pin has multiple specified physical - //pinlocations (i.e. appears on multiple sides of the block) + // Number of *physical* pins on each side. + // Note that his may be more than the logical number of pins (i.e. + // Type->num_pins) if a logical pin has multiple specified physical + // pinlocations (i.e. appears on multiple sides of the block) auto num_dir = vtr::NdMatrix({ - size_t(tile_type->width), //[0..width-1] - size_t(tile_type->height), //[0..height-1] - size_t(grid.get_num_layers()), //[0..layer-1] - NUM_2D_SIDES //[0..NUM_2D_SIDES-1] + size_t(tile_type->width), // [0..width-1] + size_t(tile_type->height), // [0..height-1] + size_t(grid.get_num_layers()), // [0..layer-1] + NUM_2D_SIDES // [0..NUM_2D_SIDES-1] }, 0); - //List of *physical* pins of the correct type on each side of the current - //block type. For a specific width/height/side the valid enteries in the - //last dimension are [0 .. num_dir[width][height][side]-1] + // List of *physical* pins of the correct type on each side of the current + // block type. For a specific width/height/side the valid enteries in the + // last dimension are [0 .. num_dir[width][height][side]-1] // //Max possible space alloced for simplicity auto dir_list = vtr::NdMatrix({ - size_t(tile_type->width), //[0..width-1] - size_t(tile_type->height), //[0..height-1] - size_t(grid.get_num_layers()), //[0..layer-1] - NUM_2D_SIDES, //[0..NUM_2D_SIDES-1] - size_t(tile_type->num_pins) * size_t(grid.get_num_layers()) //[0..num_pins * num_layers-1] + size_t(tile_type->width), // [0..width-1] + size_t(tile_type->height), // [0..height-1] + size_t(grid.get_num_layers()), // [0..layer-1] + NUM_2D_SIDES, // [0..NUM_2D_SIDES-1] + size_t(tile_type->num_pins) * size_t(grid.get_num_layers()) // [0..num_pins * num_layers-1] }, - -1); //Defensive coding: Initialize to invalid + -1); // Defensive coding: Initialize to invalid - //Number of currently assigned physical pins + // Number of currently assigned physical pins auto num_done_per_dir = vtr::NdMatrix({ - size_t(tile_type->width), //[0..width-1] - size_t(tile_type->height), //[0..height-1] - size_t(grid.get_num_layers()), //[0..layer-1] - NUM_2D_SIDES //[0..NUM_2D_SIDES-1] + size_t(tile_type->width), // [0..width-1] + size_t(tile_type->height), // [0..height-1] + size_t(grid.get_num_layers()), // [0..layer-1] + NUM_2D_SIDES // [0..NUM_2D_SIDES-1] }, 0); - //Record the physical pin locations and counts per side/offsets combination + // Record the physical pin locations and counts per side/offsets combination for (int pin = 0; pin < tile_type->num_pins; ++pin) { auto curr_pin_type = get_pin_type_from_pin_physical_num(tile_type, pin); if (curr_pin_type != pin_type) /* Doing either ipins OR opins */ @@ -3469,7 +3465,7 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin } } - //Total the number of physical pins + // Total the number of physical pins std::vector num_phys_pins_per_layer; for (int layer = 0; layer < grid.get_num_layers(); layer++) { int num_phys_pins = 0; @@ -3485,13 +3481,10 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin std::vector pin_ordering; - /* Connection block I use distributes pins evenly across the tracks * - * of ALL sides of the clb at once. Ensures that each pin connects * - * to spaced out tracks in its connection block, and that the other * - * pins (potentially in other C blocks) connect to the remaining tracks * - * first. Doesn't matter for large Fc, but should make a fairly * - * good low Fc block that leverages the fact that usually lots of pins * - * are logically equivalent. */ + // Connection block I use distributes pins evenly across the tracks of ALL sides of the clb at once. + // Ensures that each pin connects to spaced out tracks in its connection block, and that the other pins + // (potentially in other C blocks) connect to the remaining tracks first. Doesn't matter for large Fc, + // but should make a fairly good low Fc block that leverages the fact that usually lots of pins are logically equivalent. for (int layer_index = 0; layer_index < grid.get_num_layers(); layer_index++) { const e_side init_side = LEFT; @@ -3504,12 +3497,12 @@ static vtr::NdMatrix alloc_and_load_pin_to_seg_type(const e_pin_type pin int pin = 0; int pin_index = -1; - //Determine the order in which physical pins will be considered while building - //the connection block. This generally tries to order the pins so they are 'spread' - //out (in hopes of yielding good connection diversity) + // Determine the order in which physical pins will be considered while building + // the connection block. This generally tries to order the pins so they are 'spread' + // out (in hopes of yielding good connection diversity) while (pin < num_phys_pins_per_layer[layer_index]) { if (height == init_height && width == init_width && side == init_side) { - //Completed one loop through all the possible offsets/side combinations + // Completed one loop through all the possible offsets/side combinations pin_index++; } @@ -4147,7 +4140,7 @@ static void build_unidir_rr_opins(RRGraphBuilder& rr_graph_builder, int chan = (vert ? (j) : (i)); int seg = (vert ? (i) : (j)); int max_len = (vert ? grid.width() : grid.height()); - e_parallel_axis wanted_axis = chan_type == e_rr_type::CHANX ? X_AXIS : Y_AXIS; + e_parallel_axis wanted_axis = chan_type == e_rr_type::CHANX ? e_parallel_axis::X_AXIS : e_parallel_axis::Y_AXIS; int seg_index = get_parallel_seg_index(iseg, seg_index_map, wanted_axis); /*The segment at index iseg doesn't have the proper adjacency so skip building Fc_out conenctions for it*/ @@ -4202,93 +4195,6 @@ static void build_unidir_rr_opins(RRGraphBuilder& rr_graph_builder, } } -/** - * Parse out which CLB pins should connect directly to which other CLB pins then store that in a clb_to_clb_directs data structure - * This data structure supplements the the info in the "directs" data structure - * TODO: The function that does this parsing in placement is poorly done because it lacks generality on heterogeniety, should replace with this one - */ -static std::vector alloc_and_load_clb_to_clb_directs(const std::vector& directs, - int delayless_switch) { - const auto& device_ctx = g_vpr_ctx.device(); - - const int num_directs = directs.size(); - std::vector clb_to_clb_directs(num_directs); - - for (int i = 0; i < num_directs; i++) { - //clb_to_clb_directs[i].from_clb_type; - clb_to_clb_directs[i].from_clb_pin_start_index = 0; - clb_to_clb_directs[i].from_clb_pin_end_index = 0; - //clb_to_clb_directs[i].to_clb_type; - clb_to_clb_directs[i].to_clb_pin_start_index = 0; - clb_to_clb_directs[i].to_clb_pin_end_index = 0; - clb_to_clb_directs[i].switch_index = 0; - - // Load from pins - // Parse out the pb_type name, port name, and pin range - auto [start_pin_index, end_pin_index, tile_name, port_name] = parse_direct_pin_name(directs[i].from_pin, directs[i].line); - - // Figure out which type, port, and pin is used - t_physical_tile_type_ptr physical_tile = find_tile_type_by_name(tile_name, device_ctx.physical_tile_types); - if (physical_tile == nullptr) { - VPR_THROW(VPR_ERROR_ARCH, "Unable to find block %s.\n", tile_name.c_str()); - } - clb_to_clb_directs[i].from_clb_type = physical_tile; - - t_physical_tile_port tile_port = find_tile_port_by_name(physical_tile, port_name); - - if (start_pin_index == OPEN) { - VTR_ASSERT(start_pin_index == end_pin_index); - start_pin_index = 0; - end_pin_index = tile_port.num_pins - 1; - } - - // Add clb directs start/end pin indices based on the absolute pin position - // of the port defined in the direct connection. The CLB is the source one. - clb_to_clb_directs[i].from_clb_pin_start_index = tile_port.absolute_first_pin_index + start_pin_index; - clb_to_clb_directs[i].from_clb_pin_end_index = tile_port.absolute_first_pin_index + end_pin_index; - - // Load to pins - // Parse out the pb_type name, port name, and pin range - std::tie(start_pin_index, end_pin_index, tile_name, port_name) = parse_direct_pin_name(directs[i].to_pin, directs[i].line); - - // Figure out which type, port, and pin is used - physical_tile = find_tile_type_by_name(tile_name, device_ctx.physical_tile_types); - if (physical_tile == nullptr) { - VPR_THROW(VPR_ERROR_ARCH, "Unable to find block %s.\n", tile_name.c_str()); - } - clb_to_clb_directs[i].to_clb_type = physical_tile; - - tile_port = find_tile_port_by_name(physical_tile, port_name); - - if (start_pin_index == OPEN) { - VTR_ASSERT(start_pin_index == end_pin_index); - start_pin_index = 0; - end_pin_index = tile_port.num_pins - 1; - } - - // Add clb directs start/end pin indices based on the absolute pin position - // of the port defined in the direct connection. The CLB is the destination one. - clb_to_clb_directs[i].to_clb_pin_start_index = tile_port.absolute_first_pin_index + start_pin_index; - clb_to_clb_directs[i].to_clb_pin_end_index = tile_port.absolute_first_pin_index + end_pin_index; - - if (abs(clb_to_clb_directs[i].from_clb_pin_start_index - clb_to_clb_directs[i].from_clb_pin_end_index) != abs(clb_to_clb_directs[i].to_clb_pin_start_index - clb_to_clb_directs[i].to_clb_pin_end_index)) { - vpr_throw(VPR_ERROR_ARCH, get_arch_file_name(), directs[i].line, - "Range mismatch from %s to %s.\n", directs[i].from_pin.c_str(), directs[i].to_pin.c_str()); - } - - //Set the switch index - if (directs[i].switch_type > 0) { - //Use the specified switch - clb_to_clb_directs[i].switch_index = directs[i].switch_type; - } else { - //Use the delayless switch by default - clb_to_clb_directs[i].switch_index = delayless_switch; - } - } - - return clb_to_clb_directs; -} - /* Add all direct clb-pin-to-clb-pin edges to given opin * * The current opin is located at (layer,x,y) along the specified side @@ -4321,19 +4227,18 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder, VTR_ASSERT(z >= 0 && z < curr_type->capacity); const int num_directs = directs.size(); - /* Iterate through all direct connections */ + // Iterate through all direct connections for (int i = 0; i < num_directs; i++) { - /* Find matching direct clb-to-clb connections with the same type as current grid location */ + // Find matching direct clb-to-clb connections with the same type as current grid location if (clb_to_clb_directs[i].from_clb_type == curr_type) { //We are at a valid starting point - if (directs[i].from_side != NUM_2D_SIDES && directs[i].from_side != side) continue; - //Offset must be in range + // Offset must be in range if (x + directs[i].x_offset < int(device_ctx.grid.width() - 1) && x + directs[i].x_offset > 0 && y + directs[i].y_offset < int(device_ctx.grid.height() - 1) && y + directs[i].y_offset > 0) { - //Only add connections if the target clb type matches the type in the direct specification + // Only add connections if the target clb type matches the type in the direct specification t_physical_tile_type_ptr target_type = device_ctx.grid.get_physical_type({x + directs[i].x_offset, y + directs[i].y_offset, layer}); @@ -4341,7 +4246,7 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder, if (clb_to_clb_directs[i].to_clb_type == target_type && z + directs[i].sub_tile_offset < int(target_type->capacity) && z + directs[i].sub_tile_offset >= 0) { - /* Compute index of opin with regards to given pins */ + // Compute index of opin with regards to given pins int max_index = OPEN, min_index = OPEN; bool swap = false; if (clb_to_clb_directs[i].from_clb_pin_start_index > clb_to_clb_directs[i].from_clb_pin_end_index) { @@ -4356,7 +4261,7 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder, if (max_index >= relative_opin && min_index <= relative_opin) { int offset = relative_opin - min_index; - /* This opin is specified to connect directly to an ipin, now compute which ipin to connect to */ + // This opin is specified to connect directly to an ipin, now compute which ipin to connect to int relative_ipin = OPEN; if (clb_to_clb_directs[i].to_clb_pin_start_index > clb_to_clb_directs[i].to_clb_pin_end_index) { if (swap) { @@ -4387,11 +4292,11 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder, VTR_ASSERT(target_sub_tile != nullptr); if (relative_ipin >= target_sub_tile->num_phy_pins) continue; - //If this block has capacity > 1 then the pins of z position > 0 are offset - //by the number of pins per capacity instance + // If this block has capacity > 1 then the pins of z position > 0 are offset + // by the number of pins per capacity instance int ipin = get_physical_pin_from_capacity_location(target_type, relative_ipin, target_cap); - /* Add new ipin edge to list of edges */ + // Add new ipin edge to list of edges std::vector inodes; if (directs[i].to_side != NUM_2D_SIDES) { @@ -4406,10 +4311,10 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder, } if (inodes.size() > 0) { - //There may be multiple physical pins corresponding to the logical - //target ipin. We only need to connect to one of them (since the physical pins - //are logically equivalent). This also ensures the graphics look reasonable and map - //back fairly directly to the architecture file in the case of pin equivalence + // There may be multiple physical pins corresponding to the logical + // target ipin. We only need to connect to one of them (since the physical pins + // are logically equivalent). This also ensures the graphics look reasonable and map + // back fairly directly to the architecture file in the case of pin equivalence RRNodeId inode = pick_best_direct_connect_target_rr_node(rr_graph, from_rr_node, inodes); rr_edges_to_create.emplace_back(from_rr_node, inode, clb_to_clb_directs[i].switch_index, false); @@ -4422,10 +4327,7 @@ static int get_opin_direct_connections(RRGraphBuilder& rr_graph_builder, } return num_pins; } -/* Determines whether the output pins of the specified block type should be perturbed. * - * This is to prevent pathological cases where the output pin connections are * - * spaced such that the connection pattern always skips some types of wire (w.r.t. * - * starting points) */ + static std::vector alloc_and_load_perturb_opins(const t_physical_tile_type_ptr type, const vtr::Matrix& Fc_out, const int max_chan_width, diff --git a/vpr/src/route/rr_graph_generation/rr_graph.h b/vpr/src/route/rr_graph_generation/rr_graph.h index c0bb1f1bb36..475e64b1502 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph.h +++ b/vpr/src/route/rr_graph_generation/rr_graph.h @@ -9,6 +9,7 @@ #include "vpr_types.h" #include "rr_graph_builder.h" #include "rr_graph_type.h" +#include "clb2clb_directs.h" /* Warnings about the routing graph that can be returned. * This is to avoid output messages during a value sweep */ @@ -45,6 +46,31 @@ void free_rr_graph(); t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_sw_inf, const float R_minW_nmos, const float R_minW_pmos); + +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map& arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch); + +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type); + +std::vector> alloc_and_load_actual_fc(const std::vector& types, + const int max_pins, + const std::vector& segment_inf, + const std::vector& sets_per_seg_type, + const t_chan_width* nodes_per_chan, + const e_fc_type fc_type, + const enum e_directionality directionality, + bool* Fc_clipped, + bool is_flat); + // Sets the spec for the rr_switch based on the arch switch void load_rr_switch_from_arch_switch(RRGraphBuilder& rr_graph_builder, const std::map& arch_sw_inf, diff --git a/vpr/src/route/rr_graph_generation/rr_graph2.cpp b/vpr/src/route/rr_graph_generation/rr_graph2.cpp index a8b65050a92..89a7e19a0f4 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph2.cpp +++ b/vpr/src/route/rr_graph_generation/rr_graph2.cpp @@ -438,8 +438,8 @@ void alloc_and_load_chan_details(const DeviceGrid& grid, const std::vector& seg_details_y, t_chan_details& chan_details_x, t_chan_details& chan_details_y) { - chan_details_x = init_chan_details(grid, nodes_per_chan, seg_details_x, X_AXIS); - chan_details_y = init_chan_details(grid, nodes_per_chan, seg_details_y, Y_AXIS); + chan_details_x = init_chan_details(grid, nodes_per_chan, seg_details_x, e_parallel_axis::X_AXIS); + chan_details_y = init_chan_details(grid, nodes_per_chan, seg_details_y, e_parallel_axis::Y_AXIS); /* Adjust segment start/end based on obstructed channels, if any */ adjust_chan_details(grid, nodes_per_chan, chan_details_x, chan_details_y); @@ -448,11 +448,11 @@ void alloc_and_load_chan_details(const DeviceGrid& grid, t_chan_details init_chan_details(const DeviceGrid& grid, const t_chan_width& nodes_per_chan, const std::vector& seg_details, - const enum e_parallel_axis seg_parallel_axis) { + const e_parallel_axis seg_parallel_axis) { const int num_seg_details = (int)seg_details.size(); - if (seg_parallel_axis == X_AXIS) { + if (seg_parallel_axis == e_parallel_axis::X_AXIS) { VTR_ASSERT(num_seg_details <= nodes_per_chan.x_max); - } else if (seg_parallel_axis == Y_AXIS) { + } else if (seg_parallel_axis == e_parallel_axis::Y_AXIS) { VTR_ASSERT(num_seg_details <= nodes_per_chan.y_max); } @@ -467,10 +467,10 @@ t_chan_details init_chan_details(const DeviceGrid& grid, int seg_start = -1; int seg_end = -1; - if (seg_parallel_axis == X_AXIS) { + if (seg_parallel_axis == e_parallel_axis::X_AXIS) { seg_start = get_seg_start(p_seg_details, i, y, x); seg_end = get_seg_end(p_seg_details, i, seg_start, y, grid.width() - 2); //-2 for no perim channels - } else if (seg_parallel_axis == Y_AXIS) { + } else if (seg_parallel_axis == e_parallel_axis::Y_AXIS) { seg_start = get_seg_start(p_seg_details, i, x, y); seg_end = get_seg_end(p_seg_details, i, seg_start, x, grid.height() - 2); //-2 for no perim channels } @@ -478,11 +478,11 @@ t_chan_details init_chan_details(const DeviceGrid& grid, p_seg_details[i].set_seg_start(seg_start); p_seg_details[i].set_seg_end(seg_end); - if (seg_parallel_axis == X_AXIS) { + if (seg_parallel_axis == e_parallel_axis::X_AXIS) { if (i >= nodes_per_chan.x_list[y]) { p_seg_details[i].set_length(0); } - } else if (seg_parallel_axis == Y_AXIS) { + } else if (seg_parallel_axis == e_parallel_axis::Y_AXIS) { if (i >= nodes_per_chan.y_list[x]) { p_seg_details[i].set_length(0); } @@ -505,7 +505,7 @@ void adjust_chan_details(const DeviceGrid& grid, continue; adjust_seg_details(x, y, grid, nodes_per_chan, - chan_details_x, X_AXIS); + chan_details_x, e_parallel_axis::X_AXIS); } } @@ -517,7 +517,7 @@ void adjust_chan_details(const DeviceGrid& grid, continue; adjust_seg_details(x, y, grid, nodes_per_chan, - chan_details_y, Y_AXIS); + chan_details_y, e_parallel_axis::Y_AXIS); } } } @@ -527,43 +527,43 @@ void adjust_seg_details(const int x, const DeviceGrid& grid, const t_chan_width& nodes_per_chan, t_chan_details& chan_details, - const enum e_parallel_axis seg_parallel_axis) { - int seg_index = (seg_parallel_axis == X_AXIS ? x : y); + const e_parallel_axis seg_parallel_axis) { + int seg_index = (seg_parallel_axis == e_parallel_axis::X_AXIS ? x : y); int max_chan_width = 0; - if (seg_parallel_axis == X_AXIS) { + if (seg_parallel_axis == e_parallel_axis::X_AXIS) { max_chan_width = nodes_per_chan.x_max; - } else if (seg_parallel_axis == Y_AXIS) { + } else if (seg_parallel_axis == e_parallel_axis::Y_AXIS) { max_chan_width = nodes_per_chan.y_max; } else { - VTR_ASSERT(seg_parallel_axis == BOTH_AXIS); + VTR_ASSERT(seg_parallel_axis == e_parallel_axis::BOTH_AXIS); max_chan_width = nodes_per_chan.max; } for (int track = 0; track < max_chan_width; ++track) { - int lx = (seg_parallel_axis == X_AXIS ? x - 1 : x); - int ly = (seg_parallel_axis == X_AXIS ? y : y - 1); + int lx = (seg_parallel_axis == e_parallel_axis::X_AXIS ? x - 1 : x); + int ly = (seg_parallel_axis == e_parallel_axis::X_AXIS ? y : y - 1); if (lx < 0 || ly < 0 || chan_details[lx][ly][track].length() == 0) continue; while (chan_details[lx][ly][track].seg_end() >= seg_index) { chan_details[lx][ly][track].set_seg_end(seg_index - 1); - lx = (seg_parallel_axis == X_AXIS ? lx - 1 : lx); - ly = (seg_parallel_axis == X_AXIS ? ly : ly - 1); + lx = (seg_parallel_axis == e_parallel_axis::X_AXIS ? lx - 1 : lx); + ly = (seg_parallel_axis == e_parallel_axis::X_AXIS ? ly : ly - 1); if (lx < 0 || ly < 0 || chan_details[lx][ly][track].length() == 0) break; } } for (int track = 0; track < max_chan_width; ++track) { - size_t lx = (seg_parallel_axis == X_AXIS ? x + 1 : x); - size_t ly = (seg_parallel_axis == X_AXIS ? y : y + 1); + size_t lx = (seg_parallel_axis == e_parallel_axis::X_AXIS ? x + 1 : x); + size_t ly = (seg_parallel_axis == e_parallel_axis::X_AXIS ? y : y + 1); if (lx > grid.width() - 2 || ly > grid.height() - 2 || chan_details[lx][ly][track].length() == 0) //-2 for no perim channels continue; while (chan_details[lx][ly][track].seg_start() <= seg_index) { chan_details[lx][ly][track].set_seg_start(seg_index + 1); - lx = (seg_parallel_axis == X_AXIS ? lx + 1 : lx); - ly = (seg_parallel_axis == X_AXIS ? ly : ly + 1); + lx = (seg_parallel_axis == e_parallel_axis::X_AXIS ? lx + 1 : lx); + ly = (seg_parallel_axis == e_parallel_axis::X_AXIS ? ly : ly + 1); if (lx > grid.width() - 2 || ly > grid.height() - 2 || chan_details[lx][ly][track].length() == 0) //-2 for no perim channels break; } diff --git a/vpr/src/route/rr_graph_generation/rr_graph2.h b/vpr/src/route/rr_graph_generation/rr_graph2.h index 63df666f2a8..87c3024f352 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph2.h +++ b/vpr/src/route/rr_graph_generation/rr_graph2.h @@ -39,7 +39,7 @@ void alloc_and_load_chan_details(const DeviceGrid& grid, t_chan_details init_chan_details(const DeviceGrid& grid, const t_chan_width& nodes_per_chan, const std::vector& seg_details, - const enum e_parallel_axis seg_details_type); + const e_parallel_axis seg_details_type); void adjust_chan_details(const DeviceGrid& grid, const t_chan_width& nodes_per_chan, @@ -51,7 +51,7 @@ void adjust_seg_details(const int x, const DeviceGrid& grid, const t_chan_width& nodes_per_chan, t_chan_details& chan_details, - const enum e_parallel_axis seg_details_type); + const e_parallel_axis seg_details_type); int get_seg_start(const t_chan_seg_details* seg_details, const int itrack, diff --git a/vpr/src/route/rr_graph_generation/rr_graph_area.cpp b/vpr/src/route/rr_graph_generation/rr_graph_area.cpp index eaafb6669e3..6c8f6b1b9c9 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph_area.cpp +++ b/vpr/src/route/rr_graph_generation/rr_graph_area.cpp @@ -362,6 +362,7 @@ void count_unidir_routing_transistors(std::vector& /*segment_inf* from_rr_type = rr_graph.node_type(from_rr_node); switch (from_rr_type) { + case e_rr_type::MUX: case e_rr_type::CHANX: case e_rr_type::CHANY: case e_rr_type::CHANZ: @@ -378,6 +379,7 @@ void count_unidir_routing_transistors(std::vector& /*segment_inf* } switch (to_rr_type) { + case e_rr_type::MUX: case e_rr_type::CHANX: case e_rr_type::CHANY: case e_rr_type::CHANZ: @@ -451,7 +453,7 @@ void count_unidir_routing_transistors(std::vector& /*segment_inf* for (i = rr_graph.node_xlow(from_rr_node); i <= rr_graph.node_xhigh(from_rr_node); i++) cblock_counted[i] = false; - } else { /* CHANY */ + } else if (from_rr_type == e_rr_type::CHANY) { for (j = rr_graph.node_ylow(from_rr_node); j <= rr_graph.node_yhigh(from_rr_node); j++) cblock_counted[j] = false; diff --git a/vpr/src/route/rr_graph_generation/rr_node_indices.cpp b/vpr/src/route/rr_graph_generation/rr_node_indices.cpp index 54760fa96d0..c1340e8d3b6 100644 --- a/vpr/src/route/rr_graph_generation/rr_node_indices.cpp +++ b/vpr/src/route/rr_graph_generation/rr_node_indices.cpp @@ -553,7 +553,7 @@ bool verify_rr_node_indices(const DeviceGrid& grid, describe_rr_node(rr_graph, grid, rr_indexed_data, inode, is_flat).c_str()); } - } else if (rr_graph.node_type(inode) == e_rr_type::SOURCE || rr_graph.node_type(inode) == e_rr_type::SINK) { + } else if (rr_graph.node_type(inode) == e_rr_type::SOURCE || rr_graph.node_type(inode) == e_rr_type::SINK || rr_graph.node_type(inode) == e_rr_type::MUX) { // Sources have co-ordinates covering the entire block they are in, but not sinks if (!rr_graph.x_in_node_range(x, inode)) { VPR_ERROR(VPR_ERROR_ROUTE, "RR node x positions do not agree between rr_nodes (%d <-> %d) and rr_node_indices (%d): %s", diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/chan_node_details.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/chan_node_details.cpp new file mode 100644 index 00000000000..6d420464c3c --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/chan_node_details.cpp @@ -0,0 +1,303 @@ +/************************************************************************ + * This file contains member functions for class ChanNodeDetails + ***********************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" + +#include "chan_node_details.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +ChanNodeDetails::ChanNodeDetails(const ChanNodeDetails& src) { + /* duplicate */ + size_t chan_width = src.get_chan_width(); + this->reserve(chan_width); + for (size_t itrack = 0; itrack < chan_width; ++itrack) { + track_node_ids_.push_back(src.get_track_node_id(itrack)); + track_direction_.push_back(src.get_track_direction(itrack)); + seg_ids_.push_back(src.get_track_segment_id(itrack)); + seg_length_.push_back(src.get_track_segment_length(itrack)); + track_start_.push_back(src.is_track_start(itrack)); + track_end_.push_back(src.is_track_end(itrack)); + } +} + +ChanNodeDetails::ChanNodeDetails() { + this->clear(); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +size_t ChanNodeDetails::get_chan_width() const { + VTR_ASSERT(validate_chan_width()); + return track_node_ids_.size(); +} + +size_t ChanNodeDetails::get_track_node_id(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_node_ids_[track_id]; +} + +/* Return a copy of vector */ +std::vector ChanNodeDetails::get_track_node_ids() const { + std::vector copy; + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + copy.push_back(track_node_ids_[inode]); + } + return copy; +} + +Direction ChanNodeDetails::get_track_direction(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_direction_[track_id]; +} + +size_t ChanNodeDetails::get_track_segment_length(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return seg_length_[track_id]; +} + +size_t ChanNodeDetails::get_track_segment_id(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return seg_ids_[track_id]; +} + +bool ChanNodeDetails::is_track_start(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_start_[track_id]; +} + +bool ChanNodeDetails::is_track_end(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_end_[track_id]; +} + +size_t ChanNodeDetails::get_track_bend_start(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_bend_start_[track_id]; +} + +size_t ChanNodeDetails::get_track_bend_end(const size_t& track_id) const { + VTR_ASSERT(validate_track_id(track_id)); + return track_bend_end_[track_id]; +} + +/* Track_id is the starting point of group (whose is_start should be true) + * This function will try to find the track_ids with the same directionality as track_id and seg_length + * A group size is the number of such nodes between the starting points (include the 1st starting point) + */ +std::vector ChanNodeDetails::get_seg_group(const size_t& track_id) const { + VTR_ASSERT(validate_chan_width()); + VTR_ASSERT(validate_track_id(track_id)); + VTR_ASSERT(is_track_start(track_id)); + + std::vector group; + /* Make sure a clean start */ + group.clear(); + + for (size_t itrack = track_id; itrack < get_chan_width(); ++itrack) { + if ((get_track_direction(itrack) != get_track_direction(track_id)) + || (get_track_segment_id(itrack) != get_track_segment_id(track_id))) { + /* Bypass any nodes in different direction and segment information*/ + continue; + } + if ((false == is_track_start(itrack)) + || ((true == is_track_start(itrack)) && (itrack == track_id))) { + group.push_back(itrack); + continue; + } + /* Stop if this another starting point */ + if (true == is_track_start(itrack)) { + break; + } + } + return group; +} + +/* Get a list of track_ids with the given list of track indices */ +std::vector ChanNodeDetails::get_seg_group_node_id(const std::vector& seg_group) const { + std::vector group; + /* Make sure a clean start */ + group.clear(); + + for (size_t id = 0; id < seg_group.size(); ++id) { + VTR_ASSERT(validate_track_id(seg_group[id])); + group.push_back(get_track_node_id(seg_group[id])); + } + + return group; +} + +/* Get the number of tracks that starts in this routing channel */ +size_t ChanNodeDetails::get_num_starting_tracks(const Direction& track_direction) const { + size_t counter = 0; + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass unmatched track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + if (false == is_track_start(itrack)) { + continue; + } + counter++; + } + return counter; +} + +/* Get the number of tracks that ends in this routing channel */ +size_t ChanNodeDetails::get_num_ending_tracks(const Direction& track_direction) const { + size_t counter = 0; + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass unmatched track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + if (false == is_track_end(itrack)) { + continue; + } + counter++; + } + return counter; +} + +/************************************************************************ + * Mutators + ***********************************************************************/ +/* Reserve the capacitcy of vectors */ +void ChanNodeDetails::reserve(const size_t& chan_width) { + track_node_ids_.reserve(chan_width); + track_direction_.reserve(chan_width); + seg_length_.reserve(chan_width); + seg_ids_.reserve(chan_width); + track_start_.reserve(chan_width); + track_end_.reserve(chan_width); +} + +/* Add a track to the channel */ +void ChanNodeDetails::add_track(const size_t& track_node_id, const Direction& track_direction, const size_t& seg_id, const size_t& seg_length, const size_t& is_start, const size_t& is_end, const size_t& seg_bend_start, const size_t& seg_bend_end) { + track_node_ids_.push_back(track_node_id); + track_direction_.push_back(track_direction); + seg_ids_.push_back(seg_id); + seg_length_.push_back(seg_length); + track_start_.push_back(is_start); + track_end_.push_back(is_end); + track_bend_start_.push_back(seg_bend_start); + track_bend_end_.push_back(seg_bend_end); +} + +/* Update the node_id of a given track */ +void ChanNodeDetails::set_track_node_id(const size_t& track_index, const size_t& track_node_id) { + VTR_ASSERT(validate_track_id(track_index)); + track_node_ids_[track_index] = track_node_id; +} + +/* Update the node_ids from a vector */ +void ChanNodeDetails::set_track_node_ids(const std::vector& track_node_ids) { + /* the size of vector should match chan_width */ + VTR_ASSERT(get_chan_width() == track_node_ids.size()); + for (size_t inode = 0; inode < track_node_ids.size(); ++inode) { + track_node_ids_[inode] = track_node_ids[inode]; + } +} + +/* Set tracks with a given direction to start */ +void ChanNodeDetails::set_tracks_start(const Direction& track_direction) { + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Bypass non-match tracks */ + if (track_direction != get_track_direction(inode)) { + continue; /* Pass condition*/ + } + track_start_[inode] = true; + } +} + +/* Set tracks with a given direction to end */ +void ChanNodeDetails::set_tracks_end(const Direction& track_direction) { + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Bypass non-match tracks */ + if (track_direction != get_track_direction(inode)) { + continue; /* Pass condition*/ + } + track_end_[inode] = true; + } +} + +/* rotate the track_node_id by an offset */ +void ChanNodeDetails::rotate_track_node_id(const size_t& offset, const Direction& track_direction, const bool& counter_rotate) { + /* Direct return if offset = 0*/ + if (0 == offset) { + return; + } + + /* Rotate the node_ids by groups + * A group begins from a track_start and ends before another track_start + */ + VTR_ASSERT(validate_chan_width()); + for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) { + /* Bypass non-start segment */ + if (false == is_track_start(itrack)) { + continue; + } + /* Bypass segments do not match track_direction */ + if (track_direction != get_track_direction(itrack)) { + continue; + } + /* Find the group nodes */ + std::vector track_group = get_seg_group(itrack); + /* Build a vector of the node ids of the tracks */ + std::vector track_group_node_id = get_seg_group_node_id(track_group); + /* adapt offset to the range of track_group_node_id */ + size_t actual_offset = offset % track_group_node_id.size(); + /* Rotate or Counter rotate */ + if (true == counter_rotate) { + std::rotate(track_group_node_id.rbegin(), track_group_node_id.rbegin() + actual_offset, track_group_node_id.rend()); + } else { + std::rotate(track_group_node_id.begin(), track_group_node_id.begin() + actual_offset, track_group_node_id.end()); + } + /* Update the node_ids */ + for (size_t inode = 0; inode < track_group.size(); ++inode) { + track_node_ids_[track_group[inode]] = track_group_node_id[inode]; + } + } + return; +} + +void ChanNodeDetails::clear() { + track_node_ids_.clear(); + track_direction_.clear(); + seg_ids_.clear(); + seg_length_.clear(); + track_start_.clear(); + track_end_.clear(); +} + +/************************************************************************ + * Validators + ***********************************************************************/ +bool ChanNodeDetails::validate_chan_width() const { + size_t chan_width = track_node_ids_.size(); + if ((chan_width == track_direction_.size()) + && (chan_width == seg_ids_.size()) + && (chan_width == seg_length_.size()) + && (chan_width == track_start_.size()) + && (chan_width == track_end_.size())) { + return true; + } + return false; +} + +bool ChanNodeDetails::validate_track_id(const size_t& track_id) const { + if ((track_id < track_node_ids_.size()) + && (track_id < track_direction_.size()) + && (track_id < seg_ids_.size()) + && (track_id < seg_length_.size()) + && (track_id < track_start_.size()) + && (track_id < track_end_.size())) { + return true; + } + return false; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/chan_node_details.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/chan_node_details.h new file mode 100644 index 00000000000..75548e0912b --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/chan_node_details.h @@ -0,0 +1,74 @@ +#pragma once + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "vpr_types.h" +#include "rr_node_types.h" +#include "tileable_rr_graph_types.h" + +/************************************************************************ + * This file contains a class to model the details of routing node + * in a channel: + * 1. segment information: length, frequency etc. + * 2. starting point of segment + * 3. ending point of segment + * 4. potentail track_id(ptc_num) of each segment + ***********************************************************************/ + +/************************************************************************ + * ChanNodeDetails records segment length, directionality and starting of routing tracks + * +---------------------------------+ + * | Index | Direction | Start Point | + * +---------------------------------+ + * | 0 | --------> | Yes | + * +---------------------------------+ + ***********************************************************************/ + +class ChanNodeDetails { + public: /* Constructor */ + ChanNodeDetails(const ChanNodeDetails&); /* Duplication */ + ChanNodeDetails(); /* Initilization */ + public: /* Accessors */ + size_t get_chan_width() const; + size_t get_track_node_id(const size_t& track_id) const; + std::vector get_track_node_ids() const; + Direction get_track_direction(const size_t& track_id) const; + size_t get_track_segment_length(const size_t& track_id) const; + size_t get_track_segment_id(const size_t& track_id) const; + bool is_track_start(const size_t& track_id) const; + bool is_track_end(const size_t& track_id) const; + size_t get_track_bend_start(const size_t& track_id) const; + size_t get_track_bend_end(const size_t& track_id) const; + std::vector get_seg_group(const size_t& track_id) const; + std::vector get_seg_group_node_id(const std::vector& seg_group) const; + size_t get_num_starting_tracks(const Direction& track_direction) const; + size_t get_num_ending_tracks(const Direction& track_direction) const; + + public: /* Mutators */ + void reserve(const size_t& chan_width); /* Reserve the capacitcy of vectors */ + void add_track(const size_t& track_node_id, const Direction& track_direction, const size_t& seg_id, const size_t& seg_length, const size_t& is_start, const size_t& is_end, const size_t& seg_bend_start = 0, const size_t& seg_bend_end = 0); + void set_track_node_id(const size_t& track_index, const size_t& track_node_id); + void set_track_node_ids(const std::vector& track_node_ids); + void set_tracks_start(const Direction& track_direction); + void set_tracks_end(const Direction& track_direction); + void rotate_track_node_id(const size_t& offset, + const Direction& track_direction, + const bool& counter_rotate); /* rotate the track_node_id by an offset */ + void clear(); + + private: /* validators */ + bool validate_chan_width() const; + bool validate_track_id(const size_t& track_id) const; + + private: /* Internal data */ + std::vector track_node_ids_; /* indices of each track */ + std::vector track_direction_; /* direction of each track */ + std::vector seg_ids_; /* id of segment of each track */ + std::vector seg_length_; /* Length of each segment */ + std::vector track_start_; /* flag to identify if this is the starting point of the track */ + std::vector track_end_; /* flag to identify if this is the ending point of the track */ + std::vector track_bend_start_; /* flag to identify if this is the starting point of the track after bend. 0 means it is not a bend start. Int number means the corresponding bend group */ + std::vector track_bend_end_; +}; diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/device_grid_annotation.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/device_grid_annotation.cpp new file mode 100644 index 00000000000..66ce4aa0e6c --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/device_grid_annotation.cpp @@ -0,0 +1,106 @@ +#include "device_grid_annotation.h" +#include "vtr_log.h" +#include "vpr_utils.h" + +DeviceGridAnnotation::DeviceGridAnnotation(const DeviceGrid& grid, const bool perimeter_cb) { + alloc(grid); + init(grid, perimeter_cb); +} + +void DeviceGridAnnotation::alloc(const DeviceGrid& grid) { + // Allocate + chanx_existence_.resize({grid.width(), grid.height()}, false); + chany_existence_.resize({grid.width(), grid.height()}, false); +} + +void DeviceGridAnnotation::init(const DeviceGrid& grid, const bool perimeter_cb) { + // If shrink is not considered, perimeters are the borderlines + size_t start_x = 1; + size_t end_x = grid.width() - 1; + if (perimeter_cb) { + start_x = 0; + end_x = grid.width(); + } + for (size_t iy = 0; iy < grid.height() - 1; ++iy) { + for (size_t ix = start_x; ix < end_x; ++ix) { + chanx_existence_[ix][iy] = !is_empty_type(grid.get_physical_type({(int)ix, (int)iy + 1, 0})); + } + } + size_t start_y = 1; + size_t end_y = grid.height() - 1; + if (perimeter_cb) { + start_y = 0; + end_y = grid.height(); + } + for (size_t ix = 0; ix < grid.width() - 1; ++ix) { + for (size_t iy = start_y; iy < end_y; ++iy) { + chany_existence_[ix][iy] = !is_empty_type(grid.get_physical_type({(int)ix, (int)iy, 0})); + } + } +} + +bool DeviceGridAnnotation::is_chanx_exist(const vtr::Point& coord) const { + return chanx_existence_[coord.x()][coord.y()]; +} + +bool DeviceGridAnnotation::is_chany_exist(const vtr::Point& coord) const { + return chany_existence_[coord.x()][coord.y()]; +} + +bool DeviceGridAnnotation::is_chanx_start(const vtr::Point& coord) const { + vtr::Point neighbor_coord = get_neighbor_coord(coord, LEFT); + if (neighbor_coord == coord) { + return true; + } + return chanx_existence_[coord.x()][coord.y()] != chanx_existence_[neighbor_coord.x()][neighbor_coord.y()]; +} + +bool DeviceGridAnnotation::is_chanx_end(const vtr::Point& coord) const { + vtr::Point neighbor_coord = get_neighbor_coord(coord, RIGHT); + if (neighbor_coord == coord) { + return true; + } + return chanx_existence_[coord.x()][coord.y()] != chanx_existence_[neighbor_coord.x()][neighbor_coord.y()]; +} + +bool DeviceGridAnnotation::is_chany_start(const vtr::Point& coord) const { + vtr::Point neighbor_coord = get_neighbor_coord(coord, BOTTOM); + if (neighbor_coord == coord) { + return true; + } + return chany_existence_[coord.x()][coord.y()] != chany_existence_[neighbor_coord.x()][neighbor_coord.y()]; +} + +bool DeviceGridAnnotation::is_chany_end(const vtr::Point& coord) const { + vtr::Point neighbor_coord = get_neighbor_coord(coord, TOP); + if (neighbor_coord == coord) { + return true; + } + return chany_existence_[coord.x()][coord.y()] != chany_existence_[neighbor_coord.x()][neighbor_coord.y()]; +} + +vtr::Point DeviceGridAnnotation::get_neighbor_coord(const vtr::Point& coord, const e_side& side) const { + vtr::Point neighbor_coord(coord.x(), coord.y()); + if (side == LEFT) { + if (coord.x() == 0) { + return coord; + } + neighbor_coord.set_x(coord.x() - 1); + } else if (side == RIGHT) { + if (coord.x() == chanx_existence_.dim_size(0) - 1) { + return coord; + } + neighbor_coord.set_x(coord.x() + 1); + } else if (side == TOP) { + if (coord.y() == chanx_existence_.dim_size(1) - 1) { + return coord; + } + neighbor_coord.set_y(coord.y() + 1); + } else if (side == BOTTOM) { + if (coord.y() == 0) { + return coord; + } + neighbor_coord.set_y(coord.y() - 1); + } + return neighbor_coord; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/device_grid_annotation.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/device_grid_annotation.h new file mode 100644 index 00000000000..13d9ab4cbc5 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/device_grid_annotation.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "vtr_geometry.h" +#include "vtr_ndmatrix.h" +#include "physical_types.h" +#include "device_grid.h" + +/** @brief This is the data structure to provide additional data for the device grid: + * - Border of the device grid (check where the empty types cover the perimeters) + */ +class DeviceGridAnnotation { + public: // Constructor + DeviceGridAnnotation(const DeviceGrid& grid, const bool perimeter_cb); + + private: // Private mutators + void alloc(const DeviceGrid& grid); + void init(const DeviceGrid& grid, const bool perimeter_cb); + + public: // Public accessors + /** @brief Check if at a given coordinate, a X-direction routing channel should exist or not */ + bool is_chanx_exist(const vtr::Point& coord) const; + bool is_chanx_start(const vtr::Point& coord) const; + bool is_chanx_end(const vtr::Point& coord) const; + /** @brief Check if at a given coordinate, a Y-direction routing channel should exist or not */ + bool is_chany_exist(const vtr::Point& coord) const; + bool is_chany_start(const vtr::Point& coord) const; + bool is_chany_end(const vtr::Point& coord) const; + + private: // Private validators + vtr::Point get_neighbor_coord(const vtr::Point& coord, const e_side& side) const; + + private: // Internal data + vtr::NdMatrix chanx_existence_; + vtr::NdMatrix chany_existence_; +}; diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_chan.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_chan.cpp new file mode 100644 index 00000000000..fefc6d1e0d3 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_chan.cpp @@ -0,0 +1,182 @@ +/************************************************************************ + * Member functions for class RRChan + ***********************************************************************/ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "rr_chan.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +/* default constructor */ +RRChan::RRChan() { + type_ = e_rr_type::NUM_RR_TYPES; + nodes_.resize(0); + node_segments_.resize(0); +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +e_rr_type RRChan::get_type() const { + return type_; +} + +/* get the number of tracks in this channel */ +size_t RRChan::get_chan_width() const { + return nodes_.size(); +} + +/* get the track_id of a node */ +int RRChan::get_node_track_id(const RRNodeId node) const { + /* if the given node is NULL, we return an invalid id */ + if (RRNodeId::INVALID() == node) { + return -1; + } + /* check each member and return if we find a match in content */ + std::vector::const_iterator it = std::find(nodes_.begin(), nodes_.end(), node); + if (nodes_.end() == it) { + return -1; + } + return it - nodes_.begin(); +} + +/* get the rr_node with the track_id */ +RRNodeId RRChan::get_node(const size_t track_num) const { + if (false == valid_node_id(track_num)) { + return RRNodeId::INVALID(); + } + return nodes_[track_num]; +} + +/* get the segment id of a node */ +RRSegmentId RRChan::get_node_segment(const RRNodeId node) const { + int node_id = get_node_track_id(node); + if (false == valid_node_id(node_id)) { + return RRSegmentId::INVALID(); + } + return get_node_segment(node_id); +} + +/* get the segment id of a node */ +RRSegmentId RRChan::get_node_segment(const size_t track_num) const { + if (false == valid_node_id(track_num)) { + return RRSegmentId::INVALID(); + } + return node_segments_[track_num]; +} + +/* Get a list of segments used in this routing channel */ +std::vector RRChan::get_segment_ids() const { + std::vector seg_list; + + /* Traverse node_segments */ + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Try to find the node_segment id in the list */ + auto it = find(seg_list.begin(), seg_list.end(), node_segments_[inode]); + if (it == seg_list.end()) { + /* Not found, add it to the list */ + seg_list.push_back(node_segments_[inode]); + } + } + + return seg_list; +} + +/* Get a list of nodes whose segment_id is specified */ +std::vector RRChan::get_node_ids_by_segment_ids(const RRSegmentId seg_id) const { + std::vector node_list; + + // Traverse node_segments + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + // Try to find the node_segment id in the list + if (seg_id == node_segments_[inode]) { + node_list.push_back(inode); + } + } + + return node_list; +} + +/************************************************************************ + * Mutators + ***********************************************************************/ +void RRChan::set(const RRChan& rr_chan) { + // Ensure a clean start + clear(); + // Assign type of this routing channel + type_ = rr_chan.get_type(); + // Copy node and node_segments + nodes_.resize(rr_chan.get_chan_width()); + node_segments_.resize(rr_chan.get_chan_width()); + for (size_t inode = 0; inode < rr_chan.get_chan_width(); ++inode) { + nodes_[inode] = rr_chan.get_node(inode); + node_segments_[inode] = rr_chan.get_node_segment(inode); + } +} + +/* modify type */ +void RRChan::set_type(const e_rr_type type) { + VTR_ASSERT(is_rr_type_valid(type)); + type_ = type; +} + +/* Reserve node list */ +void RRChan::reserve_node(const size_t node_size) { + nodes_.reserve(node_size); + node_segments_.reserve(node_size); +} + +/* add a node to the array */ +void RRChan::add_node(const RRGraphView& rr_graph, const RRNodeId node, const RRSegmentId node_segment) { + // fill the dedicated element in the vector + nodes_.push_back(node); + node_segments_.push_back(node_segment); + + if (e_rr_type::NUM_RR_TYPES == type_) { + type_ = rr_graph.node_type(node); + } else { + VTR_ASSERT(type_ == rr_graph.node_type(node)); + } + + VTR_ASSERT(valid_node_type(rr_graph, node)); +} + +/* Clear content */ +void RRChan::clear() { + nodes_.clear(); + node_segments_.clear(); +} + +/************************************************************************ + * Internal validators + ***********************************************************************/ +/* for type, only valid type is CHANX and CHANY */ +bool RRChan::is_rr_type_valid(const e_rr_type type) const { + if ((e_rr_type::CHANX == type) || (e_rr_type::CHANY == type)) { + return true; + } + return false; +} + +/* Check each node, see if the node type is consistent with the type */ +bool RRChan::valid_node_type(const RRGraphView& rr_graph, const RRNodeId node) const { + is_rr_type_valid(rr_graph.node_type(node)); + if (e_rr_type::NUM_RR_TYPES == type_) { + return true; + } + is_rr_type_valid(type_); + if (type_ != rr_graph.node_type(node)) { + return false; + } + return true; +} + +/* check if the node id is valid */ +bool RRChan::valid_node_id(const size_t node_id) const { + if (node_id < nodes_.size()) { + return true; + } + + return false; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_chan.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_chan.h new file mode 100644 index 00000000000..96b7cab2de7 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_chan.h @@ -0,0 +1,111 @@ +#pragma once + +#include + +#include "vtr_geometry.h" + +#include "rr_graph_view.h" + +/******************************************************************** + * RRChan object aim to describe a routing channel in a routing resource graph + * - What are the nodes in the RRGraph object, for each routing track + * - What are routing segments used by each node in the channel + * - What are the directions of each routing channel + * being either X-direction or Y-direction + * + * Note : + * - This is a collection of rr_nodes from the RRGraph Object + * It does not rebuild or contruct any connects between rr_nodes + * It is just an annotation on an existing RRGraph + * ------------- ------ + * | | | | + * | | | Y | + * | CLB | | Chan | + * | | | | + * | | | | + * ------------- ------ + * ------------- + * | X | + * | Channel | + * ------------- + *******************************************************************/ +class RRChan { + public: // Constructors + RRChan(); + + public: // Accessors + e_rr_type get_type() const; + + /** + * @brief Get the number of tracks in this channel + */ + size_t get_chan_width() const; + + /** + * @brief Get the track_id of a node + */ + int get_node_track_id(const RRNodeId node) const; + + /** + * @brief Get the rr_node with the track_id + */ + RRNodeId get_node(const size_t track_num) const; + + RRSegmentId get_node_segment(const RRNodeId node) const; + + RRSegmentId get_node_segment(const size_t track_num) const; + + /** + * @brief Get a list of segments used in this routing channel + */ + std::vector get_segment_ids() const; + + /** + * @brief Get a list of segments used in this routing channel + */ + std::vector get_node_ids_by_segment_ids(const RRSegmentId seg_id) const; + + public: + void set(const RRChan&); + + /** + * @brief Modify the type of routing channel + */ + void set_type(const e_rr_type type); + + /** + * @brief Reserve a number of nodes to the array + */ + void reserve_node(const size_t node_size); + + /** + * @brief Add a node to the routing channel + */ + void add_node(const RRGraphView& rr_graph, const RRNodeId node, const RRSegmentId node_segment); + + /** + * @brief Clear the content + */ + void clear(); + + private: + /** + * @brief Check if the type of a routing channel is valid + */ + bool is_rr_type_valid(const e_rr_type type) const; + + /** + * @brief Check if the node type is consistent with the type of routing channel + */ + bool valid_node_type(const RRGraphView& rr_graph, const RRNodeId node) const; + + /** + * @brief Validate if the track number in the range + */ + bool valid_node_id(const size_t node_id) const; + + private: + e_rr_type type_; // channel type: CHANX or CHANY + std::vector nodes_; // rr nodes of each track in the channel + std::vector node_segments_; // segment of each track +}; diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_builder_utils.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_builder_utils.cpp new file mode 100644 index 00000000000..b7214431b99 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_builder_utils.cpp @@ -0,0 +1,640 @@ +/************************************************************************ + * This file contains most utilized functions for rr_graph builders + ***********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "vpr_utils.h" + +#include "rr_graph_builder_utils.h" + +/************************************************************************ + * Correct number of routing channel width to be compatible to + * uni-directional routing architecture + ***********************************************************************/ +size_t find_unidir_routing_channel_width(const size_t chan_width) { + size_t actual_chan_width = chan_width; + /* Correct the chan_width: it should be an even number */ + if (0 != actual_chan_width % 2) { + actual_chan_width++; /* increment it to be even */ + } + VTR_ASSERT(0 == actual_chan_width % 2); + + return actual_chan_width; +} + +/************************************************************************ + * Get the class index of a grid pin + ***********************************************************************/ +int get_grid_pin_class_index(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const int pin_index) { + /* check */ + t_physical_tile_loc tile_loc(x, y, layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + VTR_ASSERT(pin_index < phy_tile_type->num_pins); + return phy_tile_type->pin_class[pin_index]; +} + +/* Deteremine the side of a io grid */ +std::vector determine_io_grid_pin_side(const vtr::Point& device_size, + const vtr::Point& grid_coordinate, + const bool perimeter_cb) { + std::vector pin_sides; + /* TOP side IO of FPGA */ + if (device_size.y() == grid_coordinate.y()) { + /* Such I/O has only bottom side pins */ + pin_sides.push_back(BOTTOM); + /* If cbs are allowed around boundary I/Os, add two more sides */ + if (perimeter_cb) { + pin_sides.push_back(LEFT); + pin_sides.push_back(RIGHT); + } + } else if (device_size.x() == grid_coordinate.x()) { /* RIGHT side IO of FPGA */ + /* Such I/O has only Left side pins */ + pin_sides.push_back(LEFT); + /* If cbs are allowed around boundary I/Os, add two more sides */ + if (perimeter_cb) { + pin_sides.push_back(TOP); + pin_sides.push_back(BOTTOM); + } + } else if (0 == grid_coordinate.y()) { /* BOTTOM side IO of FPGA */ + /* Such I/O has only Top side pins */ + pin_sides.push_back(TOP); + /* If cbs are allowed around boundary I/Os, add two more sides */ + if (perimeter_cb) { + pin_sides.push_back(LEFT); + pin_sides.push_back(RIGHT); + } + } else if (0 == grid_coordinate.x()) { /* LEFT side IO of FPGA */ + /* Such I/O has only Right side pins */ + pin_sides.push_back(RIGHT); + /* If cbs are allowed around boundary I/Os, add two more sides */ + if (perimeter_cb) { + pin_sides.push_back(TOP); + pin_sides.push_back(BOTTOM); + } + } else if ((grid_coordinate.x() < device_size.x()) && (grid_coordinate.y() < device_size.y())) { + /* I/O grid in the center grid */ + return {TOP, RIGHT, BOTTOM, LEFT}; + } else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Invalid coordinate (%lu, %lu) for I/O Grid whose size is (%lu, %lu)!\n", + grid_coordinate.x(), grid_coordinate.y(), + device_size.x(), device_size.y()); + } + return pin_sides; +} + +/* Deteremine the side of a pin of a grid */ +std::vector find_grid_pin_sides(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const size_t pin_id) { + std::vector pin_sides; + + t_physical_tile_loc tile_loc(x, y, layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + int width_offset = grids.get_width_offset(tile_loc); + int height_offset = grids.get_height_offset(tile_loc); + for (const e_side side : {TOP, RIGHT, BOTTOM, LEFT}) { + if (true == phy_tile_type->pinloc[width_offset][height_offset][size_t(side)][pin_id]) { + pin_sides.push_back(side); + } + } + + return pin_sides; +} + +std::vector get_grid_side_pins(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const e_pin_type pin_type, + const e_side pin_side, + const int pin_width, + const int pin_height) { + std::vector pin_list; + + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(x, y, layer)); + for (int ipin = 0; ipin < phy_tile_type->num_pins; ++ipin) { + int class_id = phy_tile_type->pin_class[ipin]; + if ((1 == phy_tile_type->pinloc[pin_width][pin_height][pin_side][ipin]) + && (pin_type == phy_tile_type->class_inf[class_id].type)) { + pin_list.push_back(ipin); + } + } + return pin_list; +} + +/************************************************************************ + * Get the number of pins for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +size_t get_grid_num_pins(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const e_pin_type pin_type, + const std::vector& io_side) { + size_t num_pins = 0; + + /* For IO_TYPE sides */ + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(x, y, layer)); + for (const e_side side : io_side) { + /* Get pin list */ + for (int width = 0; width < phy_tile_type->width; ++width) { + for (int height = 0; height < phy_tile_type->height; ++height) { + std::vector pin_list = get_grid_side_pins(grids, layer, x, y, pin_type, side, width, height); + num_pins += pin_list.size(); + } + } + } + + return num_pins; +} + +/************************************************************************ + * Get the number of pins for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +size_t get_grid_num_classes(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const e_pin_type pin_type) { + size_t num_classes = 0; + + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(x, y, layer)); + for (size_t iclass = 0; iclass < phy_tile_type->class_inf.size(); ++iclass) { + /* Bypass unmatched pin_type */ + if (pin_type != phy_tile_type->class_inf[iclass].type) { + continue; + } + num_classes++; + } + + return num_classes; +} + +/************************************************************************ + * Identify if a X-direction routing channel exist in the fabric + * This could be entirely possible that a routig channel + * is in the middle of a multi-width and multi-height grid + * + * As the chanx always locates on top of a grid with the same coord + * + * +----------+ + * | CHANX | + * | [x][y] | + * +----------+ + * + * +----------+ + * | Grid | height_offset = height - 1 + * | [x][y] | + * +----------+ + * + * +----------+ + * | Grid | height_offset = height - 2 + * | [x][y-1] | + * +----------+ + * If the CHANX is in the middle of a multi-width and multi-height grid + * it should locate at a grid whose height_offset is lower than the its height defined in physical_tile + * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block + ***********************************************************************/ +bool is_chanx_exist(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chanx_coord, + const bool perimeter_cb, + const bool through_channel) { + size_t chanx_start = 1; + size_t chanx_end = grids.width() - 2; + if (perimeter_cb) { + chanx_start = 0; + chanx_end = grids.width() - 1; + } + if ((chanx_start > chanx_coord.x()) || (chanx_coord.x() > chanx_end)) { + return false; + } + + if (chanx_coord.y() > grids.height() - 2) { + return false; + } + + if (true == through_channel) { + return true; + } + + return (grids.get_height_offset(t_physical_tile_loc(chanx_coord.x(), chanx_coord.y(), layer)) == grids.get_physical_type(t_physical_tile_loc(chanx_coord.x(), chanx_coord.y(), layer))->height - 1); +} + +/************************************************************************ + * Idenfity if a Y-direction routing channel exist in the fabric + * This could be entirely possible that a routig channel + * is in the middle of a multi-width and multi-height grid + * + * As the chany always locates on right of a grid with the same coord + * + * +-----------+ +---------+ +--------+ + * | Grid | | Grid | | CHANY | + * | [x-1][y] | | [x][y] | | [x][y] | + * +-----------+ +---------+ +--------+ + * width_offset width_offset + * = width - 2 = width -1 + * If the CHANY is in the middle of a multi-width and multi-height grid + * it should locate at a grid whose width_offset is lower than the its width defined in physical_tile + * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block + * + * If through channel is allowed, the chany will always exists + * unless it falls out of the grid array + ***********************************************************************/ +bool is_chany_exist(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chany_coord, + const bool perimeter_cb, + const bool through_channel) { + size_t chany_start = 1; + size_t chany_end = grids.height() - 2; + if (perimeter_cb) { + chany_start = 0; + chany_end = grids.height() - 1; + } + if (chany_coord.x() > grids.width() - 2) { + return false; + } + + if ((chany_start > chany_coord.y()) || (chany_coord.y() > chany_end)) { + return false; + } + + if (true == through_channel) { + return true; + } + + return (grids.get_width_offset(t_physical_tile_loc(chany_coord.x(), chany_coord.y(), layer)) == grids.get_physical_type(t_physical_tile_loc(chany_coord.x(), chany_coord.y(), layer))->width - 1); +} + +/************************************************************************ + * Identify if a X-direction routing channel is at the right side of a + * multi-height grid + * + * +-----------------+ + * | | + * | | +-------------+ + * | Grid | | CHANX | + * | [x-1][y] | | [x][y] | + * | | +-------------+ + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chanx_right_to_multi_height_grid(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chanx_coord, + const bool perimeter_cb, + const bool through_channel) { + size_t start_x = 1; + if (perimeter_cb) { + start_x = 0; + } else { + VTR_ASSERT(0 < chanx_coord.x()); + } + if (start_x == chanx_coord.x()) { + /* This is already the LEFT side of FPGA fabric, + * it is the same results as chanx is right to a multi-height grid + */ + return true; + } + + if (false == through_channel) { + /* We check the left neighbor of chanx, if it does not exist, the chanx is left to a multi-height grid */ + vtr::Point left_chanx_coord(chanx_coord.x() - 1, chanx_coord.y()); + if (false == is_chanx_exist(grids, layer, left_chanx_coord, perimeter_cb)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a X-direction routing channel is at the left side of a + * multi-height grid + * + * +-----------------+ + * | | + * +---------------+ | | + * | CHANX | | Grid | + * | [x][y] | | [x+1][y] | + * +---------------+ | | + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chanx_left_to_multi_height_grid(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chanx_coord, + const bool perimeter_cb, + const bool through_channel) { + VTR_ASSERT(chanx_coord.x() <= grids.width() - 1); + size_t end_x = grids.width() - 2; + if (perimeter_cb) { + end_x = grids.width() - 1; + } + + if (end_x == chanx_coord.x()) { + /* This is already the RIGHT side of FPGA fabric, + * it is the same results as chanx is right to a multi-height grid + */ + return true; + } + + if (false == through_channel) { + /* We check the right neighbor of chanx, if it does not exist, the chanx is left to a multi-height grid */ + vtr::Point right_chanx_coord(chanx_coord.x() + 1, chanx_coord.y()); + if (false == is_chanx_exist(grids, layer, right_chanx_coord, perimeter_cb)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a Y-direction routing channel is at the top side of a + * multi-width grid + * + * +--------+ + * | CHANY | + * | [x][y] | + * +--------+ + * + * +-----------------+ + * | | + * | | + * | Grid | + * | [x-1][y] | + * | | + * | | + * +-----------------+ + ***********************************************************************/ +bool is_chany_top_to_multi_width_grid(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chany_coord, + const bool perimeter_cb, + const bool through_channel) { + size_t start_y = 1; + if (perimeter_cb) { + start_y = 0; + } else { + VTR_ASSERT(0 < chany_coord.y()); + } + if (start_y == chany_coord.y()) { + /* This is already the BOTTOM side of FPGA fabric, + * it is the same results as chany is at the top of a multi-width grid + */ + return true; + } + + if (false == through_channel) { + /* We check the bottom neighbor of chany, if it does not exist, the chany is top to a multi-height grid */ + vtr::Point bottom_chany_coord(chany_coord.x(), chany_coord.y() - 1); + if (false == is_chany_exist(grids, layer, bottom_chany_coord, perimeter_cb)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Identify if a Y-direction routing channel is at the bottom side of a + * multi-width grid + * + * +-----------------+ + * | | + * | | + * | Grid | + * | [x][y+1] | + * | | + * | | + * +-----------------+ + * +--------+ + * | CHANY | + * | [x][y] | + * +--------+ + * + ***********************************************************************/ +bool is_chany_bottom_to_multi_width_grid(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chany_coord, + const bool perimeter_cb, + const bool through_channel) { + VTR_ASSERT(chany_coord.y() <= grids.height() - 1); + size_t end_y = grids.height() - 2; + if (perimeter_cb) { + end_y = grids.height() - 1; + } + + if (end_y == chany_coord.y()) { + /* This is already the TOP side of FPGA fabric, + * it is the same results as chany is at the bottom of a multi-width grid + */ + return true; + } + + if (false == through_channel) { + /* We check the top neighbor of chany, if it does not exist, the chany is left to a multi-height grid */ + vtr::Point top_chany_coord(chany_coord.x(), chany_coord.y() + 1); + if (false == is_chany_exist(grids, layer, top_chany_coord, perimeter_cb)) { + return true; + } + } + + return false; +} + +/************************************************************************ + * Get the track_id of a routing track w.r.t its coordinator + * In tileable routing architecture, the track_id changes SB by SB. + * Therefore the track_ids are stored in a vector, indexed by the relative coordinator + * based on the starting point of the track + * For routing tracks in INC_DIRECTION + * (xlow, ylow) should be the starting point + * + * (xlow, ylow) (xhigh, yhigh) + * track_id[0] -------------------------------> track_id[xhigh - xlow + yhigh - ylow] + * + * For routing tracks in DEC_DIRECTION + * (xhigh, yhigh) should be the starting point + * + * (xlow, ylow) (xhigh, yhigh) + * track_id[0] <------------------------------- track_id[xhigh - xlow + yhigh - ylow] + * + * + ***********************************************************************/ +short get_rr_node_actual_track_id(const RRGraph& rr_graph, + const RRNodeId track_rr_node, + const vtr::Point& coord, + const vtr::vector>& tileable_rr_graph_node_track_ids) { + vtr::Point low_coord(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + size_t offset = (int)abs((int)coord.x() - (int)low_coord.x() + (int)coord.y() - (int)low_coord.y()); + return tileable_rr_graph_node_track_ids[track_rr_node][offset]; +} + +/************************************************************************ + * Get the ptc of a routing track in the channel where it ends + * For routing tracks in INC_DIRECTION + * the ptc is the last of track_ids + * + * For routing tracks in DEC_DIRECTION + * the ptc is the first of track_ids + ***********************************************************************/ +short get_track_rr_node_end_track_id(const RRGraph& rr_graph, + const RRNodeId track_rr_node, + const vtr::vector>& tileable_rr_graph_node_track_ids) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((e_rr_type::CHANX == rr_graph.node_type(track_rr_node)) + || (e_rr_type::CHANY == rr_graph.node_type(track_rr_node))); + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + return tileable_rr_graph_node_track_ids[track_rr_node].back(); + } + + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + return tileable_rr_graph_node_track_ids[track_rr_node].front(); +} + +/************************************************************************ + * Find the number of nodes in the same class + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_num_nodes(const RRGraph& rr_graph, + const std::vector& node_types) { + short counter = 0; + + for (const RRNodeId node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + counter++; + } + + return counter; +} + +/************************************************************************ + * Find the maximum fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_max_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + short max_fan_in = 0; + + for (const RRNodeId node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + max_fan_in = std::max(rr_graph.node_fan_in(node), max_fan_in); + } + + return max_fan_in; +} + +short find_rr_graph_min_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + short min_fan_in = std::numeric_limits::max(); + + for (const RRNodeId node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + min_fan_in = std::min(rr_graph.node_fan_in(node), min_fan_in); + } + + return min_fan_in; +} + +/************************************************************************ + * Find the average fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_average_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + /* Get the maximum SB mux size */ + size_t sum = 0; + size_t counter = 0; + + for (const RRNodeId node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + + sum += rr_graph.node_fan_in(node); + counter++; + } + + return sum / counter; +} + +/************************************************************************ + * Print statistics of multiplexers in a routing resource graph + ************************************************************************/ +void print_rr_graph_mux_stats(const RRGraph& rr_graph) { + /* Print MUX size distribution */ + std::vector sb_node_types; + sb_node_types.push_back(e_rr_type::CHANX); + sb_node_types.push_back(e_rr_type::CHANY); + + /* Print statistics */ + VTR_LOG("------------------------------------------------\n"); + VTR_LOG("Total No. of Switch Block multiplexer size: %d\n", + find_rr_graph_num_nodes(rr_graph, sb_node_types)); + VTR_LOG("Maximum Switch Block multiplexer size: %d\n", + find_rr_graph_max_fan_in(rr_graph, sb_node_types)); + VTR_LOG("Minimum Switch Block multiplexer size: %d\n", + find_rr_graph_min_fan_in(rr_graph, sb_node_types)); + VTR_LOG("Average Switch Block multiplexer size: %lu\n", + find_rr_graph_average_fan_in(rr_graph, sb_node_types)); + VTR_LOG("------------------------------------------------\n"); + + /* Get the maximum CB mux size */ + std::vector cb_node_types(1, e_rr_type::IPIN); + + VTR_LOG("------------------------------------------------\n"); + VTR_LOG("Total No. of Connection Block Multiplexer size: %d\n", + find_rr_graph_num_nodes(rr_graph, cb_node_types)); + VTR_LOG("Maximum Connection Block Multiplexer size: %d\n", + find_rr_graph_max_fan_in(rr_graph, cb_node_types)); + VTR_LOG("Minimum Connection Block Multiplexer size: %d\n", + find_rr_graph_min_fan_in(rr_graph, cb_node_types)); + VTR_LOG("Average Connection Block Multiplexer size: %lu\n", + find_rr_graph_average_fan_in(rr_graph, cb_node_types)); + VTR_LOG("------------------------------------------------\n"); +} + +int find_parallel_seg_index(const int abs_index, + const t_unified_to_parallel_seg_index& index_map, + const e_parallel_axis parallel_axis) { + int index = -1; + auto itr_pair = index_map.equal_range(abs_index); + + for (auto itr = itr_pair.first; itr != itr_pair.second; ++itr) { + if (itr->second.second == parallel_axis) { + index = itr->second.first; + } + } + + return index; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_builder_utils.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_builder_utils.h new file mode 100644 index 00000000000..3466e33f763 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_builder_utils.h @@ -0,0 +1,132 @@ +#pragma once + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "device_grid.h" +#include "rr_graph_obj.h" +#include "rr_graph_type.h" +#include "vtr_geometry.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +size_t find_unidir_routing_channel_width(const size_t chan_width); + +int get_grid_pin_class_index(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const int pin_index); + +std::vector find_grid_pin_sides(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const size_t pin_id); + +std::vector determine_io_grid_pin_side(const vtr::Point& device_size, + const vtr::Point& grid_coordinate, + const bool perimeter_cb); + +/** + * @brief Get a list of pin_index for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + */ +std::vector get_grid_side_pins(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const e_pin_type pin_type, + const e_side pin_side, + const int pin_width, + const int pin_height); + +size_t get_grid_num_pins(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const e_pin_type pin_type, + const std::vector& io_side); + +size_t get_grid_num_classes(const DeviceGrid& grids, + const size_t layer, + const size_t x, + const size_t y, + const e_pin_type pin_type); + +bool is_chanx_exist(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chanx_coord, + const bool perimeter_cb, + const bool through_channel = false); + +bool is_chany_exist(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chany_coord, + const bool perimeter_cb, + const bool through_channel = false); + +bool is_chanx_right_to_multi_height_grid(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chanx_coord, + const bool perimeter_cb, + const bool through_channel); + +bool is_chanx_left_to_multi_height_grid(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chanx_coord, + const bool perimeter_cb, + const bool through_channel); + +bool is_chany_top_to_multi_width_grid(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chany_coord, + const bool perimeter_cb, + const bool through_channel); + +bool is_chany_bottom_to_multi_width_grid(const DeviceGrid& grids, + const size_t layer, + const vtr::Point& chany_coord, + const bool perimeter_cb, + const bool through_channel); + +short get_rr_node_actual_track_id(const RRGraph& rr_graph, + const RRNodeId track_rr_node, + const vtr::Point& coord, + const vtr::vector>& tileable_rr_graph_node_track_ids); + +vtr::Point get_track_rr_node_start_coordinator(const RRGraph& rr_graph, + const RRNodeId track_rr_node); + +vtr::Point get_track_rr_node_end_coordinator(const RRGraph& rr_graph, + const RRNodeId track_rr_node); + +short get_track_rr_node_end_track_id(const RRGraph& rr_graph, + const RRNodeId track_rr_node, + const vtr::vector>& tileable_rr_graph_node_track_ids); + +short find_rr_graph_num_nodes(const RRGraph& rr_graph, + const std::vector& node_types); + +short find_rr_graph_max_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +/** + * @brief Find the minimum fan-in for a given class of nodes + * in a routing resource graph + */ +short find_rr_graph_min_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +short find_rr_graph_average_fan_in(const RRGraph& rr_graph, + const std::vector& node_types); + +void print_rr_graph_mux_stats(const RRGraph& rr_graph); + +/* A copy of the function from rr_graph2.cpp; This is keep tilable rr_graph builder self-contained */ +int find_parallel_seg_index(const int abs_index, + const t_unified_to_parallel_seg_index& index_map, + const e_parallel_axis parallel_axis); diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_view_util.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_view_util.cpp new file mode 100644 index 00000000000..cbc29f4850e --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_view_util.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** + * This file include most-utilized functions that manipulate on the + * RRGraph object + ***************************************************************************/ +#include "rr_graph_view_util.h" + +/**************************************************************************** + * Find the switches interconnecting two nodes + * Return a vector of switch ids + ***************************************************************************/ +std::vector find_rr_graph_switches(const RRGraphView& rr_graph, + const RRNodeId& from_node, + const RRNodeId& to_node) { + std::vector switches; + + std::vector edges = rr_graph.find_edges(from_node, to_node); + if (true == edges.empty()) { + /* edge is open, we return an empty vector of switches */ + return switches; + } + + /* Reach here, edge list is not empty, find switch id one by one + * and update the switch list + */ + for (auto edge : edges) { + switches.push_back(RRSwitchId(rr_graph.edge_switch(edge))); + } + + return switches; +} + +/********************************************************************* + * Like the RRGraph.find_node() but returns all matching nodes, + * rather than just the first. This is particularly useful for getting all instances + * of a specific IPIN/OPIN at a specific grid tile (x,y) location. + **********************************************************************/ +std::vector find_rr_graph_nodes(const RRGraphView& rr_graph, + const size_t& layer, + const int& x, + const int& y, + const e_rr_type& rr_type, + const int& ptc) { + std::vector indices; + + if (rr_type == e_rr_type::IPIN || rr_type == e_rr_type::OPIN) { + //For pins we need to look at all the sides of the current grid tile + + for (e_side side : TOTAL_2D_SIDES) { + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(layer, x, y, rr_type, ptc, side); + + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + } else { + //Sides do not effect non-pins so there should only be one per ptc + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(layer, x, y, rr_type, ptc); + + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} + +/********************************************************************* + * Find a list of rr nodes in a routing channel at (x,y) + **********************************************************************/ +std::vector find_rr_graph_chan_nodes(const RRGraphView& rr_graph, + const size_t& layer, + const int& x, + const int& y, + const e_rr_type& rr_type) { + std::vector indices; + + VTR_ASSERT(rr_type == e_rr_type::CHANX || rr_type == e_rr_type::CHANY); + + for (const RRNodeId& rr_node_index : rr_graph.node_lookup().find_channel_nodes(layer, x, y, rr_type)) { + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} + +/********************************************************************* + * Find a list of rr_nodes that locate at a side of a grid + **********************************************************************/ +std::vector find_rr_graph_grid_nodes(const RRGraphView& rr_graph, + const DeviceGrid& device_grid, + const size_t& layer, + const int& x, + const int& y, + const e_rr_type& rr_type, + const e_side& side, + bool include_clock) { + std::vector indices; + + VTR_ASSERT(rr_type == e_rr_type::IPIN || rr_type == e_rr_type::OPIN); + + /* Ensure that (x, y) is a valid location in grids */ + if (size_t(x) > device_grid.width() - 1 || size_t(y) > device_grid.height() - 1) { + return indices; + } + + /* Ensure we have a valid side */ + VTR_ASSERT(side != NUM_2D_SIDES); + + /* Find all the pins on the side of the grid */ + t_physical_tile_loc tile_loc(x, y, layer); + int width_offset = device_grid.get_width_offset(tile_loc); + int height_offset = device_grid.get_height_offset(tile_loc); + + for (int pin = 0; pin < device_grid.get_physical_type(tile_loc)->num_pins; ++pin) { + /* Skip those pins have been ignored during rr_graph build-up */ + if (true == device_grid.get_physical_type(tile_loc)->is_ignored_pin[pin]) { + /* If specified, force to include all the clock pins */ + if (!include_clock || std::find(device_grid.get_physical_type(tile_loc)->get_clock_pins_indices().begin(), device_grid.get_physical_type(tile_loc)->get_clock_pins_indices().end(), pin) == device_grid.get_physical_type(tile_loc)->get_clock_pins_indices().end()) { + continue; + } + } + if (false == device_grid.get_physical_type(tile_loc)->pinloc[width_offset][height_offset][side][pin]) { + /* Not the pin on this side, we skip */ + continue; + } + + /* Try to find the rr node */ + RRNodeId rr_node_index = rr_graph.node_lookup().find_node(layer, x, y, rr_type, pin, side); + if (rr_node_index != RRNodeId::INVALID()) { + indices.push_back(rr_node_index); + } + } + + return indices; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_view_util.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_view_util.h new file mode 100644 index 00000000000..4008f8c2bc8 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_view_util.h @@ -0,0 +1,35 @@ +#pragma once + +/* Include header files which include data structures used by + * the function declaration + */ +#include +#include "device_grid.h" +#include "rr_graph_view.h" + +/* Get node-to-node switches in a RRGraph */ +std::vector find_rr_graph_switches(const RRGraphView& rr_graph, + const RRNodeId& from_node, + const RRNodeId& to_node); + +std::vector find_rr_graph_nodes(const RRGraphView& rr_graph, + const size_t& layer, + const int& x, + const int& y, + const e_rr_type& rr_type, + const int& ptc); + +std::vector find_rr_graph_chan_nodes(const RRGraphView& rr_graph, + const size_t& layer, + const int& x, + const int& y, + const e_rr_type& rr_type); + +std::vector find_rr_graph_grid_nodes(const RRGraphView& rr_graph, + const DeviceGrid& device_grid, + const size_t& layer, + const int& x, + const int& y, + const e_rr_type& rr_type, + const e_side& side, + bool include_clock = false); diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_gsb.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_gsb.cpp new file mode 100644 index 00000000000..ac1aad9f4fc --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_gsb.cpp @@ -0,0 +1,1247 @@ +/************************************************************************ + * Member functions for class RRGSB + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "vpr_error.h" + +#include "tileable_rr_graph_utils.h" +#include "side_manager.h" + +#include "rr_gsb.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +/* Constructor for an empty object */ +RRGSB::RRGSB() + : coordinate_(size_t(-1), size_t(-1)) { + /* Set a clean start! */ + coordinate_.set(0, 0); + + chan_node_.clear(); + chan_node_direction_.clear(); + chan_node_in_edges_.clear(); + ipin_node_in_edges_.clear(); + + ipin_node_.clear(); + + opin_node_.clear(); + + mux_node_.clear(); + for (size_t icb_type = 0; icb_type < 2; icb_type++) { + for (size_t iside = 0; iside < NUM_2D_SIDES; iside++) { + cb_opin_node_[icb_type][iside].clear(); + } + } +} + +/************************************************************************ + * Accessors + ***********************************************************************/ +/* Get the number of sides of this SB */ +size_t RRGSB::get_num_sides() const { + VTR_ASSERT(validate_num_sides()); + return chan_node_direction_.size(); +} + +/* Get the number of routing tracks on a side */ +size_t RRGSB::get_chan_width(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return chan_node_[side_manager.to_size_t()].get_chan_width(); +} + +/* Get the number of routing tracks on a side */ +e_rr_type RRGSB::get_chan_type(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return chan_node_[side_manager.to_size_t()].get_type(); +} + +/* Get the maximum number of routing tracks on all sides */ +size_t RRGSB::get_max_chan_width() const { + size_t max_chan_width = 0; + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + max_chan_width = std::max(max_chan_width, get_chan_width(side_manager.get_side())); + } + return max_chan_width; +} + +const RRChan& RRGSB::chan(const e_side& chan_side) const { + return chan_node_[size_t(chan_side)]; +} + +/* Get the number of routing tracks of a X/Y-direction CB */ +size_t RRGSB::get_cb_chan_width(const e_rr_type& cb_type) const { + return get_chan_width(get_cb_chan_side(cb_type)); +} + +/* Get the sides of ipin_nodes belong to the cb */ +std::vector RRGSB::get_cb_ipin_sides(const e_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + + std::vector ipin_sides; + + /* Make sure a clean start */ + ipin_sides.clear(); + + switch (cb_type) { + case e_rr_type::CHANX: + ipin_sides.push_back(TOP); + ipin_sides.push_back(BOTTOM); + break; + case e_rr_type::CHANY: + ipin_sides.push_back(RIGHT); + ipin_sides.push_back(LEFT); + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid type of connection block!\n"); + } + + return ipin_sides; +} + +/* Get the sides of ipin_nodes belong to the cb */ +std::vector RRGSB::get_cb_opin_sides(const e_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + + std::vector opin_sides; + + /* Make sure a clean start */ + opin_sides.clear(); + + switch (cb_type) { + case e_rr_type::CHANX: + case e_rr_type::CHANY: + opin_sides.push_back(TOP); + opin_sides.push_back(RIGHT); + opin_sides.push_back(BOTTOM); + opin_sides.push_back(LEFT); + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid type of connection block!\n"); + } + + return opin_sides; +} + +/* Get the direction of a rr_node at a given side and track_id */ +enum PORTS RRGSB::get_chan_node_direction(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_direction_[side_manager.to_size_t()][track_id]; +} + +/* Get a list of segments used in this routing channel */ +std::vector RRGSB::get_chan_segment_ids(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + return chan_node_[side_manager.to_size_t()].get_segment_ids(); +} + +/* Get a list of rr_nodes whose sed_id is specified */ +std::vector RRGSB::get_chan_node_ids_by_segment_ids(const e_side& side, + const RRSegmentId& seg_id) const { + return chan_node_[size_t(side)].get_node_ids_by_segment_ids(seg_id); +} + +/* get a rr_node at a given side and track_id */ +RRNodeId RRGSB::get_chan_node(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_[side_manager.to_size_t()].get_node(track_id); +} + +std::vector RRGSB::get_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + /* The chan node must be an output port for the GSB, we allow users to access input edges*/ + VTR_ASSERT(OUT_PORT == get_chan_node_direction(side, track_id)); + + /* if sorted, we give sorted edges + * if not sorted, we give the empty vector + */ + if (0 == chan_node_in_edges_.size()) { + std::vector unsorted_edges; + + for (const RREdgeId& edge : rr_graph.node_in_edges(get_chan_node(side, track_id))) { + unsorted_edges.push_back(edge); + } + + return unsorted_edges; + } + + return chan_node_in_edges_[side_manager.to_size_t()][track_id]; +} + +std::vector RRGSB::get_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& ipin_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_ipin_node_id(side, ipin_id)); + + /* if sorted, we give sorted edges + * if not sorted, we give the empty vector + */ + if (0 == ipin_node_in_edges_.size()) { + std::vector unsorted_edges; + + for (const RREdgeId& edge : rr_graph.node_in_edges(get_ipin_node(side, ipin_id))) { + unsorted_edges.push_back(edge); + } + + return unsorted_edges; + } + + return ipin_node_in_edges_[side_manager.to_size_t()][ipin_id]; +} + +/* get the segment id of a channel rr_node */ +RRSegmentId RRGSB::get_chan_node_segment(const e_side& side, const size_t& track_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_track_id(side, track_id)); + + return chan_node_[side_manager.to_size_t()].get_node_segment(track_id); +} + +/* Get the number of IPIN rr_nodes on a side */ +size_t RRGSB::get_num_ipin_nodes(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return ipin_node_[side_manager.to_size_t()].size(); +} + +/* get a opin_node at a given side and track_id */ +RRNodeId RRGSB::get_ipin_node(const e_side& side, const size_t& node_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_ipin_node_id(side, node_id)); + + return ipin_node_[side_manager.to_size_t()][node_id]; +} + +/* Get the number of OPIN rr_nodes on a side */ +size_t RRGSB::get_num_opin_nodes(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + return opin_node_[side_manager.to_size_t()].size(); +} + +/* get a opin_node at a given side and track_id */ +RRNodeId RRGSB::get_opin_node(const e_side& side, const size_t& node_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_opin_node_id(side, node_id)); + + return opin_node_[side_manager.to_size_t()][node_id]; +} + +/* Get the number of OPIN rr_nodes on a side */ +size_t RRGSB::get_num_cb_opin_nodes(const e_rr_type& cb_type, const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + size_t icb_type = get_cb_opin_type_id(cb_type); + return cb_opin_node_[icb_type][side_manager.to_size_t()].size(); +} + +/* get a opin_node at a given side and track_id */ +RRNodeId RRGSB::get_cb_opin_node(const e_rr_type& cb_type, const e_side& side, const size_t& node_id) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + + /* Ensure the side is valid in the context of this switch block */ + VTR_ASSERT(validate_side(side)); + + /* Ensure the track is valid in the context of this switch block at a specific side */ + VTR_ASSERT(validate_cb_opin_node_id(cb_type, side, node_id)); + + size_t icb_type = get_cb_opin_type_id(cb_type); + return cb_opin_node_[icb_type][side_manager.to_size_t()][node_id]; +} + +/* Get the number of MUX rr_nodes */ +size_t RRGSB::get_num_mux_nodes() const { + VTR_ASSERT(!mux_node_.empty()); + return mux_node_.size(); +} + +/* get a rr_node at a given ptc number */ +RRNodeId RRGSB::get_mux_node(const size_t& ptc) const { + VTR_ASSERT(!mux_node_.empty() && mux_node_.size() > ptc); + return mux_node_[ptc]; +} + +/* Get the node index of a routing track of a connection block, return -1 if not found */ +int RRGSB::get_cb_chan_node_index(const e_rr_type& cb_type, const RRNodeId& node) const { + enum e_side chan_side = get_cb_chan_side(cb_type); + return get_chan_node_index(chan_side, node); +} + +/* Get the node index in the array, return -1 if not found */ +int RRGSB::get_chan_node_index(const e_side& node_side, const RRNodeId& node) const { + VTR_ASSERT(validate_side(node_side)); + return chan_node_[size_t(node_side)].get_node_track_id(node); +} + +/* Get the node index in the array, return -1 if not found */ +int RRGSB::get_node_index(const RRGraphView& rr_graph, + const RRNodeId& node, + const e_side& node_side, + const PORTS& node_direction) const { + size_t cnt; + int ret; + + cnt = 0; + ret = -1; + + /* Depending on the type of rr_node, we search different arrays */ + switch (rr_graph.node_type(node)) { + case e_rr_type::CHANX: + case e_rr_type::CHANY: + for (size_t inode = 0; inode < get_chan_width(node_side); ++inode) { + if ((node == chan_node_[size_t(node_side)].get_node(inode)) + /* Check if direction meets specification */ + && (node_direction == chan_node_direction_[size_t(node_side)][inode])) { + cnt++; + ret = inode; + break; + } + } + break; + case e_rr_type::IPIN: + for (size_t inode = 0; inode < get_num_ipin_nodes(node_side); ++inode) { + if (node == ipin_node_[size_t(node_side)][inode]) { + cnt++; + ret = inode; + break; + } + } + break; + case e_rr_type::OPIN: + for (size_t inode = 0; inode < get_num_opin_nodes(node_side); ++inode) { + if (node == opin_node_[size_t(node_side)][inode]) { + cnt++; + ret = inode; + break; + } + } + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid cur_rr_node type! Should be [CHANX|CHANY|IPIN|OPIN]\n"); + } + + VTR_ASSERT((0 == cnt) || (1 == cnt)); + + return ret; /* Return an invalid value: nonthing is found*/ +} + +/* Get the side of a node in this SB */ +void RRGSB::get_node_side_and_index(const RRGraphView& rr_graph, + const RRNodeId& node, + const PORTS& node_direction, + e_side& node_side, + int& node_index) const { + size_t side; + SideManager side_manager; + + /* Count the number of existence of cur_rr_node in cur_sb_info + * It could happen that same cur_rr_node appears on different sides of a SB + * For example, a routing track go vertically across the SB. + * Then its corresponding rr_node appears on both TOP and BOTTOM sides of this SB. + * We need to ensure that the found rr_node has the same direction as user want. + * By specifying the direction of rr_node, There should be only one rr_node can satisfy! + */ + for (side = 0; side < get_num_sides(); ++side) { + side_manager.set_side(side); + node_index = get_node_index(rr_graph, node, side_manager.get_side(), node_direction); + if (-1 != node_index) { + break; + } + } + + if (side == get_num_sides()) { + /* we find nothing, return NUM_SIDES, and a OPEN node (-1) */ + node_side = NUM_2D_SIDES; + VTR_ASSERT(-1 == node_index); + return; + } + + node_side = side_manager.get_side(); + VTR_ASSERT(-1 != node_index); + + return; +} + +/* Check if the node exist in the opposite side of this Switch Block */ +bool RRGSB::is_sb_node_exist_opposite_side(const RRGraphView& rr_graph, + const RRNodeId& node, + const e_side& node_side) const { + SideManager side_manager(node_side); + int index; + + VTR_ASSERT((e_rr_type::CHANX == rr_graph.node_type(node)) || (e_rr_type::CHANY == rr_graph.node_type(node))); + + /* See if we can find the same src_rr_node in the opposite chan_side + * if there is one, it means a shorted wire across the SB + */ + index = get_node_index(rr_graph, node, side_manager.get_opposite(), IN_PORT); + + return (-1 != index); +} + +bool RRGSB::is_opin_node(const RRNodeId& node) const { + std::vector sides = {TOP, RIGHT, BOTTOM, LEFT}; + for (e_side side : sides) { + for (auto opin_node : opin_node_[side]) { + if (node == opin_node) { + return true; + } + } + } + return false; +} + +bool RRGSB::is_ipin_node(const RRNodeId& node) const { + std::vector sides = {TOP, RIGHT, BOTTOM, LEFT}; + for (e_side side : sides) { + for (auto ipin_node : ipin_node_[side]) { + if (node == ipin_node) { + return true; + } + } + } + return false; +} + +bool RRGSB::is_mux_node(const RRNodeId& node) const { + for (auto mux_node : mux_node_) { + if (node == mux_node) { + return true; + } + } + return false; +} + +bool RRGSB::is_chan_node(const RRNodeId& node) const { + std::vector sides = {TOP, RIGHT, BOTTOM, LEFT}; + for (e_side side : sides) { + RRChan rr_chan = chan_node_[side]; + for (size_t inode = 0; inode < rr_chan.get_chan_width(); ++inode) { + if (node == rr_chan.get_node(inode)) { + return true; + } + } + } + return false; +} + +/* check if the CB exist in this GSB */ +bool RRGSB::is_cb_exist(const e_rr_type& cb_type) const { + /* if channel width is zero, there is no CB */ + return (0 != get_cb_chan_width(cb_type)); +} + +/* check if the SB exist in this GSB */ +bool RRGSB::is_sb_exist(const RRGraphView& rr_graph) const { + /* Count the number of sides that there are routing wires or opin nodes */ + size_t num_sides_contain_routing_wires = 0; + size_t num_sides_contain_opin_nodes = 0; + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + if (0 != get_chan_width(side_manager.get_side())) { + num_sides_contain_routing_wires++; + } + if (0 != get_num_opin_nodes(side_manager.get_side())) { + num_sides_contain_opin_nodes++; + } + } + /* When there are zero nodes, the sb does not exist */ + if (num_sides_contain_routing_wires == 0 && num_sides_contain_opin_nodes == 0) { + return false; + } + /* If there is only 1 side of nodes and there are 0 opin nodes, and there are no incoming edges, this is also an empty switch block */ + if (num_sides_contain_routing_wires == 1 && num_sides_contain_opin_nodes == 0) { + size_t num_incoming_edges = 0; + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + for (size_t itrack = 0; itrack < get_chan_width(side_manager.get_side()); ++itrack) { + if (OUT_PORT != get_chan_node_direction(side_manager.get_side(), itrack)) { + continue; + } + num_incoming_edges += get_chan_node_in_edges(rr_graph, side_manager.get_side(), itrack).size(); + } + } + return num_incoming_edges ? true : false; + } + + return true; +} + +/************************************************************************ + * Check if the node indicates a passing wire across the Switch Block part of the GSB + * Therefore, we actually do the following check + * Check if a track starts from this GSB or not + * For INC_DIRECTION + * (xlow, ylow) should be same as the GSB side coordinate + * For DEC_DIRECTION + * (xhigh, yhigh) should be same as the GSB side coordinate + ***********************************************************************/ +bool RRGSB::is_sb_node_passing_wire(const RRGraphView& rr_graph, + const e_side& node_side, + const size_t& track_id) const { + /* Get the rr_node */ + RRNodeId track_node = get_chan_node(node_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = get_side_block_coordinate(node_side); + + /* Get the coordinate of where the track starts */ + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* INC_DIRECTION start_track: (xlow, ylow) should be same as the GSB side coordinate */ + /* DEC_DIRECTION start_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + if ((track_start.x() == side_coordinate.x()) + && (track_start.y() == side_coordinate.y()) + && (OUT_PORT == get_chan_node_direction(node_side, track_id))) { + /* Double check: start track should be an OUTPUT PORT of the GSB */ + return false; /* This is a starting point */ + } + + /* Get the coordinate of where the track ends */ + vtr::Point track_end = get_track_rr_node_end_coordinate(rr_graph, track_node); + + /* INC_DIRECTION end_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + /* DEC_DIRECTION end_track: (xlow, ylow) should be same as the GSB side coordinate */ + if ((track_end.x() == side_coordinate.x()) + && (track_end.y() == side_coordinate.y()) + && (IN_PORT == get_chan_node_direction(node_side, track_id))) { + /* Double check: end track should be an INPUT PORT of the GSB */ + return false; /* This is an ending point */ + } + + /* Reach here it means that this will be a passing wire, + * we should be able to find the node on the opposite side of the GSB! + */ + if (true != is_sb_node_exist_opposite_side(rr_graph, track_node, node_side)) { + VTR_LOG("Cannot find a node on the opposite side to GSB[%lu][%lu] track node[%lu] at %s!\nDetailed node information:\n", + get_x(), get_y(), track_id, TOTAL_2D_SIDE_STRINGS[node_side]); + VTR_LOG("Node type: %s\n", rr_graph.node_type_string(track_node)); + VTR_LOG("Node coordinate: %s\n", rr_graph.node_coordinate_to_string(track_node).c_str()); + VTR_LOG("Node ptc: %d\n", rr_graph.node_ptc_num(track_node)); + } + VTR_ASSERT(true == is_sb_node_exist_opposite_side(rr_graph, track_node, node_side)); + + return true; +} + +/* check if the candidate SB satisfy the basic requirements on being a mirror of the current one */ +/* Idenify mirror Switch blocks + * Check each two switch blocks: + * Number of channel/opin/ipin rr_nodes are same + * If all above are satisfied, the two switch blocks may be mirrors ! + */ +bool RRGSB::is_sb_mirrorable(const RRGraphView& rr_graph, const RRGSB& cand) const { + /* check the numbers of sides */ + if (get_num_sides() != cand.get_num_sides()) { + return false; + } + + /* check the numbers/directionality of channel rr_nodes */ + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + + /* Ensure we have the same channel width on this side */ + if (get_chan_width(side_manager.get_side()) != cand.get_chan_width(side_manager.get_side())) { + return false; + } + + if (((size_t(-1) == get_track_id_first_short_connection(rr_graph, side_manager.get_side())) + && (size_t(-1) != cand.get_track_id_first_short_connection(rr_graph, side_manager.get_side()))) + || ((size_t(-1) != get_track_id_first_short_connection(rr_graph, side_manager.get_side())) + && (size_t(-1) == cand.get_track_id_first_short_connection(rr_graph, side_manager.get_side())))) { + return false; + } + } + + /* check the numbers of opin_rr_nodes */ + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + + if (get_num_opin_nodes(side_manager.get_side()) != cand.get_num_opin_nodes(side_manager.get_side())) { + return false; + } + } + + return true; +} + +/* Public Accessors: Cooridinator conversion */ + +/* get the x coordinate of this GSB */ +size_t RRGSB::get_x() const { + return coordinate_.x(); +} + +/* get the y coordinate of this GSB */ +size_t RRGSB::get_y() const { + return coordinate_.y(); +} + +/* get the x coordinate of this switch block */ +size_t RRGSB::get_sb_x() const { + return coordinate_.x(); +} + +/* get the y coordinate of this switch block */ +size_t RRGSB::get_sb_y() const { + return coordinate_.y(); +} + +/* Get the number of sides of this SB */ +vtr::Point RRGSB::get_sb_coordinate() const { + return coordinate_; +} + +/* get the x coordinate of this X/Y-direction block */ +size_t RRGSB::get_cb_x(const e_rr_type& cb_type) const { + return get_cb_coordinate(cb_type).x(); +} + +/* get the y coordinate of this X/Y-direction block */ +size_t RRGSB::get_cb_y(const e_rr_type& cb_type) const { + return get_cb_coordinate(cb_type).y(); +} + +/* Get the coordinate of the X/Y-direction CB */ +vtr::Point RRGSB::get_cb_coordinate(const e_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case e_rr_type::CHANX: + return coordinate_; + case e_rr_type::CHANY: + return coordinate_; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid type of connection block!\n"); + } +} + +e_side RRGSB::get_cb_chan_side(const e_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + switch (cb_type) { + case e_rr_type::CHANX: + return LEFT; + case e_rr_type::CHANY: + return BOTTOM; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid type of connection block!\n"); + } +} + +/* Get the side of routing channel in the GSB according to the side of IPIN */ +e_side RRGSB::get_cb_chan_side(const e_side& ipin_side) const { + switch (ipin_side) { + case TOP: + return LEFT; + case RIGHT: + return BOTTOM; + case BOTTOM: + return LEFT; + case LEFT: + return BOTTOM; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Invalid type of ipin_side!\n"); + } +} + +vtr::Point RRGSB::get_side_block_coordinate(const e_side& side) const { + SideManager side_manager(side); + VTR_ASSERT(side_manager.validate()); + vtr::Point ret(get_sb_x(), get_sb_y()); + + switch (side_manager.get_side()) { + case TOP: + /* (0 == side) */ + /* 1. Channel Y [x][y+1] inputs */ + ret.set_y(ret.y() + 1); + break; + case RIGHT: + /* 1 == side */ + /* 2. Channel X [x+1][y] inputs */ + ret.set_x(ret.x() + 1); + break; + case BOTTOM: + /* 2 == side */ + /* 3. Channel Y [x][y] inputs */ + break; + case LEFT: + /* 3 == side */ + /* 4. Channel X [x][y] inputs */ + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, " Invalid side!\n"); + } + + return ret; +} + +vtr::Point RRGSB::get_grid_coordinate() const { + return coordinate_; +} + +/************************************************************************ + * Public Mutators + ***********************************************************************/ +/* get a copy from a source */ +void RRGSB::set(const RRGSB& src) { + /* Copy coordinate */ + set_coordinate(src.get_sb_coordinate().x(), src.get_sb_coordinate().y()); + + /* Initialize sides */ + init_num_sides(src.get_num_sides()); + + /* Copy vectors */ + for (size_t side = 0; side < src.get_num_sides(); ++side) { + SideManager side_manager(side); + /* Copy chan_nodes */ + /* skip if there is no channel width */ + if (0 < src.get_chan_width(side_manager.get_side())) { + chan_node_[side_manager.get_side()].set(src.chan_node_[side_manager.get_side()]); + /* Copy chan_node_direction_*/ + chan_node_direction_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_chan_width(side_manager.get_side()); ++inode) { + chan_node_direction_[side_manager.get_side()].push_back(src.get_chan_node_direction(side_manager.get_side(), inode)); + } + } + + /* Copy opin_node and opin_node_grid_side_ */ + opin_node_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_num_opin_nodes(side_manager.get_side()); ++inode) { + opin_node_[side_manager.get_side()].push_back(src.get_opin_node(side_manager.get_side(), inode)); + } + + /* Copy ipin_node and ipin_node_grid_side_ */ + ipin_node_[side_manager.get_side()].clear(); + for (size_t inode = 0; inode < src.get_num_ipin_nodes(side_manager.get_side()); ++inode) { + ipin_node_[side_manager.get_side()].push_back(src.get_ipin_node(side_manager.get_side(), inode)); + } + } +} + +/* Set the coordinate (x,y) for the switch block */ +void RRGSB::set_coordinate(const size_t& x, const size_t& y) { + coordinate_.set(x, y); +} + +/* Allocate the vectors with the given number of sides */ +void RRGSB::init_num_sides(const size_t& num_sides) { + /* Initialize the vectors */ + chan_node_.resize(num_sides); + chan_node_direction_.resize(num_sides); + ipin_node_.resize(num_sides); + opin_node_.resize(num_sides); +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_chan_node(const e_side& node_side, + const RRChan& rr_chan, + const std::vector& rr_chan_dir) { + /* Validate: 1. side is valid, the type of node is valid */ + VTR_ASSERT(validate_side(node_side)); + + /* fill the dedicated element in the vector */ + chan_node_[size_t(node_side)].set(rr_chan); + chan_node_direction_[size_t(node_side)].resize(rr_chan_dir.size()); + for (size_t inode = 0; inode < rr_chan_dir.size(); ++inode) { + chan_node_direction_[size_t(node_side)][inode] = rr_chan_dir[inode]; + } +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_ipin_node(const RRNodeId& node, const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + /* push pack the dedicated element in the vector */ + ipin_node_[size_t(node_side)].push_back(node); +} + +/* Add a node to the chan_node_ list and also assign its direction in chan_node_direction_ */ +void RRGSB::add_opin_node(const RRNodeId& node, const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + /* push pack the dedicated element in the vector */ + opin_node_[size_t(node_side)].push_back(node); +} + +/* Add a node to the mux_node_ */ +void RRGSB::add_mux_node(const RRNodeId& mux_node) { + /* push pack the dedicated element in the vector */ + mux_node_.push_back(mux_node); +} + +void RRGSB::sort_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& track_id) { + std::unordered_map> from_grid_edge_map; + std::unordered_map> from_track_edge_map; + + const RRNodeId& chan_node = chan_node_[size_t(chan_side)].get_node(track_id); + + /* Count the edges and ensure every of them has been sorted */ + size_t edge_counter = 0; + + /* For each incoming edge, find the node side and index in this GSB. + * and cache these. Then we will use the data to sort the edge in the + * following sequence: + * 0----------------------------------------------------------------> num_in_edges() + * |<--TOP side-->|<--RIGHT side-->|<--BOTTOM side-->|<--LEFT side-->| + * For each side, the edge will be sorted by the node index starting from 0 + * For each side, the edge from grid pins will be the 1st part + * while the edge from routing tracks will be the 2nd part + */ + for (const RREdgeId& edge : rr_graph.node_in_edges(chan_node)) { + /* We care the source node of this edge, and it should be an input of the GSB!!! */ + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + e_side side = NUM_2D_SIDES; + int index = 0; + get_node_side_and_index(rr_graph, src_node, IN_PORT, side, index); + + /* Must have valid side and index */ + if (NUM_2D_SIDES == side) { + VTR_LOG_DEBUG("GSB[%lu][%lu]:\n", get_x(), get_y()); + VTR_LOG_DEBUG("----------------------------------\n"); + VTR_LOG_DEBUG("SRC node:\n"); + VTR_LOG_DEBUG("Node info: %s\n", rr_graph.node_coordinate_to_string(src_node).c_str()); + VTR_LOG_DEBUG("Node ptc: %d\n", rr_graph.node_ptc_num(src_node)); + VTR_LOG_DEBUG("Fan-out nodes:\n"); + for (const auto& temp_edge : rr_graph.edge_range(src_node)) { + VTR_LOG_DEBUG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_sink_node(temp_edge)).c_str()); + } + VTR_LOG_DEBUG("\n----------------------------------\n"); + VTR_LOG_DEBUG("Channel node:\n"); + VTR_LOG_DEBUG("Node info: %s\n", rr_graph.node_coordinate_to_string(chan_node).c_str()); + VTR_LOG_DEBUG("Node ptc: %d\n", rr_graph.node_ptc_num(chan_node)); + VTR_LOG_DEBUG("Fan-in nodes:\n"); + for (const auto& temp_edge : rr_graph.node_in_edges(chan_node)) { + VTR_LOG_DEBUG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_src_node(temp_edge)).c_str()); + } + } + + VTR_ASSERT(NUM_2D_SIDES != side); + VTR_ASSERT(OPEN != index); + + if (e_rr_type::OPIN == rr_graph.node_type(src_node)) { + from_grid_edge_map[side][index] = edge; + } else { + VTR_ASSERT((e_rr_type::CHANX == rr_graph.node_type(src_node)) + || (e_rr_type::CHANY == rr_graph.node_type(src_node))); + from_track_edge_map[side][index] = edge; + } + + edge_counter++; + } + + /* Store the sorted edge */ + for (size_t side = 0; side < get_num_sides(); ++side) { + /* Edges from grid outputs are the 1st part */ + for (size_t opin_id = 0; opin_id < opin_node_[side].size(); ++opin_id) { + if ((0 < from_grid_edge_map.count(side)) + && (0 < from_grid_edge_map.at(side).count(opin_id))) { + chan_node_in_edges_[size_t(chan_side)][track_id].push_back(from_grid_edge_map[side][opin_id]); + } + } + + /* Edges from routing tracks are the 2nd part */ + for (size_t itrack = 0; itrack < chan_node_[side].get_chan_width(); ++itrack) { + if ((0 < from_track_edge_map.count(side)) + && (0 < from_track_edge_map.at(side).count(itrack))) { + chan_node_in_edges_[size_t(chan_side)][track_id].push_back(from_track_edge_map[side][itrack]); + } + } + } + + VTR_ASSERT(edge_counter == chan_node_in_edges_[size_t(chan_side)][track_id].size()); +} + +void RRGSB::sort_chan_node_in_edges(const RRGraphView& rr_graph) { + /* Allocate here, as sort edge is optional, we do not allocate when adding nodes */ + chan_node_in_edges_.resize(get_num_sides()); + + for (size_t side = 0; side < get_num_sides(); ++side) { + SideManager side_manager(side); + /* Bypass boundary GSBs here. When perimeter_cb option is on, Some GSBs may have only 1 side of CHANX or CHANY. There are no edges in the GSB, so we should skip them */ + chan_node_in_edges_[side].resize(chan_node_[side].get_chan_width()); + for (size_t track_id = 0; track_id < chan_node_[side].get_chan_width(); ++track_id) { + /* Only sort the output nodes and bypass passing wires */ + if ((OUT_PORT == chan_node_direction_[side][track_id]) + && (false == is_sb_node_passing_wire(rr_graph, side_manager.get_side(), track_id))) { + sort_chan_node_in_edges(rr_graph, side_manager.get_side(), track_id); + } + } + } +} + +void RRGSB::sort_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& ipin_side, + const size_t& ipin_id) { + std::unordered_map from_track_edge_map; + std::array, NUM_2D_SIDES> from_opin_edge_map; + + e_side chan_side = get_cb_chan_side(ipin_side); + + const RRNodeId& ipin_node = ipin_node_[size_t(ipin_side)][ipin_id]; + + /* Count the edges and ensure every of them has been sorted */ + size_t edge_counter = 0; + + /* For each incoming edge, find the node side and index in this GSB. + * and cache these. Then we will use the data to sort the edge in the + * following sequence: + * 0----------------------------------------------------------------> num_in_edges() + * |<---------------------------1st part routing tracks -------------> + * |<--TOP side-->|<--RIGHT side-->|<--BOTTOM side-->|<--LEFT side-->| + * |<---------------------------2nd part IPINs -------------> + * |<--TOP side-->|<--RIGHT side-->|<--BOTTOM side-->|<--LEFT side-->| + * For each side, the edge will be sorted by the node index starting from 0 + * For each side, the edge from grid pins will be the 2nd part (sorted by ptc number) + * while the edge from routing tracks will be the 1st part + */ + for (const RREdgeId& edge : rr_graph.node_in_edges(ipin_node)) { + /* We care the source node of this edge, and it should be an input of the GSB!!! */ + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + /* In this part, we only sort routing track nodes. IPIN nodes will be handled later */ + if ((e_rr_type::CHANX != rr_graph.node_type(src_node)) + && (e_rr_type::CHANY != rr_graph.node_type(src_node))) { + continue; + } + /* The driver routing channel node can be either an input or an output to the GSB. + * Just try to find a qualified one. */ + int index = OPEN; + index = get_node_index(rr_graph, src_node, chan_side, IN_PORT); + if (OPEN == index) { + index = get_node_index(rr_graph, src_node, chan_side, OUT_PORT); + } + + /* Must have valid side and index */ + if (OPEN == index) { + VTR_LOG_DEBUG("GSB[%lu][%lu]:\n", get_x(), get_y()); + VTR_LOG_DEBUG("----------------------------------\n"); + VTR_LOG_DEBUG("SRC node:\n"); + VTR_LOG_DEBUG("Node info: %s\n", rr_graph.node_coordinate_to_string(src_node).c_str()); + VTR_LOG_DEBUG("Node ptc: %d\n", rr_graph.node_ptc_num(src_node)); + VTR_LOG_DEBUG("Fan-out nodes:\n"); + for (const auto& temp_edge : rr_graph.edge_range(src_node)) { + VTR_LOG_DEBUG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_sink_node(temp_edge)).c_str()); + } + VTR_LOG_DEBUG("\n----------------------------------\n"); + VTR_LOG_DEBUG("IPIN node:\n"); + VTR_LOG_DEBUG("Node info: %s\n", rr_graph.node_coordinate_to_string(ipin_node).c_str()); + VTR_LOG_DEBUG("Node ptc: %d\n", rr_graph.node_ptc_num(ipin_node)); + VTR_LOG_DEBUG("Fan-in nodes:\n"); + for (const auto& temp_edge : rr_graph.node_in_edges(ipin_node)) { + VTR_LOG_DEBUG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_src_node(temp_edge)).c_str()); + } + } + + VTR_ASSERT(OPEN != index); + + VTR_ASSERT((e_rr_type::CHANX == rr_graph.node_type(src_node)) + || (e_rr_type::CHANY == rr_graph.node_type(src_node))); + from_track_edge_map[index] = edge; + edge_counter++; + } + + for (const RREdgeId& edge : rr_graph.node_in_edges(ipin_node)) { + /* We care the source node of this edge, and it should be an input of the GSB!!! */ + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + /* In this part, we only sort routing track nodes. IPIN nodes will be handled later */ + if (e_rr_type::OPIN != rr_graph.node_type(src_node)) { + continue; + } + enum e_side cb_opin_side = NUM_2D_SIDES; + int cb_opin_index = -1; + get_node_side_and_index(rr_graph, src_node, IN_PORT, cb_opin_side, + cb_opin_index); + VTR_ASSERT((-1 != cb_opin_index) && (NUM_2D_SIDES != cb_opin_side)); + /* Must have valid side and index */ + if (OPEN == cb_opin_index || NUM_2D_SIDES == cb_opin_side) { + VTR_LOG_DEBUG("GSB[%lu][%lu]:\n", get_x(), get_y()); + VTR_LOG_DEBUG("----------------------------------\n"); + VTR_LOG_DEBUG("SRC node:\n"); + VTR_LOG_DEBUG("Node info: %s\n", rr_graph.node_coordinate_to_string(src_node).c_str()); + VTR_LOG_DEBUG("Node ptc: %d\n", rr_graph.node_ptc_num(src_node)); + VTR_LOG_DEBUG("Fan-out nodes:\n"); + for (const auto& temp_edge : rr_graph.edge_range(src_node)) { + VTR_LOG_DEBUG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_sink_node(temp_edge)).c_str()); + } + VTR_LOG_DEBUG("\n----------------------------------\n"); + VTR_LOG_DEBUG("IPIN node:\n"); + VTR_LOG_DEBUG("Node info: %s\n", rr_graph.node_coordinate_to_string(ipin_node).c_str()); + VTR_LOG_DEBUG("Node ptc: %d\n", rr_graph.node_ptc_num(ipin_node)); + VTR_LOG_DEBUG("Fan-in nodes:\n"); + for (const auto& temp_edge : rr_graph.node_in_edges(ipin_node)) { + VTR_LOG_DEBUG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_src_node(temp_edge)).c_str()); + } + } + from_opin_edge_map[size_t(cb_opin_side)][cb_opin_index] = edge; + edge_counter++; + } + + /* Store the sorted edge */ + for (size_t itrack = 0; itrack < chan_node_[size_t(chan_side)].get_chan_width(); ++itrack) { + if (0 < from_track_edge_map.count(itrack)) { + ipin_node_in_edges_[size_t(ipin_side)][ipin_id].push_back(from_track_edge_map[itrack]); + } + } + + for (e_side iside : {TOP, RIGHT, BOTTOM, LEFT}) { + for (size_t ipin = 0; ipin < get_num_opin_nodes(iside); ++ipin) { + if (0 < from_opin_edge_map[size_t(iside)].count(ipin)) { + ipin_node_in_edges_[size_t(ipin_side)][ipin_id].push_back(from_opin_edge_map[size_t(iside)][ipin]); + } + } + } + + VTR_ASSERT(edge_counter == ipin_node_in_edges_[size_t(ipin_side)][ipin_id].size()); +} + +void RRGSB::sort_ipin_node_in_edges(const RRGraphView& rr_graph) { + /* Allocate here, as sort edge is optional, we do not allocate when adding nodes */ + ipin_node_in_edges_.resize(get_num_sides()); + + for (e_rr_type cb_type : {e_rr_type::CHANX, e_rr_type::CHANY}) { + for (e_side ipin_side : get_cb_ipin_sides(cb_type)) { + SideManager side_manager(ipin_side); + ipin_node_in_edges_[size_t(ipin_side)].resize(ipin_node_[size_t(ipin_side)].size()); + for (size_t ipin_id = 0; ipin_id < ipin_node_[size_t(ipin_side)].size(); ++ipin_id) { + sort_ipin_node_in_edges(rr_graph, side_manager.get_side(), ipin_id); + } + } + } +} + +void RRGSB::build_cb_opin_nodes(const RRGraphView& rr_graph) { + for (e_rr_type cb_type : {e_rr_type::CHANX, e_rr_type::CHANY}) { + size_t icb_type = cb_type == e_rr_type::CHANX ? 0 : 1; + std::vector cb_ipin_sides = get_cb_ipin_sides(cb_type); + for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { + enum e_side cb_ipin_side = cb_ipin_sides[iside]; + for (size_t inode = 0; inode < get_num_ipin_nodes(cb_ipin_side); + ++inode) { + std::vector driver_rr_edges = + get_ipin_node_in_edges(rr_graph, cb_ipin_side, inode); + for (const RREdgeId curr_edge : driver_rr_edges) { + RRNodeId cand_node = rr_graph.edge_src_node(curr_edge); + if (e_rr_type::OPIN != rr_graph.node_type(cand_node)) { + continue; + } + enum e_side cb_opin_side = NUM_2D_SIDES; + int cb_opin_index = -1; + get_node_side_and_index(rr_graph, cand_node, IN_PORT, cb_opin_side, + cb_opin_index); + if ((-1 == cb_opin_index) || (NUM_2D_SIDES == cb_opin_side)) { + VTR_LOG_DEBUG("GSB[%lu][%lu]:\n", get_x(), get_y()); + VTR_LOG_DEBUG("----------------------------------\n"); + VTR_LOG_DEBUG("SRC node:\n"); + VTR_LOG_DEBUG("Node info: %s\n", rr_graph.node_coordinate_to_string(cand_node).c_str()); + VTR_LOG_DEBUG("Node ptc: %d\n", rr_graph.node_ptc_num(cand_node)); + VTR_LOG_DEBUG("Fan-out nodes:\n"); + for (const auto& temp_edge : rr_graph.edge_range(cand_node)) { + VTR_LOG_DEBUG("\t%s\n", rr_graph.node_coordinate_to_string(rr_graph.edge_sink_node(temp_edge)).c_str()); + } + } + VTR_ASSERT((-1 != cb_opin_index) && (NUM_2D_SIDES != cb_opin_side)); + + if (cb_opin_node_[icb_type][size_t(cb_opin_side)].end() == std::find(cb_opin_node_[icb_type][size_t(cb_opin_side)].begin(), cb_opin_node_[icb_type][size_t(cb_opin_side)].end(), cand_node)) { + cb_opin_node_[icb_type][size_t(cb_opin_side)].push_back(cand_node); + } + } + } + } + } +} + +/************************************************************************ + * Public Mutators: clean-up functions + ***********************************************************************/ +/* Reset the RRGSB to pristine state */ +void RRGSB::clear() { + /* Clean all the vectors */ + VTR_ASSERT(validate_num_sides()); + /* Clear the inner vector of each matrix */ + for (size_t side = 0; side < get_num_sides(); ++side) { + chan_node_direction_[side].clear(); + chan_node_[side].clear(); + ipin_node_[side].clear(); + opin_node_[side].clear(); + } + chan_node_direction_.clear(); + chan_node_.clear(); + ipin_node_.clear(); + opin_node_.clear(); +} + +/* Clean the chan_width of a side */ +void RRGSB::clear_chan_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + chan_node_[size_t(node_side)].clear(); + chan_node_direction_[size_t(node_side)].clear(); +} + +/* Clean the number of IPINs of a side */ +void RRGSB::clear_ipin_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + ipin_node_[size_t(node_side)].clear(); +} + +/* Clean the number of OPINs of a side */ +void RRGSB::clear_opin_nodes(const e_side& node_side) { + VTR_ASSERT(validate_side(node_side)); + + opin_node_[size_t(node_side)].clear(); +} + +/* Clean chan/opin/ipin nodes at one side */ +void RRGSB::clear_one_side(const e_side& node_side) { + clear_chan_nodes(node_side); + clear_ipin_nodes(node_side); + clear_opin_nodes(node_side); +} + +/************************************************************************ + * Internal Accessors: identify mirrors + ***********************************************************************/ +size_t RRGSB::get_track_id_first_short_connection(const RRGraphView& rr_graph, const e_side& node_side) const { + VTR_ASSERT(validate_side(node_side)); + + /* Walk through chan_nodes and find the first short connection */ + for (size_t inode = 0; inode < get_chan_width(node_side); ++inode) { + if (true == is_sb_node_passing_wire(rr_graph, node_side, inode)) { + return inode; + } + } + + return size_t(-1); +} + +/************************************************************************ + * Internal validators + ***********************************************************************/ +/* Validate if the number of sides are consistent among internal data arrays ! */ +bool RRGSB::validate_num_sides() const { + size_t num_sides = chan_node_direction_.size(); + + if (num_sides != chan_node_.size()) { + return false; + } + + if (num_sides != ipin_node_.size()) { + return false; + } + + if (num_sides != opin_node_.size()) { + return false; + } + + return true; +} + +/* Check if the side valid in the context: does the switch block have the side? */ +bool RRGSB::validate_side(const e_side& side) const { + return (size_t(side) < get_num_sides()); +} + +/* Check the track_id is valid for chan_node_ and chan_node_direction_ */ +bool RRGSB::validate_track_id(const e_side& side, const size_t& track_id) const { + if (false == validate_side(side)) { + return false; + } + + return ((track_id < chan_node_[size_t(side)].get_chan_width()) + && (track_id < chan_node_direction_[size_t(side)].size())); +} + +/* Check the opin_node_id is valid for opin_node_ and opin_node_grid_side_ */ +bool RRGSB::validate_opin_node_id(const e_side& side, const size_t& node_id) const { + if (false == validate_side(side)) { + return false; + } + return (node_id < opin_node_[size_t(side)].size()); +} + +/* Check the opin_node_id is valid for opin_node_ and opin_node_grid_side_ */ +bool RRGSB::validate_cb_opin_node_id(const e_rr_type& cb_type, const e_side& side, const size_t& node_id) const { + if (false == validate_side(side)) { + return false; + } + size_t icb_type = get_cb_opin_type_id(cb_type); + return (node_id < cb_opin_node_[icb_type][size_t(side)].size()); +} + +/* Check the ipin_node_id is valid for opin_node_ and opin_node_grid_side_ */ +bool RRGSB::validate_ipin_node_id(const e_side& side, const size_t& node_id) const { + if (false == validate_side(side)) { + return false; + } + return (node_id < ipin_node_[size_t(side)].size()); +} + +bool RRGSB::validate_cb_type(const e_rr_type& cb_type) const { + return ((e_rr_type::CHANX == cb_type) || (e_rr_type::CHANY == cb_type)); +} + +size_t RRGSB::get_cb_opin_type_id(const e_rr_type& cb_type) const { + VTR_ASSERT(validate_cb_type(cb_type)); + return cb_type == e_rr_type::CHANX ? 0 : 1; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_gsb.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_gsb.h new file mode 100644 index 00000000000..347a72324fb --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_gsb.h @@ -0,0 +1,285 @@ +#pragma once + +#include "vtr_geometry.h" + +#include "rr_chan.h" +#include "rr_graph_view.h" + +/******************************************************************** + * Object Generic Switch Block + * This block contains + * 1. A switch block + * 2. A X-direction Connection block locates at the left side of the switch block + * 2. A Y-direction Connection block locates at the top side of the switch block + * + * TOP SIDE + * +-------------+ +---------------------------------+ + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | X-direction | | | + * | CB | LEFT SIDE | Switch Block | RIGHT SIDE + * | [x][y] | | [x][y] | + * | | | | + * | | | CHAN_NODES CHAN_NODES | + * | | | | + * | | | OPIN_NODES OPIN_NODES | + * | | | | + * | | | OPIN_NODE CHAN_NODES OPIN_NODES | + * +-------------+ +---------------------------------+ + * BOTTOM SIDE + * +-------------+ +---------------------------------+ + * | Grid | | Y-direction CB | + * | [x][y] | | [x][y] | + * +-------------+ +---------------------------------+ + * + * num_sides: number of sides of this switch block + * chan_rr_node: a collection of rr_nodes as routing tracks locating at each side of the Switch block <0..num_sides-1><0..chan_width-1> + * chan_rr_node_direction: Indicate if this rr_node is an input or an output of the Switch block <0..num_sides-1><0..chan_width-1> + * ipin_rr_node: a collection of rr_nodes as IPIN of a GRID locating at each side of the Switch block <0..num_sides-1><0..num_ipin_rr_nodes-1> + * ipin_rr_node_grid_side: specify the side of the input pins on which side of a GRID <0..num_sides-1><0..num_ipin_rr_nodes-1> + * opin_rr_node: a collection of rr_nodes as OPIN of a GRID locating at each side of the Switch block <0..num_sides-1><0..num_opin_rr_nodes-1> + * opin_rr_node_grid_side: specify the side of the output pins on which side of a GRID <0..num_sides-1><0..num_opin_rr_nodes-1> + * num_reserved_conf_bits: number of reserved configuration bits this switch block requires (mainly due to RRAM-based multiplexers) + * num_conf_bits: number of configuration bits this switch block requires + *******************************************************************/ +class RRGSB { + public: /* Contructors */ + RRGSB(); /* Default constructor */ + public: /* Accessors */ + /* Get the number of sides of this SB */ + size_t get_num_sides() const; + + /* Get the number of routing tracks on a side */ + size_t get_chan_width(const e_side& side) const; + + /* Get the type of routing tracks on a side */ + e_rr_type get_chan_type(const e_side& side) const; + + /* Get the maximum number of routing tracks on all sides */ + size_t get_max_chan_width() const; + + /* Get the number of routing tracks of a X/Y-direction CB */ + size_t get_cb_chan_width(const e_rr_type& cb_type) const; + + /* Return read-only object of the routing channels with a given side */ + const RRChan& chan(const e_side& chan_side) const; + + /* Get the sides of CB ipins in the array */ + std::vector get_cb_ipin_sides(const e_rr_type& cb_type) const; + /* Get the sides of CB opins in the array, OPINs can only be at the same sides of IPINs. Differently, they are inputs to a connection block */ + std::vector get_cb_opin_sides(const e_rr_type& cb_type) const; + + /* Get the direction of a rr_node at a given side and track_id */ + enum PORTS get_chan_node_direction(const e_side& side, const size_t& track_id) const; + + /* Get a list of segments used in this routing channel */ + std::vector get_chan_segment_ids(const e_side& side) const; + + /* Get a list of segments used in this routing channel */ + std::vector get_chan_node_ids_by_segment_ids(const e_side& side, + const RRSegmentId& seg_id) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_chan_node(const e_side& side, const size_t& track_id) const; + + /* get all the sorted incoming edges for a rr_node at a given side and track_id */ + std::vector get_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& track_id) const; + + /* get all the sorted incoming edges for a IPIN rr_node at a given side and ipin_id */ + std::vector get_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& side, + const size_t& ipin_id) const; + + /* get the segment id of a channel rr_node */ + RRSegmentId get_chan_node_segment(const e_side& side, const size_t& track_id) const; + + /* Get the number of IPIN rr_nodes on a side */ + size_t get_num_ipin_nodes(const e_side& side) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_ipin_node(const e_side& side, const size_t& node_id) const; + + /* Get the number of OPIN rr_nodes on a side */ + size_t get_num_opin_nodes(const e_side& side) const; + /* Get the number of OPIN rr_nodes on a side of a connection block */ + size_t get_num_cb_opin_nodes(const e_rr_type& cb_type, const e_side& side) const; + + /* get a rr_node at a given side and track_id */ + RRNodeId get_opin_node(const e_side& side, const size_t& node_id) const; + /* get a rr_node at a given side and track_id for a connection block */ + RRNodeId get_cb_opin_node(const e_rr_type& cb_type, const e_side& side, const size_t& node_id) const; + + /* Get the number of MUX rr_nodes */ + size_t get_num_mux_nodes() const; + + /* get a rr_node at a given ptc number */ + RRNodeId get_mux_node(const size_t& ptc) const; + + int get_cb_chan_node_index(const e_rr_type& cb_type, const RRNodeId& node) const; + + int get_chan_node_index(const e_side& node_side, const RRNodeId& node) const; + + /* Get the node index in the array, return -1 if not found */ + int get_node_index(const RRGraphView& rr_graph, const RRNodeId& node, const e_side& node_side, const PORTS& node_direction) const; + + /* Given a rr_node, try to find its side and index in the Switch block */ + void get_node_side_and_index(const RRGraphView& rr_graph, const RRNodeId& node, const PORTS& node_direction, e_side& node_side, int& node_index) const; + + /* Check if the node exist in the opposite side of this Switch Block */ + bool is_sb_node_exist_opposite_side(const RRGraphView& rr_graph, const RRNodeId& node, const e_side& node_side) const; + + bool is_opin_node(const RRNodeId& node) const; + bool is_ipin_node(const RRNodeId& node) const; + bool is_mux_node(const RRNodeId& node) const; + bool is_chan_node(const RRNodeId& node) const; + + public: /* Accessors: to identify mirrors */ + /* check if the connect block exists in the GSB */ + bool is_cb_exist(const e_rr_type& cb_type) const; + + /* check if the switch block exists in the GSB, this function checks if a switch block physically exists (no routing wires, no OPIN nodes, and no interconnecting wires) */ + bool is_sb_exist(const RRGraphView& rr_graph) const; + + /* Check if the node imply a short connection inside the SB, which happens to long wires across a FPGA fabric */ + bool is_sb_node_passing_wire(const RRGraphView& rr_graph, const e_side& node_side, const size_t& track_id) const; + + /* check if the candidate SB satisfy the basic requirements + * on being a mirror of the current one + */ + bool is_sb_mirrorable(const RRGraphView& rr_graph, const RRGSB& cand) const; + + public: /* Cooridinator conversion and output */ + size_t get_x() const; /* get the x coordinate of this switch block */ + size_t get_y() const; /* get the y coordinate of this switch block */ + size_t get_sb_x() const; /* get the x coordinate of this switch block */ + size_t get_sb_y() const; /* get the y coordinate of this switch block */ + vtr::Point get_sb_coordinate() const; /* Get the coordinate of the SB */ + size_t get_cb_x(const e_rr_type& cb_type) const; /* get the x coordinate of this X/Y-direction block */ + size_t get_cb_y(const e_rr_type& cb_type) const; /* get the y coordinate of this X/Y-direction block */ + vtr::Point get_cb_coordinate(const e_rr_type& cb_type) const; /* Get the coordinate of the X/Y-direction CB */ + e_side get_cb_chan_side(const e_rr_type& cb_type) const; /* get the side of a Connection block */ + e_side get_cb_chan_side(const e_side& ipin_side) const; /* get the side of a Connection block */ + vtr::Point get_side_block_coordinate(const e_side& side) const; + vtr::Point get_grid_coordinate() const; + + public: /* Mutators */ + /* get a copy from a source */ + void set(const RRGSB& src); + void set_coordinate(const size_t& x, const size_t& y); + + /* Allocate the vectors with the given number of sides */ + void init_num_sides(const size_t& num_sides); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_chan_node(const e_side& node_side, + const RRChan& rr_chan, + const std::vector& rr_chan_dir); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_ipin_node(const RRNodeId& node, + const e_side& node_side); + + /* Add a node to the chan_rr_node_ list and also + * assign its direction in chan_rr_node_direction_ + */ + void add_opin_node(const RRNodeId& node, + const e_side& node_side); + + /* Add a node to the mux_node_ */ + void add_mux_node(const RRNodeId& mux_node); + + /* Sort all the incoming edges for routing channel rr_node */ + void sort_chan_node_in_edges(const RRGraphView& rr_graph); + /* Sort all the incoming edges for input pin rr_node */ + void sort_ipin_node_in_edges(const RRGraphView& rr_graph); + /* Build the lists of opin node for connection blocks. This is required after adding all the nodes */ + void build_cb_opin_nodes(const RRGraphView& rr_graph); + + public: /* Mutators: cleaners */ + void clear(); + + /* Clean the chan_width of a side */ + void clear_chan_nodes(const e_side& node_side); + + /* Clean the number of IPINs of a side */ + void clear_ipin_nodes(const e_side& node_side); + + /* Clean the number of OPINs of a side */ + void clear_opin_nodes(const e_side& node_side); + + /* Clean chan/opin/ipin nodes at one side */ + void clear_one_side(const e_side& node_side); + + private: /* Private Mutators: edge sorting */ + /* Sort all the incoming edges for one channel rr_node */ + void sort_chan_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& track_id); + + /* Sort all the incoming edges for one input pin rr_node */ + void sort_ipin_node_in_edges(const RRGraphView& rr_graph, + const e_side& chan_side, + const size_t& ipin_id); + + private: /* internal functions */ + size_t get_track_id_first_short_connection(const RRGraphView& rr_graph, const e_side& node_side) const; + + private: /* internal validators */ + bool validate_num_sides() const; + bool validate_side(const e_side& side) const; + bool validate_track_id(const e_side& side, const size_t& track_id) const; + bool validate_cb_opin_node_id(const e_rr_type& cb_type, const e_side& side, const size_t& node_id) const; + bool validate_opin_node_id(const e_side& side, const size_t& node_id) const; + bool validate_ipin_node_id(const e_side& side, const size_t& node_id) const; + bool validate_cb_type(const e_rr_type& cb_type) const; + size_t get_cb_opin_type_id(const e_rr_type& cb_type) const; + + private: /* Internal Data */ + /* Coordinator */ + vtr::Point coordinate_; + + /* Routing channel data + * Each GSB may have four sides of routing track nodes + */ + /* Node id in rr_graph denoting each routing track */ + std::vector chan_node_; + + /* Direction of a port when the channel node appear in the GSB module */ + std::vector> chan_node_direction_; + + /* Sequence of edge ids for each routing channel node, + * this is sorted by the location of edge source nodes in the context of GSB + * The edge sorting is critical to uniquify the routing modules in OpenFPGA + * This is due to that VPR allocate and sort edges randomly when building the rr_graph + * As a result, previous nodes of a chan node may be the same in different GSBs + * but their sequence is not. This will cause graph comparison to fail when uniquifying + * the routing modules. Therefore, edge sorting can be done inside the GSB + * + * Storage organization: + * [chan_side][chan_node][edge_id_in_gsb_context] + */ + std::vector>> chan_node_in_edges_; + /* Sequence of edge ids for each input pin node. Same rules applied as the channel nodes */ + std::vector>> ipin_node_in_edges_; + + /* Logic Block Inputs data */ + std::vector> ipin_node_; + + /* Logic Block Outputs data */ + std::vector> opin_node_; + /* Logic block outputs which directly drive IPINs in connection block, + * CBX -> array[0], CBY -> array[1] + * Each CB may have OPINs from all sides + */ + std::array, NUM_2D_SIDES>, 2> cb_opin_node_; + + /* MUX Nodes Data */ + std::vector mux_node_; +}; diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/side_manager.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/side_manager.cpp new file mode 100644 index 00000000000..73e51567110 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/side_manager.cpp @@ -0,0 +1,170 @@ +/******************************************************************** + * Memeber function for class SideManagerManager + *******************************************************************/ +#include "side_manager.h" + +/* Constructors */ +SideManager::SideManager(enum e_side side) { + side_ = side; +} + +SideManager::SideManager() { + side_ = NUM_2D_SIDES; +} + +SideManager::SideManager(size_t side) { + set_side(side); +} + +/* Public Accessors */ +enum e_side SideManager::get_side() const { + return side_; +} + +enum e_side SideManager::get_opposite() const { + switch (side_) { + case TOP: + return BOTTOM; + case RIGHT: + return LEFT; + case BOTTOM: + return TOP; + case LEFT: + return RIGHT; + default: + return NUM_2D_SIDES; + } +} + +enum e_side SideManager::get_rotate_clockwise() const { + switch (side_) { + case TOP: + return RIGHT; + case RIGHT: + return BOTTOM; + case BOTTOM: + return LEFT; + case LEFT: + return TOP; + default: + return NUM_2D_SIDES; + } +} + +enum e_side SideManager::get_rotate_counterclockwise() const { + switch (side_) { + case TOP: + return LEFT; + case RIGHT: + return TOP; + case BOTTOM: + return RIGHT; + case LEFT: + return BOTTOM; + default: + return NUM_2D_SIDES; + } +} + +bool SideManager::validate() const { + if (NUM_2D_SIDES == side_) { + return false; + } + return true; +} + +size_t SideManager::to_size_t() const { + switch (side_) { + case TOP: + return 0; + case RIGHT: + return 1; + case BOTTOM: + return 2; + case LEFT: + return 3; + default: + return 4; + } +} + +/* Convert to char* */ +const char* SideManager::c_str() const { + switch (side_) { + case TOP: + return "top"; + case RIGHT: + return "right"; + case BOTTOM: + return "bottom"; + case LEFT: + return "left"; + default: + return "invalid_side"; + } +} + +/* Convert to char* */ +std::string SideManager::to_string() const { + std::string ret; + switch (side_) { + case TOP: + ret.assign("top"); + break; + case RIGHT: + ret.assign("right"); + break; + case BOTTOM: + ret.assign("bottom"); + break; + case LEFT: + ret.assign("left"); + break; + default: + ret.assign("invalid_side"); + break; + } + + return ret; +} + +/* Public Mutators */ +void SideManager::set_side(size_t side) { + switch (side) { + case 0: + side_ = TOP; + return; + case 1: + side_ = RIGHT; + return; + case 2: + side_ = BOTTOM; + return; + case 3: + side_ = LEFT; + return; + default: + side_ = NUM_2D_SIDES; + return; + } +} + +void SideManager::set_side(enum e_side side) { + side_ = side; + return; +} + +void SideManager::set_opposite() { + side_ = get_opposite(); + return; +} + +void SideManager::rotate_clockwise() { + side_ = get_rotate_clockwise(); + return; +} + +void SideManager::rotate_counterclockwise() { + side_ = get_rotate_counterclockwise(); + return; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/side_manager.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/side_manager.h new file mode 100644 index 00000000000..266d4a5cda7 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/side_manager.h @@ -0,0 +1,44 @@ +#pragma once + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include + +/* Header files form archfpga library */ +#include "physical_types.h" + +/******************************************************************** + * Define a class for the sides of a physical block in FPGA architecture + * Basically, each block has four sides : + * TOP, RIGHT, BOTTOM, LEFT + * This class aims to provide a easy proctol for manipulating a side + ********************************************************************/ + +class SideManager { + public: /* Constructor */ + SideManager(enum e_side side); + SideManager(); + SideManager(size_t side); + + public: /* Accessors */ + enum e_side get_side() const; + enum e_side get_opposite() const; + enum e_side get_rotate_clockwise() const; + enum e_side get_rotate_counterclockwise() const; + bool validate() const; + size_t to_size_t() const; + const char* c_str() const; + std::string to_string() const; + + public: /* Mutators */ + void set_side(size_t side); + void set_side(enum e_side side); + void set_opposite(); + void rotate_clockwise(); + void rotate_counterclockwise(); + + private: /* internal data */ + enum e_side side_; +}; diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_chan_details_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_chan_details_builder.cpp new file mode 100644 index 00000000000..0573cd335d7 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_chan_details_builder.cpp @@ -0,0 +1,337 @@ +/************************************************************************ + * This file contains a builder for the ChanNodeDetails data structure + * Different from VPR rr_graph builders, this builder aims to create a + * highly regular routing channel. Thus, it is called tileable, + * which brings significant advantage in producing large FPGA fabrics. + ***********************************************************************/ +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" + +/************************************************************************ + * Generate the number of tracks for each types of routing segments + * w.r.t. the frequency of each of segments and channel width + * Note that if we dertermine the number of tracks per type using + * chan_width * segment_frequency / total_freq may cause + * The total track num may not match the chan_width, + * therefore, we assign tracks one by one until we meet the frequency requirement + * In this way, we can assign the number of tracks with repect to frequency + ***********************************************************************/ +std::vector get_num_tracks_per_seg_type(const size_t& chan_width, + const std::vector& segment_inf, + const bool& use_full_seg_groups) { + + const size_t num_segments = segment_inf.size(); + std::vector result(num_segments); + std::vector demand(num_segments); + + if (segment_inf.empty()) { + return result; + } + + /* Scale factor so we can divide by any length + * and still use integers */ + /* Get the sum of frequency */ + size_t scale = 1; + size_t freq_sum = 0; + for (size_t iseg = 0; iseg < num_segments; ++iseg) { + scale *= segment_inf[iseg].length; + freq_sum += segment_inf[iseg].frequency; + } + size_t reduce = scale * freq_sum; + + /* Init assignments to 0 and set the demand values */ + /* Get the fraction of each segment type considering the frequency: + * num_track_per_seg = chan_width * (freq_of_seg / sum_freq) + */ + for (size_t iseg = 0; iseg < num_segments; ++iseg) { + result[iseg] = 0; + demand[iseg] = scale * chan_width * segment_inf[iseg].frequency; + if (true == use_full_seg_groups) { + demand[iseg] /= segment_inf[iseg].length; + } + } + + /* check if the sum of num_tracks, matches the chan_width */ + /* Keep assigning tracks until we use them up */ + size_t assigned = 0; + size_t size = 0; + size_t imax = 0; + while (assigned < chan_width) { + /* Find current maximum demand */ + double max = 0; + for (size_t iseg = 0; iseg < num_segments; ++iseg) { + if (demand[iseg] > max) { + imax = iseg; + } + max = std::max(demand[iseg], max); + } + + /* Assign tracks to the type and reduce the types demand */ + size = (use_full_seg_groups ? segment_inf[imax].length : 1); + demand[imax] -= reduce; + result[imax] += size; + assigned += size; + } + + /* Undo last assignment if we were closer to goal without it */ + if ((assigned - chan_width) > (size / 2)) { + result[imax] -= size; + } + + return result; +} + +/************************************************************************ + * Adapt the number of channel width to a tileable routing architecture + ***********************************************************************/ +int adapt_to_tileable_route_chan_width(const int& chan_width, + const std::vector& segment_infs) { + int tileable_chan_width = 0; + + /* Estimate the number of segments per type by the given ChanW*/ + std::vector num_tracks_per_seg_type = get_num_tracks_per_seg_type(chan_width, + segment_infs, + true); /* Force to use the full segment group */ + /* Sum-up the number of tracks */ + for (size_t iseg = 0; iseg < num_tracks_per_seg_type.size(); ++iseg) { + tileable_chan_width += num_tracks_per_seg_type[iseg]; + } + + return tileable_chan_width; +} + +/************************************************************************ + * Build details of routing tracks in a channel + * The function will + * 1. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * + * 2. The starting point of each segment in the channel will be assigned + * For each segment group with same directionality (tracks have the same length), + * every L track will be a starting point (where L denotes the length of segments) + * In this case, if the number of tracks is not a multiple of L, + * indeed we may have some | Yes | No | + * +---------------------------------------+--------------+ + * | 1 | <--------MUX | Yes | No | + * +---------------------------------------+--------------+ + * | 2 | --------> | No | No | + * +---------------------------------------+--------------+ + * | 3 | <-------- | No | No | + * +---------------------------------------+--------------+ + * | 4 | --------> | No | No | + * +---------------------------------------+--------------+ + * | 5 | <-------- | No | No | + * +---------------------------------------+--------------+ + * | 7 | -------->MUX | No | Yes | + * +---------------------------------------+--------------+ + * | 8 | MUX<-------- | No | Yes | + * +---------------------------------------+--------------+ + * | 9 | MUX--------> | Yes | No | + * +---------------------------------------+--------------+ + * | 10 | <--------MUX | Yes | No | + * +---------------------------------------+--------------+ + * | 11 | -------->MUX | No | Yes | + * +------------------------------------------------------+ + * | 12 | <-------- | No | No | + * +------------------------------------------------------+ + * + * 3. SPECIAL for fringes: TOP|RIGHT|BOTTOM|RIGHT + * if device_side is NUM_SIDES, we assume this channel does not locate on borders + * All segments will start and ends with no exception + * + * 4. IMPORTANT: we should be aware that channel width maybe different + * in X-direction and Y-direction channels!!! + * So we will load segment details for different channels + ***********************************************************************/ +ChanNodeDetails build_unidir_chan_node_details(const size_t& chan_width, + const size_t& max_seg_length, + const bool& force_start, + const bool& force_end, + const std::vector& segment_inf) { + ChanNodeDetails chan_node_details; + size_t actual_chan_width = find_unidir_routing_channel_width(chan_width); + VTR_ASSERT(0 == actual_chan_width % 2); + + /* Reserve channel width */ + chan_node_details.reserve(chan_width); + /* Return if zero width is forced */ + if (0 == actual_chan_width) { + return chan_node_details; + } + + /* Find the number of segments required by each group */ + std::vector num_tracks = get_num_tracks_per_seg_type(actual_chan_width / 2, segment_inf, false); + + /* Add node to ChanNodeDetails */ + size_t cur_track = 0; + size_t bend_num = 0; // The index for bend segments + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + if (!segment_inf[iseg].is_bend) { + /* segment length will be set to maxium segment length if this is a longwire */ + size_t seg_len = segment_inf[iseg].length; + if (true == segment_inf[iseg].longline) { + seg_len = max_seg_length; + } + for (size_t itrack = 0; itrack < num_tracks[iseg]; ++itrack) { + bool seg_start = false; + bool seg_end = false; + /* Every first track of a group of Length-N wires, we set a starting point */ + if (0 == itrack % seg_len) { + seg_start = true; + } + /* Every last track of a group of Length-N wires or this is the last track in this group, we set an ending point */ + if ((seg_len - 1 == itrack % seg_len) + || (itrack == num_tracks[iseg] - 1)) { + seg_end = true; + } + /* Since this is a unidirectional routing architecture, + * Add a pair of tracks, 1 INC track and 1 DEC track + */ + int seg_index = segment_inf[iseg].seg_index; + chan_node_details.add_track(cur_track, Direction::INC, seg_index, seg_len, seg_start, seg_end); + cur_track++; + chan_node_details.add_track(cur_track, Direction::DEC, seg_index, seg_len, seg_start, seg_end); + cur_track++; + } + } else { // bend segment + bend_num++; + VTR_ASSERT(segment_inf[iseg].is_bend); + std::vector seg_len = segment_inf[iseg].part_len; + std::vector bend = segment_inf[iseg].bend; + VTR_ASSERT(seg_len.size() == 2); // Only support one bend position for a segment. + + std::vector num_tracks_bend; + /* Each bend part tracks number * + * For example, a length-5 segment with bend pattern: <- - U -> has 20 tracks. * + * Its num_tracks_bend is [20 * 3/5, 20 * 2/5] = [12, 8] */ + for (size_t i = 0; i < seg_len.size(); i++) + num_tracks_bend.push_back(num_tracks[iseg] * seg_len[i] / segment_inf[iseg].length); + + VTR_ASSERT(num_tracks_bend[0] + num_tracks_bend[1] == num_tracks[iseg]); + + for (size_t itrack = 0; itrack < num_tracks[iseg]; ++itrack) { + bool seg_start = false; + bool seg_end = false; + size_t seg_bend_start = 0; // seg_bend_start = 0 means not a bend start. + // seg_bend_start = i (i > 0) means a bend start for bend segment i. + size_t seg_bend_end = 0; // The same as seg_bend_start. + // Tracks has same seg_bend_start and seg_bend_end values will be + // connected by a delayless switch. + /* Every first track of a group of Length-N wires, we set a starting point */ + if (0 == itrack % segment_inf[iseg].length) { + seg_start = true; + } + /* Number seg_len[0] track of a group of Length-N wires, we set a bend start point */ + if (seg_len[0] == int(itrack) % segment_inf[iseg].length) { + seg_start = true; + seg_bend_start = bend_num; + } + /* Number seg_len[0] - 1 track of a group of Length-N wires, we set a bend end point */ + if (seg_len[0] - 1 == int(itrack) % segment_inf[iseg].length) { + seg_end = true; + seg_bend_end = bend_num; + } + /* Every last track of a group of Length-N wires or this is the last track in this group, we set an ending point */ + if ((segment_inf[iseg].length - 1 == int(itrack) % segment_inf[iseg].length) + || (itrack == num_tracks[iseg] - 1)) { + seg_end = true; + } + + int seg_index = segment_inf[iseg].seg_index; + + chan_node_details.add_track(cur_track, Direction::INC, seg_index, seg_len[0], seg_start, seg_end, seg_bend_start, seg_bend_end); + cur_track++; + chan_node_details.add_track(cur_track, Direction::DEC, seg_index, seg_len[0], seg_start, seg_end, seg_bend_start, seg_bend_end); + cur_track++; + } + + /*for (size_t itrack = 0; itrack < num_tracks_bend[0]; ++itrack) { + * + * bool seg_start = false; + * bool seg_end = false; + * size_t seg_bend_start = 0; + * size_t seg_bend_end = 0; + * + * if (0 == itrack % seg_len[0]) { + * seg_start = true; + * } + * + * if ((seg_len[0] - 1 == itrack % seg_len[0]) + * || (itrack == num_tracks_bend[0] - 1)) { + * seg_end = true; + * seg_bend_end = bend_num; + * } + * int seg_index = segment_inf[iseg].seg_index; + * + * chan_node_details.add_track(cur_track, Direction::INC, seg_index, seg_len[0], seg_start, seg_end, seg_bend_start, seg_bend_end); + * cur_track++; + * chan_node_details.add_track(cur_track, Direction::DEC, seg_index, seg_len[0], seg_start, seg_end, seg_bend_start, seg_bend_end); + * cur_track++; + * + * } + * for (size_t itrack = 0; itrack < num_tracks_bend[1]; ++itrack) { + * + * bool seg_start = false; + * bool seg_end = false; + * size_t seg_bend_start = 0; + * size_t seg_bend_end = 0; + * + * if (0 == itrack % seg_len[1]) { + * seg_start = true; + * seg_bend_start = bend_num; + * } + * + * if ((seg_len[1] - 1 == itrack % seg_len[1]) + * || (itrack == num_tracks_bend[1] - 1)) { + * seg_end = true; + * } + * int seg_index = segment_inf[iseg].seg_index; + * + * chan_node_details.add_track(cur_track, Direction::INC, seg_index, seg_len[1], seg_start, seg_end, seg_bend_start, seg_bend_end); + * cur_track++; + * + * chan_node_details.add_track(cur_track, Direction::DEC, seg_index, seg_len[1], seg_start, seg_end, seg_bend_start, seg_bend_end); + * cur_track++; + * + * }*/ + } + } + /* Check if all the tracks have been satisified */ + VTR_ASSERT(cur_track == actual_chan_width); + + /* If this is on the border of a device/heterogeneous blocks, segments should start/end */ + if (true == force_start) { + /* INC should all start */ + chan_node_details.set_tracks_start(Direction::INC); + /* DEC should all end */ + chan_node_details.set_tracks_end(Direction::DEC); + } + + /* If this is on the border of a device/heterogeneous blocks, segments should start/end */ + if (true == force_end) { + /* INC should all end */ + chan_node_details.set_tracks_end(Direction::INC); + /* DEC should all start */ + chan_node_details.set_tracks_start(Direction::DEC); + } + + return chan_node_details; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_chan_details_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_chan_details_builder.h new file mode 100644 index 00000000000..4b71874c0ee --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_chan_details_builder.h @@ -0,0 +1,24 @@ +#pragma once + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include "physical_types.h" +#include "chan_node_details.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +std::vector get_num_tracks_per_seg_type(const size_t& chan_width, + const std::vector& segment_inf, + const bool& use_full_seg_groups); + +int adapt_to_tileable_route_chan_width(const int& chan_width, const std::vector& segment_inf); + +ChanNodeDetails build_unidir_chan_node_details(const size_t& chan_width, + const size_t& max_seg_length, + const bool& force_start, + const bool& force_end, + const std::vector& segment_inf); 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 new file mode 100644 index 00000000000..491bd952ae3 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp @@ -0,0 +1,309 @@ +/************************************************************************ + * This file contains a builder for the complex rr_graph data structure + * Different from VPR rr_graph builders, this builder aims to create a + * highly regular rr_graph, where each Connection Block (CB), Switch + * Block (SB) is the same (except for those on the borders). Thus, the + * rr_graph is called tileable, which brings significant advantage in + * producing large FPGA fabrics. + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_time.h" +#include "vtr_log.h" +#include "vtr_memory.h" + +#include "vpr_error.h" +#include "vpr_utils.h" + +#include "rr_graph.h" +#include "check_rr_graph.h" +#include "get_parallel_segs.h" +#include "device_grid_annotation.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_node_builder.h" +#include "tileable_rr_graph_edge_builder.h" +#include "tileable_rr_graph_builder.h" + +#include "globals.h" + +/************************************************************************ + * Main function of this file + * Builder for a detailed uni-directional tileable rr_graph + * Global graph is not supported here, the VPR rr_graph generator can be used + * It follows the procedures to complete the rr_graph generation + * 1. Assign the segments for each routing channel, + * To be specific, for each routing track, we assign a routing segment. + * The assignment is subject to users' specifications, such as + * a. length of each type of segment + * b. frequency of each type of segment. + * c. routing channel width + * 2. Estimate the number of nodes in the rr_graph + * This will estimate the number of + * a. IPINs, input pins of each grid + * b. OPINs, output pins of each grid + * c. SOURCE, virtual node which drives OPINs + * d. SINK, virtual node which is connected to IPINs + * e. CHANX and CHANY, routing segments of each channel + * 3. Create the connectivity of OPINs + * a. Evenly assign connections to OPINs to routing tracks + * b. the connection pattern should be same across the fabric + * 4. Create the connectivity of IPINs + * a. Evenly assign connections from routing tracks to IPINs + * b. the connection pattern should be same across the fabric + * 5. Create the switch block patterns, + * It is based on the type of switch block, the supported patterns are + * a. Disjoint, which connects routing track (i)th from (i)th and (i)th routing segments + * b. Universal, which connects routing track (i)th from (i)th and (M-i)th routing segments + * c. Wilton, which rotates the connection of Disjoint by 1 track + * 6. Allocate rr_graph, fill the node information + * For each node, fill + * a. basic information: coordinate(xlow, xhigh, ylow, yhigh), ptc_num + * b. edges (both incoming and outcoming) + * c. handle direct-connections + * 7. Build fast look-up for the rr_graph + * 8. Allocate external data structures + * a. cost_index + * b. RC tree + ***********************************************************************/ + +void build_tileable_unidir_rr_graph(const std::vector& types, + const DeviceGrid& grids, + const t_chan_width& chan_width, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& sub_fs, + const std::vector& segment_inf, + const int& delayless_switch, + const int& wire_to_arch_ipin_switch, + const float R_minW_nmos, + const float R_minW_pmos, + const enum e_base_cost_type& base_cost_type, + const std::vector& directs, + int* wire_to_rr_ipin_switch, + const bool& shrink_boundary, + const bool& perimeter_cb, + const bool& through_channel, + const bool& opin2all_sides, + const bool& concat_wire, + const bool& wire_opposite_side, + int* Warnings) { + vtr::ScopedStartFinishTimer timer("Build tileable routing resource graph"); + + // Reset warning flag + *Warnings = RR_GRAPH_NO_WARN; + + // Create a matrix of grid + // Create a vector of channel width, we support X-direction and Y-direction has different W + vtr::Point device_chan_width(chan_width.x_max, chan_width.y_max); + + VTR_LOG("X-direction routing channel width is %lu\n", device_chan_width.x()); + VTR_LOG("Y-direction routing channel width is %lu\n", device_chan_width.y()); + + // Get a mutable device ctx so that we have a mutable rr_graph + DeviceContext& device_ctx = g_vpr_ctx.mutable_device(); + + // Set the tileable flag to true + device_ctx.rr_graph_builder.set_tileable(true); + + // Annotate the device grid on the boundry + DeviceGridAnnotation device_grid_annotation(device_ctx.grid, perimeter_cb); + + // The number of segments are in general small, reserve segments may not bring + // significant memory efficiency + device_ctx.rr_graph_builder.reserve_segments(segment_inf.size()); + // Create the segments + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + device_ctx.rr_graph_builder.add_rr_segment(segment_inf[iseg]); + } + + // TODO: Load architecture switch to rr_graph switches + // Draft the switches as internal data of RRGraph object + // These are temporary switches copied from arch switches + // We use them to build the edges + // We will reset all the switches in the function + // alloc_and_load_rr_switch_inf() + // TODO: Spot the switch id in the architecture switch list + RRSwitchId wire_to_ipin_rr_switch = RRSwitchId::INVALID(); + RRSwitchId delayless_rr_switch = RRSwitchId::INVALID(); + + device_ctx.rr_graph_builder.reserve_switches(device_ctx.arch_switch_inf.size()); + // Create the switches + for (size_t iswitch = 0; iswitch < device_ctx.arch_switch_inf.size(); ++iswitch) { + const t_rr_switch_inf& temp_rr_switch = create_rr_switch_from_arch_switch(device_ctx.arch_switch_inf[iswitch], R_minW_nmos, R_minW_pmos); + RRSwitchId rr_switch = device_ctx.rr_graph_builder.add_rr_switch(temp_rr_switch); + if ((int)iswitch == wire_to_arch_ipin_switch) { + wire_to_ipin_rr_switch = rr_switch; + } + if ((int)iswitch == delayless_switch) { + delayless_rr_switch = rr_switch; + } + } + + // Validate the special switches + VTR_ASSERT(true == device_ctx.rr_graph.valid_switch(wire_to_ipin_rr_switch)); + VTR_ASSERT(true == device_ctx.rr_graph.valid_switch(delayless_rr_switch)); + + // A temp data about the driver switch ids for each rr_node + vtr::vector rr_node_driver_switches; + + // A temp data about the track ids for each CHANX and CHANY rr_node + std::map> rr_node_track_ids; + + // Get the routing segments on X-axis and Y-axis separately + t_unified_to_parallel_seg_index segment_index_map; + std::vector segment_inf_x = get_parallel_segs(segment_inf, segment_index_map, e_parallel_axis::X_AXIS, true); + std::vector segment_inf_y = get_parallel_segs(segment_inf, segment_index_map, e_parallel_axis::Y_AXIS, true); + + // Get vib grid + const auto& vib_grid = device_ctx.vib_grid; + + // Allocate the rr_nodes + alloc_tileable_rr_graph_nodes(device_ctx.rr_graph_builder, + rr_node_driver_switches, + grids, vib_grid, 0, + device_chan_width, + segment_inf_x, segment_inf_y, + device_grid_annotation, + shrink_boundary, + perimeter_cb, + through_channel); + + // Create all the rr_nodes + create_tileable_rr_graph_nodes(device_ctx.rr_graph, + device_ctx.rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + device_ctx.rr_rc_data, + grids, vib_grid, 0, + device_chan_width, + segment_inf_x, segment_inf_y, + segment_index_map, + wire_to_ipin_rr_switch, + delayless_rr_switch, + device_grid_annotation, + shrink_boundary, + perimeter_cb, + through_channel); + + // Create the connectivity of OPINs + // a. Evenly assign connections to OPINs to routing tracks + // b. the connection pattern should be same across the fabric + // Create the connectivity of IPINs + // a. Evenly assign connections from routing tracks to IPINs + // b. the connection pattern should be same across the fabric + // Global routing uses a single longwire track + int max_chan_width = find_unidir_routing_channel_width(chan_width.max); + VTR_ASSERT(max_chan_width > 0); + + // get maximum number of pins across all blocks + int max_pins = types[0].num_pins; + for (const auto& type : types) { + if (is_empty_type(&type)) { + continue; + } + + if (type.num_pins > max_pins) { + max_pins = type.num_pins; + } + } + + // Fc assignment still uses the old function from VPR. + // Should use tileable version so that we have can have full control + std::vector num_tracks = get_num_tracks_per_seg_type(max_chan_width / 2, segment_inf, false); + std::vector sets_per_seg_type(segment_inf.size()); + VTR_ASSERT(num_tracks.size() == segment_inf.size()); + for (size_t iseg = 0; iseg < num_tracks.size(); ++iseg) { + sets_per_seg_type[iseg] = num_tracks[iseg]; + } + + bool Fc_clipped = false; + // [0..num_types-1][0..num_pins-1] + std::vector> Fc_in; + Fc_in = alloc_and_load_actual_fc(types, max_pins, segment_inf, sets_per_seg_type, (const t_chan_width*)&chan_width, + e_fc_type::IN, UNI_DIRECTIONAL, &Fc_clipped, false); + if (Fc_clipped) { + *Warnings |= RR_GRAPH_WARN_FC_CLIPPED; + } + + Fc_clipped = false; + // [0..num_types-1][0..num_pins-1] + std::vector> Fc_out; + Fc_out = alloc_and_load_actual_fc(types, max_pins, segment_inf, sets_per_seg_type, (const t_chan_width*)&chan_width, + e_fc_type::OUT, UNI_DIRECTIONAL, &Fc_clipped, false); + + if (Fc_clipped) { + *Warnings |= RR_GRAPH_WARN_FC_CLIPPED; + } + + // Build the connections tile by tile: + // We classify rr_nodes into a general switch block (GSB) data structure + // where we create edges to each rr_nodes in the GSB with respect to + // Fc_in and Fc_out, switch block patterns + // In addition, we will also handle direct-connections: + // Add edges that bridge OPINs and IPINs to the rr_graph + // Create edges for a tileable rr_graph + build_rr_graph_edges(device_ctx.rr_graph, + device_ctx.rr_graph_builder, + rr_node_driver_switches, + grids, vib_grid, 0, + device_chan_width, + segment_inf, segment_inf_x, segment_inf_y, + Fc_in, Fc_out, + sb_type, Fs, sb_subtype, sub_fs, + perimeter_cb, + opin2all_sides, concat_wire, + wire_opposite_side, + delayless_rr_switch); + + // Build direction connection lists + // TODO: use tile direct builder + // Create data structure of direct-connections + auto clb_to_clb_directs = alloc_and_load_clb_to_clb_directs(directs, delayless_switch); + std::vector clb2clb_directs; + for (size_t idirect = 0; idirect < directs.size(); ++idirect) { + /* Sanity checks on rr switch id */ + VTR_ASSERT(true == device_ctx.rr_graph.valid_switch(RRSwitchId(clb_to_clb_directs[idirect].switch_index))); + clb2clb_directs.push_back(clb_to_clb_directs[idirect]); + } + + build_rr_graph_direct_connections(device_ctx.rr_graph, device_ctx.rr_graph_builder, device_ctx.grid, 0, + directs, clb2clb_directs); + + // Allocate and load routing resource switches, which are derived from the switches from the architecture file, + // based on their fanin in the rr graph. This routine also adjusts the rr nodes to point to these new rr switches + device_ctx.rr_graph_builder.init_fan_in(); + alloc_and_load_rr_switch_inf(device_ctx.rr_graph_builder, device_ctx.switch_fanin_remap, device_ctx.all_sw_inf, R_minW_nmos, R_minW_pmos, wire_to_arch_ipin_switch, wire_to_rr_ipin_switch); + + // Save the channel widths for the newly constructed graph + device_ctx.chan_width = chan_width; + + // Save the track ids for tileable routing resource graph + device_ctx.rr_node_track_ids = rr_node_track_ids; + + // Build incoming edges + device_ctx.rr_graph_builder.partition_edges(); + device_ctx.rr_graph_builder.build_in_edges(); + + // Allocate external data structures + // a. cost_index + // b. RC tree + rr_graph_externals(segment_inf, segment_inf_x, segment_inf_y, + *wire_to_rr_ipin_switch, base_cost_type); + + // Sanitizer for the rr_graph, check connectivities of rr_nodes + // Essential check for rr_graph, build look-up and + if (false == device_ctx.rr_graph_builder.validate()) { + // Error out if built-in validator of rr_graph fails + vpr_throw(VPR_ERROR_ROUTE, + __FILE__, + __LINE__, + "Fundamental errors occurred when validating rr_graph object!\n"); + } + + // No clock network support yet; Does not support flatten rr_graph yet + + check_rr_graph(device_ctx.rr_graph, types, device_ctx.rr_indexed_data, grids, vib_grid, device_ctx.chan_width, e_graph_type::UNIDIR_TILEABLE, false); +} 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 new file mode 100644 index 00000000000..d8395927de1 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.h @@ -0,0 +1,39 @@ +#ifndef TILEABLE_RR_GRAPH_BUILDER_H +#define TILEABLE_RR_GRAPH_BUILDER_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "physical_types.h" +#include "device_grid.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void build_tileable_unidir_rr_graph(const std::vector& types, + const DeviceGrid& grids, + const t_chan_width& chan_width, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& sub_fs, + const std::vector& segment_inf, + const int& delayless_switch, + const int& wire_to_arch_ipin_switch, + const float R_minW_nmos, + const float R_minW_pmos, + const enum e_base_cost_type& base_cost_type, + const std::vector& directs, + int* wire_to_rr_ipin_switch, + const bool& shrink_boundary, + const bool& perimeter_cb, + const bool& through_channel, + const bool& opin2all_sides, + const bool& concat_wire, + const bool& wire_opposite_side, + 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 new file mode 100644 index 00000000000..09b25caa5d0 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.cpp @@ -0,0 +1,355 @@ +/************************************************************************ + * This file contains functions that are used to build edges + * between nodes of a tileable routing resource graph + ***********************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_time.h" + +#include "vpr_utils.h" + +#include "rr_graph_builder_utils.h" +#include "tileable_rr_graph_gsb.h" +#include "tileable_rr_graph_edge_builder.h" + +/************************************************************************ + * Build the edges for all the SOURCE and SINKs nodes: + * 1. create edges between SOURCE and OPINs + ***********************************************************************/ +void build_rr_graph_edges_for_source_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + size_t& num_edges_to_create) { + size_t edge_count = 0; + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass all the non OPIN nodes */ + if (e_rr_type::OPIN != rr_graph.node_type(node)) { + continue; + } + /* Now, we have an OPIN node, we get the source node index */ + short xlow = rr_graph.node_xlow(node); + short ylow = rr_graph.node_ylow(node); + short src_node_class_num = get_grid_pin_class_index(grids, layer, xlow, ylow, + rr_graph.node_pin_num(node)); + /* Create edges between SOURCE and OPINs */ + t_physical_tile_loc tile_loc(xlow, ylow, layer); + RRNodeId src_node = rr_graph.node_lookup().find_node(layer, + xlow - grids.get_width_offset(tile_loc), + ylow - grids.get_height_offset(tile_loc), + e_rr_type::SOURCE, src_node_class_num); + VTR_ASSERT(true == rr_graph.valid_node(src_node)); + + /* add edges to the src_node */ + rr_graph_builder.create_edge_in_cache(src_node, node, rr_node_driver_switches[node], false); + edge_count++; + } + /* Allocate edges for all the source nodes */ + rr_graph_builder.build_edges(true); + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build the edges for all the SINKs nodes: + * 1. create edges between IPINs and SINKs + ***********************************************************************/ +void build_rr_graph_edges_for_sink_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + size_t& num_edges_to_create) { + size_t edge_count = 0; + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass all the non IPIN nodes */ + if (e_rr_type::IPIN != rr_graph.node_type(node)) { + continue; + } + /* Now, we have an OPIN node, we get the source node index */ + short xlow = rr_graph.node_xlow(node); + short ylow = rr_graph.node_ylow(node); + short sink_node_class_num = get_grid_pin_class_index(grids, layer, xlow, ylow, + rr_graph.node_pin_num(node)); + /* 1. create edges between IPINs and SINKs */ + t_physical_tile_loc tile_loc(xlow, ylow, 0); + const RRNodeId& sink_node = rr_graph.node_lookup().find_node(layer, + xlow - grids.get_width_offset(tile_loc), + ylow - grids.get_height_offset(tile_loc), + e_rr_type::SINK, sink_node_class_num, TOTAL_2D_SIDES[0]); + VTR_ASSERT(true == rr_graph.valid_node(sink_node)); + + /* add edges to connect the IPIN node to SINK nodes */ + rr_graph_builder.create_edge_in_cache(node, sink_node, rr_node_driver_switches[sink_node], false); + edge_count++; + } + /* Allocate edges for all the source nodes */ + rr_graph_builder.build_edges(true); + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build the edges of each rr_node tile by tile: + * We classify rr_nodes into a general switch block (GSB) data structure + * where we create edges to each rr_nodes in the GSB with respect to + * Fc_in and Fc_out, switch block patterns + * For each GSB: + * 1. create edges between CHANX | CHANY and IPINs (connections inside connection blocks) + * 2. create edges between OPINs, CHANX and CHANY (connections inside switch blocks) + * 3. create edges between OPINs and IPINs (direct-connections) + ***********************************************************************/ +void build_rr_graph_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const std::vector>& Fc_in, + const std::vector>& Fc_out, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& sub_fs, + const bool& perimeter_cb, + const bool& opin2all_sides, + const bool& concat_wire, + const bool& wire_opposite_side, + const RRSwitchId& delayless_switch) { + + if (!vib_grid.is_empty()) { + build_rr_graph_vib_edges(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + grids, + vib_grid, + layer, + device_chan_width, + segment_inf, + segment_inf_x, + segment_inf_y, + perimeter_cb, + delayless_switch); + } else { + build_rr_graph_regular_edges(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + grids, + layer, + device_chan_width, + segment_inf, + segment_inf_x, + segment_inf_y, + Fc_in, + Fc_out, + sb_type, + Fs, + sb_subtype, + sub_fs, + perimeter_cb, + opin2all_sides, + concat_wire, + wire_opposite_side); + } +} + +/************************************************************************ + * Build direct edges for Grids * + ***********************************************************************/ +void build_rr_graph_direct_connections(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const size_t& layer, + const std::vector& directs, + const std::vector& clb_to_clb_directs) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + t_physical_tile_loc tile_loc(ix, iy, layer); + /* Skip EMPTY tiles */ + if (true == is_empty_type(grids.get_physical_type(tile_loc))) { + continue; + } + /* Skip height > 1 or width > 1 tiles (mostly heterogeneous blocks) */ + if ((0 < grids.get_width_offset(tile_loc)) + || (0 < grids.get_height_offset(tile_loc))) { + continue; + } + vtr::Point from_grid_coordinate(ix, iy); + build_direct_connections_for_one_gsb(rr_graph, + rr_graph_builder, + grids, layer, + from_grid_coordinate, + directs, clb_to_clb_directs); + } + } +} + +void build_rr_graph_vib_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const bool& perimeter_cb, + const RRSwitchId& delayless_switch) { + /* Create map from mux name to index */ + + size_t num_edges_to_create = 0; + /* Create edges for SOURCE and SINK nodes for a tileable rr_graph */ + build_rr_graph_edges_for_source_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, layer, num_edges_to_create); + build_rr_graph_edges_for_sink_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, layer, num_edges_to_create); + + vtr::Point gsb_range(grids.width() - 2, grids.height() - 2); + + /* Go Switch Block by Switch Block */ + for (size_t ix = 0; ix <= gsb_range.x(); ++ix) { + for (size_t iy = 0; iy <= gsb_range.y(); ++iy) { + //vpr_printf(TIO_MESSAGE_INFO, "Building edges for GSB[%lu][%lu]\n", ix, iy); + + vtr::Point gsb_coord(ix, iy); + + /* adapt the bend_conn */ + t_bend_track2track_map sb_bend_conn; /* [0..from_gsb_side][0..chan_width-1][track_indices] */ + sb_bend_conn = build_bend_track_to_track_map(grids, rr_graph_builder, rr_graph, + segment_inf, + layer, gsb_coord, delayless_switch, rr_node_driver_switches); + + /* Create a GSB object */ + const RRGSB& rr_gsb = build_one_tileable_rr_gsb(grids, rr_graph, + device_chan_width, segment_inf_x, segment_inf_y, + layer, gsb_coord, perimeter_cb); + + t_vib_map vib_map; + vib_map = build_vib_map(rr_graph, grids, vib_grid, rr_gsb, segment_inf, layer, gsb_coord, gsb_coord); + build_edges_for_one_tileable_vib(rr_graph_builder, vib_map, sb_bend_conn, rr_node_driver_switches, num_edges_to_create); + + rr_graph_builder.build_edges(true); + } + } + + /* Process boundary */ + + size_t ix, iy; + // process top boundary + iy = gsb_range.y() + 1; // == grids.height() - 1 + for (ix = 0; ix < gsb_range.x() + 1; ++ix) { + vtr::Point actual_coord(ix, iy); + vtr::Point gsb_coord(ix, iy - 1); + + /* Create a GSB object */ + const RRGSB& rr_gsb = build_one_tileable_rr_gsb(grids, rr_graph, + device_chan_width, segment_inf_x, segment_inf_y, + layer, gsb_coord, perimeter_cb); + + t_vib_map vib_map; + vib_map = build_vib_map(rr_graph, grids, vib_grid, rr_gsb, segment_inf, layer, gsb_coord, actual_coord); + //build_edges_for_one_tileable_vib(rr_graph_builder, vib_map, sb_bend_conn, rr_node_driver_switches, num_edges_to_create); + size_t edge_count = 0; + for (auto iter = vib_map.begin(); iter != vib_map.end(); ++iter) { + for (auto to_node : iter->second) { + rr_graph_builder.create_edge_in_cache(iter->first, to_node, rr_node_driver_switches[to_node], false); + edge_count++; + } + } + num_edges_to_create += edge_count; + //rr_graph_builder.build_edges(true); + } + + // process right boundary + ix = gsb_range.x() + 1; + for (iy = 0; iy < gsb_range.y() + 1; ++iy) { + vtr::Point actual_coord(ix, iy); + vtr::Point gsb_coord(ix - 1, iy); + + /* Create a GSB object */ + const RRGSB& rr_gsb = build_one_tileable_rr_gsb(grids, rr_graph, + device_chan_width, segment_inf_x, segment_inf_y, + layer, gsb_coord, perimeter_cb); + + t_vib_map vib_map; + vib_map = build_vib_map(rr_graph, grids, vib_grid, rr_gsb, segment_inf, layer, gsb_coord, actual_coord); + //build_edges_for_one_tileable_vib(rr_graph_builder, vib_map, sb_bend_conn, rr_node_driver_switches, num_edges_to_create); + size_t edge_count = 0; + for (auto iter = vib_map.begin(); iter != vib_map.end(); ++iter) { + for (auto to_node : iter->second) { + rr_graph_builder.create_edge_in_cache(iter->first, to_node, rr_node_driver_switches[to_node], false); + edge_count++; + } + } + num_edges_to_create += edge_count; + //rr_graph_builder.build_edges(true); + } + rr_graph_builder.build_edges(true); +} + +void build_rr_graph_regular_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const std::vector>& Fc_in, + const std::vector>& Fc_out, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& sub_fs, + const bool& perimeter_cb, + const bool& opin2all_sides, + const bool& concat_wire, + const bool& wire_opposite_side) { + size_t num_edges_to_create = 0; + /* Create edges for SOURCE and SINK nodes for a tileable rr_graph */ + build_rr_graph_edges_for_source_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, layer, num_edges_to_create); + build_rr_graph_edges_for_sink_nodes(rr_graph, rr_graph_builder, rr_node_driver_switches, grids, layer, num_edges_to_create); + + vtr::Point gsb_range(grids.width() - 1, grids.height() - 1); + + /* Go Switch Block by Switch Block */ + for (size_t ix = 0; ix <= gsb_range.x(); ++ix) { + for (size_t iy = 0; iy <= gsb_range.y(); ++iy) { + //vpr_printf(TIO_MESSAGE_INFO, "Building edges for GSB[%lu][%lu]\n", ix, iy); + + vtr::Point gsb_coord(ix, iy); + /* Create a GSB object */ + const RRGSB& rr_gsb = build_one_tileable_rr_gsb(grids, rr_graph, + device_chan_width, segment_inf_x, segment_inf_y, + layer, gsb_coord, 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); + + /* 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); + + /* 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); + + /* 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); + /* 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 new file mode 100644 index 00000000000..73f8ff65f88 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_edge_builder.h @@ -0,0 +1,97 @@ +#pragma once + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_ndmatrix.h" +#include "vtr_geometry.h" + +#include "physical_types.h" +#include "device_grid.h" +#include "rr_graph_obj.h" +#include "rr_graph_type.h" +#include "rr_graph_view.h" +#include "rr_graph.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void build_rr_graph_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const std::vector>& Fc_in, + const std::vector>& Fc_out, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& sub_fs, + const bool& perimeter_cb, + const bool& opin2all_sides, + const bool& concat_wire, + const bool& wire_opposite_side, + const RRSwitchId& delayless_switch); + +void build_rr_graph_direct_connections(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const size_t& layer, + const std::vector& directs, + const std::vector& clb_to_clb_directs); + +void build_rr_graph_edges_for_source_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + size_t& num_edges_to_create); + +void build_rr_graph_edges_for_sink_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + size_t& num_edges_to_create); + +void build_rr_graph_vib_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const bool& perimeter_cb, + const RRSwitchId& delayless_switch); + +void build_rr_graph_regular_edges(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& device_chan_width, + const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const std::vector>& Fc_in, + const std::vector>& Fc_out, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& sub_fs, + const bool& perimeter_cb, + const bool& opin2all_sides, + const bool& concat_wire, + const bool& wire_opposite_side); 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 new file mode 100644 index 00000000000..9980bb402d2 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -0,0 +1,2109 @@ +/************************************************************************ + * This file contains a builder for track-to-track connections inside a + * tileable General Switch Block (GSB). + ***********************************************************************/ +#include +#include +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from openfpgautil library */ +#include "side_manager.h" + +#include "vpr_utils.h" +#include "rr_graph_view_util.h" +#include "tileable_rr_graph_utils.h" +#include "rr_graph_builder_utils.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_gsb.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Internal data structures + ***********************************************************************/ +typedef std::vector> t_track_group; + +/************************************************************************ + * A enumeration to list the status of a track inside a GSB + * 1. start; 2. end; 3. passing + * This is used to group tracks which ease the building of + * track-to-track mapping matrix + ***********************************************************************/ +enum e_track_status { + TRACK_START, + TRACK_END, + TRACK_PASS, + NUM_TRACK_STATUS /* just a place holder to get the number of status */ +}; + +/************************************************************************ + * Check if a track starts from this GSB or not + * (xlow, ylow) should be same as the GSB side coordinate + * + * Check if a track ends at this GSB or not + * (xhigh, yhigh) should be same as the GSB side coordinate + ***********************************************************************/ +static enum e_track_status determine_track_status_of_gsb(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& gsb_side, + const size_t& track_id) { + enum e_track_status track_status = TRACK_PASS; + /* Get the rr_node */ + RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + /* Get the coordinate of where the track starts */ + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* INC_DIRECTION start_track: (xlow, ylow) should be same as the GSB side coordinate */ + /* DEC_DIRECTION start_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + if ((track_start.x() == side_coordinate.x()) + && (track_start.y() == side_coordinate.y()) + && (OUT_PORT == rr_gsb.get_chan_node_direction(gsb_side, track_id))) { + /* Double check: start track should be an OUTPUT PORT of the GSB */ + track_status = TRACK_START; + } + + /* Get the coordinate of where the track ends */ + vtr::Point track_end = get_track_rr_node_end_coordinate(rr_graph, track_node); + + /* INC_DIRECTION end_track: (xhigh, yhigh) should be same as the GSB side coordinate */ + /* DEC_DIRECTION end_track: (xlow, ylow) should be same as the GSB side coordinate */ + if ((track_end.x() == side_coordinate.x()) + && (track_end.y() == side_coordinate.y()) + && (IN_PORT == rr_gsb.get_chan_node_direction(gsb_side, track_id))) { + /* Double check: end track should be an INPUT PORT of the GSB */ + track_status = TRACK_END; + } + + return track_status; +} + +/************************************************************************ + * Check if the GSB is in the Connection Block (CB) population list of the segment + * SB population of a L4 wire: 1 0 0 1 + * + * +----+ +----+ +----+ +----+ + * | CB |--->| CB |--->| CB |--->| CB | + * +----+ +----+ +----+ +----+ + * Engage CB connection Yes No No Yes + * + * We will find the offset between gsb_side_coordinate and (xlow,ylow) of the track + * Use the offset to check if the tracks should engage in this GSB connection + ***********************************************************************/ +static bool is_gsb_in_track_cb_population(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_side& gsb_side, + const int& track_id, + const std::vector& segment_inf) { + /* Get the rr_node */ + RRNodeId track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* Get the offset */ + size_t offset = std::abs((int)side_coordinate.x() - (int)track_start.x()) + + std::abs((int)side_coordinate.y() - (int)track_start.y()); + + /* Get segment id */ + RRSegmentId seg_id = rr_gsb.get_chan_node_segment(gsb_side, track_id); + /* validate offset */ + VTR_ASSERT(offset < segment_inf[size_t(seg_id)].cb.size()); + + /* Get the SB population */ + bool in_cb_population = false; + if (true == segment_inf[size_t(seg_id)].cb[offset]) { + in_cb_population = true; + } + + return in_cb_population; +} + +/************************************************************************ + * Check if the GSB is in the Switch Block (SB) population list of the segment + * SB population of a L3 wire: 1 0 0 1 + * + * +----+ +----+ +----+ +----+ + * | SB |--->| SB |--->| SB |--->| SB | + * +----+ +----+ +----+ +----+ + * Engage SB connection Yes No No Yes + * + * We will find the offset between gsb_side_coordinate and (xlow,ylow) of the track + * Use the offset to check if the tracks should engage in this GSB connection + ***********************************************************************/ +static bool is_gsb_in_track_sb_population(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_side& gsb_side, + const int& track_id, + const std::vector& segment_inf) { + /* Get the rr_node */ + const RRNodeId& track_node = rr_gsb.get_chan_node(gsb_side, track_id); + /* Get the coordinates */ + vtr::Point side_coordinate = rr_gsb.get_side_block_coordinate(gsb_side); + + vtr::Point track_start = get_track_rr_node_start_coordinate(rr_graph, track_node); + + /* Get the offset */ + size_t offset = std::abs((int)side_coordinate.x() - (int)track_start.x()) + + std::abs((int)side_coordinate.y() - (int)track_start.y()); + + /* Get segment id */ + RRSegmentId seg_id = rr_gsb.get_chan_node_segment(gsb_side, track_id); + /* validate offset */ + VTR_ASSERT(offset < segment_inf[size_t(seg_id)].sb.size()); + + /* Get the SB population */ + bool in_sb_population = false; + if (true == segment_inf[size_t(seg_id)].sb[offset]) { + in_sb_population = true; + } + + return in_sb_population; +} + +/************************************************************************ + * Create a list of track_id based on the to_track and num_to_tracks + * We consider the following list [to_track, to_track + Fs/3 - 1] + * if the [to_track + Fs/3 - 1] exceeds the num_to_tracks, we start over from 0! + ***********************************************************************/ +static std::vector get_to_track_list(const int& Fs, const int& to_track, const int& num_to_tracks) { + std::vector to_tracks; + + for (int i = 0; i < Fs; i = i + 3) { + /* TODO: currently, for Fs > 3, I always search the next from_track until Fs is satisfied + * The optimal track selection should be done in a more scientific way!!! + */ + int to_track_i = to_track + i; + /* make sure the track id is still in range */ + if (to_track_i > num_to_tracks - 1) { + to_track_i = to_track_i % num_to_tracks; + } + /* Ensure we are in the range */ + VTR_ASSERT(to_track_i < num_to_tracks); + /* from track must be connected */ + to_tracks.push_back(to_track_i); + } + return to_tracks; +} + +/************************************************************************ + * This function aims to return the track indices that drive the from_track + * in a Switch Block + * The track_ids to return will depend on different topologies of SB + * SUBSET, UNIVERSAL, and WILTON. + ***********************************************************************/ +static std::vector get_switch_block_to_track_id(const e_switch_block_type& switch_block_type, + const int& Fs, + const e_side& from_side, + const int& from_track, + const e_side& to_side, + const int& num_to_tracks) { + /* This routine returns the track number to which the from_track should + * connect. It supports any Fs % 3 == 0, switch blocks. + */ + std::vector to_tracks; + + /* TODO: currently, for Fs > 3, I always search the next from_track until Fs is satisfied + * The optimal track selection should be done in a more scientific way!!! + */ + VTR_ASSERT(0 == Fs % 3); + + /* Adapt from_track to fit in the range of num_to_tracks */ + size_t actual_from_track = from_track % num_to_tracks; + + switch (switch_block_type) { + case SUBSET: /* NB: Global routing uses SUBSET too */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + /* Finish, we return */ + return to_tracks; + case UNIVERSAL: + if ((from_side == LEFT) + || (from_side == RIGHT)) { + /* For the prev_side, to_track is from_track + * For the next_side, to_track is num_to_tracks - 1 - from_track + * For the opposite_side, to_track is always from_track + */ + SideManager side_manager(from_side); + if ((to_side == side_manager.get_opposite()) + || (to_side == side_manager.get_rotate_counterclockwise())) { + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == side_manager.get_rotate_clockwise()) { + to_tracks = get_to_track_list(Fs, num_to_tracks - 1 - actual_from_track, num_to_tracks); + } + } + + if ((from_side == TOP) + || (from_side == BOTTOM)) { + /* For the next_side, to_track is from_track + * For the prev_side, to_track is num_to_tracks - 1 - from_track + * For the opposite_side, to_track is always from_track + */ + SideManager side_manager(from_side); + if ((to_side == side_manager.get_opposite()) + || (to_side == side_manager.get_rotate_clockwise())) { + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == side_manager.get_rotate_counterclockwise()) { + to_tracks = get_to_track_list(Fs, num_to_tracks - 1 - actual_from_track, num_to_tracks); + } + } + /* Finish, we return */ + return to_tracks; + /* End switch_block_type == UNIVERSAL case. */ + case WILTON: + /* See S. Wilton Phd thesis, U of T, 1996 p. 103 for details on following. */ + if (from_side == LEFT) { + if (to_side == RIGHT) { /* CHANX to CHANX */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == TOP) { /* from CHANX to CHANY */ + to_tracks = get_to_track_list(Fs, (num_to_tracks - actual_from_track) % num_to_tracks, num_to_tracks); + } else if (to_side == BOTTOM) { + to_tracks = get_to_track_list(Fs, (num_to_tracks + actual_from_track - 1) % num_to_tracks, num_to_tracks); + } + } else if (from_side == RIGHT) { + if (to_side == LEFT) { /* CHANX to CHANX */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == TOP) { /* from CHANX to CHANY */ + to_tracks = get_to_track_list(Fs, (num_to_tracks + actual_from_track - 1) % num_to_tracks, num_to_tracks); + } else if (to_side == BOTTOM) { + to_tracks = get_to_track_list(Fs, (2 * num_to_tracks - 2 - actual_from_track) % num_to_tracks, num_to_tracks); + } + } else if (from_side == BOTTOM) { + if (to_side == TOP) { /* CHANY to CHANY */ + to_tracks = get_to_track_list(Fs, actual_from_track, num_to_tracks); + } else if (to_side == LEFT) { /* from CHANY to CHANX */ + to_tracks = get_to_track_list(Fs, (actual_from_track + 1) % num_to_tracks, num_to_tracks); + } else if (to_side == RIGHT) { + to_tracks = get_to_track_list(Fs, (2 * num_to_tracks - 2 - actual_from_track) % num_to_tracks, num_to_tracks); + } + } else if (from_side == TOP) { + if (to_side == BOTTOM) { /* CHANY to CHANY */ + to_tracks = get_to_track_list(Fs, from_track, num_to_tracks); + } else if (to_side == LEFT) { /* from CHANY to CHANX */ + to_tracks = get_to_track_list(Fs, (num_to_tracks - actual_from_track) % num_to_tracks, num_to_tracks); + } else if (to_side == RIGHT) { + to_tracks = get_to_track_list(Fs, (actual_from_track + 1) % num_to_tracks, num_to_tracks); + } + } + /* Finish, we return */ + return to_tracks; + /* End switch_block_type == WILTON case. */ + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Invalid switch block pattern !\n"); + } + + return to_tracks; +} + +/************************************************************************ + * Build the track_to_track_map[from_side][0..chan_width-1][to_side][track_indices] + * For a group of from_track nodes and to_track nodes + * For each side of from_tracks, we call a routine to get the list of to_tracks + * Then, we fill the track2track_map + ***********************************************************************/ +static void build_gsb_one_group_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const bool& wire_opposite_side, + const t_track_group& from_tracks, /* [0..gsb_side][track_indices] */ + const t_track_group& to_tracks, /* [0..gsb_side][track_indices] */ + t_track2track_map& track2track_map) { + for (size_t side = 0; side < from_tracks.size(); ++side) { + SideManager side_manager(side); + e_side from_side = side_manager.get_side(); + /* Find the other sides where the start tracks will locate */ + std::vector to_track_sides; + /* 0. opposite side */ + to_track_sides.push_back(side_manager.get_opposite()); + /* 1. prev side */ + /* Previous side definition: TOP => LEFT; RIGHT=>TOP; BOTTOM=>RIGHT; LEFT=>BOTTOM */ + to_track_sides.push_back(side_manager.get_rotate_counterclockwise()); + /* 2. next side */ + /* Next side definition: TOP => RIGHT; RIGHT=>BOTTOM; BOTTOM=>LEFT; LEFT=>TOP */ + to_track_sides.push_back(side_manager.get_rotate_clockwise()); + + for (size_t inode = 0; inode < from_tracks[side].size(); ++inode) { + for (size_t to_side_id = 0; to_side_id < to_track_sides.size(); ++to_side_id) { + enum e_side to_side = to_track_sides[to_side_id]; + SideManager to_side_manager(to_side); + size_t to_side_index = to_side_manager.to_size_t(); + /* Bypass those to_sides have no nodes */ + if (0 == to_tracks[to_side_index].size()) { + continue; + } + /* Bypass those from_side is same as to_side */ + if (from_side == to_side) { + continue; + } + /* Bypass those from_side is opposite to to_side if required */ + if (!wire_opposite_side + && (to_side_manager.get_opposite() == from_side)) { + continue; + } + /* Get other track_ids depending on the switch block pattern */ + /* Find the track ids that will start at the other sides */ + std::vector to_track_ids = get_switch_block_to_track_id(sb_type, Fs, from_side, inode, + to_side, + to_tracks[to_side_index].size()); + /* Update the track2track_map: */ + for (size_t to_track_id = 0; to_track_id < to_track_ids.size(); ++to_track_id) { + size_t from_side_index = side_manager.to_size_t(); + size_t from_track_index = from_tracks[side][inode]; + /* Check the id is still in the range !*/ + VTR_ASSERT(to_track_ids[to_track_id] < to_tracks[to_side_index].size()); + size_t to_track_index = to_tracks[to_side_index][to_track_ids[to_track_id]]; + //printf("from_track(size=%lu): %lu , to_track_ids[%lu]:%lu, to_track_index: %lu in a group of %lu tracks\n", + // from_tracks[side].size(), inode, to_track_id, to_track_ids[to_track_id], + // to_track_index, to_tracks[to_side_index].size()); + const RRNodeId& to_track_node = rr_gsb.get_chan_node(to_side, to_track_index); + VTR_ASSERT(true == rr_graph.valid_node(to_track_node)); + + /* from_track should be IN_PORT */ + VTR_ASSERT(IN_PORT == rr_gsb.get_chan_node_direction(from_side, from_track_index)); + /* to_track should be OUT_PORT */ + VTR_ASSERT(OUT_PORT == rr_gsb.get_chan_node_direction(to_side, to_track_index)); + + //VTR_LOG("Consider a connection from pass tracks %d on side %s to track node %ld on side %s\n", from_track_index, SIDE_STRING[from_side], size_t(to_track_node), SIDE_STRING[to_side]); + + /* Check if the to_track_node is already in the list ! */ + std::vector::iterator it = std::find(track2track_map[from_side_index][from_track_index].begin(), + track2track_map[from_side_index][from_track_index].end(), + to_track_node); + if (it != track2track_map[from_side_index][from_track_index].end()) { + continue; /* the node_id is already in the list, go for the next */ + } + /* Clear, we should add to the list */ + track2track_map[from_side_index][from_track_index].push_back(to_track_node); + //VTR_LOG("Built a connection from pass tracks %d on side %s to track node %ld on side %s\n", from_track_index, SIDE_STRING[from_side], size_t(to_track_node), SIDE_STRING[to_side]); + } + } + } + } +} + +/************************************************************************ + * Build the track_to_track_map[from_side][0..chan_width-1][to_side][track_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * The track_indices is the indices of tracks that the node at from_side and [0..chan_width-1] will drive + * IMPORTANT: the track_indices are the indicies in the GSB context, but not the rr_graph!!! + * We separate the connections into two groups: + * Group 1: the routing tracks start from this GSB + * We will apply switch block patterns (SUBSET, UNIVERSAL, WILTON) + * Group 2: the routing tracks do not start from this GSB (bypassing wires) + * We will apply switch block patterns (SUBSET, UNIVERSAL, WILTON) + * but we will check the Switch Block (SB) population of these + * routing segments, and determine which requires connections + * + * CHANY CHANY CHANY CHANY + * [0] [1] [2] [3] + * start yes no yes no + * end +-------------------------+ start Group 1 Group 2 + * no CHANX[0] | TOP | CHANX[0] yes TOP/BOTTOM TOP/BOTTOM + * | | CHANY[0,2] CHANY[1,3] + * yes CHANX[1] | | CHANX[1] no + * | LEFT RIGHT | + * no CHANX[2] | | CHANX[2] yes + * | | + * yes CHANX[3] | BOTTOM | CHANX[3] no + * +-------------------------+ + * CHANY CHANY CHANY CHANY + * [0] [1] [2] [3] + * start yes no yes no + * + * The mapping is done in the following steps: (For each side of the GSB) + * 1. Build a list of tracks that will start from this side + * if a track starts, its xlow/ylow is the same as the x,y of this gsb + * 2. Build a list of tracks on the other sides belonging to Group 1. + * Take the example of RIGHT side, we will collect + * a. tracks that will end at the LEFT side + * b. tracks that will start at the TOP side + * c. tracks that will start at the BOTTOM side + * 3. Apply switch block patterns to Group 1 (SUBSET, UNIVERSAL, WILTON) + * 4. Build a list of tracks on the other sides belonging to Group 1. + * Take the example of RIGHT side, we will collect + * a. tracks that will bypass at the TOP side + * b. tracks that will bypass at the BOTTOM side + * 5. Apply switch block patterns to Group 2 (SUBSET, UNIVERSAL, WILTON) + ***********************************************************************/ +t_track2track_map build_gsb_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& sub_fs, + const bool& concat_wire, + const bool& wire_opposite_side, + const std::vector& segment_inf) { + t_track2track_map track2track_map; /* [0..gsb_side][0..chan_width][track_indices] */ + + /* Categorize tracks into 3 groups: + * (1) tracks will start here + * (2) tracks will end here + * (2) tracks will just pass through the SB */ + t_track_group start_tracks; /* [0..gsb_side][track_indices] */ + t_track_group end_tracks; /* [0..gsb_side][track_indices] */ + t_track_group pass_tracks; /* [0..gsb_side][track_indices] */ + + /* resize to the number of sides */ + start_tracks.resize(rr_gsb.get_num_sides()); + end_tracks.resize(rr_gsb.get_num_sides()); + pass_tracks.resize(rr_gsb.get_num_sides()); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + e_side gsb_side = side_manager.get_side(); + /* Build a list of tracks that will start from this side */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + /* We need to check Switch block population of this track + * The track node will not be considered if there supposed to be no SB at this position + */ + if (false == is_gsb_in_track_sb_population(rr_graph, rr_gsb, gsb_side, inode, segment_inf)) { + continue; /* skip this node and go to the next */ + } + /* check if this track will start from here */ + enum e_track_status track_status = determine_track_status_of_gsb(rr_graph, rr_gsb, gsb_side, inode); + + switch (track_status) { + case TRACK_START: + /* update starting track list */ + start_tracks[side].push_back(inode); + break; + case TRACK_END: + /* Update end track list */ + end_tracks[side].push_back(inode); + break; + case TRACK_PASS: + /* Update passing track list */ + /* Note that the pass_track should be IN_PORT only !!! */ + if (IN_PORT == rr_gsb.get_chan_node_direction(gsb_side, inode)) { + pass_tracks[side].push_back(inode); + } + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Invalid track status!\n"); + } + } + } + + /* Allocate track2track map */ + track2track_map.resize(rr_gsb.get_num_sides()); + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side gsb_side = side_manager.get_side(); + /* allocate track2track_map[gsb_side] */ + track2track_map[side].resize(rr_gsb.get_chan_width(gsb_side)); + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + /* allocate track2track_map[gsb_side][inode] */ + track2track_map[side][inode].clear(); + } + } + + /* For Group 1: we build connections between end_tracks and start_tracks*/ + build_gsb_one_group_track_to_track_map(rr_graph, rr_gsb, + sb_type, Fs, + concat_wire, /* End tracks should always to wired to start tracks */ + end_tracks, start_tracks, + track2track_map); + + /* For Group 2: we build connections between end_tracks and start_tracks*/ + /* Currently, I use the same Switch Block pattern for the passing tracks and end tracks, + * TODO: This can be improved with different patterns! + */ + //for (e_side curr_side : SIDES) { + // VTR_LOG("Number of pass tracks %d on side %s\n", pass_tracks[size_t(curr_side)].size(), SIDE_STRING[curr_side]); + //} + build_gsb_one_group_track_to_track_map(rr_graph, rr_gsb, + sb_subtype, sub_fs, + wire_opposite_side, /* Pass tracks may not be wired to start tracks */ + pass_tracks, start_tracks, + track2track_map); + + return track2track_map; +} + +t_bend_track2track_map build_bend_track_to_track_map(const DeviceGrid& grids, + RRGraphBuilder& rr_graph_builder, + const RRGraphView& rr_graph, + const std::vector& segment_inf, + const size_t& layer, + const vtr::Point& gsb_coordinate, + const RRSwitchId& delayless_switch, + vtr::vector& rr_node_driver_switches) { + + std::vector>>> chan_rr_nodes_all_sides; //[side][bend_num][start/end][node] + chan_rr_nodes_all_sides.resize(4); + + int bend_seg_num = 0; + std::vector bend_seg_type; //bend type: 1: U; 2: D + for (size_t iseg = 0; iseg < segment_inf.size(); iseg++) { + if (segment_inf[iseg].is_bend) { + bend_seg_num++; + + for (size_t i = 0; i < segment_inf[iseg].bend.size(); i++) { + if (segment_inf[iseg].bend[i] != 0) { + bend_seg_type.push_back(segment_inf[iseg].bend[i]); + break; + } + } + } + } + VTR_ASSERT(bend_seg_num == int(bend_seg_type.size())); + for (size_t side = 0; side < 4; ++side) { + std::vector rr_nodes; + switch (side) { + case TOP: /* TOP = 0 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.y() == grids.height() - 2) { + + break; + } + + chan_rr_nodes_all_sides[0].resize(bend_seg_num); + for (int i = 0; i < bend_seg_num; i++) { + chan_rr_nodes_all_sides[0][i].resize(2); //start/end track for bend + } + + rr_nodes = find_rr_graph_chan_nodes(rr_graph, + layer, gsb_coordinate.x(), gsb_coordinate.y() + 1, + e_rr_type::CHANY); + + for (auto inode : rr_nodes) { + VTR_ASSERT(rr_graph.node_type(inode) == e_rr_type::CHANY); + Direction direction = rr_graph.node_direction(inode); + size_t xlow = rr_graph.node_xlow(inode); + size_t ylow = rr_graph.node_ylow(inode); + int bend_start = rr_graph.node_bend_start(inode); + int bend_end = rr_graph.node_bend_end(inode); + + VTR_ASSERT((bend_start <= bend_seg_num) && (bend_end <= bend_seg_num)); + if (direction == Direction::INC && bend_start != 0 && xlow == gsb_coordinate.x() && (ylow == gsb_coordinate.y() + 1)) { + VTR_ASSERT(bend_end == 0); + chan_rr_nodes_all_sides[0][bend_start - 1][0].push_back(inode); + } + if (direction == Direction::DEC && bend_end != 0 && xlow == gsb_coordinate.x() && (ylow == gsb_coordinate.y() + 1)) { + VTR_ASSERT(bend_start == 0); + chan_rr_nodes_all_sides[0][bend_end - 1][1].push_back(inode); + } + } + + break; + case RIGHT: /* RIGHT = 1 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.x() == grids.width() - 2) { + + break; + } + + chan_rr_nodes_all_sides[1].resize(bend_seg_num); + for (int i = 0; i < bend_seg_num; i++) { + chan_rr_nodes_all_sides[1][i].resize(2); //start/end track for bend + } + + rr_nodes = find_rr_graph_chan_nodes(rr_graph, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y(), + e_rr_type::CHANX); + + for (auto inode : rr_nodes) { + VTR_ASSERT(rr_graph.node_type(inode) == e_rr_type::CHANX); + Direction direction = rr_graph.node_direction(inode); + size_t xlow = rr_graph.node_xlow(inode); + size_t ylow = rr_graph.node_ylow(inode); + int bend_start = rr_graph.node_bend_start(inode); + int bend_end = rr_graph.node_bend_end(inode); + + VTR_ASSERT((bend_start <= bend_seg_num) && (bend_end <= bend_seg_num)); + if (direction == Direction::INC && bend_start != 0 && (xlow == gsb_coordinate.x() + 1) && ylow == gsb_coordinate.y()) { + VTR_ASSERT(bend_end == 0); + chan_rr_nodes_all_sides[1][bend_start - 1][0].push_back(inode); + } + if (direction == Direction::DEC && bend_end != 0 && (xlow == gsb_coordinate.x() + 1) && ylow == gsb_coordinate.y()) { + VTR_ASSERT(bend_start == 0); + chan_rr_nodes_all_sides[1][bend_end - 1][1].push_back(inode); + } + } + break; + case BOTTOM: /* BOTTOM = 2 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.y() == 0) { + + break; + } + + chan_rr_nodes_all_sides[2].resize(bend_seg_num); + for (int i = 0; i < bend_seg_num; i++) { + chan_rr_nodes_all_sides[2][i].resize(2); //start/end track for bend + } + + rr_nodes = find_rr_graph_chan_nodes(rr_graph, + layer, gsb_coordinate.x(), gsb_coordinate.y(), + e_rr_type::CHANY); + + for (auto inode : rr_nodes) { + VTR_ASSERT(rr_graph.node_type(inode) == e_rr_type::CHANY); + Direction direction = rr_graph.node_direction(inode); + size_t xhigh = rr_graph.node_xhigh(inode); + size_t yhigh = rr_graph.node_yhigh(inode); + int bend_start = rr_graph.node_bend_start(inode); + int bend_end = rr_graph.node_bend_end(inode); + + VTR_ASSERT((bend_start <= bend_seg_num) && (bend_end <= bend_seg_num)); + if (direction == Direction::INC && bend_end != 0 && xhigh == gsb_coordinate.x() && yhigh == gsb_coordinate.y()) { + VTR_ASSERT(bend_start == 0); + chan_rr_nodes_all_sides[2][bend_end - 1][1].push_back(inode); + } + if (direction == Direction::DEC && bend_start != 0 && xhigh == gsb_coordinate.x() && yhigh == gsb_coordinate.y()) { + VTR_ASSERT(bend_end == 0); + chan_rr_nodes_all_sides[2][bend_start - 1][0].push_back(inode); + } + } + break; + case LEFT: /* BOTTOM = 2 */ + /* For the bording, we should take special care */ + if (gsb_coordinate.x() == 0) { + + break; + } + + chan_rr_nodes_all_sides[3].resize(bend_seg_num); + for (int i = 0; i < bend_seg_num; i++) { + chan_rr_nodes_all_sides[3][i].resize(2); //start/end track for bend + } + + rr_nodes = find_rr_graph_chan_nodes(rr_graph, + layer, gsb_coordinate.x(), gsb_coordinate.y(), + e_rr_type::CHANX); + + for (auto inode : rr_nodes) { + VTR_ASSERT(rr_graph.node_type(inode) == e_rr_type::CHANX); + Direction direction = rr_graph.node_direction(inode); + size_t xhigh = rr_graph.node_xhigh(inode); + size_t yhigh = rr_graph.node_yhigh(inode); + int bend_start = rr_graph.node_bend_start(inode); + int bend_end = rr_graph.node_bend_end(inode); + + VTR_ASSERT((bend_start <= bend_seg_num) && (bend_end <= bend_seg_num)); + if (direction == Direction::INC && bend_end != 0 && xhigh == gsb_coordinate.x() && yhigh == gsb_coordinate.y()) { + VTR_ASSERT(bend_start == 0); + chan_rr_nodes_all_sides[3][bend_end - 1][1].push_back(inode); + } + if (direction == Direction::DEC && bend_start != 0 && xhigh == gsb_coordinate.x() && yhigh == gsb_coordinate.y()) { + VTR_ASSERT(bend_end == 0); + chan_rr_nodes_all_sides[3][bend_start - 1][0].push_back(inode); + } + } + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Invalid side index!\n"); + } + } + std::map bend_seg_head2bend_seg_end_map; + for (size_t ibend_seg = 0; ibend_seg < (size_t)bend_seg_num; ibend_seg++) { + int bend_type = bend_seg_type[ibend_seg]; //bend_type 1:U 2:D + VTR_ASSERT(bend_type == 1 || bend_type == 2); + + if (bend_type == 1) { //bend type U + for (size_t side = 0; side < 4; side++) { + size_t to_side = (side + 1) % 4; + if (chan_rr_nodes_all_sides[side].size() > 0) + for (size_t inode = 0; inode < chan_rr_nodes_all_sides[side][ibend_seg][1].size(); inode++) { + + if (chan_rr_nodes_all_sides[to_side].size() > 0) { + VTR_ASSERT(chan_rr_nodes_all_sides[side][ibend_seg][1].size() == chan_rr_nodes_all_sides[to_side][ibend_seg][0].size()); + bend_seg_head2bend_seg_end_map.emplace(std::make_pair(chan_rr_nodes_all_sides[side][ibend_seg][1][inode], chan_rr_nodes_all_sides[to_side][ibend_seg][0][inode])); + rr_node_driver_switches[chan_rr_nodes_all_sides[to_side][ibend_seg][0][inode]] = delayless_switch; + } else { + rr_graph_builder.set_node_bend_end(chan_rr_nodes_all_sides[side][ibend_seg][1][inode], 0); + } + } + else { + if (chan_rr_nodes_all_sides[to_side].size() > 0) { + for (size_t inode = 0; inode < chan_rr_nodes_all_sides[to_side][ibend_seg][0].size(); inode++) { + rr_graph_builder.set_node_bend_start(chan_rr_nodes_all_sides[to_side][ibend_seg][0][inode], 0); + } + } + } + } + + } else if (bend_type == 2) { //bend type D + for (size_t side = 0; side < 4; side++) { + size_t to_side = (side + 3) % 4; + if (chan_rr_nodes_all_sides[side].size() > 0) + for (size_t inode = 0; inode < chan_rr_nodes_all_sides[side][ibend_seg][1].size(); inode++) { + + if (chan_rr_nodes_all_sides[to_side].size() > 0) { + VTR_ASSERT(chan_rr_nodes_all_sides[side][ibend_seg][1].size() == chan_rr_nodes_all_sides[to_side][ibend_seg][0].size()); + bend_seg_head2bend_seg_end_map.emplace(std::make_pair(chan_rr_nodes_all_sides[side][ibend_seg][1][inode], chan_rr_nodes_all_sides[to_side][ibend_seg][0][inode])); + rr_node_driver_switches[chan_rr_nodes_all_sides[to_side][ibend_seg][0][inode]] = delayless_switch; + } else { + rr_graph_builder.set_node_bend_end(chan_rr_nodes_all_sides[side][ibend_seg][1][inode], 0); + } + } + else { + if (chan_rr_nodes_all_sides[to_side].size() > 0) { + for (size_t inode = 0; inode < chan_rr_nodes_all_sides[to_side][ibend_seg][0].size(); inode++) { + rr_graph_builder.set_node_bend_start(chan_rr_nodes_all_sides[to_side][ibend_seg][0][inode], 0); + } + } + } + } + } + } + + return bend_seg_head2bend_seg_end_map; +} + +/* Build a RRChan Object with the given channel type and coorindators */ +static RRChan build_one_tileable_rr_chan(const size_t& layer, + const vtr::Point& chan_coordinate, + const e_rr_type& chan_type, + const RRGraphView& rr_graph, + const ChanNodeDetails& chan_details) { + std::vector chan_rr_nodes; + + /* Create a rr_chan object and check if it is unique in the graph */ + RRChan rr_chan; + + /* Fill the information */ + rr_chan.set_type(chan_type); + + /* Collect rr_nodes for this channel */ + chan_rr_nodes = find_rr_graph_chan_nodes(rr_graph, + layer, chan_coordinate.x(), chan_coordinate.y(), + chan_type); + + /* Reserve */ + /* rr_chan.reserve_node(size_t(chan_width)); */ + + /* Fill the rr_chan */ + for (size_t itrack = 0; itrack < chan_rr_nodes.size(); ++itrack) { + size_t iseg = chan_details.get_track_segment_id(itrack); + rr_chan.add_node(rr_graph, chan_rr_nodes[itrack], RRSegmentId(iseg)); + } + + return rr_chan; +} + +/*********************************************************************** + * Build a General Switch Block (GSB) + * which includes: + * [I] A Switch Box subckt consists of following ports: + * 1. Channel Y [x][y] inputs + * 2. Channel X [x+1][y] inputs + * 3. Channel Y [x][y-1] outputs + * 4. Channel X [x][y] outputs + * 5. Grid[x][y+1] Right side outputs pins + * 6. Grid[x+1][y+1] Left side output pins + * 7. Grid[x+1][y+1] Bottom side output pins + * 8. Grid[x+1][y] Top side output pins + * 9. Grid[x+1][y] Left side output pins + * 10. Grid[x][y] Right side output pins + * 11. Grid[x][y] Top side output pins + * 12. Grid[x][y+1] Bottom side output pins + * + * -------------- -------------- + * | | CBY | | + * | Grid | ChanY | Grid | + * | [x][y+1] | [x][y+1] | [x+1][y+1] | + * | | | | + * -------------- -------------- + * ---------- + * ChanX & CBX | Switch | ChanX + * [x][y] | Box | [x+1][y] + * | [x][y] | + * ---------- + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y] | [x][y] | [x+1][y] | + * | | | | + * -------------- -------------- + * For channels chanY with INC_DIRECTION on the top side, they should be marked as outputs + * For channels chanY with DEC_DIRECTION on the top side, they should be marked as inputs + * For channels chanY with INC_DIRECTION on the bottom side, they should be marked as inputs + * For channels chanY with DEC_DIRECTION on the bottom side, they should be marked as outputs + * For channels chanX with INC_DIRECTION on the left side, they should be marked as inputs + * For channels chanX with DEC_DIRECTION on the left side, they should be marked as outputs + * For channels chanX with INC_DIRECTION on the right side, they should be marked as outputs + * For channels chanX with DEC_DIRECTION on the right side, they should be marked as inputs + * + * [II] A X-direction Connection Block [x][y] + * The connection block shares the same routing channel[x][y] with the Switch Block + * We just need to fill the ipin nodes at TOP and BOTTOM sides + * as well as properly fill the ipin_grid_side information + * [III] A Y-direction Connection Block [x][y+1] + * The connection block shares the same routing channel[x][y+1] with the Switch Block + * We just need to fill the ipin nodes at LEFT and RIGHT sides + * as well as properly fill the ipin_grid_side information + ***********************************************************************/ +RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, + const RRGraphView& rr_graph, + const vtr::Point& device_chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const size_t& layer, + const vtr::Point& gsb_coordinate, + const bool& perimeter_cb) { + /* Create an object to return */ + RRGSB rr_gsb; + + /* Check */ + VTR_ASSERT(gsb_coordinate.x() <= grids.width()); + VTR_ASSERT(gsb_coordinate.y() <= grids.height()); + + /* Coordinator initialization */ + rr_gsb.set_coordinate(gsb_coordinate.x(), gsb_coordinate.y()); + + /* Basic information*/ + rr_gsb.init_num_sides(4); /* Fixed number of sides */ + + /* Find all rr_nodes of channels */ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + /* Local variables inside this for loop */ + SideManager side_manager(side); + vtr::Point coordinate = rr_gsb.get_side_block_coordinate(side_manager.get_side()); + RRChan rr_chan; + std::vector> temp_opin_rr_nodes(2); + enum e_side opin_grid_side[2] = {NUM_2D_SIDES, NUM_2D_SIDES}; + enum PORTS chan_dir_to_port_dir_mapping[2] = {OUT_PORT, IN_PORT}; /* 0: INC_DIRECTION => ?; 1: DEC_DIRECTION => ? */ + + /* Build a segment details, where we need the segment ids for building rr_chan + * We do not care starting and ending points here, so set chan_side as NUM_SIDES + */ + ChanNodeDetails chanx_details = build_unidir_chan_node_details(device_chan_width.x(), grids.width() - 1, + false, false, segment_inf_x); + ChanNodeDetails chany_details = build_unidir_chan_node_details(device_chan_width.y(), grids.height() - 1, + false, false, segment_inf_y); + + switch (side) { + case TOP: /* TOP = 0 */ + /* For the border, we should take special care. */ + if (gsb_coordinate.y() == grids.height() - 1) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(layer, coordinate, e_rr_type::CHANY, rr_graph, chany_details); + chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */ + chan_dir_to_port_dir_mapping[1] = IN_PORT; /* DEC_DIRECTION => IN_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x][y+1] RIGHT side outputs pins */ + opin_grid_side[0] = RIGHT; + /* Grid[x+1][y+1] left side outputs pins */ + opin_grid_side[1] = LEFT; + + /* Build the Switch block: opin and opin_grid_side */ + /* Include Grid[x][y+1] RIGHT side outputs pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x(), gsb_coordinate.y() + 1, + e_rr_type::OPIN, opin_grid_side[0]); + /* Include Grid[x+1][y+1] Left side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y() + 1, + e_rr_type::OPIN, opin_grid_side[1]); + + break; + case RIGHT: /* RIGHT = 1 */ + /* For the border, we should take special care. The rightmost column (W-1) does not have any right side routing channel. If perimeter connection block is not enabled, even the last second rightmost column (W-2) does not have any right side routing channel */ + if (gsb_coordinate.x() == grids.width() - 1) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for top: chany[x][y+1] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(layer, coordinate, e_rr_type::CHANX, rr_graph, chanx_details); + chan_dir_to_port_dir_mapping[0] = OUT_PORT; /* INC_DIRECTION => OUT_PORT */ + chan_dir_to_port_dir_mapping[1] = IN_PORT; /* DEC_DIRECTION => IN_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x+1][y+1] BOTTOM side outputs pins */ + opin_grid_side[0] = BOTTOM; + /* Grid[x+1][y] TOP side outputs pins */ + opin_grid_side[1] = TOP; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x+1][y+1] Bottom side output pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y() + 1, + e_rr_type::OPIN, opin_grid_side[0]); + /* include Grid[x+1][y] Top side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y(), + e_rr_type::OPIN, opin_grid_side[1]); + break; + case BOTTOM: /* BOTTOM = 2*/ + if (!perimeter_cb && gsb_coordinate.y() == 0) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for bottom: chany[x][y] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(layer, coordinate, e_rr_type::CHANY, rr_graph, chany_details); + chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */ + chan_dir_to_port_dir_mapping[1] = OUT_PORT; /* DEC_DIRECTION => OUT_PORT */ + + /* Assign grid side of OPIN */ + /* Grid[x+1][y] LEFT side outputs pins */ + opin_grid_side[0] = LEFT; + /* Grid[x][y] RIGHT side outputs pins */ + opin_grid_side[1] = RIGHT; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x+1][y] Left side output pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x() + 1, gsb_coordinate.y(), + e_rr_type::OPIN, opin_grid_side[0]); + /* include Grid[x][y] Right side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x(), gsb_coordinate.y(), + e_rr_type::OPIN, opin_grid_side[1]); + break; + case LEFT: /* LEFT = 3 */ + if (!perimeter_cb && gsb_coordinate.x() == 0) { + rr_gsb.clear_one_side(side_manager.get_side()); + break; + } + /* Routing channels*/ + /* SideManager: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */ + /* Collect rr_nodes for Tracks for left: chanx[x][y] */ + /* Create a rr_chan object and check if it is unique in the graph */ + rr_chan = build_one_tileable_rr_chan(layer, coordinate, e_rr_type::CHANX, rr_graph, chanx_details); + chan_dir_to_port_dir_mapping[0] = IN_PORT; /* INC_DIRECTION => IN_PORT */ + chan_dir_to_port_dir_mapping[1] = OUT_PORT; /* DEC_DIRECTION => OUT_PORT */ + + /* Grid[x][y+1] BOTTOM side outputs pins */ + opin_grid_side[0] = BOTTOM; + /* Grid[x][y] TOP side outputs pins */ + opin_grid_side[1] = TOP; + + /* Build the Switch block: opin and opin_grid_side */ + /* include Grid[x][y+1] Bottom side outputs pins */ + temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x(), gsb_coordinate.y() + 1, + e_rr_type::OPIN, opin_grid_side[0]); + /* include Grid[x][y] Top side output pins */ + temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(rr_graph, grids, + layer, gsb_coordinate.x(), gsb_coordinate.y(), + e_rr_type::OPIN, opin_grid_side[1]); + + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Invalid side index!\n"); + } + + /* Organize a vector of port direction */ + if (0 < rr_chan.get_chan_width()) { + std::vector rr_chan_dir; + rr_chan_dir.resize(rr_chan.get_chan_width()); + for (size_t itrack = 0; itrack < rr_chan.get_chan_width(); ++itrack) { + /* Identify the directionality, record it in rr_node_direction */ + if (Direction::INC == rr_graph.node_direction(rr_chan.get_node(itrack))) { + rr_chan_dir[itrack] = chan_dir_to_port_dir_mapping[0]; + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(rr_chan.get_node(itrack))); + rr_chan_dir[itrack] = chan_dir_to_port_dir_mapping[1]; + } + } + /* Fill chan_rr_nodes */ + rr_gsb.add_chan_node(side_manager.get_side(), rr_chan, rr_chan_dir); + } + + /* Fill opin_rr_nodes */ + /* Copy from temp_opin_rr_node to opin_rr_node */ + for (const RRNodeId& inode : temp_opin_rr_nodes[0]) { + /* Grid[x+1][y+1] Bottom side outputs pins */ + rr_gsb.add_opin_node(inode, side_manager.get_side()); + } + for (const RRNodeId& inode : temp_opin_rr_nodes[1]) { + /* Grid[x+1][y] TOP side outputs pins */ + rr_gsb.add_opin_node(inode, side_manager.get_side()); + } + + /* Clean ipin_rr_nodes */ + /* We do not have any IPIN for a Switch Block */ + rr_gsb.clear_ipin_nodes(side_manager.get_side()); + + /* Clear the temp data */ + temp_opin_rr_nodes[0].clear(); + temp_opin_rr_nodes[1].clear(); + opin_grid_side[0] = NUM_2D_SIDES; + opin_grid_side[1] = NUM_2D_SIDES; + } + + /* Add IPIN nodes from adjacent grids: the 4 grids sitting on the 4 corners of the Switch Block + * + * - The concept of top/bottom side of connection block in GSB domain: + * + * | Grid[x][y+1] | + * | BOTTOM side | + * +-----------------------+ + * | + * v + * +-----------------------+ + * | TOP side | + * | X- Connection Block | + * | BOTTOM side | + * +-----------------------+ + * ^ + * | + * +-----------------------+ + * | TOP side | + * | Grid[x][y] | + * + * - The concept of top/bottom side of connection block in GSB domain: + * + * ---------------+ +---------------------- ... ---------------------+ +---------------- + * Grid[x][y] |->| Y- Connection Block Y- Connection Block |<-| Grid[x+1][y] + * RIGHT side | | LEFT side ... RIGHT side | | LEFT side + * --------------+ +---------------------- ... ---------------------+ +---------------- + * + */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + size_t ix; + size_t iy; + enum e_side chan_side; + std::vector temp_ipin_rr_nodes; + enum e_side ipin_rr_node_grid_side; + + switch (side) { + case TOP: + /* Consider the routing channel that is connected to the left side of the switch block */ + chan_side = LEFT; + /* The input pins of the routing channel come from the bottom side of Grid[x][y+1] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y() + 1; + ipin_rr_node_grid_side = BOTTOM; + break; + case RIGHT: + /* Consider the routing channel that is connected to the top side of the switch block */ + chan_side = BOTTOM; + /* The input pins of the routing channel come from the left side of Grid[x+1][y+1] */ + ix = rr_gsb.get_sb_x() + 1; + iy = rr_gsb.get_sb_y(); + ipin_rr_node_grid_side = LEFT; + break; + case BOTTOM: + /* Consider the routing channel that is connected to the left side of the switch block */ + chan_side = LEFT; + /* The input pins of the routing channel come from the top side of Grid[x][y] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y(); + ipin_rr_node_grid_side = TOP; + break; + case LEFT: + /* Consider the routing channel that is connected to the top side of the switch block */ + chan_side = BOTTOM; + /* The input pins of the routing channel come from the right side of Grid[x][y+1] */ + ix = rr_gsb.get_sb_x(); + iy = rr_gsb.get_sb_y(); + ipin_rr_node_grid_side = RIGHT; + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Invalid side index!\n"); + } + + /* If there is no channel at this side, we skip ipin_node annotation */ + if (0 == rr_gsb.get_chan_width(chan_side)) { + continue; + } + /* Collect IPIN rr_nodes*/ + temp_ipin_rr_nodes = find_rr_graph_grid_nodes(rr_graph, grids, + layer, ix, iy, e_rr_type::IPIN, ipin_rr_node_grid_side); + /* Fill the ipin nodes of RRGSB */ + for (const RRNodeId& inode : temp_ipin_rr_nodes) { + rr_gsb.add_ipin_node(inode, side_manager.get_side()); + } + /* Clear the temp data */ + temp_ipin_rr_nodes.clear(); + } + + /* Find all MUX rr_nodes */ + std::vector mux_rr_nodes = rr_graph.node_lookup().find_grid_nodes_at_all_sides(layer, gsb_coordinate.x(), gsb_coordinate.y(), e_rr_type::MUX); + for (auto mux_rr_node : mux_rr_nodes) { + rr_gsb.add_mux_node(mux_rr_node); + } + /* For TOP and RIGHT borders, we need to add extra mux nodes. */ + if (gsb_coordinate.y() == grids.height() - 2) { + std::vector extra_mux_rr_nodes = rr_graph.node_lookup().find_grid_nodes_at_all_sides(layer, gsb_coordinate.x(), gsb_coordinate.y() + 1, e_rr_type::MUX); + for (auto mux_rr_node : extra_mux_rr_nodes) { + rr_gsb.add_mux_node(mux_rr_node); + } + } + + if (gsb_coordinate.x() == grids.width() - 2) { + std::vector extra_mux_rr_nodes = rr_graph.node_lookup().find_grid_nodes_at_all_sides(layer, gsb_coordinate.x() + 1, gsb_coordinate.y(), e_rr_type::MUX); + for (auto mux_rr_node : extra_mux_rr_nodes) { + rr_gsb.add_mux_node(mux_rr_node); + } + } + + if ((gsb_coordinate.x() == grids.width() - 2) && (gsb_coordinate.y() == grids.height() - 2)) { + std::vector extra_mux_rr_nodes = rr_graph.node_lookup().find_grid_nodes_at_all_sides(layer, gsb_coordinate.x() + 1, gsb_coordinate.y() + 1, e_rr_type::MUX); + for (auto mux_rr_node : extra_mux_rr_nodes) { + rr_gsb.add_mux_node(mux_rr_node); + } + } + + return rr_gsb; +} + +/************************************************************************ + * Create edges for each rr_node of a General Switch Blocks (GSB): + * 1. create edges between CHANX | CHANY and IPINs (connections inside connection blocks) + * 2. create edges between OPINs, CHANX and CHANY (connections inside switch blocks) + * 3. create edges between OPINs and IPINs (direct-connections) + ***********************************************************************/ +void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const t_track2pin_map& track2ipin_map, + const t_pin2track_map& opin2track_map, + const t_track2track_map& track2track_map, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges_to_create) { + size_t edge_count = 0; + /* Walk through each sides */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side gsb_side = side_manager.get_side(); + + /* Find OPINs */ + for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(gsb_side); ++inode) { + const RRNodeId& opin_node = rr_gsb.get_opin_node(gsb_side, inode); + + 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++; + } + } + } + + /* Find CHANX or CHANY */ + /* For TRACKs to IPINs, we only care LEFT and TOP sides + * Skip RIGHT and BOTTOM for the ipin2track_map since they should be handled in other GSBs + */ + if ((side_manager.get_side() == rr_gsb.get_cb_chan_side(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++; + } + } + } + + num_edges_to_create += edge_count; +} + +void build_edges_for_one_tileable_rr_gsb_vib(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const t_bend_track2track_map& sb_bend_conn, + const t_track2pin_map& track2ipin_map, + const t_pin2track_map& opin2track_map, + const t_track2track_map& track2track_map, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges_to_create) { + size_t edge_count = 0; + /* Walk through each sides */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side gsb_side = side_manager.get_side(); + + /* Find OPINs */ + for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(gsb_side); ++inode) { + const RRNodeId& opin_node = rr_gsb.get_opin_node(gsb_side, inode); + + 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++; + } + } + } + + /* Find CHANX or CHANY */ + /* For TRACKs to IPINs, we only care LEFT and TOP sides + * Skip RIGHT and BOTTOM for the ipin2track_map since they should be handled in other GSBs + */ + if ((side_manager.get_side() == rr_gsb.get_cb_chan_side(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++; + } + } + } + /* Create edges between bend nodes */ + for (auto iter = sb_bend_conn.begin(); iter != sb_bend_conn.end(); ++iter) { + rr_graph_builder.create_edge_in_cache(iter->first, iter->second, rr_node_driver_switches[iter->second], false); + edge_count++; + } + num_edges_to_create += edge_count; +} + +/************************************************************************ + * Build track2ipin_map for an IPIN + * 1. build a list of routing tracks which are allowed for connections + * We will check the Connection Block (CB) population of each routing track. + * By comparing current chan_y - ylow, we can determine if a CB connection + * is required for each routing track + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between IPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +static void build_gsb_one_ipin_track2pin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& ipin_side, + const size_t& ipin_node_id, + const std::vector& Fc, + const size_t& offset, + const std::vector& segment_inf, + t_track2pin_map& track2ipin_map) { + /* Get a list of segment_ids*/ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + std::vector seg_list = rr_gsb.get_chan_segment_ids(chan_side); + size_t chan_width = rr_gsb.get_chan_width(chan_side); + SideManager ipin_side_manager(ipin_side); + const RRNodeId& ipin_node = rr_gsb.get_ipin_node(ipin_side, ipin_node_id); + + for (size_t iseg = 0; iseg < seg_list.size(); ++iseg) { + /* Get a list of node that have the segment id */ + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(chan_side, seg_list[iseg]); + /* Refine the track_list: keep those will have connection blocks in the GSB */ + std::vector actual_track_list; + for (size_t inode = 0; inode < track_list.size(); ++inode) { + /* Check if tracks allow connection blocks in the GSB*/ + if (false == is_gsb_in_track_cb_population(rr_graph, rr_gsb, chan_side, track_list[inode], segment_inf)) { + continue; /* Bypass condition */ + } + /* Push the node to actual_track_list */ + actual_track_list.push_back(track_list[inode]); + } + /* Check the actual track list */ + VTR_ASSERT(0 == actual_track_list.size() % 2); + + /* Scale Fc */ + int actual_Fc = std::ceil((float)Fc[iseg] * (float)actual_track_list.size() / (float)chan_width); + /* Minimum Fc should be 2 : ensure we will connect to a pair of routing tracks */ + actual_Fc = std::max(1, actual_Fc); + /* Compute the step between two connection from this IPIN to tracks: + * step = W' / Fc', W' and Fc' are the adapted W and Fc from actual_track_list and Fc_in + */ + size_t track_step = std::floor((float)actual_track_list.size() / (float)actual_Fc); + /* Make sure step should be at least 2 */ + track_step = std::max(1, (int)track_step); + /* Adapt offset to the range of actual_track_list */ + size_t actual_offset = offset % actual_track_list.size(); + /* rotate the track list by an offset */ + if (0 < actual_offset) { + std::rotate(actual_track_list.begin(), actual_track_list.begin() + actual_offset, actual_track_list.end()); + } + + /* Assign tracks: since we assign 2 track per round, we increment itrack by 2* step */ + //int track_cnt = 0; + /* Keep assigning until we meet the Fc requirement */ + for (size_t itrack = 0; itrack < actual_track_list.size(); itrack = itrack + 2 * track_step) { + /* Update pin2track map */ + size_t chan_side_index = chan_side_manager.to_size_t(); + /* itrack may exceed the size of actual_track_list, adapt it */ + size_t actual_itrack = itrack % actual_track_list.size(); + /* track_index may exceed the chan_width(), adapt it */ + size_t track_index = actual_track_list[actual_itrack] % chan_width; + + track2ipin_map[chan_side_index][track_index].push_back(ipin_node); + + /* track_index may exceed the chan_width(), adapt it */ + track_index = (actual_track_list[actual_itrack] + 1) % chan_width; + + track2ipin_map[chan_side_index][track_index].push_back(ipin_node); + + //track_cnt += 2; + } + + /* Ensure the number of tracks is similar to Fc */ + /* Give a warning if Fc is < track_cnt */ + /* + * if (actual_Fc != track_cnt) { + * vpr_printf(TIO_MESSAGE_INFO, + * "IPIN Node(%lu) will have a different Fc(=%lu) than specified(=%lu)!\n", + * ipin_node - rr_graph->rr_node, track_cnt, actual_Fc); + * } + */ + } +} + +/************************************************************************ + * Build opin2track_map for an OPIN + * 1. build a list of routing tracks which are allowed for connections + * We will check the Switch Block (SB) population of each routing track. + * By comparing current chan_y - ylow, we can determine if a SB connection + * is required for each routing track + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between OPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +static void build_gsb_one_opin_pin2track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const enum e_side& opin_side, + const size_t& opin_node_id, + const enum e_side& chan_side, + const std::vector& Fc, + const size_t& offset, + const std::vector& segment_inf, + t_pin2track_map& opin2track_map) { + /* Get a list of segment_ids*/ + std::vector seg_list = rr_gsb.get_chan_segment_ids(opin_side); + size_t chan_width = rr_gsb.get_chan_width(chan_side); + SideManager opin_side_manager(opin_side); + SideManager chan_side_manager(chan_side); + + for (size_t iseg = 0; iseg < seg_list.size(); ++iseg) { + /* Get a list of node that have the segment id */ + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(chan_side, seg_list[iseg]); + /* Refine the track_list: keep those will have connection blocks in the GSB */ + std::vector actual_track_list; + for (size_t inode = 0; inode < track_list.size(); ++inode) { + /* Check if tracks allow connection blocks in the GSB*/ + if (false == is_gsb_in_track_sb_population(rr_graph, rr_gsb, chan_side, track_list[inode], segment_inf)) { + continue; /* Bypass condition */ + } + if (TRACK_START != determine_track_status_of_gsb(rr_graph, rr_gsb, chan_side, track_list[inode])) { + continue; /* Bypass condition */ + } + /* Push the node to actual_track_list */ + actual_track_list.push_back(track_list[inode]); + } + + /* Go the next segment if offset is zero or actual_track_list is empty */ + if (0 == actual_track_list.size()) { + continue; + } + + /* Scale Fc */ + int actual_Fc = std::ceil((float)Fc[iseg] * (float)actual_track_list.size() / (float)chan_width); + /* Minimum Fc should be 1 : ensure we will drive 1 routing track */ + actual_Fc = std::max(1, actual_Fc); + /* Compute the step between two connection from this IPIN to tracks: + * step = W' / Fc', W' and Fc' are the adapted W and Fc from actual_track_list and Fc_in + */ + size_t track_step = std::floor((float)actual_track_list.size() / (float)actual_Fc); + /* Track step mush be a multiple of 2!!!*/ + /* Make sure step should be at least 1 */ + track_step = std::max(1, (int)track_step); + /* Adapt offset to the range of actual_track_list */ + size_t actual_offset = offset % actual_track_list.size(); + + /* No need to rotate if offset is zero */ + if (0 < actual_offset) { + /* rotate the track list by an offset */ + std::rotate(actual_track_list.begin(), actual_track_list.begin() + actual_offset, actual_track_list.end()); + } + + /* Assign tracks */ + int track_cnt = 0; + /* Keep assigning until we meet the Fc requirement */ + for (size_t itrack = 0; itrack < actual_track_list.size(); itrack = itrack + track_step) { + /* Update pin2track map */ + size_t opin_side_index = opin_side_manager.to_size_t(); + /* itrack may exceed the size of actual_track_list, adapt it */ + size_t actual_itrack = itrack % actual_track_list.size(); + size_t track_index = actual_track_list[actual_itrack]; + const RRNodeId& track_rr_node_index = rr_gsb.get_chan_node(chan_side, track_index); + opin2track_map[opin_side_index][opin_node_id][chan_side_manager.to_size_t()].push_back(track_rr_node_index); + /* update track counter */ + track_cnt++; + /* Stop when we have enough Fc: this may lead to some tracks have zero drivers. + * So I comment it. And we just make sure its track_cnt >= actual_Fc + * if (actual_Fc == track_cnt) { + * break; + * } + */ + } + + /* Ensure the number of tracks is similar to Fc */ + /* Give a warning if Fc is < track_cnt */ + /* + * if (actual_Fc != track_cnt) { + * vpr_printf(TIO_MESSAGE_INFO, + * "OPIN Node(%lu) will have a different Fc(=%lu) than specified(=%lu)!\n", + * opin_node_id, track_cnt, actual_Fc); + * } + */ + } +} + +/************************************************************************ + * Build the track_to_ipin_map[gsb_side][0..chan_width-1][ipin_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * This function supports both X-directional and Y-directional tracks + * The mapping is done in the following steps: + * 1. Build ipin_to_track_map[gsb_side][0..num_ipin_nodes-1][track_indices] + * For each IPIN, we ensure at least one connection to the tracks. + * Then, we assign IPINs to tracks evenly while satisfying the actual_Fc + * 2. Convert the ipin_to_track_map to track_to_ipin_map + ***********************************************************************/ +t_track2pin_map build_gsb_track_to_ipin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_in) { + t_track2pin_map track2ipin_map; + /* Resize the matrix */ + track2ipin_map.resize(rr_gsb.get_num_sides()); + + /* offset counter: it aims to balance the track-to-IPIN for each connection block */ + size_t offset_size = 0; + std::vector offset; + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side ipin_side = side_manager.get_side(); + /* Get the chan_side */ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + /* resize offset to the maximum chan_side*/ + offset_size = std::max(offset_size, chan_side_manager.to_size_t() + 1); + } + /* Initial offset */ + offset.resize(offset_size); + offset.assign(offset.size(), 0); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side ipin_side = side_manager.get_side(); + /* Get the chan_side */ + enum e_side chan_side = rr_gsb.get_cb_chan_side(ipin_side); + SideManager chan_side_manager(chan_side); + /* This track2pin mapping is for Connection Blocks, so we only care two sides! */ + /* Get channel width and resize the matrix */ + size_t chan_width = rr_gsb.get_chan_width(chan_side); + track2ipin_map[chan_side_manager.to_size_t()].resize(chan_width); + /* Find the ipin/opin nodes */ + for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(ipin_side); ++inode) { + const RRNodeId& ipin_node = rr_gsb.get_ipin_node(ipin_side, inode); + t_physical_tile_loc ipin_node_phy_tile_loc(rr_graph.node_xlow(ipin_node), rr_graph.node_ylow(ipin_node), 0); + /* Skip EMPTY type */ + if (true == is_empty_type(grids.get_physical_type(ipin_node_phy_tile_loc))) { + continue; + } + + int grid_type_index = grids.get_physical_type(ipin_node_phy_tile_loc)->index; + /* Get Fc of the ipin */ + /* skip Fc = 0 or unintialized, those pins are in the */ + bool skip_conn2track = true; + std::vector ipin_Fc_out; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + int ipin_Fc = Fc_in[grid_type_index][rr_graph.node_pin_num(ipin_node)][iseg]; + ipin_Fc_out.push_back(ipin_Fc); + if (0 != ipin_Fc) { + skip_conn2track = false; + continue; + } + } + + if (true == skip_conn2track) { + continue; + } + + //VTR_ASSERT(ipin_Fc_out.size() == segment_inf.size()); + + /* Build track2ipin_map for this IPIN */ + build_gsb_one_ipin_track2pin_map(rr_graph, rr_gsb, ipin_side, inode, ipin_Fc_out, + /* Give an offset for the first track that this ipin will connect to */ + offset[chan_side_manager.to_size_t()], + segment_inf, track2ipin_map); + /* update offset */ + offset[chan_side_manager.to_size_t()] += 2; + //printf("offset[%lu]=%lu\n", chan_side_manager.to_size_t(), offset[chan_side_manager.to_size_t()]); + } + } + + return track2ipin_map; +} + +/************************************************************************ + * Build the opin_to_track_map[gsb_side][0..num_opin_nodes-1][track_indices] + * based on the existing routing resources in the General Switch Block (GSB) + * This function supports both X-directional and Y-directional tracks + * The mapping is done in the following steps: + * 1. Build a list of routing tracks whose starting points locate at this GSB + * (xlow - gsb_x == 0) + * 2. Divide the routing tracks by segment types, so that we can balance + * the connections between OPINs and different types of routing tracks. + * 3. Scale the Fc of each pin to the actual number of routing tracks + * actual_Fc = (int) Fc * num_tracks / chan_width + ***********************************************************************/ +t_pin2track_map build_gsb_opin_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_out, + const bool& opin2all_sides) { + t_pin2track_map opin2track_map; + /* Resize the matrix */ + opin2track_map.resize(rr_gsb.get_num_sides()); + + /* offset counter: it aims to balance the OPIN-to-track for each switch block */ + std::vector offset; + /* Get the chan_side: which is the same as the opin side */ + offset.resize(rr_gsb.get_num_sides()); + /* Initial offset */ + offset.assign(offset.size(), 0); + + /* Walk through each side */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + enum e_side opin_side = side_manager.get_side(); + /* Get the chan_side */ + /* This track2pin mapping is for Connection Blocks, so we only care two sides! */ + /* Get channel width and resize the matrix */ + size_t num_opin_nodes = rr_gsb.get_num_opin_nodes(opin_side); + opin2track_map[side].resize(num_opin_nodes); + /* Find the ipin/opin nodes */ + for (size_t inode = 0; inode < num_opin_nodes; ++inode) { + const RRNodeId& opin_node = rr_gsb.get_opin_node(opin_side, inode); + t_physical_tile_loc opin_node_phy_tile_loc(rr_graph.node_xlow(opin_node), rr_graph.node_ylow(opin_node), 0); + /* Skip EMPTY type */ + if (true == is_empty_type(grids.get_physical_type(opin_node_phy_tile_loc))) { + continue; + } + int grid_type_index = grids.get_physical_type(opin_node_phy_tile_loc)->index; + + /* Get Fc of the ipin */ + /* skip Fc = 0 or unintialized, those pins are in the */ + bool skip_conn2track = true; + std::vector opin_Fc_out; + for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) { + int opin_Fc = Fc_out[grid_type_index][rr_graph.node_pin_num(opin_node)][iseg]; + opin_Fc_out.push_back(opin_Fc); + if (0 != opin_Fc) { + skip_conn2track = false; + continue; + } + } + if (rr_gsb.get_sb_x() == grids.width() - 1 || rr_gsb.get_sb_y() == grids.height() - 1) { + skip_conn2track = true; + } + + if (true == skip_conn2track) { + continue; + } + VTR_ASSERT(opin_Fc_out.size() == segment_inf.size()); + + /* Build track2ipin_map for this IPIN */ + opin2track_map[side][inode].resize(rr_gsb.get_num_sides()); + if (opin2all_sides) { + for (size_t track_side = 0; track_side < rr_gsb.get_num_sides(); ++track_side) { + SideManager track_side_mgr(track_side); + build_gsb_one_opin_pin2track_map(rr_graph, rr_gsb, opin_side, inode, track_side_mgr.get_side(), opin_Fc_out, + /* Give an offset for the first track that this ipin will connect to */ + offset[side_manager.to_size_t()], + segment_inf, opin2track_map); + } + } else { + build_gsb_one_opin_pin2track_map(rr_graph, rr_gsb, opin_side, inode, opin_side, opin_Fc_out, + /* Give an offset for the first track that this ipin will connect to */ + offset[side_manager.to_size_t()], + segment_inf, opin2track_map); + } + /* update offset: aim to rotate starting tracks by 1*/ + offset[side_manager.to_size_t()] += 1; + } + + /* Check: + * 1. We want to ensure that each OPIN will drive at least one track + * 2. We want to ensure that each track will be driven by at least 1 OPIN */ + } + + return opin2track_map; +} + +/************************************************************************ + * Add all direct clb-pin-to-clb-pin edges to given opin + ***********************************************************************/ +void build_direct_connections_for_one_gsb(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& from_grid_coordinate, + const std::vector& directs, + const std::vector& clb_to_clb_directs) { + VTR_ASSERT(directs.size() == clb_to_clb_directs.size()); + + t_physical_tile_type_ptr grid_type = grids.get_physical_type(t_physical_tile_loc(from_grid_coordinate.x(), from_grid_coordinate.y(), layer)); + + /* Iterate through all direct connections */ + for (size_t i = 0; i < directs.size(); ++i) { + /* Bypass unmatched direct clb-to-clb connections */ + if (grid_type != clb_to_clb_directs[i].from_clb_type) { + continue; + } + + /* This opin is specified to connect directly to an ipin, + * now compute which ipin to connect to + */ + vtr::Point to_grid_coordinate(from_grid_coordinate.x() + directs[i].x_offset, + from_grid_coordinate.y() + directs[i].y_offset); + + /* Bypass unmatched direct clb-to-clb connections */ + t_physical_tile_type_ptr to_grid_type = grids.get_physical_type(t_physical_tile_loc(to_grid_coordinate.x(), to_grid_coordinate.y(), layer)); + /* Check if to_grid if the same grid */ + if (to_grid_type != clb_to_clb_directs[i].to_clb_type) { + continue; + } + + bool swap; + int max_index, min_index; + /* Compute index of opin with regards to given pins */ + if (clb_to_clb_directs[i].from_clb_pin_start_index + > clb_to_clb_directs[i].from_clb_pin_end_index) { + swap = true; + max_index = clb_to_clb_directs[i].from_clb_pin_start_index; + min_index = clb_to_clb_directs[i].from_clb_pin_end_index; + } else { + swap = false; + min_index = clb_to_clb_directs[i].from_clb_pin_start_index; + max_index = clb_to_clb_directs[i].from_clb_pin_end_index; + } + + /* get every opin in the range */ + for (int opin = min_index; opin <= max_index; ++opin) { + int offset = opin - min_index; + + if ((to_grid_coordinate.x() < grids.width() - 1) + && (to_grid_coordinate.y() < grids.height() - 1)) { + int ipin = OPEN; + if (clb_to_clb_directs[i].to_clb_pin_start_index + > clb_to_clb_directs[i].to_clb_pin_end_index) { + if (true == swap) { + ipin = clb_to_clb_directs[i].to_clb_pin_end_index + offset; + } else { + ipin = clb_to_clb_directs[i].to_clb_pin_start_index - offset; + } + } else { + if (true == swap) { + ipin = clb_to_clb_directs[i].to_clb_pin_end_index - offset; + } else { + ipin = clb_to_clb_directs[i].to_clb_pin_start_index + offset; + } + } + + /* Get the pin index in the rr_graph */ + t_physical_tile_loc from_tile_loc(from_grid_coordinate.x(), from_grid_coordinate.y(), layer); + int from_grid_width_ofs = grids.get_width_offset(from_tile_loc); + int from_grid_height_ofs = grids.get_height_offset(from_tile_loc); + t_physical_tile_loc to_tile_loc(to_grid_coordinate.x(), to_grid_coordinate.y(), layer); + int to_grid_width_ofs = grids.get_width_offset(to_tile_loc); + int to_grid_height_ofs = grids.get_height_offset(to_tile_loc); + + /* Find the side of grid pins, the pin location should be unique! + * Pin location is required by searching a node in rr_graph + */ + std::vector opin_grid_side = find_grid_pin_sides(grids, layer, from_grid_coordinate.x(), from_grid_coordinate.y(), opin); + VTR_ASSERT(1 == opin_grid_side.size()); + + std::vector ipin_grid_side = find_grid_pin_sides(grids, layer, to_grid_coordinate.x(), to_grid_coordinate.y(), ipin); + VTR_ASSERT(1 == ipin_grid_side.size()); + + RRNodeId opin_node_id = rr_graph.node_lookup().find_node(layer, + from_grid_coordinate.x() - from_grid_width_ofs, + from_grid_coordinate.y() - from_grid_height_ofs, + e_rr_type::OPIN, opin, opin_grid_side[0]); + RRNodeId ipin_node_id = rr_graph.node_lookup().find_node(layer, + to_grid_coordinate.x() - to_grid_width_ofs, + to_grid_coordinate.y() - to_grid_height_ofs, + e_rr_type::IPIN, ipin, ipin_grid_side[0]); + + /* add edges to the opin_node */ + VTR_ASSERT(opin_node_id && ipin_node_id); + rr_graph_builder.create_edge_in_cache(opin_node_id, ipin_node_id, RRSwitchId(clb_to_clb_directs[i].switch_index), false); + } + } + } + /* Build actual edges */ + rr_graph_builder.build_edges(true); +} + +/* Vib edge builder */ +t_vib_map build_vib_map(const RRGraphView& rr_graph, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const RRGSB& rr_gsb, + const std::vector& segment_inf, + const size_t& layer, + const vtr::Point& gsb_coordinate, + const vtr::Point& actual_coordinate) { + VTR_ASSERT(rr_gsb.get_x() == gsb_coordinate.x() && rr_gsb.get_y() == gsb_coordinate.y()); + + t_vib_map vib_map; + + const VibInf* vib = vib_grid.get_vib(layer, actual_coordinate.x(), actual_coordinate.y()); + auto phy_type = grids.get_physical_type({(int)actual_coordinate.x(), (int)actual_coordinate.y(), (int)layer}); + VTR_ASSERT(vib->get_pbtype_name() == phy_type->name); + const std::vector first_stages = vib->get_first_stages(); + for (size_t i_first_stage = 0; i_first_stage < first_stages.size(); i_first_stage++) { + std::vector froms = first_stages[i_first_stage].froms; + RRNodeId to_node = rr_graph.node_lookup().find_node(layer, actual_coordinate.x(), actual_coordinate.y(), e_rr_type::MUX, i_first_stage); + VTR_ASSERT(to_node.is_valid()); + VTR_ASSERT(rr_gsb.is_mux_node(to_node)); + for (auto from : froms) { + RRNodeId from_node; + if (from.from_type == e_multistage_mux_from_or_to_type::PB) { + + if (from.type_name != vib->get_pbtype_name()) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong from type name!\n"); + } + + for (e_side side : TOTAL_2D_SIDES) { + from_node = rr_graph.node_lookup().find_node(layer, actual_coordinate.x(), actual_coordinate.y(), e_rr_type::OPIN, from.phy_pin_index, side); + if (from_node.is_valid()) + break; + } + if (!from_node.is_valid()) { + VTR_LOGF_WARN(__FILE__, __LINE__, + "Can not find from node %s:%d!\n", from.type_name.c_str(), from.phy_pin_index); + continue; + } + if (!rr_gsb.is_opin_node(from_node)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Opin node %d is not in the GSB (%d, %d)\n", from_node, rr_gsb.get_x(), rr_gsb.get_y()); + } + } else if (from.from_type == e_multistage_mux_from_or_to_type::SEGMENT) { + char from_dir = from.seg_dir; + //int from_index = from.seg_index; + t_segment_inf segment = segment_inf[from.type_index]; + VTR_ASSERT(segment.name == from.type_name); + t_seg_group seg_group; + for (auto seg : vib->get_seg_groups()) { + if (seg.name == segment.name) { + seg_group = seg; + break; + } + } + VTR_ASSERT(from.seg_index < seg_group.track_num * segment.length); + e_side side; + if (from_dir == 'W') + side = RIGHT; + else if (from_dir == 'E') + side = LEFT; + else if (from_dir == 'N') + side = BOTTOM; + else if (from_dir == 'S') + side = TOP; + else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong segment from direction!\n"); + } + + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(side, RRSegmentId(segment.seg_index)); + if (track_list.size() == 0) + continue; + else { + VTR_ASSERT((int)track_list.size() >= (from.seg_index + 1) * 2); + size_t seg_id; + if (side == LEFT || side == BOTTOM) { //INC + seg_id = from.seg_index * 2; + } else { //DEC + VTR_ASSERT(side == RIGHT || side == TOP); + seg_id = from.seg_index * 2 + 1; + } + from_node = rr_gsb.get_chan_node(side, track_list[seg_id]); + VTR_ASSERT(IN_PORT == rr_gsb.get_chan_node_direction(side, track_list[seg_id])); + if (!rr_gsb.is_chan_node(from_node)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wire node %d is not in the GSB (%d, %d)\n", from_node, rr_gsb.get_x(), rr_gsb.get_y()); + } + } + + } else if (from.from_type == e_multistage_mux_from_or_to_type::MUX) { + size_t from_mux_index = vib->mux_index_by_name(from.type_name); + from_node = rr_graph.node_lookup().find_node(layer, actual_coordinate.x(), actual_coordinate.y(), e_rr_type::MUX, from_mux_index); + if (!rr_gsb.is_mux_node(from_node)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "MUX node %d is not in the GSB (%d, %d)\n", from_node, rr_gsb.get_x(), rr_gsb.get_y()); + } + } else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong from type!\n"); + } + VTR_ASSERT(from_node.is_valid()); + auto iter = vib_map.begin(); + for (; iter != vib_map.end(); ++iter) { + if (iter->first == from_node) { + vib_map[from_node].push_back(to_node); + } + } + if (iter == vib_map.end()) { + std::vector to_nodes; + to_nodes.push_back(to_node); + vib_map.emplace(std::make_pair(from_node, to_nodes)); + } + } + } + /* Second stages*/ + const std::vector second_stages = vib->get_second_stages(); + for (size_t i_second_stage = 0; i_second_stage < second_stages.size(); i_second_stage++) { + std::vector froms = second_stages[i_second_stage].froms; + std::vector tos = second_stages[i_second_stage].to; + + std::vector to_nodes; + for (auto to : tos) { + RRNodeId to_node; + if (to.from_type == e_multistage_mux_from_or_to_type::PB) { + + if (to.type_name != vib->get_pbtype_name()) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong to type name!\n"); + } + + for (e_side side : TOTAL_2D_SIDES) { + to_node = rr_graph.node_lookup().find_node(layer, actual_coordinate.x(), actual_coordinate.y(), e_rr_type::IPIN, to.phy_pin_index, side); + if (to_node.is_valid()) + break; + } + if (!to_node.is_valid()) { + VTR_LOGF_WARN(__FILE__, __LINE__, + "Can not find from node %s:%d!\n", to.type_name.c_str(), to.phy_pin_index); + continue; + } + if (!rr_gsb.is_ipin_node(to_node)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "MUX node %d is not in the GSB (%d, %d)\n", to_node, rr_gsb.get_x(), rr_gsb.get_y()); + } + } else if (to.from_type == e_multistage_mux_from_or_to_type::SEGMENT) { + char to_dir = to.seg_dir; + //int from_index = from.seg_index; + t_segment_inf segment = segment_inf[to.type_index]; + VTR_ASSERT(segment.name == to.type_name); + t_seg_group seg_group; + for (auto seg : vib->get_seg_groups()) { + if (seg.name == segment.name) { + seg_group = seg; + break; + } + } + VTR_ASSERT(to.seg_index < seg_group.track_num * segment.length); + e_side side; + if (to_dir == 'W') + side = LEFT; + else if (to_dir == 'E') + side = RIGHT; + else if (to_dir == 'N') + side = TOP; + else if (to_dir == 'S') + side = BOTTOM; + else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong segment from direction!\n"); + } + + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(side, RRSegmentId(segment.seg_index)); + if (track_list.size() == 0) + continue; + else { + //enum e_track_status track_status = determine_track_status_of_gsb + VTR_ASSERT((int)track_list.size() >= (to.seg_index + 1) * 2); + size_t seg_id; + if (side == LEFT || side == BOTTOM) { //DEC + seg_id = to.seg_index * 2 + 1; + } else { //INC + VTR_ASSERT(side == RIGHT || side == TOP); + seg_id = to.seg_index * 2; + } + enum e_track_status track_status = determine_track_status_of_gsb(rr_graph, rr_gsb, side, track_list[seg_id]); + VTR_ASSERT(track_status == TRACK_START); + to_node = rr_gsb.get_chan_node(side, track_list[seg_id]); + VTR_ASSERT(OUT_PORT == rr_gsb.get_chan_node_direction(side, track_list[seg_id])); + if (!rr_gsb.is_chan_node(to_node)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "MUX node %d is not in the GSB (%d, %d)\n", to_node, rr_gsb.get_x(), rr_gsb.get_y()); + } + } + + } else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong from type!\n"); + } + VTR_ASSERT(to_node.is_valid()); + to_nodes.push_back(to_node); + } + + std::vector from_nodes; + for (auto from : froms) { + RRNodeId from_node; + if (from.from_type == e_multistage_mux_from_or_to_type::PB) { + + if (from.type_name != vib->get_pbtype_name()) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong from type name!\n"); + } + + for (e_side side : TOTAL_2D_SIDES) { + from_node = rr_graph.node_lookup().find_node(layer, actual_coordinate.x(), actual_coordinate.y(), e_rr_type::OPIN, from.phy_pin_index, side); + if (from_node.is_valid()) + break; + } + if (!from_node.is_valid()) { + VTR_LOGF_WARN(__FILE__, __LINE__, + "Can not find from node %s:%d!\n", from.type_name.c_str(), from.phy_pin_index); + continue; + } + if (!rr_gsb.is_opin_node(from_node)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "MUX node %d is not in the GSB (%d, %d)\n", from_node, rr_gsb.get_x(), rr_gsb.get_y()); + } + } else if (from.from_type == e_multistage_mux_from_or_to_type::SEGMENT) { + char from_dir = from.seg_dir; + //int from_index = from.seg_index; + t_segment_inf segment = segment_inf[from.type_index]; + VTR_ASSERT(segment.name == from.type_name); + t_seg_group seg_group; + for (auto seg : vib->get_seg_groups()) { + if (seg.name == segment.name) { + seg_group = seg; + break; + } + } + VTR_ASSERT(from.seg_index < seg_group.track_num * segment.length); + e_side side; + if (from_dir == 'W') + side = RIGHT; + else if (from_dir == 'E') + side = LEFT; + else if (from_dir == 'N') + side = BOTTOM; + else if (from_dir == 'S') + side = TOP; + else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong segment from direction!\n"); + } + + std::vector track_list = rr_gsb.get_chan_node_ids_by_segment_ids(side, RRSegmentId(segment.seg_index)); + if (track_list.size() == 0) + continue; + else { + VTR_ASSERT((int)track_list.size() >= (from.seg_index + 1) * 2); + size_t seg_id; + if (side == LEFT || side == BOTTOM) { //INC + seg_id = from.seg_index * 2; + } else { //DEC + VTR_ASSERT(side == RIGHT || side == TOP); + seg_id = from.seg_index * 2 + 1; + } + from_node = rr_gsb.get_chan_node(side, track_list[seg_id]); + VTR_ASSERT(IN_PORT == rr_gsb.get_chan_node_direction(side, track_list[seg_id])); + if (!rr_gsb.is_chan_node(from_node)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "MUX node %d is not in the GSB (%d, %d)\n", from_node, rr_gsb.get_x(), rr_gsb.get_y()); + } + } + + } else if (from.from_type == e_multistage_mux_from_or_to_type::MUX) { + size_t from_mux_index = vib->mux_index_by_name(from.type_name); + from_node = rr_graph.node_lookup().find_node(layer, actual_coordinate.x(), actual_coordinate.y(), e_rr_type::MUX, from_mux_index); + if (!rr_gsb.is_mux_node(from_node)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "MUX node %d is not in the GSB (%d, %d)\n", from_node, rr_gsb.get_x(), rr_gsb.get_y()); + } + } else { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Wrong from type!\n"); + } + VTR_ASSERT(from_node.is_valid()); + from_nodes.push_back(from_node); + } + + if (to_nodes.size() > 0 && from_nodes.size() > 0) { + for (auto from_node : from_nodes) { + auto iter = vib_map.begin(); + for (; iter != vib_map.end(); ++iter) { + if (iter->first == from_node) { + for (auto to_node : to_nodes) { + vib_map[from_node].push_back(to_node); + } + } + } + if (iter == vib_map.end()) { + vib_map.emplace(std::make_pair(from_node, to_nodes)); + } + } + } + } + return vib_map; +} + +void build_edges_for_one_tileable_vib(RRGraphBuilder& rr_graph_builder, + const t_vib_map& vib_map, + const t_bend_track2track_map& sb_bend_conn, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges_to_create) { + + size_t edge_count = 0; + for (auto iter = vib_map.begin(); iter != vib_map.end(); ++iter) { + for (auto to_node : iter->second) { + rr_graph_builder.create_edge_in_cache(iter->first, to_node, rr_node_driver_switches[to_node], false); + edge_count++; + } + } + for (auto iter = sb_bend_conn.begin(); iter != sb_bend_conn.end(); ++iter) { + rr_graph_builder.create_edge_in_cache(iter->first, iter->second, rr_node_driver_switches[iter->second], false); + edge_count++; + } + num_edges_to_create += edge_count; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.h new file mode 100644 index 00000000000..317410a4d88 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.h @@ -0,0 +1,115 @@ +#pragma once + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include + +#include "vtr_vector.h" +#include "vtr_geometry.h" + +#include "physical_types.h" +#include "device_grid.h" + +#include "rr_gsb.h" +#include "rr_graph_obj.h" +#include "rr_graph.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/************************************************************************ + * Data stuctures related to the functions + ***********************************************************************/ +typedef std::map t_bend_track2track_map; +typedef std::map> t_vib_map; +typedef std::vector>> t_track2track_map; +typedef std::vector>> t_track2pin_map; +typedef std::vector>>> t_pin2track_map; + +/************************************************************************ + * Functions + ***********************************************************************/ +t_track2track_map build_gsb_track_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const e_switch_block_type& sb_type, + const int& Fs, + const e_switch_block_type& sb_subtype, + const int& sub_fs, + const bool& concat_wire, + const bool& wire_opposite_side, + const std::vector& segment_inf); + +t_bend_track2track_map build_bend_track_to_track_map(const DeviceGrid& grids, + RRGraphBuilder& rr_graph_builder, + const RRGraphView& rr_graph, + const std::vector& segment_inf, + const size_t& layer, + const vtr::Point& gsb_coordinate, + const RRSwitchId& delayless_switch, + vtr::vector& rr_node_driver_switches); + +RRGSB build_one_tileable_rr_gsb(const DeviceGrid& grids, + const RRGraphView& rr_graph, + const vtr::Point& device_chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const size_t& layer, + const vtr::Point& gsb_coordinate, + const bool& perimeter_cb); + +void build_edges_for_one_tileable_rr_gsb(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const t_track2pin_map& track2ipin_map, + const t_pin2track_map& opin2track_map, + const t_track2track_map& track2track_map, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges); + +void build_edges_for_one_tileable_rr_gsb_vib(RRGraphBuilder& rr_graph_builder, + const RRGSB& rr_gsb, + const t_bend_track2track_map& sb_bend_conn, + const t_track2pin_map& track2ipin_map, + const t_pin2track_map& opin2track_map, + const t_track2track_map& track2track_map, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges); + +t_track2pin_map build_gsb_track_to_ipin_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_in); + +t_pin2track_map build_gsb_opin_to_track_map(const RRGraphView& rr_graph, + const RRGSB& rr_gsb, + const DeviceGrid& grids, + const std::vector& segment_inf, + const std::vector>& Fc_out, + const bool& opin2all_sides); + +void build_direct_connections_for_one_gsb(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& from_grid_coordinate, + const std::vector& directs, + const std::vector& clb_to_clb_directs); + +t_vib_map build_vib_map(const RRGraphView& rr_graph, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const RRGSB& rr_gsb, + const std::vector& segment_inf, + const size_t& layer, + const vtr::Point& gsb_coordinate, + const vtr::Point& actual_coordinate); + +void build_edges_for_one_tileable_vib(RRGraphBuilder& rr_graph_builder, + const t_vib_map& vib_map, + const t_bend_track2track_map& sb_bend_conn, + const vtr::vector& rr_node_driver_switches, + size_t& num_edges_to_create); diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_node_builder.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_node_builder.cpp new file mode 100644 index 00000000000..97ce60c8b64 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_node_builder.cpp @@ -0,0 +1,1309 @@ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_geometry.h" + +#include "side_manager.h" + +#include "vpr_types.h" +#include "vpr_utils.h" + +#include "rr_node.h" + +#include "rr_graph_builder_utils.h" +#include "rr_graph_builder.h" +#include "tileable_chan_details_builder.h" +#include "tileable_rr_graph_node_builder.h" +#include "rr_rc_data.h" +#include "physical_types_util.h" + +/** + * @brief Find the number output pins by considering all the grid + */ +static size_t estimate_num_grid_rr_nodes_by_type(const DeviceGrid& grids, + const size_t& layer, + const e_rr_type& node_type, + const bool perimeter_cb) { + size_t num_grid_rr_nodes = 0; + + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + t_physical_tile_loc tile_loc(ix, iy, layer); + // Skip EMPTY tiles + if (true == is_empty_type(grids.get_physical_type(tile_loc))) { + continue; + } + + // Skip height > 1 or width > 1 tiles (mostly heterogeneous blocks) + if ((0 < grids.get_width_offset(tile_loc)) + || (0 < grids.get_height_offset(tile_loc))) { + continue; + } + + std::vector io_side = {TOP, RIGHT, BOTTOM, LEFT}; + + // If this is the block on borders, we consider IO side + if (grids.get_physical_type(tile_loc)->is_io()) { + vtr::Point io_device_size(grids.width() - 1, grids.height() - 1); + vtr::Point grid_coordinate(ix, iy); + io_side = determine_io_grid_pin_side(io_device_size, grid_coordinate, perimeter_cb); + } + + switch (node_type) { + case e_rr_type::OPIN: + // get the number of OPINs + num_grid_rr_nodes += get_grid_num_pins(grids, layer, ix, iy, DRIVER, io_side); + break; + case e_rr_type::IPIN: + // get the number of IPINs + num_grid_rr_nodes += get_grid_num_pins(grids, layer, ix, iy, RECEIVER, io_side); + break; + case e_rr_type::SOURCE: + // SOURCE: number of classes whose type is DRIVER + num_grid_rr_nodes += get_grid_num_classes(grids, layer, ix, iy, DRIVER); + break; + case e_rr_type::SINK: + // SINK: number of classes whose type is RECEIVER + num_grid_rr_nodes += get_grid_num_classes(grids, layer, ix, iy, RECEIVER); + break; + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "Invalid routing resource node!\n"); + } + } + } + + return num_grid_rr_nodes; +} + +static size_t estimate_num_mux_rr_nodes(const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer) { + size_t num_grid_rr_nodes = 0; + + VTR_ASSERT(grids.width() == vib_grid.width() && grids.height() == vib_grid.height()); + for (size_t ix = 0; ix < grids.width(); ++ix) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + + const VibInf* vib = vib_grid.get_vib(layer, ix, iy); + if (!vib) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "VIB at (%d, %d) is EMPTY!\n", ix, iy); + } + + size_t count = 0; + for (size_t i_first_stage = 0; i_first_stage < vib->get_first_stages().size(); i_first_stage++) { + auto first_stage = vib->get_first_stages()[i_first_stage]; + if (first_stage.froms.size() == 0) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "VIB first stage '%s' at (%d, %d) has no from!\n", first_stage.mux_name.c_str(), ix, iy); + } + count++; + } + + VTR_ASSERT(count == vib->get_first_stages().size()); + num_grid_rr_nodes += count; + } + } + + return num_grid_rr_nodes; +} + +/************************************************************************ + * For X-direction Channel: CHANX + * We pair each x-direction routing channel to the grid below it + * as they share the same coordinate + * + * As such, the range of CHANX coordinate starts from x = 1, y = 0 + * which is the grid (I/O) at the left bottom of the fabric + * + * As such, the range of CHANX coordinate ends to x = width - 2, y = height - 2 + * which is the grid at the top right of the core fabric + * Note that the I/O ring is + * + * TOP SIDE OF FPGA + * + * +-------------+ +-------------+ +---------------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][height-1] | + * +-------------+ +-------------+ +---------------------+ + * + * +-------------+ +-------------+ +---------------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][0] | | [2][0] | | [width-2][height-2] | + * +-------------+ +-------------+ +---------------------+ + * + * +-------------+ +-------------+ +---------------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][height-2] | + * +-------------+ +-------------+ +---------------------+ + * + * ... ... ... + * + * +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][1] | | [2][1] | | [width-2][1] | + * +-------------+ +-------------+ +--------------+ + * + * LEFT +-------------+ +-------------+ +--------------+ RIGHT + * SIDE | Grid | | Grid | ... | Grid | SIDE + * GRID | [1][1] | | [2][1] | | [width-2][1] | GRID + * +-------------+ +-------------+ +--------------+ + * + * +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | ... | X-Channel | + * | [1][0] | | [2][0] | | [width-2][0] | + * +-------------+ +-------------+ +--------------+ + * + * +-------------+ +-------------+ +--------------+ + * | Grid | | Grid | ... | Grid | + * | [1][0] | | [2][0] | | [width-2][0] | + * +-------------+ +-------------+ +--------------+ + * + * BOTTOM SIDE OF FPGA + * + * The figure above describe how the X-direction routing channels are + * organized in a homogeneous FPGA fabric + * Note that we talk about general-purpose uni-directional routing architecture here + * It means that a routing track may span across multiple grids + * However, the hard limits are as follows + * All the routing tracks will start at the most LEFT routing channel + * All the routing tracks will end at the most RIGHT routing channel + * + * Things will become more complicated in terms of track starting and end + * in the context of heterogeneous FPGAs + * We may have a grid which span multiple column and rows, as exemplified in the figure below + * In such case, + * all the routing tracks [x-1][y] at the left side of the grid [x][y] are forced to end + * all the routing tracks [x+2][y] at the right side of the grid [x][y] are forced to start + * And there are no routing tracks inside the grid[x][y] + * It means that X-channel [x][y] & [x+1][y] will no exist + * + * +------------+ +-------------+ +-------------+ +--------------+ + * | X-Channel | | X-Channel | | X-Channel | | X-Channel | + * | [x-1][y+2] | | [x][y+2] | | [x+1][y+2] | | [x+2][y+2] | + * +------------+ +-------------+ +-------------+ +--------------+ + * + * +------------+ +-----------------------------------+ +--------------+ + * | Grid | | | | Grid | + * | [x-1][y+1] | | | | [x+2][y+1] | + * +------------+ | | +--------------+ + * | | + * +------------+ | | +--------------+ + * | X-channel | | Grid | | X-Channel | + * | [x-1][y] | | [x][y] - [x+1][y+1] | | [x+2][y] | + * +------------+ | | +--------------+ + * | | + * +------------+ | | +--------------+ + * | Grid | | | | Grid | + * | [x-1][y] | | | | [x+2][y] | + * +------------+ +-----------------------------------+ +--------------+ + * + * + * + ***********************************************************************/ +static size_t estimate_num_chanx_rr_nodes(const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel) { + size_t num_chanx_rr_nodes = 0; + // Default x-channel boundary box + size_t start_x = 1; + size_t end_x = grids.width() - 1; + if (perimeter_cb) { + start_x = 0; + end_x = grids.width(); + } + size_t max_seg_length = grids.width() - 2; + if (perimeter_cb) { + max_seg_length = grids.width(); + } + + for (size_t iy = 0; iy < grids.height() - 1; ++iy) { + for (size_t ix = start_x; ix < end_x; ++ix) { + vtr::Point chanx_coord(ix, iy); + + // Bypass if the routing channel does not exist when through channels are not allowed + if ((false == through_channel) + && (false == is_chanx_exist(grids, layer, chanx_coord, perimeter_cb))) { + continue; + } + // Bypass if the routing channel does not exist when a shrink boundary is considered + if (shrink_boundary && !device_grid_annotation.is_chanx_exist(chanx_coord)) { + continue; + } + + bool force_start = false; + bool force_end = false; + + // All the tracks have to start when + // - the routing channel touch the RIGHT side a heterogeneous block + // - the routing channel touch the LEFT side of FPGA + if (true == is_chanx_right_to_multi_height_grid(grids, layer, chanx_coord, perimeter_cb, through_channel)) { + force_start = true; + } + if (shrink_boundary && device_grid_annotation.is_chanx_start(chanx_coord)) { + force_start = true; + } + + // All the tracks have to end when + // - the routing channel touch the LEFT side a heterogeneous block + // - the routing channel touch the RIGHT side of FPGA + if (true == is_chanx_left_to_multi_height_grid(grids, layer, chanx_coord, perimeter_cb, through_channel)) { + force_end = true; + } + if (shrink_boundary && device_grid_annotation.is_chanx_end(chanx_coord)) { + force_end = true; + } + + // Evaluate if the routing channel locates in the middle of a grid + ChanNodeDetails chanx_details = build_unidir_chan_node_details(chan_width, max_seg_length, force_start, force_end, segment_infs); + // When an INC_DIRECTION CHANX starts, we need a new rr_node + num_chanx_rr_nodes += chanx_details.get_num_starting_tracks(Direction::INC); + // When an DEC_DIRECTION CHANX ends, we need a new rr_node + num_chanx_rr_nodes += chanx_details.get_num_ending_tracks(Direction::DEC); + } + } + + return num_chanx_rr_nodes; +} + +/** + * @brief Estimate the number of CHANY rr_nodes for Y-direction routing channels + * @details The technical rationale is very similar to the X-direction routing channel + * Refer to the detailed explanation there + */ +static size_t estimate_num_chany_rr_nodes(const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel) { + size_t num_chany_rr_nodes = 0; + // Default x-channel boundary box + size_t start_y = 1; + size_t end_y = grids.height() - 1; + if (perimeter_cb) { + start_y = 0; + end_y = grids.height(); + } + size_t max_seg_length = grids.height() - 2; + if (perimeter_cb) { + max_seg_length = grids.height(); + } + + for (size_t ix = 0; ix < grids.width() - 1; ++ix) { + for (size_t iy = start_y; iy < end_y; ++iy) { + vtr::Point chany_coord(ix, iy); + + // Bypass if the routing channel does not exist when through channel are not allowed + if ((false == through_channel) + && (false == is_chany_exist(grids, layer, chany_coord, perimeter_cb))) { + continue; + } + + // Bypass if the routing channel does not exist when a shrink boundary is considered + if (shrink_boundary && !device_grid_annotation.is_chany_exist(chany_coord)) { + continue; + } + + bool force_start = false; + bool force_end = false; + + // All the tracks have to start when + // - the routing channel touch the TOP side a heterogeneous block + // - the routing channel touch the BOTTOM side of FPGA + if (true == is_chany_top_to_multi_width_grid(grids, layer, chany_coord, perimeter_cb, through_channel)) { + force_start = true; + } + if (shrink_boundary && device_grid_annotation.is_chany_start(chany_coord)) { + force_start = true; + } + + // All the tracks have to end when + // - the routing channel touch the BOTTOM side a heterogeneous block + // - the routing channel touch the TOP side of FPGA + if (true == is_chany_bottom_to_multi_width_grid(grids, layer, chany_coord, perimeter_cb, through_channel)) { + force_end = true; + } + if (shrink_boundary && device_grid_annotation.is_chany_end(chany_coord)) { + force_end = true; + } + + ChanNodeDetails chany_details = build_unidir_chan_node_details(chan_width, max_seg_length, force_start, force_end, segment_infs); + // When an INC_DIRECTION CHANX starts, we need a new rr_node + num_chany_rr_nodes += chany_details.get_num_starting_tracks(Direction::INC); + // When an DEC_DIRECTION CHANX ends, we need a new rr_node + num_chany_rr_nodes += chany_details.get_num_ending_tracks(Direction::DEC); + } + } + + return num_chany_rr_nodes; +} + +/** + * @brief Estimate the number of nodes by each type in a routing resource graph + */ +static vtr::vector estimate_num_rr_nodes(const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel) { + + // Reset the OPIN, IPIN, SOURCE, SINK counter to be zero + vtr::vector num_rr_nodes_per_type(static_cast(e_rr_type::NUM_RR_TYPES), 0); + + // 1 Find number of rr nodes related to grids + if (!vib_grid.is_empty()) + num_rr_nodes_per_type[e_rr_type::MUX] = estimate_num_mux_rr_nodes(grids, vib_grid, layer); + else + num_rr_nodes_per_type[e_rr_type::MUX] = 0; + + num_rr_nodes_per_type[e_rr_type::OPIN] = estimate_num_grid_rr_nodes_by_type(grids, layer, e_rr_type::OPIN, perimeter_cb); + num_rr_nodes_per_type[e_rr_type::IPIN] = estimate_num_grid_rr_nodes_by_type(grids, layer, e_rr_type::IPIN, perimeter_cb); + num_rr_nodes_per_type[e_rr_type::SOURCE] = estimate_num_grid_rr_nodes_by_type(grids, layer, e_rr_type::SOURCE, perimeter_cb); + num_rr_nodes_per_type[e_rr_type::SINK] = estimate_num_grid_rr_nodes_by_type(grids, layer, e_rr_type::SINK, perimeter_cb); + + // 2. Assign the segments for each routing channel, + // To be specific, for each routing track, we assign a routing segment. + // The assignment is subject to users' specifications, such as + // a. length of each type of segment + // b. frequency of each type of segment. + // c. routing channel width + // + // SPECIAL for fringes: + // All segments will start and ends with no exception + // + // IMPORTANT: we should be aware that channel width maybe different + // in X-direction and Y-direction channels. + // So we will load segment details for different channels + num_rr_nodes_per_type[e_rr_type::CHANX] = estimate_num_chanx_rr_nodes(grids, layer, + chan_width.x(), + segment_inf_x, + device_grid_annotation, + shrink_boundary, + perimeter_cb, + through_channel); + num_rr_nodes_per_type[e_rr_type::CHANY] = estimate_num_chany_rr_nodes(grids, layer, + chan_width.y(), + segment_inf_y, + device_grid_annotation, + shrink_boundary, + perimeter_cb, + through_channel); + + return num_rr_nodes_per_type; +} + +void alloc_tileable_rr_graph_nodes(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel) { + VTR_ASSERT(0 == rr_graph_builder.rr_nodes().size()); + + vtr::vector num_rr_nodes_per_type = estimate_num_rr_nodes(grids, + vib_grid, + layer, + chan_width, + segment_inf_x, + segment_inf_y, + device_grid_annotation, + shrink_boundary, + perimeter_cb, + through_channel); + + // Reserve the number of node to be memory efficient + size_t num_nodes = 0; + for (const size_t& num_node_per_type : num_rr_nodes_per_type) { + num_nodes += num_node_per_type; + } + + rr_graph_builder.reserve_nodes(num_nodes); + + rr_node_driver_switches.resize(num_nodes); +} + +/** + * @brief Configure OPIN rr_nodes for this grid + * @details This function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + */ +static void load_one_grid_opin_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const std::vector& wanted_sides, + const RRSwitchId& delayless_switch) { + // Walk through the width height of each grid, + // get pins and configure the rr_nodes + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + for (int width = 0; width < phy_tile_type->width; ++width) { + for (int height = 0; height < phy_tile_type->height; ++height) { + // Walk through sides + for (e_side side : wanted_sides) { + SideManager side_manager(side); + // Find OPINs + // Configure pins by pins + std::vector opin_list = get_grid_side_pins(grids, layer, grid_coordinate.x(), grid_coordinate.y(), DRIVER, side_manager.get_side(), + width, height); + for (const int& pin_num : opin_list) { + // Create a new node and fill information + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x() + width, grid_coordinate.y() + height, e_rr_type::OPIN, pin_num, side); + + // node bounding box + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x() + width, + grid_coordinate.y() + height, + grid_coordinate.x() + width, + grid_coordinate.y() + height); + rr_graph_builder.add_node_side(node, side_manager.get_side()); + rr_graph_builder.set_node_pin_num(node, pin_num); + + rr_graph_builder.set_node_capacity(node, 1); + rr_graph_builder.set_node_layer(node, layer); + + // cost index is a FIXED value for OPIN + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(OPIN_COST_INDEX)); + + // Switch info + rr_node_driver_switches[node] = delayless_switch; + + // RC data + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } // End of loading OPIN rr_nodes + } // End of side enumeration + } // End of height enumeration + } // End of width enumeration +} + +/** + * @brief Configure IPIN rr_nodes for this grid + * @details This function should be applied ONLY to grid with 0 width offset and 0 height offset. + */ +static void load_one_grid_ipin_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const std::vector& wanted_sides, + const RRSwitchId& wire_to_ipin_switch) { + // Walk through the width and height of each grid, + // get pins and configure the rr_nodes + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer)); + for (int width = 0; width < phy_tile_type->width; ++width) { + for (int height = 0; height < phy_tile_type->height; ++height) { + // Walk through sides + for (e_side side : wanted_sides) { + SideManager side_manager(side); + // Find IPINs + // Configure pins by pins + std::vector ipin_list = get_grid_side_pins(grids, layer, grid_coordinate.x(), grid_coordinate.y(), RECEIVER, side_manager.get_side(), width, height); + for (const int& pin_num : ipin_list) { + // Create a new node and fill information + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x() + width, grid_coordinate.y() + height, e_rr_type::IPIN, pin_num, side); + + // node bounding box + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x() + width, + grid_coordinate.y() + height, + grid_coordinate.x() + width, + grid_coordinate.y() + height); + rr_graph_builder.add_node_side(node, side_manager.get_side()); + rr_graph_builder.set_node_pin_num(node, pin_num); + + rr_graph_builder.set_node_capacity(node, 1); + rr_graph_builder.set_node_layer(node, layer); + + // cost index is a FIXED value for OPIN + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(IPIN_COST_INDEX)); + + // Switch info + rr_node_driver_switches[node] = wire_to_ipin_switch; + + // RC data + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } // End of loading IPIN rr_nodes + } // End of side enumeration + } // End of height enumeration + } // End of width enumeration +} + +/** + * @brief Configure SOURCE rr_nodes for this grid + * @details This function should be applied ONLY to grid with 0 width offset and 0 height offset. + */ +static void load_one_grid_source_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const RRSwitchId& delayless_switch) { + // Set a SOURCE rr_node for each DRIVER class + t_physical_tile_loc tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + for (size_t iclass = 0; iclass < phy_tile_type->class_inf.size(); ++iclass) { + // Set a SINK rr_node for the OPIN + if (DRIVER != phy_tile_type->class_inf[iclass].type) { + continue; + } + + // Create a new node and fill information + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x(), grid_coordinate.y(), e_rr_type::SOURCE, iclass); + + // node bounding box + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x(), + grid_coordinate.y(), + grid_coordinate.x() + phy_tile_type->width - 1, + grid_coordinate.y() + phy_tile_type->height - 1); + rr_graph_builder.set_node_class_num(node, iclass); + rr_graph_builder.set_node_layer(node, (int)layer); + + // The capacity should be the number of pins in this class + rr_graph_builder.set_node_capacity(node, phy_tile_type->class_inf[iclass].num_pins); + + // cost index is a FIXED value for SOURCE + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(SOURCE_COST_INDEX)); + + // Switch info + rr_node_driver_switches[node] = delayless_switch; + + // RC data + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } // End of class enumeration +} + +/** + * @brief Configure SINK rr_nodes for this grid + * @details This function should be applied ONLY to grid with 0 width offset and 0 height offset. + */ +static void load_one_grid_sink_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const RRSwitchId& delayless_switch) { + // Set a SINK rr_node for each RECEIVER class + t_physical_tile_loc tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer); + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc); + for (size_t iclass = 0; iclass < phy_tile_type->class_inf.size(); ++iclass) { + // Set a SINK rr_node for the OPIN + if (RECEIVER != phy_tile_type->class_inf[iclass].type) { + continue; + } + + // Create a new node and fill information + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x(), grid_coordinate.y(), e_rr_type::SINK, iclass); + + // node bounding box + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x(), + grid_coordinate.y(), + grid_coordinate.x() + phy_tile_type->width - 1, + grid_coordinate.y() + phy_tile_type->height - 1); + rr_graph_builder.set_node_class_num(node, iclass); + rr_graph_builder.set_node_layer(node, layer); + + rr_graph_builder.set_node_capacity(node, 1); + + // The capacity should be the number of pins in this class + rr_graph_builder.set_node_capacity(node, phy_tile_type->class_inf[iclass].num_pins); + + // cost index is a FIXED value for SINK + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(SINK_COST_INDEX)); + + // Switch info + rr_node_driver_switches[node] = delayless_switch; + + // RC data + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + + } // End of class enumeration +} + +static void load_one_grid_mux_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const VibDeviceGrid& vib_grid) { + + const VibInf* vib = vib_grid.get_vib(layer, grid_coordinate.x(), grid_coordinate.y()); + size_t num_mux_nodes = vib->get_first_stages().size(); + for (size_t i_mux = 0; i_mux < num_mux_nodes; i_mux++) { + // Create a new node and fill information + RRNodeId node = rr_graph_builder.create_node(layer, grid_coordinate.x(), grid_coordinate.y(), e_rr_type::MUX, i_mux, TOTAL_2D_SIDES[0]); + // node bounding box + rr_graph_builder.set_node_coordinates(node, grid_coordinate.x(), + grid_coordinate.y(), + grid_coordinate.x(), + grid_coordinate.y()); + //rr_graph_builder.add_node_side(node, SIDES[0]); + rr_graph_builder.set_node_mux_num(node, i_mux); + + rr_graph_builder.set_node_capacity(node, 1); + rr_graph_builder.set_node_layer(node, layer); + + // cost index is a FIXED value for MUX + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(MUX_COST_INDEX)); + + // Switch info + rr_node_driver_switches[node] = RRSwitchId(vib->get_switch_idx()); + + // RC data + rr_graph_builder.set_node_rc_index(node, NodeRCIndex(find_create_rr_rc_data(0., 0., rr_rc_data))); + } +} + +/** + * @brief Create all the rr_nodes for grids + */ +static void load_grid_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch, + const bool perimeter_cb) { + for (size_t iy = 0; iy < grids.height(); ++iy) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + t_physical_tile_loc tile_loc(ix, iy, layer); + // Skip EMPTY tiles + if (true == is_empty_type(grids.get_physical_type(tile_loc))) { + continue; + } + + // We only build rr_nodes for grids with width_offset = 0 and height_offset = 0 + if ((0 < grids.get_width_offset(tile_loc)) + || (0 < grids.get_height_offset(tile_loc))) { + continue; + } + + vtr::Point grid_coordinate(ix, iy); + std::vector wanted_sides{TOP, RIGHT, BOTTOM, LEFT}; + + // If this is the block on borders, we consider IO side + if (grids.get_physical_type(tile_loc)->is_io()) { + vtr::Point io_device_size(grids.width() - 1, grids.height() - 1); + wanted_sides = determine_io_grid_pin_side(io_device_size, grid_coordinate, perimeter_cb); + } + + for (e_side side : wanted_sides) { + for (int width_offset = 0; width_offset < grids.get_physical_type(tile_loc)->width; ++width_offset) { + int x_tile = ix + width_offset; + for (int height_offset = 0; height_offset < grids.get_physical_type(tile_loc)->height; ++height_offset) { + int y_tile = iy + height_offset; + rr_graph_builder.node_lookup().reserve_nodes(layer, x_tile, y_tile, e_rr_type::OPIN, grids.get_physical_type(tile_loc)->num_pins, side); + rr_graph_builder.node_lookup().reserve_nodes(layer, x_tile, y_tile, e_rr_type::IPIN, grids.get_physical_type(tile_loc)->num_pins, side); + } + } + } + + // Configure source rr_nodes for this grid + load_one_grid_source_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + grids, + delayless_switch); + + // Configure sink rr_nodes for this grid + load_one_grid_sink_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + grids, + delayless_switch); + + // Configure opin rr_nodes for this grid + load_one_grid_opin_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + grids, + wanted_sides, + delayless_switch); + + // Configure ipin rr_nodes for this grid + load_one_grid_ipin_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + grids, + wanted_sides, + wire_to_ipin_switch); + } + } + + if (!vib_grid.is_empty()) { + // Create MUX nodes + VTR_ASSERT(grids.width() == vib_grid.width() && grids.height() == vib_grid.height()); + for (size_t iy = 0; iy < grids.height(); ++iy) { + for (size_t ix = 0; ix < grids.width(); ++ix) { + + t_physical_tile_loc tile_loc(ix, iy, layer); + VTR_ASSERT(vib_grid.vib_pbtype_name(layer, ix, iy) == grids.get_physical_type(tile_loc)->name); + vtr::Point grid_coordinate(ix, iy); + + rr_graph_builder.node_lookup().reserve_nodes(layer, ix, iy, e_rr_type::MUX, vib_grid.num_mux_nodes(layer, ix, iy), TOTAL_2D_SIDES[0]); + + load_one_grid_mux_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + layer, grid_coordinate, + vib_grid); + } + } + } + + // Copy the SOURCE/SINK nodes to all offset positions for blocks with width > 1 and/or height > 1 + // This ensures that look-ups on non-root locations will still find the correct SOURCE/SINK + for (size_t x = 0; x < grids.width(); x++) { + for (size_t y = 0; y < grids.height(); y++) { + t_physical_tile_loc tile_loc(x, y, 0); + int width_offset = grids.get_width_offset(tile_loc); + int height_offset = grids.get_height_offset(tile_loc); + if (width_offset != 0 || height_offset != 0) { + int root_x = x - width_offset; + int root_y = y - height_offset; + + rr_graph_builder.node_lookup().mirror_nodes(0, + vtr::Point(root_x, root_y), + vtr::Point(x, y), + e_rr_type::SOURCE, + TOTAL_2D_SIDES[0]); + rr_graph_builder.node_lookup().mirror_nodes(0, + vtr::Point(root_x, root_y), + vtr::Point(x, y), + e_rr_type::SINK, + TOTAL_2D_SIDES[0]); + } + } + } +} + +/** + * @brief Initialize the basic information of routing track rr_nodes + * @details coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + */ +static void load_one_chan_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& chan_coordinate, + const e_rr_type& chan_type, + ChanNodeDetails& chan_details, + const std::vector& segment_infs, + const t_unified_to_parallel_seg_index& seg_index_map, + const int& cost_index_offset) { + // Check each node_id(potential ptc_num) in the channel : + // If this is a starting point, we set a new rr_node with xlow/ylow, ptc_num + // If this is a ending point, we set xhigh/yhigh and track_ids + // For other nodes, we set changes in track_ids + for (size_t itrack = 0; itrack < chan_details.get_chan_width(); ++itrack) { + // For INC direction, a starting point requires a new chan rr_node + if (((true == chan_details.is_track_start(itrack)) + && (Direction::INC == chan_details.get_track_direction(itrack))) + // For DEC direction, an ending point requires a new chan rr_node + || ((true == chan_details.is_track_end(itrack)) + && (Direction::DEC == chan_details.get_track_direction(itrack)))) { + // Create a new chan rr_node + RRNodeId node = rr_graph_builder.create_node(layer, chan_coordinate.x(), chan_coordinate.y(), chan_type, itrack); + + rr_graph_builder.set_node_direction(node, chan_details.get_track_direction(itrack)); + rr_graph_builder.add_node_track_num(node, chan_coordinate, itrack); + rr_node_track_ids[node].push_back(itrack); + + rr_graph_builder.set_node_capacity(node, 1); + rr_graph_builder.set_node_layer(node, layer); + + // assign switch id + size_t seg_id = chan_details.get_track_segment_id(itrack); + e_parallel_axis wanted_axis = chan_type == e_rr_type::CHANX ? e_parallel_axis::X_AXIS : e_parallel_axis::Y_AXIS; + size_t parallel_seg_id = find_parallel_seg_index(seg_id, seg_index_map, wanted_axis); + rr_node_driver_switches[node] = RRSwitchId(segment_infs[parallel_seg_id].arch_opin_switch); + + // Update chan_details with node_id + chan_details.set_track_node_id(itrack, size_t(node)); + + // cost index depends on the segment index + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(cost_index_offset + parallel_seg_id)); + + if (chan_details.is_track_start(itrack)) { + rr_graph_builder.set_node_bend_start(node, chan_details.get_track_bend_start(itrack)); + } + if (chan_details.is_track_end(itrack)) { + rr_graph_builder.set_node_bend_end(node, chan_details.get_track_bend_end(itrack)); + } + // Finish here, go to next + } + + // For INC direction, an ending point requires an update on xhigh and yhigh + if (((true == chan_details.is_track_end(itrack)) + && (Direction::INC == chan_details.get_track_direction(itrack))) + || + // For DEC direction, an starting point requires an update on xlow and ylow + ((true == chan_details.is_track_start(itrack)) + && (Direction::DEC == chan_details.get_track_direction(itrack)))) { + // Get the node_id + const RRNodeId rr_node_id = RRNodeId(chan_details.get_track_node_id(itrack)); + + // Do a quick check, make sure we do not mistakenly modify other nodes + VTR_ASSERT(chan_type == rr_graph.node_type(rr_node_id)); + VTR_ASSERT(chan_details.get_track_direction(itrack) == rr_graph.node_direction(rr_node_id)); + + // set xhigh/yhigh and push changes to track_ids + rr_graph_builder.set_node_coordinates(rr_node_id, rr_graph.node_xlow(rr_node_id), + rr_graph.node_ylow(rr_node_id), + chan_coordinate.x(), + chan_coordinate.y()); + + // Do not update track_ids for length-1 wires, they should have only 1 track_id + if ((rr_graph.node_xhigh(rr_node_id) > rr_graph.node_xlow(rr_node_id)) + || (rr_graph.node_yhigh(rr_node_id) > rr_graph.node_ylow(rr_node_id))) { + rr_node_track_ids[rr_node_id].push_back(itrack); + rr_graph_builder.add_node_track_num(rr_node_id, chan_coordinate, itrack); + } + // Finish node RC attributes + size_t seg_id = chan_details.get_track_segment_id(itrack); + e_parallel_axis wanted_axis = chan_type == e_rr_type::CHANX ? e_parallel_axis::X_AXIS : e_parallel_axis::Y_AXIS; + size_t parallel_seg_id = find_parallel_seg_index(seg_id, seg_index_map, wanted_axis); + float node_R = rr_graph.node_length(rr_node_id) * segment_infs[parallel_seg_id].Rmetal; + float node_C = rr_graph.node_length(rr_node_id) * segment_infs[parallel_seg_id].Cmetal; + rr_graph_builder.set_node_rc_index(rr_node_id, NodeRCIndex(find_create_rr_rc_data(node_R, node_C, rr_rc_data))); + + if (chan_details.is_track_start(itrack)) { + rr_graph_builder.set_node_bend_start(rr_node_id, chan_details.get_track_bend_start(itrack)); + } + if (chan_details.is_track_end(itrack)) { + rr_graph_builder.set_node_bend_end(rr_node_id, chan_details.get_track_bend_end(itrack)); + } + // Finish here, go to next + } + + // Finish processing starting and ending tracks + if ((true == chan_details.is_track_start(itrack)) + || (true == chan_details.is_track_end(itrack))) { + // Finish here, go to next + continue; + } + + // For other nodes, we get the node_id and just update track_ids + // Ensure those nodes are neither starting nor ending points + VTR_ASSERT((false == chan_details.is_track_start(itrack)) + && (false == chan_details.is_track_end(itrack))); + + // Get the node_id + const RRNodeId rr_node_id = RRNodeId(chan_details.get_track_node_id(itrack)); + + // Do a quick check, make sure we do not mistakenly modify other nodes + VTR_ASSERT(chan_type == rr_graph.node_type(rr_node_id)); + VTR_ASSERT(chan_details.get_track_direction(itrack) == rr_graph.node_direction(rr_node_id)); + + // Deposit xhigh and yhigh using the current chan_coordinate + // We will update when this track ends + rr_graph_builder.set_node_coordinates(rr_node_id, rr_graph.node_xlow(rr_node_id), + rr_graph.node_ylow(rr_node_id), + chan_coordinate.x(), + chan_coordinate.y()); + + // Update track_ids + rr_node_track_ids[rr_node_id].push_back(itrack); + rr_graph_builder.add_node_track_num(rr_node_id, chan_coordinate, itrack); + // Finish here, go to next + } +} + +/** + * @brief Initialize the basic information of X-channel rr_nodes + * @details coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + * grid_info : pb_graph_pin + */ +static void load_chanx_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const t_unified_to_parallel_seg_index& segment_index_map, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel) { + size_t start_x = 1; + size_t end_x = grids.width() - 1; + if (perimeter_cb) { + start_x = 0; + end_x = grids.width(); + } + size_t max_seg_length = grids.width() - 2; + if (perimeter_cb) { + max_seg_length = grids.width(); + } + + // For X-direction Channel: CHANX + for (size_t iy = 0; iy < grids.height() - 1; ++iy) { + // Keep a vector of node_ids for the channels, because we will rotate them when walking through ix + std::vector track_node_ids; + + for (size_t ix = start_x; ix < end_x; ++ix) { + vtr::Point chanx_coord(ix, iy); + + // Bypass if the routing channel does not exist when through channels are not allowed + if ((false == through_channel) + && (false == is_chanx_exist(grids, layer, chanx_coord, perimeter_cb))) { + continue; + } + // Bypass if the routing channel does not exist when a shrink boundary is considered + if (shrink_boundary && !device_grid_annotation.is_chanx_exist(chanx_coord)) { + continue; + } + + bool force_start = false; + bool force_end = false; + + // All the tracks have to start when + // - the routing channel touch the RIGHT side a heterogeneous block + // - the routing channel touch the LEFT side of FPGA + if (true == is_chanx_right_to_multi_height_grid(grids, layer, chanx_coord, perimeter_cb, through_channel)) { + force_start = true; + } + if (shrink_boundary && device_grid_annotation.is_chanx_start(chanx_coord)) { + force_start = true; + } + + // All the tracks have to end when + // - the routing channel touch the LEFT side a heterogeneous block + // - the routing channel touch the RIGHT side of FPGA + if (true == is_chanx_left_to_multi_height_grid(grids, layer, chanx_coord, perimeter_cb, through_channel)) { + force_end = true; + } + if (shrink_boundary && device_grid_annotation.is_chanx_end(chanx_coord)) { + force_end = true; + } + + ChanNodeDetails chanx_details = build_unidir_chan_node_details(chan_width, max_seg_length, + force_start, force_end, segment_infs); + // Force node_ids from the previous chanx + if (0 < track_node_ids.size()) { + // Rotate should be done based on a typical case of routing tracks. + // Tracks on the borders are not regularly started and ended, + // which causes the node_rotation malfunction + ChanNodeDetails chanx_details_tt = build_unidir_chan_node_details(chan_width, max_seg_length, + false, false, segment_infs); + chanx_details_tt.set_track_node_ids(track_node_ids); + + // TODO: + // Do NOT rotate the tracks when the routing channel + // locates inside a multi-height and multi-width grid + // Let the routing channel passing through the grid (if through channel is allowed!) + // An example: + // + // +------------------------------ + // | | + // | Grid | + // track0 ----->+-----------------------------+----> track0 + // | | + if (true == is_chanx_exist(grids, layer, chanx_coord, perimeter_cb, through_channel)) { + // Rotate the chanx_details by an offset of ix - 1, the distance to the most left channel + // For INC_DIRECTION, we use clockwise rotation + // node_id A ----> -----> node_id D + // node_id B ----> / ----> node_id A + // node_id C ----> / ----> node_id B + // node_id D ----> ----> node_id C + chanx_details_tt.rotate_track_node_id(1, Direction::INC, true); + // For DEC_DIRECTION, we use clockwise rotation + // node_id A <----- <----- node_id B + // node_id B <----- \ <----- node_id C + // node_id C <----- \ <----- node_id D + // node_id D <----- <----- node_id A + chanx_details_tt.rotate_track_node_id(1, Direction::DEC, false); + } + + track_node_ids = chanx_details_tt.get_track_node_ids(); + chanx_details.set_track_node_ids(track_node_ids); + } + + // Configure CHANX in this channel + load_one_chan_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + rr_rc_data, + layer, chanx_coord, e_rr_type::CHANX, + chanx_details, + segment_infs, + segment_index_map, + CHANX_COST_INDEX_START); + // Get a copy of node_ids + track_node_ids = chanx_details.get_track_node_ids(); + } + } +} + +/** + * @brief Initialize the basic information of Y-channel rr_nodes + * @details coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + */ +static void load_chany_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const size_t& num_segment_x, + const t_unified_to_parallel_seg_index& seg_index_map, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel) { + // Default y-channel boundary box + size_t start_y = 1; + size_t end_y = grids.height() - 1; + if (perimeter_cb) { + start_y = 0; + end_y = grids.height(); + } + size_t max_seg_length = grids.height() - 2; + if (perimeter_cb) { + max_seg_length = grids.height(); + } + + // For Y-direction Channel: CHANY + for (size_t ix = 0; ix < grids.width() - 1; ++ix) { + // Keep a vector of node_ids for the channels, because we will rotate them when walking through ix + std::vector track_node_ids; + + for (size_t iy = start_y; iy < end_y; ++iy) { + vtr::Point chany_coord(ix, iy); + + // Bypass if the routing channel does not exist when through channel are not allowed + if ((false == through_channel) + && (false == is_chany_exist(grids, layer, chany_coord, perimeter_cb))) { + continue; + } + // Bypass if the routing channel does not exist when a shrink boundary is considered + if (shrink_boundary && !device_grid_annotation.is_chany_exist(chany_coord)) { + continue; + } + + bool force_start = false; + bool force_end = false; + + // All the tracks have to start when + // - the routing channel touch the TOP side a heterogeneous block + // - the routing channel touch the BOTTOM side of FPGA + if (true == is_chany_top_to_multi_width_grid(grids, layer, chany_coord, perimeter_cb, through_channel)) { + force_start = true; + } + if (shrink_boundary && device_grid_annotation.is_chany_start(chany_coord)) { + force_start = true; + } + + // All the tracks have to end when + // - the routing channel touch the BOTTOM side a heterogeneous block + // - the routing channel touch the TOP side of FPGA + if (true == is_chany_bottom_to_multi_width_grid(grids, layer, chany_coord, perimeter_cb, through_channel)) { + force_end = true; + } + if (shrink_boundary && device_grid_annotation.is_chany_end(chany_coord)) { + force_end = true; + } + + ChanNodeDetails chany_details = build_unidir_chan_node_details(chan_width, max_seg_length, + force_start, force_end, segment_infs); + // Force node_ids from the previous chany + // This will not be applied when the routing channel is cut off (force to start) + if (0 < track_node_ids.size()) { + // Rotate should be done based on a typical case of routing tracks. + // Tracks on the borders are not regularly started and ended, + // which causes the node_rotation malfunction + ChanNodeDetails chany_details_tt = build_unidir_chan_node_details(chan_width, max_seg_length, + false, false, segment_infs); + + chany_details_tt.set_track_node_ids(track_node_ids); + + // TODO: + // Do NOT rotate the tracks when the routing channel + // locates inside a multi-height and multi-width grid + // Let the routing channel passing through the grid (if through channel is allowed!) + // An example: + // + // +------------------------------ + // | | + // | Grid | + // track0 ----->+-----------------------------+----> track0 + // | | + // we should rotate only once at the bottom side of a grid + if (true == is_chany_exist(grids, layer, chany_coord, perimeter_cb, through_channel)) { + // Rotate the chany_details by an offset of 1 + // For INC_DIRECTION, we use clockwise rotation + // node_id A ----> -----> node_id D + // node_id B ----> / ----> node_id A + // node_id C ----> / ----> node_id B + // node_id D ----> ----> node_id C + chany_details_tt.rotate_track_node_id(1, Direction::INC, true); + // For DEC_DIRECTION, we use clockwise rotation + // node_id A <----- <----- node_id B + // node_id B <----- \ <----- node_id C + // node_id C <----- \ <----- node_id D + // node_id D <----- <----- node_id A + chany_details_tt.rotate_track_node_id(1, Direction::DEC, false); + } + + track_node_ids = chany_details_tt.get_track_node_ids(); + chany_details.set_track_node_ids(track_node_ids); + } + // Configure CHANX in this channel + load_one_chan_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + rr_rc_data, + layer, chany_coord, e_rr_type::CHANY, + chany_details, + segment_infs, + seg_index_map, + CHANX_COST_INDEX_START + num_segment_x); + // Get a copy of node_ids + track_node_ids = chany_details.get_track_node_ids(); + } + } +} + +/** + * @brief Reverse the track_ids of CHANX and CHANY nodes in DEC_DIRECTION + * @details This is required as the track ids are allocated in the sequence + * of incrementing x and y + * However, DEC direction routing tracks should have a reversed sequence in + * track ids + */ +static void reverse_dec_chan_rr_node_track_ids(const RRGraphView& rr_graph, + std::map>& rr_node_track_ids) { + // this should call rr_graph_builder to do the job + for (const RRNodeId node : rr_graph.nodes()) { + // Bypass condition: only focus on CHANX and CHANY in DEC_DIRECTION + if (e_rr_type::CHANX != rr_graph.node_type(node) && e_rr_type::CHANY != rr_graph.node_type(node)) { + continue; + } + // Reach here, we must have a node of CHANX or CHANY + if (Direction::DEC != rr_graph.node_direction(node)) { + continue; + } + std::reverse(rr_node_track_ids[node].begin(), + rr_node_track_ids[node].end()); + } +} + +/** + * @brief Create all the rr_nodes covering both grids and routing channels + */ +void create_tileable_rr_graph_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const t_unified_to_parallel_seg_index& segment_index_map, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel) { + // Allocates and loads all the structures needed for fast lookups of the + // index of an rr_node. rr_node_indices is a matrix containing the index + // of the *first* rr_node at a given (i,j) location. + + // Alloc the lookup table + // .. warning: It is mandatory. There are bugs in resize() when called incrementally in RRSpatialLookup. + // When comment the following block out, you will see errors + for (e_rr_type rr_type : RR_TYPES) { + rr_graph_builder.node_lookup().resize_nodes(layer, grids.width(), grids.height(), rr_type, NUM_2D_SIDES); + } + + load_grid_nodes_basic_info(rr_graph_builder, + rr_node_driver_switches, + rr_rc_data, + grids, vib_grid, layer, + wire_to_ipin_switch, + delayless_switch, perimeter_cb); + + load_chanx_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + rr_rc_data, + grids, layer, + chan_width.x(), + segment_inf_x, + segment_index_map, + device_grid_annotation, + shrink_boundary, + perimeter_cb, + through_channel); + + load_chany_rr_nodes_basic_info(rr_graph, + rr_graph_builder, + rr_node_driver_switches, + rr_node_track_ids, + rr_rc_data, + grids, layer, + chan_width.y(), + segment_inf_y, + segment_inf_x.size(), + segment_index_map, + device_grid_annotation, + shrink_boundary, + perimeter_cb, + through_channel); + + reverse_dec_chan_rr_node_track_ids(rr_graph, + rr_node_track_ids); + + // Update node look-up for CHANX and CHANY nodes + for (const RRNodeId rr_node_id : rr_graph.nodes()) { + if (e_rr_type::CHANX == rr_graph.node_type(rr_node_id) || e_rr_type::CHANY == rr_graph.node_type(rr_node_id)) { + rr_graph_builder.add_track_node_to_lookup(rr_node_id); + } + } +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_node_builder.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_node_builder.h new file mode 100644 index 00000000000..e5249dcee2e --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_node_builder.h @@ -0,0 +1,56 @@ +#pragma once + +/** + * @file tileable_rr_graph_node_builder.h + * @brief This file contains functions that are used to allocate nodes + * for the tileable routing resource graph builder + */ + +#include "vtr_geometry.h" + +#include "physical_types.h" + +#include "device_grid.h" +#include "device_grid_annotation.h" +#include "rr_node_types.h" +#include "rr_graph_type.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" + +/** + * @brief Allocate rr_nodes to a rr_graph object + * @details This function just allocate the memory and ensure its efficiency + * It will NOT fill detailed information for each node!!! + * Note: ensure that there are NO nodes in the rr_graph + */ +void alloc_tileable_rr_graph_nodes(RRGraphBuilder& rr_graph_builder, + vtr::vector& driver_switches, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel); + +void create_tileable_rr_graph_nodes(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + std::vector& rr_rc_data, + const DeviceGrid& grids, + const VibDeviceGrid& vib_grid, + const size_t& layer, + const vtr::Point& chan_width, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + const t_unified_to_parallel_seg_index& segment_index_map, + const RRSwitchId& wire_to_ipin_switch, + const RRSwitchId& delayless_switch, + const DeviceGridAnnotation& device_grid_annotation, + const bool shrink_boundary, + const bool perimeter_cb, + const bool through_channel); diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_types.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_types.h new file mode 100644 index 00000000000..1ca30df121e --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_types.h @@ -0,0 +1,29 @@ +#pragma once + +#include "vtr_array.h" + +/** + * @file tileable_rr_graph_types.h + * @brief Data types required by routing resource graph (RRGraph) definition + */ + +/** + * @brief Directionality of a routing track (node type CHANX and CHANY) in + * a routing resource graph + */ +enum class e_direction { + INC_DIRECTION = 0, + DEC_DIRECTION = 1, + BI_DIRECTION = 2, + NO_DIRECTION = 3, + NUM_DIRECTIONS +}; + +/** + * @brief String used in describe_rr_node() and write_xml_rr_graph_obj() + */ +constexpr vtr::array(e_direction::NUM_DIRECTIONS)> DIRECTION_STRING_WRITE_XML = { + "INC_DIR", + "DEC_DIR", + "BI_DIR", + "NO_DIR"}; diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_utils.cpp b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_utils.cpp new file mode 100644 index 00000000000..5cb63851a39 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_utils.cpp @@ -0,0 +1,153 @@ +#include + +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "tileable_rr_graph_utils.h" +#include "tileable_rr_graph_types.h" +#include "rr_graph_view.h" +#include "vtr_geometry.h" + +vtr::Point get_track_rr_node_start_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node) { + // Make sure we have CHANX or CHANY + VTR_ASSERT((e_rr_type::CHANX == rr_graph.node_type(track_rr_node)) + || (e_rr_type::CHANY == rr_graph.node_type(track_rr_node))); + + vtr::Point start_coordinator(size_t(-1), size_t(-1)); + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + start_coordinator.set(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + start_coordinator.set(rr_graph.node_xhigh(track_rr_node), rr_graph.node_yhigh(track_rr_node)); + } + + return start_coordinator; +} + +vtr::Point get_track_rr_node_end_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node) { + // Make sure we have CHANX or CHANY + VTR_ASSERT((e_rr_type::CHANX == rr_graph.node_type(track_rr_node)) + || (e_rr_type::CHANY == rr_graph.node_type(track_rr_node))); + + vtr::Point end_coordinator(size_t(-1), size_t(-1)); + + if (Direction::INC == rr_graph.node_direction(track_rr_node)) { + end_coordinator.set(rr_graph.node_xhigh(track_rr_node), rr_graph.node_yhigh(track_rr_node)); + } else { + VTR_ASSERT(Direction::DEC == rr_graph.node_direction(track_rr_node)); + end_coordinator.set(rr_graph.node_xlow(track_rr_node), rr_graph.node_ylow(track_rr_node)); + } + + return end_coordinator; +} + +std::vector get_rr_graph_driver_switches(const RRGraphView& rr_graph, + const RRNodeId node) { + std::vector driver_switches; + + for (const RREdgeId edge : rr_graph.node_in_edges(node)) { + if (driver_switches.end() == std::find(driver_switches.begin(), driver_switches.end(), RRSwitchId(rr_graph.edge_switch(edge)))) { + driver_switches.push_back(RRSwitchId(rr_graph.edge_switch(edge))); + } + } + + return driver_switches; +} + +std::vector get_rr_graph_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId node) { + std::vector driver_nodes; + + for (const RREdgeId edge : rr_graph.node_in_edges(node)) { + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +std::vector get_rr_graph_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId node) { + std::vector driver_nodes; + + for (const RREdgeId edge : rr_graph.node_in_edges(node)) { + // Bypass non-configurable edges + if (false == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId node) { + std::vector driver_nodes; + + for (const RREdgeId edge : rr_graph.node_in_edges(node)) { + // Bypass configurable edges + if (true == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +bool is_opin_direct_connected_ipin(const RRGraphView& rr_graph, + const RRNodeId node) { + // We only accept OPIN + VTR_ASSERT(e_rr_type::OPIN == rr_graph.node_type(node)); + + if (1 != rr_graph.node_out_edges(node).size()) { + return false; + } + + VTR_ASSERT(1 == rr_graph.node_out_edges(node).size()); + for (auto edge : rr_graph.node_out_edges(node)) { + const RRNodeId& sink_node = rr_graph.edge_sink_node(node, edge); + if (e_rr_type::IPIN != rr_graph.node_type(sink_node)) { + return false; + } + } + + return true; +} + +bool is_ipin_direct_connected_opin(const RRGraphView& rr_graph, + const RRNodeId node) { + // We only accept IPIN + VTR_ASSERT(e_rr_type::IPIN == rr_graph.node_type(node)); + + if (1 != rr_graph.node_in_edges(node).size()) { + return false; + } + + VTR_ASSERT(1 == rr_graph.node_in_edges(node).size()); + for (const RREdgeId edge : rr_graph.node_in_edges(node)) { + const RRNodeId& src_node = rr_graph.edge_src_node(edge); + if (e_rr_type::OPIN != rr_graph.node_type(src_node)) { + return false; + } + } + + return true; +} + +e_side get_rr_graph_single_node_side(const RRGraphView& rr_graph, + const RRNodeId node) { + e_side node_side = NUM_2D_SIDES; + int num_sides = 0; + for (e_side candidate_side : TOTAL_2D_SIDES) { + if (rr_graph.is_node_on_specific_side(node, candidate_side)) { + node_side = candidate_side; + num_sides++; + } + } + VTR_ASSERT(1 == num_sides && node_side != NUM_2D_SIDES); + return node_side; +} diff --git a/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_utils.h b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_utils.h new file mode 100644 index 00000000000..8bc707e0a60 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_utils.h @@ -0,0 +1,83 @@ +#pragma once + +/** + * @file tileable_rr_graph_utils.cpp + * @brief This file includes most utilized functions for the rr_graph + * data structure in the OpenFPGA context + */ + +#include "vtr_geometry.h" + +/* Headers from vpr library */ +#include "rr_graph_obj.h" +#include "rr_graph_view.h" + +/** + * @brief Get the coordinator of a starting point of a routing track + * For routing tracks in INC_DIRECTION + * (xlow, ylow) should be the starting point + * For routing tracks in DEC_DIRECTION + * (xhigh, yhigh) should be the starting point + */ +vtr::Point get_track_rr_node_start_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node); + +/** + * @brief Get the coordinator of a end point of a routing track + * For routing tracks in INC_DIRECTION + * (xhigh, yhigh) should be the starting point + * For routing tracks in DEC_DIRECTION + * (xlow, ylow) should be the starting point + */ +vtr::Point get_track_rr_node_end_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node); + +/** + * @brief Find the driver switches for a node in the rr_graph + * This function only return unique driver switches + */ +std::vector get_rr_graph_driver_switches(const RRGraphView& rr_graph, + const RRNodeId node); + +/** + * @brief Find the driver nodes for a node in the rr_graph + */ +std::vector get_rr_graph_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId node); + +/** + * @brief Find the configurable driver nodes for a node in the rr_graph + */ +std::vector get_rr_graph_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId node); + +/** + * @brief Find the configurable driver nodes for a node in the rr_graph + */ +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId node); + +/** + * @brief Check if an OPIN of a rr_graph is directly driving an IPIN + * To meet this requirement, the OPIN must: + * - Have only 1 fan-out + * - The only fan-out is an IPIN + */ +bool is_opin_direct_connected_ipin(const RRGraphView& rr_graph, + const RRNodeId node); + +/** + * @brief Check if an IPIN of a rr_graph is directly connected to an OPIN + * To meet this requirement, the IPIN must: + * - Have only 1 fan-in + * - The only fan-in is an OPIN + */ +bool is_ipin_direct_connected_opin(const RRGraphView& rr_graph, + const RRNodeId node); + +/** + * @brief Get a side of a given node in a routing resource graph. + * Note that this function expect one valid side to be got. Otherwise, it will fail! + */ +e_side get_rr_graph_single_node_side(const RRGraphView& rr_graph, + const RRNodeId node); diff --git a/vpr/src/route/segment_stats.cpp b/vpr/src/route/segment_stats.cpp index f3e693bfcf1..a17824b6b92 100644 --- a/vpr/src/route/segment_stats.cpp +++ b/vpr/src/route/segment_stats.cpp @@ -27,18 +27,18 @@ void get_segment_usage_stats(std::vector& segment_inf) { int max_segment_name_length = 0; std::map> directed_occ_by_length = { - {X_AXIS, std::map()}, - {Y_AXIS, std::map()}}; + {e_parallel_axis::X_AXIS, std::map()}, + {e_parallel_axis::Y_AXIS, std::map()}}; std::map> directed_cap_by_length = { - {X_AXIS, std::map()}, - {Y_AXIS, std::map()}}; + {e_parallel_axis::X_AXIS, std::map()}, + {e_parallel_axis::Y_AXIS, std::map()}}; std::set> segment_lengths; for (const t_segment_inf& seg_inf : segment_inf) { int seg_length = seg_inf.longline ? LONGLINE : seg_inf.length; - for (e_parallel_axis ax : {X_AXIS, Y_AXIS}) { + for (e_parallel_axis ax : {e_parallel_axis::X_AXIS, e_parallel_axis::Y_AXIS}) { directed_cap_by_length[ax].insert({seg_length, 0}); directed_occ_by_length[ax].insert({seg_length, 0}); } @@ -49,12 +49,12 @@ void get_segment_usage_stats(std::vector& segment_inf) { } std::map> directed_occ_by_type = { - {X_AXIS, std::vector(segment_inf.size(), 0)}, - {Y_AXIS, std::vector(segment_inf.size(), 0)}}; + {e_parallel_axis::X_AXIS, std::vector(segment_inf.size(), 0)}, + {e_parallel_axis::Y_AXIS, std::vector(segment_inf.size(), 0)}}; std::map> directed_cap_by_type = { - {X_AXIS, std::vector(segment_inf.size(), 0)}, - {Y_AXIS, std::vector(segment_inf.size(), 0)}}; + {e_parallel_axis::X_AXIS, std::vector(segment_inf.size(), 0)}, + {e_parallel_axis::Y_AXIS, std::vector(segment_inf.size(), 0)}}; for (RRNodeId inode : device_ctx.rr_graph.nodes()) { e_rr_type node_type = rr_graph.node_type(inode); @@ -65,7 +65,7 @@ void get_segment_usage_stats(std::vector& segment_inf) { const short& inode_capacity = rr_graph.node_capacity(inode); int occ = route_ctx.rr_node_route_inf[inode].occ(); - e_parallel_axis ax = (node_type == e_rr_type::CHANX) ? X_AXIS : Y_AXIS; + e_parallel_axis ax = (node_type == e_rr_type::CHANX) ? e_parallel_axis::X_AXIS : e_parallel_axis::Y_AXIS; directed_occ_by_length[ax][length] += occ; directed_cap_by_length[ax][length] += inode_capacity; @@ -79,8 +79,8 @@ void get_segment_usage_stats(std::vector& segment_inf) { VTR_LOG("Total Number of Wiring Segments by Direction: direction length number\n"); VTR_LOG(" --------- ------ -------\n"); for (int length : segment_lengths) { - for (e_parallel_axis ax : {X_AXIS, Y_AXIS}) { - std::string ax_name = (ax == X_AXIS) ? "X" : "Y"; + for (e_parallel_axis ax : {e_parallel_axis::X_AXIS, e_parallel_axis::Y_AXIS}) { + std::string ax_name = (ax == e_parallel_axis::X_AXIS) ? "X" : "Y"; if (directed_cap_by_length[ax][length] != 0) { std::string length_str = (length == LONGLINE) ? "longline" : std::to_string(length); VTR_LOG(" %s%s %s%s %6d\n", @@ -93,8 +93,8 @@ void get_segment_usage_stats(std::vector& segment_inf) { } } - for (e_parallel_axis ax : {X_AXIS, Y_AXIS}) { - std::string ax_name = (ax == X_AXIS) ? "X" : "Y"; + for (e_parallel_axis ax : {e_parallel_axis::X_AXIS, e_parallel_axis::Y_AXIS}) { + std::string ax_name = (ax == e_parallel_axis::X_AXIS) ? "X" : "Y"; VTR_LOG("\n"); VTR_LOG("%s - Directed Wiring Segment usage by length: length utilization\n", ax_name.c_str()); VTR_LOG(" ------ -----------\n"); @@ -114,12 +114,12 @@ void get_segment_usage_stats(std::vector& segment_inf) { VTR_LOG("Segment occupancy by length: Length utilization\n"); VTR_LOG(" ------ -----------\n"); for (const int seg_length : segment_lengths) { - if (directed_cap_by_length[X_AXIS][seg_length] != 0 || directed_cap_by_length[Y_AXIS][seg_length] != 0) { + if (directed_cap_by_length[e_parallel_axis::X_AXIS][seg_length] != 0 || directed_cap_by_length[e_parallel_axis::Y_AXIS][seg_length] != 0) { std::string seg_name = "L" + std::to_string(seg_length); int occ = 0; int cap = 0; - for (e_parallel_axis ax : {X_AXIS, Y_AXIS}) { + for (e_parallel_axis ax : {e_parallel_axis::X_AXIS, e_parallel_axis::Y_AXIS}) { occ += directed_occ_by_length[ax][seg_length]; cap += directed_cap_by_length[ax][seg_length]; } @@ -133,12 +133,12 @@ void get_segment_usage_stats(std::vector& segment_inf) { VTR_LOG(" %s ---- -----------\n", std::string(std::max(4, max_segment_name_length), '-').c_str()); for (size_t seg_type = 0; seg_type < segment_inf.size(); seg_type++) { - if (directed_cap_by_type[X_AXIS][seg_type] != 0 || directed_cap_by_type[Y_AXIS][seg_type] != 0) { + if (directed_cap_by_type[e_parallel_axis::X_AXIS][seg_type] != 0 || directed_cap_by_type[e_parallel_axis::Y_AXIS][seg_type] != 0) { std::string seg_name = segment_inf[seg_type].name; int seg_name_size = static_cast(seg_name.size()); int occ = 0; int cap = 0; - for (e_parallel_axis ax : {X_AXIS, Y_AXIS}) { + for (e_parallel_axis ax : {e_parallel_axis::X_AXIS, e_parallel_axis::Y_AXIS}) { occ += directed_occ_by_type[ax][seg_type]; cap += directed_cap_by_type[ax][seg_type]; } diff --git a/vpr/src/server/telegramoptions.cpp b/vpr/src/server/telegramoptions.cpp index 86c4803c0d9..dde6391745f 100644 --- a/vpr/src/server/telegramoptions.cpp +++ b/vpr/src/server/telegramoptions.cpp @@ -11,9 +11,9 @@ namespace server { TelegramOptions::TelegramOptions(const std::string& data, const std::vector& expected_keys) { // parse data string - std::vector options = vtr::split(data, ";"); + std::vector options = vtr::StringToken(data).split(";"); for (const std::string& option_str : options) { - std::vector fragments = vtr::split(option_str, ":"); + std::vector fragments = vtr::StringToken(option_str).split(":"); if (fragments.size() == TOTAL_INDEXES_NUM) { std::string name{std::move(fragments[INDEX_NAME])}; Option option{std::move(fragments[INDEX_TYPE]), std::move(fragments[INDEX_VALUE])}; @@ -35,13 +35,13 @@ std::map> TelegramOptions::get_map_of_sets(co std::map> result; std::string data_str = get_string(name); if (!data_str.empty()) { - std::vector paths = vtr::split(data_str, "|"); + std::vector paths = vtr::StringToken(data_str).split("|"); for (const std::string& path : paths) { - std::vector path_struct = vtr::split(path, "#"); + std::vector path_struct = vtr::StringToken(path).split("#"); if (path_struct.size() == 2) { std::string path_index_str = path_struct[0]; std::string path_element_indexes_str = path_struct[1]; - std::vector path_element_indexes = vtr::split(path_element_indexes_str, ","); + std::vector path_element_indexes = vtr::StringToken(path_element_indexes_str).split(","); std::set elements; for (const std::string& path_element_index_Str : path_element_indexes) { if (std::optional opt_value = try_convert_to_int(path_element_index_Str)) { diff --git a/vpr/src/timing/PreClusterTimingManager.cpp b/vpr/src/timing/PreClusterTimingManager.cpp index f7215dc0592..d1f7d44b77a 100644 --- a/vpr/src/timing/PreClusterTimingManager.cpp +++ b/vpr/src/timing/PreClusterTimingManager.cpp @@ -10,7 +10,7 @@ #include #include "PreClusterDelayCalculator.h" #include "PreClusterTimingGraphResolver.h" -#include "SetupGrid.h" +#include "setup_grid.h" #include "atom_lookup.h" #include "atom_netlist.h" #include "atom_netlist_fwd.h" diff --git a/vpr/src/util/vpr_utils.cpp b/vpr/src/util/vpr_utils.cpp index fb123540b67..5681dac64ca 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -522,15 +522,6 @@ std::pair get_pin_range_for_block(const ClusterBlockId blk_id) { return {pin_low, pin_high}; } -t_physical_tile_type_ptr find_tile_type_by_name(const std::string& name, const std::vector& types) { - for (auto const& type : types) { - if (type.name == name) { - return &type; - } - } - return nullptr; //Not found -} - t_block_loc get_block_loc(const ParentBlockId& block_id, bool is_flat) { auto& place_ctx = g_vpr_ctx.placement(); ClusterBlockId cluster_block_id = ClusterBlockId::INVALID(); @@ -1351,7 +1342,7 @@ void free_pb_stats(t_pb* pb) { ***************************************************************************************/ std::tuple parse_direct_pin_name(std::string_view src_string, int line) { - if (vtr::split(src_string).size() > 1) { + if (vtr::StringToken(src_string).split(" \t\n").size() > 1) { VPR_THROW(VPR_ERROR_ARCH, "Only a single port pin range specification allowed for direct connect (was: '%s')", src_string); } @@ -1641,7 +1632,7 @@ std::vector get_all_pb_graph_node_primitives(const t_pb_ bool is_inter_cluster_node(const RRGraphView& rr_graph_view, RRNodeId node_id) { auto node_type = rr_graph_view.node_type(node_id); - if (node_type == e_rr_type::CHANX || node_type == e_rr_type::CHANY || node_type == e_rr_type::CHANZ) { + if (node_type == e_rr_type::CHANX || node_type == e_rr_type::CHANY || node_type == e_rr_type::CHANZ || node_type == e_rr_type::MUX) { return true; } else { int x_low = rr_graph_view.node_xlow(node_id); diff --git a/vtr_flow/arch/VIB/vib_test_arch.xml b/vtr_flow/arch/VIB/vib_test_arch.xml new file mode 100644 index 00000000000..eb3ba461032 --- /dev/null +++ b/vtr_flow/arch/VIB/vib_test_arch.xml @@ -0,0 +1,13289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io_topL[7:0].outpad io_topL[7:0].inpad io_topL.clock + + + + + + + + + + + + + + + + + + + io_leftL[7:0].outpad io_leftL[7:0].inpad io_leftL.clock + + + + + + + + + + + + + + + + + + + io_rightL[7:0].outpad io_rightL[7:0].inpad io_rightL.clock + + + + + + + + + + + + + + + + + + + io_bottomL[7:0].outpad io_bottomL[7:0].inpad io_bottomL.clock + + + + + + + + + + + + + + + + + + + + + + + + + + + clb.Ia[5:0] clb.Ib[5:0] clb.Ic[5:0] clb.Id[5:0] clb.Ie[5:0] clb.If[5:0] clb.Ig[5:0] clb.Ih[5:0] clb.o[7:0] clb.q[7:0] + clb.clk + + + + + + + + + + + + + + mult_36.a[0:8] mult_36.b[0:8] mult_36.out[0:17] + mult_36.a[9:17] mult_36.b[9:17] mult_36.out[18:35] + mult_36.a[18:26] mult_36.b[18:26] mult_36.out[36:53] + mult_36.a[27:35] mult_36.b[27:35] mult_36.out[54:71] + + + + + + + + + + + + + + + + + + + + memory.clk memory.addr1[0:2] memory.addr2[0:2] memory.data[0:9] memory.out[0:10] + memory.addr1[3:5] memory.addr2[3:5] memory.data[10:19] memory.out[11:21] + memory.addr1[6:8] memory.addr2[6:8] memory.data[20:29] memory.out[22:32] + memory.addr1[9:11] memory.addr2[9:11] memory.data[30:39] memory.out[33:43] + memory.addr1[12:14] memory.addr2[12:14] memory.data[40:49] memory.out[44:53] + memory.we1 memory.we2 memory.data[50:63] memory.out[54:63] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1.43259e-10 + 1.43259e-10 + 1.43259e-10 + 1.43259e-10 + 1.43259e-10 + 1.43259e-10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + 1 0 1 + 1 1 + + + + 1 0 0 1 + 1 0 1 + - D + + + + 1 0 0 0 0 0 1 + 1 0 0 0 0 1 + + + + 1 0 0 0 0 0 0 0 1 + 1 0 0 0 0 0 0 1 + + + + 1 0 0 0 0 0 0 0 0 0 0 0 1 + 1 0 0 0 0 0 0 0 0 0 0 1 + - - D - - - - - - - - + + + + 1 1 + 1 + + + + 1 1 + 1 + + + + 1 1 + 1 + + + + + + + + + + + + + + clb.o[0] clb.q[0] clb.o[1] clb.q[1] clb.o[2] clb.q[2] clb.o[3] clb.q[3] + + + clb.o[4] clb.q[4] clb.o[5] clb.q[5] clb.o[6] clb.q[6] clb.o[7] clb.q[7] + + + clb.o[0] clb.q[0] clb.o[1] clb.q[1] clb.o[2] clb.q[2] clb.o[3] clb.q[3] + + + clb.o[4] clb.q[4] clb.o[5] clb.q[5] clb.o[6] clb.q[6] clb.o[7] clb.q[7] + + + clb.o[0] clb.q[0] clb.o[1] clb.q[1] clb.o[2] clb.q[2] clb.o[3] clb.q[3] + + + clb.o[4] clb.q[4] clb.o[5] clb.q[5] clb.o[6] clb.q[6] clb.o[7] clb.q[7] + + + clb.o[0] clb.q[0] clb.o[1] clb.q[1] clb.o[2] clb.q[2] clb.o[3] clb.q[3] + + + clb.o[4] clb.q[4] clb.o[5] clb.q[5] clb.o[6] clb.q[6] clb.o[7] clb.q[7] + + + clb.o[0] clb.q[0] clb.o[1] clb.q[1] clb.o[2] clb.q[2] clb.o[3] clb.q[3] + + + clb.o[4] clb.q[4] clb.o[5] clb.q[5] clb.o[6] clb.q[6] clb.o[7] clb.q[7] + + + clb.o[0] clb.q[0] clb.o[1] clb.q[1] clb.o[2] clb.q[2] clb.o[3] clb.q[3] + + + clb.o[4] clb.q[4] clb.o[5] clb.q[5] clb.o[6] clb.q[6] clb.o[7] clb.q[7] + + + clb.o[0] clb.q[0] clb.o[1] clb.q[1] clb.o[2] clb.q[2] clb.o[3] clb.q[3] + + + clb.o[4] clb.q[4] clb.o[5] clb.q[5] clb.o[6] clb.q[6] clb.o[7] clb.q[7] + + + clb.o[0] clb.q[0] clb.o[1] clb.q[1] clb.o[2] clb.q[2] clb.o[3] clb.q[3] + + + clb.o[4] clb.q[4] clb.o[5] clb.q[5] clb.o[6] clb.q[6] clb.o[7] clb.q[7] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 clb.o[0] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 clb.o[1] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 clb.o[2] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 clb.o[3] + + + l1.N1 l6.N1 l6.N18 l8.N11 clb.o[4] + + + l1.E1 l6.E1 l6.E18 l8.E11 clb.o[5] + + + l1.S1 l6.S1 l6.S18 l8.S11 clb.o[6] + + + l1.W1 l6.W1 l6.W18 l8.W11 clb.o[7] + + + l1.N2 l6.N2 l6.N19 l8.N12 clb.q[0] + + + l1.E2 l6.E2 l6.E19 l8.E12 clb.q[1] + + + l1.S2 l6.S2 l6.S19 l8.S12 clb.q[2] + + + l1.W2 l6.W2 l6.W19 l8.W12 clb.q[3] + + + l1.N3 l6.N3 l6.N20 l8.N13 clb.q[4] + + + l1.E3 l6.E3 l6.E20 l8.E13 clb.q[5] + + + l1.S3 l6.S3 l6.S20 l8.S13 clb.q[6] + + + l1.W3 l6.W3 l6.W20 l8.W13 clb.q[7] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 clb.o[0] + + + l2.E3 l6.E8 l8.E1 l12.E2 clb.o[1] + + + l2.S3 l6.S8 l8.S1 l12.S2 clb.o[2] + + + l2.W3 l6.W8 l8.W1 l12.W2 clb.o[3] + + + l2.N4 l6.N9 l8.N2 l12.N3 clb.o[4] + + + l2.E4 l6.E9 l8.E2 l12.E3 clb.o[5] + + + l2.S4 l6.S9 l8.S2 l12.S3 clb.o[6] + + + l2.W4 l6.W9 l8.W2 l12.W3 clb.o[7] + + + l2.N5 l6.N10 l8.N3 l12.N4 clb.q[0] + + + l2.E5 l6.E10 l8.E3 l12.E4 clb.q[1] + + + l2.S5 l6.S10 l8.S3 l12.S4 clb.q[2] + + + l2.W5 l6.W10 l8.W3 l12.W4 clb.q[3] + + + l3.N0 l6.N11 l8.N4 l12.N5 clb.q[4] + + + l3.E0 l6.E11 l8.E4 l12.E5 clb.q[5] + + + l3.S0 l6.S11 l8.S4 l12.S5 clb.q[6] + + + l3.W0 l6.W11 l8.W4 l12.W5 clb.q[7] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 clb.o[0] + + + l3.E5 l6.E16 l8.E9 l12.E10 clb.o[1] + + + l3.S5 l6.S16 l8.S9 l12.S10 clb.o[2] + + + l3.W5 l6.W16 l8.W9 l12.W10 clb.o[3] + + + l1.N0 l3.N0 l6.N5 l6.N16 clb.o[0] + + + l1.E0 l3.E0 l6.E5 l6.E16 clb.o[1] + + + l1.S0 l3.S0 l6.S5 l6.S16 clb.o[2] + + + l1.W0 l3.W0 l6.W5 l6.W16 clb.o[3] + + + l1.N1 l3.N1 l6.N6 l6.N17 clb.o[4] + + + l1.E1 l3.E1 l6.E6 l6.E17 clb.o[5] + + + l1.S1 l3.S1 l6.S6 l6.S17 clb.o[6] + + + l1.W1 l3.W1 l6.W6 l6.W17 clb.o[7] + + + l1.N2 l3.N2 l6.N7 l6.N18 clb.q[0] + + + l1.E2 l3.E2 l6.E7 l6.E18 clb.q[1] + + + l1.S2 l3.S2 l6.S7 l6.S18 clb.q[2] + + + l1.W2 l3.W2 l6.W7 l6.W18 clb.q[3] + + + l1.N3 l3.N3 l6.N8 l6.N19 clb.q[4] + + + l1.E3 l3.E3 l6.E8 l6.E19 clb.q[5] + + + l1.S3 l3.S3 l6.S8 l6.S19 clb.q[6] + + + l1.W3 l3.W3 l6.W8 l6.W19 clb.q[7] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + clb.Ia[0] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + clb.Ib[0] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + clb.Ic[0] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + clb.Id[0] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + clb.Ie[0] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + clb.If[0] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + clb.Ig[0] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + clb.Ih[0] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + clb.Ia[1] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + clb.Ib[1] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + clb.Ic[1] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + clb.Id[1] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + clb.Ie[1] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + clb.If[1] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + clb.Ig[1] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + clb.Ih[1] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + clb.Ia[2] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + clb.Ib[2] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + clb.Ic[2] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + clb.Id[2] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + clb.Ie[2] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + clb.If[2] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + clb.Ig[2] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + clb.Ih[2] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + clb.Ia[3] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + clb.Ib[3] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + clb.Ic[3] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + clb.Id[3] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + clb.Ie[3] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + clb.If[3] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + clb.Ig[3] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + clb.Ih[3] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + clb.Ia[4] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + clb.Ib[4] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + clb.Ic[4] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + clb.Id[4] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + clb.Ie[4] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + clb.If[4] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + clb.Ig[4] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + clb.Ih[4] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + clb.Ia[5] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + clb.Ib[5] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + clb.Ic[5] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + clb.Id[5] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + clb.Ie[5] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + clb.If[5] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + clb.Ig[5] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + clb.Ih[5] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + io_left.inpad[0] io_left.inpad[0] io_left.inpad[1] io_left.inpad[1] io_left.inpad[2] io_left.inpad[2] io_left.inpad[3] io_left.inpad[3] + + + io_left.inpad[4] io_left.inpad[4] io_left.inpad[5] io_left.inpad[5] io_left.inpad[6] io_left.inpad[6] io_left.inpad[7] io_left.inpad[7] + + + io_left.inpad[0] io_left.inpad[0] io_left.inpad[1] io_left.inpad[1] io_left.inpad[2] io_left.inpad[2] io_left.inpad[3] io_left.inpad[3] + + + io_left.inpad[4] io_left.inpad[4] io_left.inpad[5] io_left.inpad[5] io_left.inpad[6] io_left.inpad[6] io_left.inpad[7] io_left.inpad[7] + + + io_left.inpad[0] io_left.inpad[0] io_left.inpad[1] io_left.inpad[1] io_left.inpad[2] io_left.inpad[2] io_left.inpad[3] io_left.inpad[3] + + + io_left.inpad[4] io_left.inpad[4] io_left.inpad[5] io_left.inpad[5] io_left.inpad[6] io_left.inpad[6] io_left.inpad[7] io_left.inpad[7] + + + io_left.inpad[0] io_left.inpad[0] io_left.inpad[1] io_left.inpad[1] io_left.inpad[2] io_left.inpad[2] io_left.inpad[3] io_left.inpad[3] + + + io_left.inpad[4] io_left.inpad[4] io_left.inpad[5] io_left.inpad[5] io_left.inpad[6] io_left.inpad[6] io_left.inpad[7] io_left.inpad[7] + + + io_left.inpad[0] io_left.inpad[0] io_left.inpad[1] io_left.inpad[1] io_left.inpad[2] io_left.inpad[2] io_left.inpad[3] io_left.inpad[3] + + + io_left.inpad[4] io_left.inpad[4] io_left.inpad[5] io_left.inpad[5] io_left.inpad[6] io_left.inpad[6] io_left.inpad[7] io_left.inpad[7] + + + io_left.inpad[0] io_left.inpad[0] io_left.inpad[1] io_left.inpad[1] io_left.inpad[2] io_left.inpad[2] io_left.inpad[3] io_left.inpad[3] + + + io_left.inpad[4] io_left.inpad[4] io_left.inpad[5] io_left.inpad[5] io_left.inpad[6] io_left.inpad[6] io_left.inpad[7] io_left.inpad[7] + + + io_left.inpad[0] io_left.inpad[0] io_left.inpad[1] io_left.inpad[1] io_left.inpad[2] io_left.inpad[2] io_left.inpad[3] io_left.inpad[3] + + + io_left.inpad[4] io_left.inpad[4] io_left.inpad[5] io_left.inpad[5] io_left.inpad[6] io_left.inpad[6] io_left.inpad[7] io_left.inpad[7] + + + io_left.inpad[0] io_left.inpad[0] io_left.inpad[1] io_left.inpad[1] io_left.inpad[2] io_left.inpad[2] io_left.inpad[3] io_left.inpad[3] + + + io_left.inpad[4] io_left.inpad[4] io_left.inpad[5] io_left.inpad[5] io_left.inpad[6] io_left.inpad[6] io_left.inpad[7] io_left.inpad[7] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 io_left.inpad[0] + + + io_left.inpad[1] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 io_left.inpad[2] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 io_left.inpad[3] + + + l1.N1 l6.N1 l6.N18 l8.N11 io_left.inpad[4] + + + io_left.inpad[5] + + + l1.S1 l6.S1 l6.S18 l8.S11 io_left.inpad[6] + + + l1.W1 l6.W1 l6.W18 l8.W11 io_left.inpad[7] + + + l1.N2 l6.N2 l6.N19 l8.N12 io_left.inpad[0] + + + io_left.inpad[1] + + + l1.S2 l6.S2 l6.S19 l8.S12 io_left.inpad[2] + + + l1.W2 l6.W2 l6.W19 l8.W12 io_left.inpad[3] + + + l1.N3 l6.N3 l6.N20 l8.N13 io_left.inpad[4] + + + io_left.inpad[5] + + + l1.S3 l6.S3 l6.S20 l8.S13 io_left.inpad[6] + + + l1.W3 l6.W3 l6.W20 l8.W13 io_left.inpad[7] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 io_left.inpad[0] + + + io_left.inpad[1] + + + l2.S3 l6.S8 l8.S1 l12.S2 io_left.inpad[2] + + + l2.W3 l6.W8 l8.W1 l12.W2 io_left.inpad[3] + + + l2.N4 l6.N9 l8.N2 l12.N3 io_left.inpad[4] + + + io_left.inpad[5] + + + l2.S4 l6.S9 l8.S2 l12.S3 io_left.inpad[6] + + + l2.W4 l6.W9 l8.W2 l12.W3 io_left.inpad[7] + + + l2.N5 l6.N10 l8.N3 l12.N4 io_left.inpad[0] + + + io_left.inpad[1] + + + l2.S5 l6.S10 l8.S3 l12.S4 io_left.inpad[2] + + + l2.W5 l6.W10 l8.W3 l12.W4 io_left.inpad[3] + + + l3.N0 l6.N11 l8.N4 l12.N5 io_left.inpad[4] + + + io_left.inpad[5] + + + l3.S0 l6.S11 l8.S4 l12.S5 io_left.inpad[6] + + + l3.W0 l6.W11 l8.W4 l12.W5 io_left.inpad[7] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 io_left.inpad[0] + + + io_left.inpad[1] + + + l3.S5 l6.S16 l8.S9 l12.S10 io_left.inpad[2] + + + l3.W5 l6.W16 l8.W9 l12.W10 io_left.inpad[3] + + + l1.N0 l3.N0 l6.N5 l6.N16 io_left.inpad[0] + + + io_left.inpad[1] + + + l1.S0 l3.S0 l6.S5 l6.S16 io_left.inpad[2] + + + l1.W0 l3.W0 l6.W5 l6.W16 io_left.inpad[3] + + + l1.N1 l3.N1 l6.N6 l6.N17 io_left.inpad[4] + + + io_left.inpad[5] + + + l1.S1 l3.S1 l6.S6 l6.S17 io_left.inpad[6] + + + l1.W1 l3.W1 l6.W6 l6.W17 io_left.inpad[7] + + + l1.N2 l3.N2 l6.N7 l6.N18 io_left.inpad[0] + + + io_left.inpad[1] + + + l1.S2 l3.S2 l6.S7 l6.S18 io_left.inpad[2] + + + l1.W2 l3.W2 l6.W7 l6.W18 io_left.inpad[3] + + + l1.N3 l3.N3 l6.N8 l6.N19 io_left.inpad[4] + + + io_left.inpad[5] + + + l1.S3 l3.S3 l6.S8 l6.S19 io_left.inpad[6] + + + l1.W3 l3.W3 l6.W8 l6.W19 io_left.inpad[7] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + io_left.outpad[0] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + io_left.outpad[1] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + io_left.outpad[2] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + io_left.outpad[3] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + io_left.outpad[4] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + io_left.outpad[5] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + io_left.outpad[6] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + io_left.outpad[7] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + io_left.outpad[0] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + io_left.outpad[1] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + io_left.outpad[2] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + io_left.outpad[3] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + io_left.outpad[4] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + io_left.outpad[5] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + io_left.outpad[6] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + io_left.outpad[7] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + io_left.outpad[0] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + io_left.outpad[1] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + io_left.outpad[2] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + io_left.outpad[3] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + io_left.outpad[4] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + io_left.outpad[5] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + io_left.outpad[6] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + io_left.outpad[7] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + io_left.outpad[0] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + io_left.outpad[1] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + io_left.outpad[2] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + io_left.outpad[3] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + io_left.outpad[4] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + io_left.outpad[5] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + io_left.outpad[6] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + io_left.outpad[7] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + io_left.outpad[0] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + io_left.outpad[1] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + io_left.outpad[2] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + io_left.outpad[3] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + io_left.outpad[4] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + io_left.outpad[5] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + io_left.outpad[6] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + io_left.outpad[7] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + io_left.outpad[0] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + io_left.outpad[1] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + io_left.outpad[2] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + io_left.outpad[3] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + io_left.outpad[4] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + io_left.outpad[5] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + io_left.outpad[6] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + io_left.outpad[7] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + + + + + + + + + + + + + io_right.inpad[0] io_right.inpad[0] io_right.inpad[1] io_right.inpad[1] io_right.inpad[2] io_right.inpad[2] io_right.inpad[3] io_right.inpad[3] + + + io_right.inpad[4] io_right.inpad[4] io_right.inpad[5] io_right.inpad[5] io_right.inpad[6] io_right.inpad[6] io_right.inpad[7] io_right.inpad[7] + + + io_right.inpad[0] io_right.inpad[0] io_right.inpad[1] io_right.inpad[1] io_right.inpad[2] io_right.inpad[2] io_right.inpad[3] io_right.inpad[3] + + + io_right.inpad[4] io_right.inpad[4] io_right.inpad[5] io_right.inpad[5] io_right.inpad[6] io_right.inpad[6] io_right.inpad[7] io_right.inpad[7] + + + io_right.inpad[0] io_right.inpad[0] io_right.inpad[1] io_right.inpad[1] io_right.inpad[2] io_right.inpad[2] io_right.inpad[3] io_right.inpad[3] + + + io_right.inpad[4] io_right.inpad[4] io_right.inpad[5] io_right.inpad[5] io_right.inpad[6] io_right.inpad[6] io_right.inpad[7] io_right.inpad[7] + + + io_right.inpad[0] io_right.inpad[0] io_right.inpad[1] io_right.inpad[1] io_right.inpad[2] io_right.inpad[2] io_right.inpad[3] io_right.inpad[3] + + + io_right.inpad[4] io_right.inpad[4] io_right.inpad[5] io_right.inpad[5] io_right.inpad[6] io_right.inpad[6] io_right.inpad[7] io_right.inpad[7] + + + io_right.inpad[0] io_right.inpad[0] io_right.inpad[1] io_right.inpad[1] io_right.inpad[2] io_right.inpad[2] io_right.inpad[3] io_right.inpad[3] + + + io_right.inpad[4] io_right.inpad[4] io_right.inpad[5] io_right.inpad[5] io_right.inpad[6] io_right.inpad[6] io_right.inpad[7] io_right.inpad[7] + + + io_right.inpad[0] io_right.inpad[0] io_right.inpad[1] io_right.inpad[1] io_right.inpad[2] io_right.inpad[2] io_right.inpad[3] io_right.inpad[3] + + + io_right.inpad[4] io_right.inpad[4] io_right.inpad[5] io_right.inpad[5] io_right.inpad[6] io_right.inpad[6] io_right.inpad[7] io_right.inpad[7] + + + io_right.inpad[0] io_right.inpad[0] io_right.inpad[1] io_right.inpad[1] io_right.inpad[2] io_right.inpad[2] io_right.inpad[3] io_right.inpad[3] + + + io_right.inpad[4] io_right.inpad[4] io_right.inpad[5] io_right.inpad[5] io_right.inpad[6] io_right.inpad[6] io_right.inpad[7] io_right.inpad[7] + + + io_right.inpad[0] io_right.inpad[0] io_right.inpad[1] io_right.inpad[1] io_right.inpad[2] io_right.inpad[2] io_right.inpad[3] io_right.inpad[3] + + + io_right.inpad[4] io_right.inpad[4] io_right.inpad[5] io_right.inpad[5] io_right.inpad[6] io_right.inpad[6] io_right.inpad[7] io_right.inpad[7] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 io_right.inpad[0] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 io_right.inpad[1] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 io_right.inpad[2] + + + io_right.inpad[3] + + + l1.N1 l6.N1 l6.N18 l8.N11 io_right.inpad[4] + + + l1.E1 l6.E1 l6.E18 l8.E11 io_right.inpad[5] + + + l1.S1 l6.S1 l6.S18 l8.S11 io_right.inpad[6] + + + io_right.inpad[7] + + + l1.N2 l6.N2 l6.N19 l8.N12 io_right.inpad[0] + + + l1.E2 l6.E2 l6.E19 l8.E12 io_right.inpad[1] + + + l1.S2 l6.S2 l6.S19 l8.S12 io_right.inpad[2] + + + io_right.inpad[3] + + + l1.N3 l6.N3 l6.N20 l8.N13 io_right.inpad[4] + + + l1.E3 l6.E3 l6.E20 l8.E13 io_right.inpad[5] + + + l1.S3 l6.S3 l6.S20 l8.S13 io_right.inpad[6] + + + io_right.inpad[7] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 io_right.inpad[0] + + + l2.E3 l6.E8 l8.E1 l12.E2 io_right.inpad[1] + + + l2.S3 l6.S8 l8.S1 l12.S2 io_right.inpad[2] + + + io_right.inpad[3] + + + l2.N4 l6.N9 l8.N2 l12.N3 io_right.inpad[4] + + + l2.E4 l6.E9 l8.E2 l12.E3 io_right.inpad[5] + + + l2.S4 l6.S9 l8.S2 l12.S3 io_right.inpad[6] + + + io_right.inpad[7] + + + l2.N5 l6.N10 l8.N3 l12.N4 io_right.inpad[0] + + + l2.E5 l6.E10 l8.E3 l12.E4 io_right.inpad[1] + + + l2.S5 l6.S10 l8.S3 l12.S4 io_right.inpad[2] + + + io_right.inpad[3] + + + l3.N0 l6.N11 l8.N4 l12.N5 io_right.inpad[4] + + + l3.E0 l6.E11 l8.E4 l12.E5 io_right.inpad[5] + + + l3.S0 l6.S11 l8.S4 l12.S5 io_right.inpad[6] + + + io_right.inpad[7] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 io_right.inpad[0] + + + l3.E5 l6.E16 l8.E9 l12.E10 io_right.inpad[1] + + + l3.S5 l6.S16 l8.S9 l12.S10 io_right.inpad[2] + + + io_right.inpad[3] + + + l1.N0 l3.N0 l6.N5 l6.N16 io_right.inpad[0] + + + l1.E0 l3.E0 l6.E5 l6.E16 io_right.inpad[1] + + + l1.S0 l3.S0 l6.S5 l6.S16 io_right.inpad[2] + + + io_right.inpad[3] + + + l1.N1 l3.N1 l6.N6 l6.N17 io_right.inpad[4] + + + l1.E1 l3.E1 l6.E6 l6.E17 io_right.inpad[5] + + + l1.S1 l3.S1 l6.S6 l6.S17 io_right.inpad[6] + + + io_right.inpad[7] + + + l1.N2 l3.N2 l6.N7 l6.N18 io_right.inpad[0] + + + l1.E2 l3.E2 l6.E7 l6.E18 io_right.inpad[1] + + + l1.S2 l3.S2 l6.S7 l6.S18 io_right.inpad[2] + + + io_right.inpad[3] + + + l1.N3 l3.N3 l6.N8 l6.N19 io_right.inpad[4] + + + l1.E3 l3.E3 l6.E8 l6.E19 io_right.inpad[5] + + + l1.S3 l3.S3 l6.S8 l6.S19 io_right.inpad[6] + + + io_right.inpad[7] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + omux-3 + + + + + io_right.outpad[0] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + io_right.outpad[1] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + io_right.outpad[2] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + io_right.outpad[3] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + io_right.outpad[4] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + io_right.outpad[5] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + io_right.outpad[6] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + io_right.outpad[7] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + io_right.outpad[0] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + io_right.outpad[1] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + io_right.outpad[2] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + io_right.outpad[3] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + io_right.outpad[4] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + io_right.outpad[5] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + io_right.outpad[6] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + io_right.outpad[7] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + io_right.outpad[0] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + io_right.outpad[1] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + io_right.outpad[2] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + io_right.outpad[3] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + io_right.outpad[4] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + io_right.outpad[5] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + io_right.outpad[6] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + io_right.outpad[7] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + io_right.outpad[0] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + io_right.outpad[1] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + io_right.outpad[2] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + io_right.outpad[3] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + io_right.outpad[4] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + io_right.outpad[5] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + io_right.outpad[6] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + io_right.outpad[7] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + io_right.outpad[0] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + io_right.outpad[1] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + io_right.outpad[2] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + io_right.outpad[3] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + io_right.outpad[4] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + io_right.outpad[5] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + io_right.outpad[6] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + io_right.outpad[7] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + io_right.outpad[0] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + io_right.outpad[1] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + io_right.outpad[2] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + io_right.outpad[3] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + io_right.outpad[4] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + io_right.outpad[5] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + io_right.outpad[6] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + io_right.outpad[7] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + io_top.inpad[0] io_top.inpad[0] io_top.inpad[1] io_top.inpad[1] io_top.inpad[2] io_top.inpad[2] io_top.inpad[3] io_top.inpad[3] + + + io_top.inpad[4] io_top.inpad[4] io_top.inpad[5] io_top.inpad[5] io_top.inpad[6] io_top.inpad[6] io_top.inpad[7] io_top.inpad[7] + + + io_top.inpad[0] io_top.inpad[0] io_top.inpad[1] io_top.inpad[1] io_top.inpad[2] io_top.inpad[2] io_top.inpad[3] io_top.inpad[3] + + + io_top.inpad[4] io_top.inpad[4] io_top.inpad[5] io_top.inpad[5] io_top.inpad[6] io_top.inpad[6] io_top.inpad[7] io_top.inpad[7] + + + io_top.inpad[0] io_top.inpad[0] io_top.inpad[1] io_top.inpad[1] io_top.inpad[2] io_top.inpad[2] io_top.inpad[3] io_top.inpad[3] + + + io_top.inpad[4] io_top.inpad[4] io_top.inpad[5] io_top.inpad[5] io_top.inpad[6] io_top.inpad[6] io_top.inpad[7] io_top.inpad[7] + + + io_top.inpad[0] io_top.inpad[0] io_top.inpad[1] io_top.inpad[1] io_top.inpad[2] io_top.inpad[2] io_top.inpad[3] io_top.inpad[3] + + + io_top.inpad[4] io_top.inpad[4] io_top.inpad[5] io_top.inpad[5] io_top.inpad[6] io_top.inpad[6] io_top.inpad[7] io_top.inpad[7] + + + io_top.inpad[0] io_top.inpad[0] io_top.inpad[1] io_top.inpad[1] io_top.inpad[2] io_top.inpad[2] io_top.inpad[3] io_top.inpad[3] + + + io_top.inpad[4] io_top.inpad[4] io_top.inpad[5] io_top.inpad[5] io_top.inpad[6] io_top.inpad[6] io_top.inpad[7] io_top.inpad[7] + + + io_top.inpad[0] io_top.inpad[0] io_top.inpad[1] io_top.inpad[1] io_top.inpad[2] io_top.inpad[2] io_top.inpad[3] io_top.inpad[3] + + + io_top.inpad[4] io_top.inpad[4] io_top.inpad[5] io_top.inpad[5] io_top.inpad[6] io_top.inpad[6] io_top.inpad[7] io_top.inpad[7] + + + io_top.inpad[0] io_top.inpad[0] io_top.inpad[1] io_top.inpad[1] io_top.inpad[2] io_top.inpad[2] io_top.inpad[3] io_top.inpad[3] + + + io_top.inpad[4] io_top.inpad[4] io_top.inpad[5] io_top.inpad[5] io_top.inpad[6] io_top.inpad[6] io_top.inpad[7] io_top.inpad[7] + + + io_top.inpad[0] io_top.inpad[0] io_top.inpad[1] io_top.inpad[1] io_top.inpad[2] io_top.inpad[2] io_top.inpad[3] io_top.inpad[3] + + + io_top.inpad[4] io_top.inpad[4] io_top.inpad[5] io_top.inpad[5] io_top.inpad[6] io_top.inpad[6] io_top.inpad[7] io_top.inpad[7] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 io_top.inpad[0] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 io_top.inpad[1] + + + io_top.inpad[2] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 io_top.inpad[3] + + + l1.N1 l6.N1 l6.N18 l8.N11 io_top.inpad[4] + + + l1.E1 l6.E1 l6.E18 l8.E11 io_top.inpad[5] + + + io_top.inpad[6] + + + l1.W1 l6.W1 l6.W18 l8.W11 io_top.inpad[7] + + + l1.N2 l6.N2 l6.N19 l8.N12 io_top.inpad[0] + + + l1.E2 l6.E2 l6.E19 l8.E12 io_top.inpad[1] + + + io_top.inpad[2] + + + l1.W2 l6.W2 l6.W19 l8.W12 io_top.inpad[3] + + + l1.N3 l6.N3 l6.N20 l8.N13 io_top.inpad[4] + + + l1.E3 l6.E3 l6.E20 l8.E13 io_top.inpad[5] + + + io_top.inpad[6] + + + l1.W3 l6.W3 l6.W20 l8.W13 io_top.inpad[7] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 io_top.inpad[0] + + + l2.E3 l6.E8 l8.E1 l12.E2 io_top.inpad[1] + + + io_top.inpad[2] + + + l2.W3 l6.W8 l8.W1 l12.W2 io_top.inpad[3] + + + l2.N4 l6.N9 l8.N2 l12.N3 io_top.inpad[4] + + + l2.E4 l6.E9 l8.E2 l12.E3 io_top.inpad[5] + + + io_top.inpad[6] + + + l2.W4 l6.W9 l8.W2 l12.W3 io_top.inpad[7] + + + l2.N5 l6.N10 l8.N3 l12.N4 io_top.inpad[0] + + + l2.E5 l6.E10 l8.E3 l12.E4 io_top.inpad[1] + + + io_top.inpad[2] + + + l2.W5 l6.W10 l8.W3 l12.W4 io_top.inpad[3] + + + l3.N0 l6.N11 l8.N4 l12.N5 io_top.inpad[4] + + + l3.E0 l6.E11 l8.E4 l12.E5 io_top.inpad[5] + + + io_top.inpad[6] + + + l3.W0 l6.W11 l8.W4 l12.W5 io_top.inpad[7] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 io_top.inpad[0] + + + l3.E5 l6.E16 l8.E9 l12.E10 io_top.inpad[1] + + + io_top.inpad[2] + + + l3.W5 l6.W16 l8.W9 l12.W10 io_top.inpad[3] + + + l1.N0 l3.N0 l6.N5 l6.N16 io_top.inpad[0] + + + l1.E0 l3.E0 l6.E5 l6.E16 io_top.inpad[1] + + + io_top.inpad[2] + + + l1.W0 l3.W0 l6.W5 l6.W16 io_top.inpad[3] + + + l1.N1 l3.N1 l6.N6 l6.N17 io_top.inpad[4] + + + l1.E1 l3.E1 l6.E6 l6.E17 io_top.inpad[5] + + + io_top.inpad[6] + + + l1.W1 l3.W1 l6.W6 l6.W17 io_top.inpad[7] + + + l1.N2 l3.N2 l6.N7 l6.N18 io_top.inpad[0] + + + l1.E2 l3.E2 l6.E7 l6.E18 io_top.inpad[1] + + + io_top.inpad[2] + + + l1.W2 l3.W2 l6.W7 l6.W18 io_top.inpad[3] + + + l1.N3 l3.N3 l6.N8 l6.N19 io_top.inpad[4] + + + l1.E3 l3.E3 l6.E8 l6.E19 io_top.inpad[5] + + + io_top.inpad[6] + + + l1.W3 l3.W3 l6.W8 l6.W19 io_top.inpad[7] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + io_top.outpad[0] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + io_top.outpad[1] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + io_top.outpad[2] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + io_top.outpad[3] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + io_top.outpad[4] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + io_top.outpad[5] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + io_top.outpad[6] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + io_top.outpad[7] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + io_top.outpad[0] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + io_top.outpad[1] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + io_top.outpad[2] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + io_top.outpad[3] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + io_top.outpad[4] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + io_top.outpad[5] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + io_top.outpad[6] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + io_top.outpad[7] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + io_top.outpad[0] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + io_top.outpad[1] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + io_top.outpad[2] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + io_top.outpad[3] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + io_top.outpad[4] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + io_top.outpad[5] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + io_top.outpad[6] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + io_top.outpad[7] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + io_top.outpad[0] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + io_top.outpad[1] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + io_top.outpad[2] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + io_top.outpad[3] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + io_top.outpad[4] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + io_top.outpad[5] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + io_top.outpad[6] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + io_top.outpad[7] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + io_top.outpad[0] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + io_top.outpad[1] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + io_top.outpad[2] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + io_top.outpad[3] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + io_top.outpad[4] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + io_top.outpad[5] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + io_top.outpad[6] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + io_top.outpad[7] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + io_top.outpad[0] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + io_top.outpad[1] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + io_top.outpad[2] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + io_top.outpad[3] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + io_top.outpad[4] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + io_top.outpad[5] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + io_top.outpad[6] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + io_top.outpad[7] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + io_bottom.inpad[0] io_bottom.inpad[0] io_bottom.inpad[1] io_bottom.inpad[1] io_bottom.inpad[2] io_bottom.inpad[2] io_bottom.inpad[3] io_bottom.inpad[3] + + + io_bottom.inpad[4] io_bottom.inpad[4] io_bottom.inpad[5] io_bottom.inpad[5] io_bottom.inpad[6] io_bottom.inpad[6] io_bottom.inpad[7] io_bottom.inpad[7] + + + io_bottom.inpad[0] io_bottom.inpad[0] io_bottom.inpad[1] io_bottom.inpad[1] io_bottom.inpad[2] io_bottom.inpad[2] io_bottom.inpad[3] io_bottom.inpad[3] + + + io_bottom.inpad[4] io_bottom.inpad[4] io_bottom.inpad[5] io_bottom.inpad[5] io_bottom.inpad[6] io_bottom.inpad[6] io_bottom.inpad[7] io_bottom.inpad[7] + + + io_bottom.inpad[0] io_bottom.inpad[0] io_bottom.inpad[1] io_bottom.inpad[1] io_bottom.inpad[2] io_bottom.inpad[2] io_bottom.inpad[3] io_bottom.inpad[3] + + + io_bottom.inpad[4] io_bottom.inpad[4] io_bottom.inpad[5] io_bottom.inpad[5] io_bottom.inpad[6] io_bottom.inpad[6] io_bottom.inpad[7] io_bottom.inpad[7] + + + io_bottom.inpad[0] io_bottom.inpad[0] io_bottom.inpad[1] io_bottom.inpad[1] io_bottom.inpad[2] io_bottom.inpad[2] io_bottom.inpad[3] io_bottom.inpad[3] + + + io_bottom.inpad[4] io_bottom.inpad[4] io_bottom.inpad[5] io_bottom.inpad[5] io_bottom.inpad[6] io_bottom.inpad[6] io_bottom.inpad[7] io_bottom.inpad[7] + + + io_bottom.inpad[0] io_bottom.inpad[0] io_bottom.inpad[1] io_bottom.inpad[1] io_bottom.inpad[2] io_bottom.inpad[2] io_bottom.inpad[3] io_bottom.inpad[3] + + + io_bottom.inpad[4] io_bottom.inpad[4] io_bottom.inpad[5] io_bottom.inpad[5] io_bottom.inpad[6] io_bottom.inpad[6] io_bottom.inpad[7] io_bottom.inpad[7] + + + io_bottom.inpad[0] io_bottom.inpad[0] io_bottom.inpad[1] io_bottom.inpad[1] io_bottom.inpad[2] io_bottom.inpad[2] io_bottom.inpad[3] io_bottom.inpad[3] + + + io_bottom.inpad[4] io_bottom.inpad[4] io_bottom.inpad[5] io_bottom.inpad[5] io_bottom.inpad[6] io_bottom.inpad[6] io_bottom.inpad[7] io_bottom.inpad[7] + + + io_bottom.inpad[0] io_bottom.inpad[0] io_bottom.inpad[1] io_bottom.inpad[1] io_bottom.inpad[2] io_bottom.inpad[2] io_bottom.inpad[3] io_bottom.inpad[3] + + + io_bottom.inpad[4] io_bottom.inpad[4] io_bottom.inpad[5] io_bottom.inpad[5] io_bottom.inpad[6] io_bottom.inpad[6] io_bottom.inpad[7] io_bottom.inpad[7] + + + io_bottom.inpad[0] io_bottom.inpad[0] io_bottom.inpad[1] io_bottom.inpad[1] io_bottom.inpad[2] io_bottom.inpad[2] io_bottom.inpad[3] io_bottom.inpad[3] + + + io_bottom.inpad[4] io_bottom.inpad[4] io_bottom.inpad[5] io_bottom.inpad[5] io_bottom.inpad[6] io_bottom.inpad[6] io_bottom.inpad[7] io_bottom.inpad[7] + + + io_bottom.inpad[0] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 io_bottom.inpad[1] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 io_bottom.inpad[2] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 io_bottom.inpad[3] + + + io_bottom.inpad[4] + + + l1.E1 l6.E1 l6.E18 l8.E11 io_bottom.inpad[5] + + + l1.S1 l6.S1 l6.S18 l8.S11 io_bottom.inpad[6] + + + l1.W1 l6.W1 l6.W18 l8.W11 io_bottom.inpad[7] + + + io_bottom.inpad[0] + + + l1.E2 l6.E2 l6.E19 l8.E12 io_bottom.inpad[1] + + + l1.S2 l6.S2 l6.S19 l8.S12 io_bottom.inpad[2] + + + l1.W2 l6.W2 l6.W19 l8.W12 io_bottom.inpad[3] + + + io_bottom.inpad[4] + + + l1.E3 l6.E3 l6.E20 l8.E13 io_bottom.inpad[5] + + + l1.S3 l6.S3 l6.S20 l8.S13 io_bottom.inpad[6] + + + l1.W3 l6.W3 l6.W20 l8.W13 io_bottom.inpad[7] + + + omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + io_bottom.inpad[0] + + + l2.E3 l6.E8 l8.E1 l12.E2 io_bottom.inpad[1] + + + l2.S3 l6.S8 l8.S1 l12.S2 io_bottom.inpad[2] + + + l2.W3 l6.W8 l8.W1 l12.W2 io_bottom.inpad[3] + + + io_bottom.inpad[4] + + + l2.E4 l6.E9 l8.E2 l12.E3 io_bottom.inpad[5] + + + l2.S4 l6.S9 l8.S2 l12.S3 io_bottom.inpad[6] + + + l2.W4 l6.W9 l8.W2 l12.W3 io_bottom.inpad[7] + + + io_bottom.inpad[0] + + + l2.E5 l6.E10 l8.E3 l12.E4 io_bottom.inpad[1] + + + l2.S5 l6.S10 l8.S3 l12.S4 io_bottom.inpad[2] + + + l2.W5 l6.W10 l8.W3 l12.W4 io_bottom.inpad[3] + + + io_bottom.inpad[4] + + + l3.E0 l6.E11 l8.E4 l12.E5 io_bottom.inpad[5] + + + l3.S0 l6.S11 l8.S4 l12.S5 io_bottom.inpad[6] + + + l3.W0 l6.W11 l8.W4 l12.W5 io_bottom.inpad[7] + + + omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + io_bottom.inpad[0] + + + l3.E5 l6.E16 l8.E9 l12.E10 io_bottom.inpad[1] + + + l3.S5 l6.S16 l8.S9 l12.S10 io_bottom.inpad[2] + + + l3.W5 l6.W16 l8.W9 l12.W10 io_bottom.inpad[3] + + + io_bottom.inpad[0] + + + l1.E0 l3.E0 l6.E5 l6.E16 io_bottom.inpad[1] + + + l1.S0 l3.S0 l6.S5 l6.S16 io_bottom.inpad[2] + + + l1.W0 l3.W0 l6.W5 l6.W16 io_bottom.inpad[3] + + + io_bottom.inpad[4] + + + l1.E1 l3.E1 l6.E6 l6.E17 io_bottom.inpad[5] + + + l1.S1 l3.S1 l6.S6 l6.S17 io_bottom.inpad[6] + + + l1.W1 l3.W1 l6.W6 l6.W17 io_bottom.inpad[7] + + + io_bottom.inpad[0] + + + l1.E2 l3.E2 l6.E7 l6.E18 io_bottom.inpad[1] + + + l1.S2 l3.S2 l6.S7 l6.S18 io_bottom.inpad[2] + + + l1.W2 l3.W2 l6.W7 l6.W18 io_bottom.inpad[3] + + + io_bottom.inpad[4] + + + l1.E3 l3.E3 l6.E8 l6.E19 io_bottom.inpad[5] + + + l1.S3 l3.S3 l6.S8 l6.S19 io_bottom.inpad[6] + + + l1.W3 l3.W3 l6.W8 l6.W19 io_bottom.inpad[7] + + + omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + io_bottom.outpad[0] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + io_bottom.outpad[1] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + io_bottom.outpad[2] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + io_bottom.outpad[3] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + io_bottom.outpad[4] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + io_bottom.outpad[5] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + io_bottom.outpad[6] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + io_bottom.outpad[7] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + io_bottom.outpad[0] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + io_bottom.outpad[1] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + io_bottom.outpad[2] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + io_bottom.outpad[3] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + io_bottom.outpad[4] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + io_bottom.outpad[5] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + io_bottom.outpad[6] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + io_bottom.outpad[7] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + io_bottom.outpad[0] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + io_bottom.outpad[1] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + io_bottom.outpad[2] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + io_bottom.outpad[3] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + io_bottom.outpad[4] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + io_bottom.outpad[5] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + io_bottom.outpad[6] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + io_bottom.outpad[7] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + io_bottom.outpad[0] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + io_bottom.outpad[1] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + io_bottom.outpad[2] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + io_bottom.outpad[3] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + io_bottom.outpad[4] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + io_bottom.outpad[5] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + io_bottom.outpad[6] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + io_bottom.outpad[7] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + io_bottom.outpad[0] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + io_bottom.outpad[1] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + io_bottom.outpad[2] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + io_bottom.outpad[3] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + io_bottom.outpad[4] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + io_bottom.outpad[5] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + io_bottom.outpad[6] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + io_bottom.outpad[7] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + io_bottom.outpad[0] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + io_bottom.outpad[1] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + io_bottom.outpad[2] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + io_bottom.outpad[3] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + io_bottom.outpad[4] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + io_bottom.outpad[5] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + io_bottom.outpad[6] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + io_bottom.outpad[7] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 + + + l1.N1 l6.N1 l6.N18 l8.N11 + + + l1.E1 l6.E1 l6.E18 l8.E11 + + + l1.S1 l6.S1 l6.S18 l8.S11 + + + l1.W1 l6.W1 l6.W18 l8.W11 + + + l1.N2 l6.N2 l6.N19 l8.N12 + + + l1.E2 l6.E2 l6.E19 l8.E12 + + + l1.S2 l6.S2 l6.S19 l8.S12 + + + l1.W2 l6.W2 l6.W19 l8.W12 + + + l1.N3 l6.N3 l6.N20 l8.N13 + + + l1.E3 l6.E3 l6.E20 l8.E13 + + + l1.S3 l6.S3 l6.S20 l8.S13 + + + l1.W3 l6.W3 l6.W20 l8.W13 + + + l1.N4 l6.N4 l6.N21 l8.N14 + + + l1.E4 l6.E4 l6.E21 l8.E14 + + + l1.S4 l6.S4 l6.S21 l8.S14 + + + l1.W4 l6.W4 l6.W21 l8.W14 + + + l2.N0 l6.N5 l6.N22 l8.N15 + + + l2.E0 l6.E5 l6.E22 l8.E15 + + + l2.S0 l6.S5 l6.S22 l8.S15 + + + l2.W0 l6.W5 l6.W22 l8.W15 + + + l2.N1 l6.N6 l6.N23 l12.N0 + + + l2.E1 l6.E6 l6.E23 l12.E0 + + + l2.S1 l6.S6 l6.S23 l12.S0 + + + l2.W1 l6.W6 l6.W23 l12.W0 + + + l2.N2 l6.N7 l8.N0 l12.N1 + + + l2.E2 l6.E7 l8.E0 l12.E1 + + + l2.S2 l6.S7 l8.S0 l12.S1 + + + l2.W2 l6.W7 l8.W0 l12.W1 + + + l2.N3 l6.N8 l8.N1 l12.N2 + + + l2.E3 l6.E8 l8.E1 l12.E2 + + + l2.S3 l6.S8 l8.S1 l12.S2 + + + l2.W3 l6.W8 l8.W1 l12.W2 + + + l2.N4 l6.N9 l8.N2 l12.N3 + + + l2.E4 l6.E9 l8.E2 l12.E3 + + + l2.S4 l6.S9 l8.S2 l12.S3 + + + l2.W4 l6.W9 l8.W2 l12.W3 + + + l2.N5 l6.N10 l8.N3 l12.N4 + + + l2.E5 l6.E10 l8.E3 l12.E4 + + + l2.S5 l6.S10 l8.S3 l12.S4 + + + l2.W5 l6.W10 l8.W3 l12.W4 + + + l3.N0 l6.N11 l8.N4 l12.N5 + + + l3.E0 l6.E11 l8.E4 l12.E5 + + + l3.S0 l6.S11 l8.S4 l12.S5 + + + l3.W0 l6.W11 l8.W4 l12.W5 + + + l3.N1 l6.N12 l8.N5 l12.N6 + + + l3.E1 l6.E12 l8.E5 l12.E6 + + + l3.S1 l6.S12 l8.S5 l12.S6 + + + l3.W1 l6.W12 l8.W5 l12.W6 + + + l3.N2 l6.N13 l8.N6 l12.N7 + + + l3.E2 l6.E13 l8.E6 l12.E7 + + + l3.S2 l6.S13 l8.S6 l12.S7 + + + l3.W2 l6.W13 l8.W6 l12.W7 + + + l3.N3 l6.N14 l8.N7 l12.N8 + + + l3.E3 l6.E14 l8.E7 l12.E8 + + + l3.S3 l6.S14 l8.S7 l12.S8 + + + l3.W3 l6.W14 l8.W7 l12.W8 + + + l3.N4 l6.N15 l8.N8 l12.N9 + + + l3.E4 l6.E15 l8.E8 l12.E9 + + + l3.S4 l6.S15 l8.S8 l12.S9 + + + l3.W4 l6.W15 l8.W8 l12.W9 + + + l3.N5 l6.N16 l8.N9 l12.N10 + + + l3.E5 l6.E16 l8.E9 l12.E10 + + + l3.S5 l6.S16 l8.S9 l12.S10 + + + l3.W5 l6.W16 l8.W9 l12.W10 + + + l1.N0 l3.N0 l6.N5 l6.N16 + + + l1.E0 l3.E0 l6.E5 l6.E16 + + + l1.S0 l3.S0 l6.S5 l6.S16 + + + l1.W0 l3.W0 l6.W5 l6.W16 + + + l1.N1 l3.N1 l6.N6 l6.N17 + + + l1.E1 l3.E1 l6.E6 l6.E17 + + + l1.S1 l3.S1 l6.S6 l6.S17 + + + l1.W1 l3.W1 l6.W6 l6.W17 + + + l1.N2 l3.N2 l6.N7 l6.N18 + + + l1.E2 l3.E2 l6.E7 l6.E18 + + + l1.S2 l3.S2 l6.S7 l6.S18 + + + l1.W2 l3.W2 l6.W7 l6.W18 + + + l1.N3 l3.N3 l6.N8 l6.N19 + + + l1.E3 l3.E3 l6.E8 l6.E19 + + + l1.S3 l3.S3 l6.S8 l6.S19 + + + l1.W3 l3.W3 l6.W8 l6.W19 + + + l1.N4 l3.N4 l6.N9 l6.N20 + + + l1.E4 l3.E4 l6.E9 l6.E20 + + + l1.S4 l3.S4 l6.S9 l6.S20 + + + l1.W4 l3.W4 l6.W9 l6.W20 + + + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 + + + + + + + + + + + + + + + mult_36.out[54] mult_36.out[70] mult_36.out[62] mult_36.out[55] mult_36.out[71] mult_36.out[63] mult_36.out[56] mult_36.out[64] mult_36.out[57] mult_36.out[65] + + + mult_36.out[58] mult_36.out[66] mult_36.out[59] mult_36.out[67] mult_36.out[60] mult_36.out[68] mult_36.out[61] mult_36.out[69] + + + mult_36.out[54] mult_36.out[70] mult_36.out[62] mult_36.out[55] mult_36.out[71] mult_36.out[63] mult_36.out[56] mult_36.out[64] mult_36.out[57] mult_36.out[65] + + + mult_36.out[58] mult_36.out[66] mult_36.out[59] mult_36.out[67] mult_36.out[60] mult_36.out[68] mult_36.out[61] mult_36.out[69] + + + mult_36.out[54] mult_36.out[70] mult_36.out[62] mult_36.out[55] mult_36.out[71] mult_36.out[63] mult_36.out[56] mult_36.out[64] mult_36.out[57] mult_36.out[65] + + + mult_36.out[58] mult_36.out[66] mult_36.out[59] mult_36.out[67] mult_36.out[60] mult_36.out[68] mult_36.out[61] mult_36.out[69] + + + mult_36.out[54] mult_36.out[70] mult_36.out[62] mult_36.out[55] mult_36.out[71] mult_36.out[63] mult_36.out[56] mult_36.out[64] mult_36.out[57] mult_36.out[65] + + + mult_36.out[58] mult_36.out[66] mult_36.out[59] mult_36.out[67] mult_36.out[60] mult_36.out[68] mult_36.out[61] mult_36.out[69] + + + mult_36.out[54] mult_36.out[70] mult_36.out[62] mult_36.out[55] mult_36.out[71] mult_36.out[63] mult_36.out[56] mult_36.out[64] mult_36.out[57] mult_36.out[65] + + + mult_36.out[58] mult_36.out[66] mult_36.out[59] mult_36.out[67] mult_36.out[60] mult_36.out[68] mult_36.out[61] mult_36.out[69] + + + mult_36.out[54] mult_36.out[70] mult_36.out[62] mult_36.out[55] mult_36.out[71] mult_36.out[63] mult_36.out[56] mult_36.out[64] mult_36.out[57] mult_36.out[65] + + + mult_36.out[58] mult_36.out[66] mult_36.out[59] mult_36.out[67] mult_36.out[60] mult_36.out[68] mult_36.out[61] mult_36.out[69] + + + mult_36.out[54] mult_36.out[70] mult_36.out[62] mult_36.out[55] mult_36.out[71] mult_36.out[63] mult_36.out[56] mult_36.out[64] mult_36.out[57] mult_36.out[65] + + + mult_36.out[58] mult_36.out[66] mult_36.out[59] mult_36.out[67] mult_36.out[60] mult_36.out[68] mult_36.out[61] mult_36.out[69] + + + mult_36.out[54] mult_36.out[70] mult_36.out[62] mult_36.out[55] mult_36.out[71] mult_36.out[63] mult_36.out[56] mult_36.out[64] mult_36.out[57] mult_36.out[65] + + + mult_36.out[58] mult_36.out[66] mult_36.out[59] mult_36.out[67] mult_36.out[60] mult_36.out[68] mult_36.out[61] mult_36.out[69] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 mult_36.out[54] mult_36.out[70] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 mult_36.out[55] mult_36.out[71] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 mult_36.out[56] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 mult_36.out[57] + + + l1.N1 l6.N1 l6.N18 l8.N11 mult_36.out[58] + + + l1.E1 l6.E1 l6.E18 l8.E11 mult_36.out[59] + + + l1.S1 l6.S1 l6.S18 l8.S11 mult_36.out[60] + + + l1.W1 l6.W1 l6.W18 l8.W11 mult_36.out[61] + + + l1.N2 l6.N2 l6.N19 l8.N12 mult_36.out[62] + + + l1.E2 l6.E2 l6.E19 l8.E12 mult_36.out[63] + + + l1.S2 l6.S2 l6.S19 l8.S12 mult_36.out[64] + + + l1.W2 l6.W2 l6.W19 l8.W12 mult_36.out[65] + + + l1.N3 l6.N3 l6.N20 l8.N13 mult_36.out[66] + + + l1.E3 l6.E3 l6.E20 l8.E13 mult_36.out[67] + + + l1.S3 l6.S3 l6.S20 l8.S13 mult_36.out[68] + + + l1.W3 l6.W3 l6.W20 l8.W13 mult_36.out[69] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 mult_36.out[54] mult_36.out[70] + + + l2.E3 l6.E8 l8.E1 l12.E2 mult_36.out[55] mult_36.out[71] + + + l2.S3 l6.S8 l8.S1 l12.S2 mult_36.out[56] + + + l2.W3 l6.W8 l8.W1 l12.W2 mult_36.out[57] + + + l2.N4 l6.N9 l8.N2 l12.N3 mult_36.out[58] + + + l2.E4 l6.E9 l8.E2 l12.E3 mult_36.out[59] + + + l2.S4 l6.S9 l8.S2 l12.S3 mult_36.out[60] + + + l2.W4 l6.W9 l8.W2 l12.W3 mult_36.out[61] + + + l2.N5 l6.N10 l8.N3 l12.N4 mult_36.out[62] + + + l2.E5 l6.E10 l8.E3 l12.E4 mult_36.out[63] + + + l2.S5 l6.S10 l8.S3 l12.S4 mult_36.out[64] + + + l2.W5 l6.W10 l8.W3 l12.W4 mult_36.out[65] + + + l3.N0 l6.N11 l8.N4 l12.N5 mult_36.out[66] + + + l3.E0 l6.E11 l8.E4 l12.E5 mult_36.out[67] + + + l3.S0 l6.S11 l8.S4 l12.S5 mult_36.out[68] + + + l3.W0 l6.W11 l8.W4 l12.W5 mult_36.out[69] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 mult_36.out[54] mult_36.out[70] + + + l3.E5 l6.E16 l8.E9 l12.E10 mult_36.out[55] mult_36.out[71] + + + l3.S5 l6.S16 l8.S9 l12.S10 mult_36.out[56] + + + l3.W5 l6.W16 l8.W9 l12.W10 mult_36.out[57] + + + l1.N0 l3.N0 l6.N5 l6.N16 mult_36.out[54] mult_36.out[70] + + + l1.E0 l3.E0 l6.E5 l6.E16 mult_36.out[55] mult_36.out[71] + + + l1.S0 l3.S0 l6.S5 l6.S16 mult_36.out[56] + + + l1.W0 l3.W0 l6.W5 l6.W16 mult_36.out[57] + + + l1.N1 l3.N1 l6.N6 l6.N17 mult_36.out[58] + + + l1.E1 l3.E1 l6.E6 l6.E17 mult_36.out[59] + + + l1.S1 l3.S1 l6.S6 l6.S17 mult_36.out[60] + + + l1.W1 l3.W1 l6.W6 l6.W17 mult_36.out[61] + + + l1.N2 l3.N2 l6.N7 l6.N18 mult_36.out[62] + + + l1.E2 l3.E2 l6.E7 l6.E18 mult_36.out[63] + + + l1.S2 l3.S2 l6.S7 l6.S18 mult_36.out[64] + + + l1.W2 l3.W2 l6.W7 l6.W18 mult_36.out[65] + + + l1.N3 l3.N3 l6.N8 l6.N19 mult_36.out[66] + + + l1.E3 l3.E3 l6.E8 l6.E19 mult_36.out[67] + + + l1.S3 l3.S3 l6.S8 l6.S19 mult_36.out[68] + + + l1.W3 l3.W3 l6.W8 l6.W19 mult_36.out[69] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + mult_36.a[27] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + mult_36.a[33] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + mult_36.b[30] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + mult_36.a[27] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + mult_36.a[33] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + mult_36.b[30] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + mult_36.a[27] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + mult_36.a[33] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + mult_36.a[28] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + mult_36.a[34] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + mult_36.b[31] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + mult_36.a[28] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + mult_36.a[34] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + mult_36.b[31] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + mult_36.a[28] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + mult_36.a[34] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + mult_36.a[29] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + mult_36.a[35] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + mult_36.b[32] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + mult_36.a[29] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + mult_36.a[35] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + mult_36.b[32] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + mult_36.a[29] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + mult_36.a[35] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + mult_36.a[30] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + mult_36.b[27] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + mult_36.b[33] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + mult_36.a[30] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + mult_36.b[27] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + mult_36.b[33] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + mult_36.a[30] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + mult_36.b[27] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + mult_36.a[31] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + mult_36.b[28] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + mult_36.b[34] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + mult_36.a[31] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + mult_36.b[28] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + mult_36.b[34] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + mult_36.a[31] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + mult_36.b[28] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + mult_36.a[32] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + mult_36.b[29] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + mult_36.b[35] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + mult_36.a[32] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + mult_36.b[29] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + mult_36.b[35] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + mult_36.a[32] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + mult_36.b[29] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + mult_36.out[36] mult_36.out[52] mult_36.out[44] mult_36.out[37] mult_36.out[53] mult_36.out[45] mult_36.out[38] mult_36.out[46] mult_36.out[39] mult_36.out[47] + + + mult_36.out[40] mult_36.out[48] mult_36.out[41] mult_36.out[49] mult_36.out[42] mult_36.out[50] mult_36.out[43] mult_36.out[51] + + + mult_36.out[36] mult_36.out[52] mult_36.out[44] mult_36.out[37] mult_36.out[53] mult_36.out[45] mult_36.out[38] mult_36.out[46] mult_36.out[39] mult_36.out[47] + + + mult_36.out[40] mult_36.out[48] mult_36.out[41] mult_36.out[49] mult_36.out[42] mult_36.out[50] mult_36.out[43] mult_36.out[51] + + + mult_36.out[36] mult_36.out[52] mult_36.out[44] mult_36.out[37] mult_36.out[53] mult_36.out[45] mult_36.out[38] mult_36.out[46] mult_36.out[39] mult_36.out[47] + + + mult_36.out[40] mult_36.out[48] mult_36.out[41] mult_36.out[49] mult_36.out[42] mult_36.out[50] mult_36.out[43] mult_36.out[51] + + + mult_36.out[36] mult_36.out[52] mult_36.out[44] mult_36.out[37] mult_36.out[53] mult_36.out[45] mult_36.out[38] mult_36.out[46] mult_36.out[39] mult_36.out[47] + + + mult_36.out[40] mult_36.out[48] mult_36.out[41] mult_36.out[49] mult_36.out[42] mult_36.out[50] mult_36.out[43] mult_36.out[51] + + + mult_36.out[36] mult_36.out[52] mult_36.out[44] mult_36.out[37] mult_36.out[53] mult_36.out[45] mult_36.out[38] mult_36.out[46] mult_36.out[39] mult_36.out[47] + + + mult_36.out[40] mult_36.out[48] mult_36.out[41] mult_36.out[49] mult_36.out[42] mult_36.out[50] mult_36.out[43] mult_36.out[51] + + + mult_36.out[36] mult_36.out[52] mult_36.out[44] mult_36.out[37] mult_36.out[53] mult_36.out[45] mult_36.out[38] mult_36.out[46] mult_36.out[39] mult_36.out[47] + + + mult_36.out[40] mult_36.out[48] mult_36.out[41] mult_36.out[49] mult_36.out[42] mult_36.out[50] mult_36.out[43] mult_36.out[51] + + + mult_36.out[36] mult_36.out[52] mult_36.out[44] mult_36.out[37] mult_36.out[53] mult_36.out[45] mult_36.out[38] mult_36.out[46] mult_36.out[39] mult_36.out[47] + + + mult_36.out[40] mult_36.out[48] mult_36.out[41] mult_36.out[49] mult_36.out[42] mult_36.out[50] mult_36.out[43] mult_36.out[51] + + + mult_36.out[36] mult_36.out[52] mult_36.out[44] mult_36.out[37] mult_36.out[53] mult_36.out[45] mult_36.out[38] mult_36.out[46] mult_36.out[39] mult_36.out[47] + + + mult_36.out[40] mult_36.out[48] mult_36.out[41] mult_36.out[49] mult_36.out[42] mult_36.out[50] mult_36.out[43] mult_36.out[51] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 mult_36.out[36] mult_36.out[52] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 mult_36.out[37] mult_36.out[53] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 mult_36.out[38] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 mult_36.out[39] + + + l1.N1 l6.N1 l6.N18 l8.N11 mult_36.out[40] + + + l1.E1 l6.E1 l6.E18 l8.E11 mult_36.out[41] + + + l1.S1 l6.S1 l6.S18 l8.S11 mult_36.out[42] + + + l1.W1 l6.W1 l6.W18 l8.W11 mult_36.out[43] + + + l1.N2 l6.N2 l6.N19 l8.N12 mult_36.out[44] + + + l1.E2 l6.E2 l6.E19 l8.E12 mult_36.out[45] + + + l1.S2 l6.S2 l6.S19 l8.S12 mult_36.out[46] + + + l1.W2 l6.W2 l6.W19 l8.W12 mult_36.out[47] + + + l1.N3 l6.N3 l6.N20 l8.N13 mult_36.out[48] + + + l1.E3 l6.E3 l6.E20 l8.E13 mult_36.out[49] + + + l1.S3 l6.S3 l6.S20 l8.S13 mult_36.out[50] + + + l1.W3 l6.W3 l6.W20 l8.W13 mult_36.out[51] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 mult_36.out[36] mult_36.out[52] + + + l2.E3 l6.E8 l8.E1 l12.E2 mult_36.out[37] mult_36.out[53] + + + l2.S3 l6.S8 l8.S1 l12.S2 mult_36.out[38] + + + l2.W3 l6.W8 l8.W1 l12.W2 mult_36.out[39] + + + l2.N4 l6.N9 l8.N2 l12.N3 mult_36.out[40] + + + l2.E4 l6.E9 l8.E2 l12.E3 mult_36.out[41] + + + l2.S4 l6.S9 l8.S2 l12.S3 mult_36.out[42] + + + l2.W4 l6.W9 l8.W2 l12.W3 mult_36.out[43] + + + l2.N5 l6.N10 l8.N3 l12.N4 mult_36.out[44] + + + l2.E5 l6.E10 l8.E3 l12.E4 mult_36.out[45] + + + l2.S5 l6.S10 l8.S3 l12.S4 mult_36.out[46] + + + l2.W5 l6.W10 l8.W3 l12.W4 mult_36.out[47] + + + l3.N0 l6.N11 l8.N4 l12.N5 mult_36.out[48] + + + l3.E0 l6.E11 l8.E4 l12.E5 mult_36.out[49] + + + l3.S0 l6.S11 l8.S4 l12.S5 mult_36.out[50] + + + l3.W0 l6.W11 l8.W4 l12.W5 mult_36.out[51] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 mult_36.out[36] mult_36.out[52] + + + l3.E5 l6.E16 l8.E9 l12.E10 mult_36.out[37] mult_36.out[53] + + + l3.S5 l6.S16 l8.S9 l12.S10 mult_36.out[38] + + + l3.W5 l6.W16 l8.W9 l12.W10 mult_36.out[39] + + + l1.N0 l3.N0 l6.N5 l6.N16 mult_36.out[36] mult_36.out[52] + + + l1.E0 l3.E0 l6.E5 l6.E16 mult_36.out[37] mult_36.out[53] + + + l1.S0 l3.S0 l6.S5 l6.S16 mult_36.out[38] + + + l1.W0 l3.W0 l6.W5 l6.W16 mult_36.out[39] + + + l1.N1 l3.N1 l6.N6 l6.N17 mult_36.out[40] + + + l1.E1 l3.E1 l6.E6 l6.E17 mult_36.out[41] + + + l1.S1 l3.S1 l6.S6 l6.S17 mult_36.out[42] + + + l1.W1 l3.W1 l6.W6 l6.W17 mult_36.out[43] + + + l1.N2 l3.N2 l6.N7 l6.N18 mult_36.out[44] + + + l1.E2 l3.E2 l6.E7 l6.E18 mult_36.out[45] + + + l1.S2 l3.S2 l6.S7 l6.S18 mult_36.out[46] + + + l1.W2 l3.W2 l6.W7 l6.W18 mult_36.out[47] + + + l1.N3 l3.N3 l6.N8 l6.N19 mult_36.out[48] + + + l1.E3 l3.E3 l6.E8 l6.E19 mult_36.out[49] + + + l1.S3 l3.S3 l6.S8 l6.S19 mult_36.out[50] + + + l1.W3 l3.W3 l6.W8 l6.W19 mult_36.out[51] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + mult_36.a[18] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + mult_36.a[24] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + mult_36.b[21] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + mult_36.a[18] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + mult_36.a[24] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + mult_36.b[21] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + mult_36.a[18] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + mult_36.a[24] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + mult_36.a[19] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + mult_36.a[25] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + mult_36.b[22] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + mult_36.a[19] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + mult_36.a[25] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + mult_36.b[22] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + mult_36.a[19] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + mult_36.a[25] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + mult_36.a[20] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + mult_36.a[26] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + mult_36.b[23] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + mult_36.a[20] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + mult_36.a[26] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + mult_36.b[23] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + mult_36.a[20] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + mult_36.a[26] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + mult_36.a[21] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + mult_36.b[18] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + mult_36.b[24] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + mult_36.a[21] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + mult_36.b[18] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + mult_36.b[24] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + mult_36.a[21] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + mult_36.b[18] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + mult_36.a[22] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + mult_36.b[19] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + mult_36.b[25] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + mult_36.a[22] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + mult_36.b[19] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + mult_36.b[25] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + mult_36.a[22] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + mult_36.b[19] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + mult_36.a[23] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + mult_36.b[20] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + mult_36.b[26] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + mult_36.a[23] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + mult_36.b[20] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + mult_36.b[26] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + mult_36.a[23] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + mult_36.b[20] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + mult_36.out[18] mult_36.out[34] mult_36.out[26] mult_36.out[19] mult_36.out[35] mult_36.out[27] mult_36.out[20] mult_36.out[28] mult_36.out[21] mult_36.out[29] + + + mult_36.out[22] mult_36.out[30] mult_36.out[23] mult_36.out[31] mult_36.out[24] mult_36.out[32] mult_36.out[25] mult_36.out[33] + + + mult_36.out[18] mult_36.out[34] mult_36.out[26] mult_36.out[19] mult_36.out[35] mult_36.out[27] mult_36.out[20] mult_36.out[28] mult_36.out[21] mult_36.out[29] + + + mult_36.out[22] mult_36.out[30] mult_36.out[23] mult_36.out[31] mult_36.out[24] mult_36.out[32] mult_36.out[25] mult_36.out[33] + + + mult_36.out[18] mult_36.out[34] mult_36.out[26] mult_36.out[19] mult_36.out[35] mult_36.out[27] mult_36.out[20] mult_36.out[28] mult_36.out[21] mult_36.out[29] + + + mult_36.out[22] mult_36.out[30] mult_36.out[23] mult_36.out[31] mult_36.out[24] mult_36.out[32] mult_36.out[25] mult_36.out[33] + + + mult_36.out[18] mult_36.out[34] mult_36.out[26] mult_36.out[19] mult_36.out[35] mult_36.out[27] mult_36.out[20] mult_36.out[28] mult_36.out[21] mult_36.out[29] + + + mult_36.out[22] mult_36.out[30] mult_36.out[23] mult_36.out[31] mult_36.out[24] mult_36.out[32] mult_36.out[25] mult_36.out[33] + + + mult_36.out[18] mult_36.out[34] mult_36.out[26] mult_36.out[19] mult_36.out[35] mult_36.out[27] mult_36.out[20] mult_36.out[28] mult_36.out[21] mult_36.out[29] + + + mult_36.out[22] mult_36.out[30] mult_36.out[23] mult_36.out[31] mult_36.out[24] mult_36.out[32] mult_36.out[25] mult_36.out[33] + + + mult_36.out[18] mult_36.out[34] mult_36.out[26] mult_36.out[19] mult_36.out[35] mult_36.out[27] mult_36.out[20] mult_36.out[28] mult_36.out[21] mult_36.out[29] + + + mult_36.out[22] mult_36.out[30] mult_36.out[23] mult_36.out[31] mult_36.out[24] mult_36.out[32] mult_36.out[25] mult_36.out[33] + + + mult_36.out[18] mult_36.out[34] mult_36.out[26] mult_36.out[19] mult_36.out[35] mult_36.out[27] mult_36.out[20] mult_36.out[28] mult_36.out[21] mult_36.out[29] + + + mult_36.out[22] mult_36.out[30] mult_36.out[23] mult_36.out[31] mult_36.out[24] mult_36.out[32] mult_36.out[25] mult_36.out[33] + + + mult_36.out[18] mult_36.out[34] mult_36.out[26] mult_36.out[19] mult_36.out[35] mult_36.out[27] mult_36.out[20] mult_36.out[28] mult_36.out[21] mult_36.out[29] + + + mult_36.out[22] mult_36.out[30] mult_36.out[23] mult_36.out[31] mult_36.out[24] mult_36.out[32] mult_36.out[25] mult_36.out[33] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 mult_36.out[18] mult_36.out[34] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 mult_36.out[19] mult_36.out[35] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 mult_36.out[20] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 mult_36.out[21] + + + l1.N1 l6.N1 l6.N18 l8.N11 mult_36.out[22] + + + l1.E1 l6.E1 l6.E18 l8.E11 mult_36.out[23] + + + l1.S1 l6.S1 l6.S18 l8.S11 mult_36.out[24] + + + l1.W1 l6.W1 l6.W18 l8.W11 mult_36.out[25] + + + l1.N2 l6.N2 l6.N19 l8.N12 mult_36.out[26] + + + l1.E2 l6.E2 l6.E19 l8.E12 mult_36.out[27] + + + l1.S2 l6.S2 l6.S19 l8.S12 mult_36.out[28] + + + l1.W2 l6.W2 l6.W19 l8.W12 mult_36.out[29] + + + l1.N3 l6.N3 l6.N20 l8.N13 mult_36.out[30] + + + l1.E3 l6.E3 l6.E20 l8.E13 mult_36.out[31] + + + l1.S3 l6.S3 l6.S20 l8.S13 mult_36.out[32] + + + l1.W3 l6.W3 l6.W20 l8.W13 mult_36.out[33] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 mult_36.out[18] mult_36.out[34] + + + l2.E3 l6.E8 l8.E1 l12.E2 mult_36.out[19] mult_36.out[35] + + + l2.S3 l6.S8 l8.S1 l12.S2 mult_36.out[20] + + + l2.W3 l6.W8 l8.W1 l12.W2 mult_36.out[21] + + + l2.N4 l6.N9 l8.N2 l12.N3 mult_36.out[22] + + + l2.E4 l6.E9 l8.E2 l12.E3 mult_36.out[23] + + + l2.S4 l6.S9 l8.S2 l12.S3 mult_36.out[24] + + + l2.W4 l6.W9 l8.W2 l12.W3 mult_36.out[25] + + + l2.N5 l6.N10 l8.N3 l12.N4 mult_36.out[26] + + + l2.E5 l6.E10 l8.E3 l12.E4 mult_36.out[27] + + + l2.S5 l6.S10 l8.S3 l12.S4 mult_36.out[28] + + + l2.W5 l6.W10 l8.W3 l12.W4 mult_36.out[29] + + + l3.N0 l6.N11 l8.N4 l12.N5 mult_36.out[30] + + + l3.E0 l6.E11 l8.E4 l12.E5 mult_36.out[31] + + + l3.S0 l6.S11 l8.S4 l12.S5 mult_36.out[32] + + + l3.W0 l6.W11 l8.W4 l12.W5 mult_36.out[33] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 mult_36.out[18] mult_36.out[34] + + + l3.E5 l6.E16 l8.E9 l12.E10 mult_36.out[19] mult_36.out[35] + + + l3.S5 l6.S16 l8.S9 l12.S10 mult_36.out[20] + + + l3.W5 l6.W16 l8.W9 l12.W10 mult_36.out[21] + + + l1.N0 l3.N0 l6.N5 l6.N16 mult_36.out[18] mult_36.out[34] + + + l1.E0 l3.E0 l6.E5 l6.E16 mult_36.out[19] mult_36.out[35] + + + l1.S0 l3.S0 l6.S5 l6.S16 mult_36.out[20] + + + l1.W0 l3.W0 l6.W5 l6.W16 mult_36.out[21] + + + l1.N1 l3.N1 l6.N6 l6.N17 mult_36.out[22] + + + l1.E1 l3.E1 l6.E6 l6.E17 mult_36.out[23] + + + l1.S1 l3.S1 l6.S6 l6.S17 mult_36.out[24] + + + l1.W1 l3.W1 l6.W6 l6.W17 mult_36.out[25] + + + l1.N2 l3.N2 l6.N7 l6.N18 mult_36.out[26] + + + l1.E2 l3.E2 l6.E7 l6.E18 mult_36.out[27] + + + l1.S2 l3.S2 l6.S7 l6.S18 mult_36.out[28] + + + l1.W2 l3.W2 l6.W7 l6.W18 mult_36.out[29] + + + l1.N3 l3.N3 l6.N8 l6.N19 mult_36.out[30] + + + l1.E3 l3.E3 l6.E8 l6.E19 mult_36.out[31] + + + l1.S3 l3.S3 l6.S8 l6.S19 mult_36.out[32] + + + l1.W3 l3.W3 l6.W8 l6.W19 mult_36.out[33] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + mult_36.a[9] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + mult_36.a[15] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + mult_36.b[12] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + mult_36.a[9] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + mult_36.a[15] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + mult_36.b[12] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + mult_36.a[9] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + mult_36.a[15] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + mult_36.a[10] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + mult_36.a[16] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + mult_36.b[13] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + mult_36.a[10] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + mult_36.a[16] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + mult_36.b[13] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + mult_36.a[10] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + mult_36.a[16] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + mult_36.a[11] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + mult_36.a[17] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + mult_36.b[14] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + mult_36.a[11] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + mult_36.a[17] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + mult_36.b[14] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + mult_36.a[11] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + mult_36.a[17] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + mult_36.a[12] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + mult_36.b[9] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + mult_36.b[15] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + mult_36.a[12] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + mult_36.b[9] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + mult_36.b[15] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + mult_36.a[12] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + mult_36.b[9] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + mult_36.a[13] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + mult_36.b[10] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + mult_36.b[16] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + mult_36.a[13] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + mult_36.b[10] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + mult_36.b[16] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + mult_36.a[13] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + mult_36.b[10] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + mult_36.a[14] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + mult_36.b[11] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + mult_36.b[17] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + mult_36.a[14] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + mult_36.b[11] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + mult_36.b[17] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + mult_36.a[14] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + mult_36.b[11] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + mult_36.out[0] mult_36.out[16] mult_36.out[8] mult_36.out[1] mult_36.out[17] mult_36.out[9] mult_36.out[2] mult_36.out[10] mult_36.out[3] mult_36.out[11] + + + mult_36.out[4] mult_36.out[12] mult_36.out[5] mult_36.out[13] mult_36.out[6] mult_36.out[14] mult_36.out[7] mult_36.out[15] + + + mult_36.out[0] mult_36.out[16] mult_36.out[8] mult_36.out[1] mult_36.out[17] mult_36.out[9] mult_36.out[2] mult_36.out[10] mult_36.out[3] mult_36.out[11] + + + mult_36.out[4] mult_36.out[12] mult_36.out[5] mult_36.out[13] mult_36.out[6] mult_36.out[14] mult_36.out[7] mult_36.out[15] + + + mult_36.out[0] mult_36.out[16] mult_36.out[8] mult_36.out[1] mult_36.out[17] mult_36.out[9] mult_36.out[2] mult_36.out[10] mult_36.out[3] mult_36.out[11] + + + mult_36.out[4] mult_36.out[12] mult_36.out[5] mult_36.out[13] mult_36.out[6] mult_36.out[14] mult_36.out[7] mult_36.out[15] + + + mult_36.out[0] mult_36.out[16] mult_36.out[8] mult_36.out[1] mult_36.out[17] mult_36.out[9] mult_36.out[2] mult_36.out[10] mult_36.out[3] mult_36.out[11] + + + mult_36.out[4] mult_36.out[12] mult_36.out[5] mult_36.out[13] mult_36.out[6] mult_36.out[14] mult_36.out[7] mult_36.out[15] + + + mult_36.out[0] mult_36.out[16] mult_36.out[8] mult_36.out[1] mult_36.out[17] mult_36.out[9] mult_36.out[2] mult_36.out[10] mult_36.out[3] mult_36.out[11] + + + mult_36.out[4] mult_36.out[12] mult_36.out[5] mult_36.out[13] mult_36.out[6] mult_36.out[14] mult_36.out[7] mult_36.out[15] + + + mult_36.out[0] mult_36.out[16] mult_36.out[8] mult_36.out[1] mult_36.out[17] mult_36.out[9] mult_36.out[2] mult_36.out[10] mult_36.out[3] mult_36.out[11] + + + mult_36.out[4] mult_36.out[12] mult_36.out[5] mult_36.out[13] mult_36.out[6] mult_36.out[14] mult_36.out[7] mult_36.out[15] + + + mult_36.out[0] mult_36.out[16] mult_36.out[8] mult_36.out[1] mult_36.out[17] mult_36.out[9] mult_36.out[2] mult_36.out[10] mult_36.out[3] mult_36.out[11] + + + mult_36.out[4] mult_36.out[12] mult_36.out[5] mult_36.out[13] mult_36.out[6] mult_36.out[14] mult_36.out[7] mult_36.out[15] + + + mult_36.out[0] mult_36.out[16] mult_36.out[8] mult_36.out[1] mult_36.out[17] mult_36.out[9] mult_36.out[2] mult_36.out[10] mult_36.out[3] mult_36.out[11] + + + mult_36.out[4] mult_36.out[12] mult_36.out[5] mult_36.out[13] mult_36.out[6] mult_36.out[14] mult_36.out[7] mult_36.out[15] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 mult_36.out[0] mult_36.out[16] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 mult_36.out[1] mult_36.out[17] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 mult_36.out[2] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 mult_36.out[3] + + + l1.N1 l6.N1 l6.N18 l8.N11 mult_36.out[4] + + + l1.E1 l6.E1 l6.E18 l8.E11 mult_36.out[5] + + + l1.S1 l6.S1 l6.S18 l8.S11 mult_36.out[6] + + + l1.W1 l6.W1 l6.W18 l8.W11 mult_36.out[7] + + + l1.N2 l6.N2 l6.N19 l8.N12 mult_36.out[8] + + + l1.E2 l6.E2 l6.E19 l8.E12 mult_36.out[9] + + + l1.S2 l6.S2 l6.S19 l8.S12 mult_36.out[10] + + + l1.W2 l6.W2 l6.W19 l8.W12 mult_36.out[11] + + + l1.N3 l6.N3 l6.N20 l8.N13 mult_36.out[12] + + + l1.E3 l6.E3 l6.E20 l8.E13 mult_36.out[13] + + + l1.S3 l6.S3 l6.S20 l8.S13 mult_36.out[14] + + + l1.W3 l6.W3 l6.W20 l8.W13 mult_36.out[15] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 mult_36.out[0] mult_36.out[16] + + + l2.E3 l6.E8 l8.E1 l12.E2 mult_36.out[1] mult_36.out[17] + + + l2.S3 l6.S8 l8.S1 l12.S2 mult_36.out[2] + + + l2.W3 l6.W8 l8.W1 l12.W2 mult_36.out[3] + + + l2.N4 l6.N9 l8.N2 l12.N3 mult_36.out[4] + + + l2.E4 l6.E9 l8.E2 l12.E3 mult_36.out[5] + + + l2.S4 l6.S9 l8.S2 l12.S3 mult_36.out[6] + + + l2.W4 l6.W9 l8.W2 l12.W3 mult_36.out[7] + + + l2.N5 l6.N10 l8.N3 l12.N4 mult_36.out[8] + + + l2.E5 l6.E10 l8.E3 l12.E4 mult_36.out[9] + + + l2.S5 l6.S10 l8.S3 l12.S4 mult_36.out[10] + + + l2.W5 l6.W10 l8.W3 l12.W4 mult_36.out[11] + + + l3.N0 l6.N11 l8.N4 l12.N5 mult_36.out[12] + + + l3.E0 l6.E11 l8.E4 l12.E5 mult_36.out[13] + + + l3.S0 l6.S11 l8.S4 l12.S5 mult_36.out[14] + + + l3.W0 l6.W11 l8.W4 l12.W5 mult_36.out[15] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 mult_36.out[0] mult_36.out[16] + + + l3.E5 l6.E16 l8.E9 l12.E10 mult_36.out[1] mult_36.out[17] + + + l3.S5 l6.S16 l8.S9 l12.S10 mult_36.out[2] + + + l3.W5 l6.W16 l8.W9 l12.W10 mult_36.out[3] + + + l1.N0 l3.N0 l6.N5 l6.N16 mult_36.out[0] mult_36.out[16] + + + l1.E0 l3.E0 l6.E5 l6.E16 mult_36.out[1] mult_36.out[17] + + + l1.S0 l3.S0 l6.S5 l6.S16 mult_36.out[2] + + + l1.W0 l3.W0 l6.W5 l6.W16 mult_36.out[3] + + + l1.N1 l3.N1 l6.N6 l6.N17 mult_36.out[4] + + + l1.E1 l3.E1 l6.E6 l6.E17 mult_36.out[5] + + + l1.S1 l3.S1 l6.S6 l6.S17 mult_36.out[6] + + + l1.W1 l3.W1 l6.W6 l6.W17 mult_36.out[7] + + + l1.N2 l3.N2 l6.N7 l6.N18 mult_36.out[8] + + + l1.E2 l3.E2 l6.E7 l6.E18 mult_36.out[9] + + + l1.S2 l3.S2 l6.S7 l6.S18 mult_36.out[10] + + + l1.W2 l3.W2 l6.W7 l6.W18 mult_36.out[11] + + + l1.N3 l3.N3 l6.N8 l6.N19 mult_36.out[12] + + + l1.E3 l3.E3 l6.E8 l6.E19 mult_36.out[13] + + + l1.S3 l3.S3 l6.S8 l6.S19 mult_36.out[14] + + + l1.W3 l3.W3 l6.W8 l6.W19 mult_36.out[15] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + mult_36.a[0] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + mult_36.a[6] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + mult_36.b[3] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + mult_36.a[0] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + mult_36.a[6] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + mult_36.b[3] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + mult_36.a[0] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + mult_36.a[6] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + mult_36.a[1] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + mult_36.a[7] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + mult_36.b[4] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + mult_36.a[1] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + mult_36.a[7] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + mult_36.b[4] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + mult_36.a[1] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + mult_36.a[7] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + mult_36.a[2] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + mult_36.a[8] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + mult_36.b[5] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + mult_36.a[2] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + mult_36.a[8] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + mult_36.b[5] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + mult_36.a[2] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + mult_36.a[8] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + mult_36.a[3] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + mult_36.b[0] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + mult_36.b[6] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + mult_36.a[3] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + mult_36.b[0] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + mult_36.b[6] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + mult_36.a[3] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + mult_36.b[0] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + mult_36.a[4] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + mult_36.b[1] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + mult_36.b[7] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + mult_36.a[4] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + mult_36.b[1] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + mult_36.b[7] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + mult_36.a[4] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + mult_36.b[1] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + mult_36.a[5] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + mult_36.b[2] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + mult_36.b[8] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + mult_36.a[5] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + mult_36.b[2] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + mult_36.b[8] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + mult_36.a[5] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + mult_36.b[2] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + memory.out[54] memory.out[62] memory.out[55] memory.out[63] memory.out[56] memory.out[54] memory.out[57] memory.out[55] + + + memory.out[58] memory.out[56] memory.out[59] memory.out[57] memory.out[60] memory.out[58] memory.out[61] memory.out[59] + + + memory.out[54] memory.out[62] memory.out[55] memory.out[63] memory.out[56] memory.out[54] memory.out[57] memory.out[55] + + + memory.out[58] memory.out[56] memory.out[59] memory.out[57] memory.out[60] memory.out[58] memory.out[61] memory.out[59] + + + memory.out[54] memory.out[62] memory.out[55] memory.out[63] memory.out[56] memory.out[54] memory.out[57] memory.out[55] + + + memory.out[58] memory.out[56] memory.out[59] memory.out[57] memory.out[60] memory.out[58] memory.out[61] memory.out[59] + + + memory.out[54] memory.out[62] memory.out[55] memory.out[63] memory.out[56] memory.out[54] memory.out[57] memory.out[55] + + + memory.out[58] memory.out[56] memory.out[59] memory.out[57] memory.out[60] memory.out[58] memory.out[61] memory.out[59] + + + memory.out[54] memory.out[62] memory.out[55] memory.out[63] memory.out[56] memory.out[54] memory.out[57] memory.out[55] + + + memory.out[58] memory.out[56] memory.out[59] memory.out[57] memory.out[60] memory.out[58] memory.out[61] memory.out[59] + + + memory.out[54] memory.out[62] memory.out[55] memory.out[63] memory.out[56] memory.out[54] memory.out[57] memory.out[55] + + + memory.out[58] memory.out[56] memory.out[59] memory.out[57] memory.out[60] memory.out[58] memory.out[61] memory.out[59] + + + memory.out[54] memory.out[62] memory.out[55] memory.out[63] memory.out[56] memory.out[54] memory.out[57] memory.out[55] + + + memory.out[58] memory.out[56] memory.out[59] memory.out[57] memory.out[60] memory.out[58] memory.out[61] memory.out[59] + + + memory.out[54] memory.out[62] memory.out[55] memory.out[63] memory.out[56] memory.out[54] memory.out[57] memory.out[55] + + + memory.out[58] memory.out[56] memory.out[59] memory.out[57] memory.out[60] memory.out[58] memory.out[61] memory.out[59] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 memory.out[54] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 memory.out[55] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 memory.out[56] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 memory.out[57] + + + l1.N1 l6.N1 l6.N18 l8.N11 memory.out[58] + + + l1.E1 l6.E1 l6.E18 l8.E11 memory.out[59] + + + l1.S1 l6.S1 l6.S18 l8.S11 memory.out[60] + + + l1.W1 l6.W1 l6.W18 l8.W11 memory.out[61] + + + l1.N2 l6.N2 l6.N19 l8.N12 memory.out[62] + + + l1.E2 l6.E2 l6.E19 l8.E12 memory.out[63] + + + l1.S2 l6.S2 l6.S19 l8.S12 memory.out[54] + + + l1.W2 l6.W2 l6.W19 l8.W12 memory.out[55] + + + l1.N3 l6.N3 l6.N20 l8.N13 memory.out[56] + + + l1.E3 l6.E3 l6.E20 l8.E13 memory.out[57] + + + l1.S3 l6.S3 l6.S20 l8.S13 memory.out[58] + + + l1.W3 l6.W3 l6.W20 l8.W13 memory.out[59] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 memory.out[54] + + + l2.E3 l6.E8 l8.E1 l12.E2 memory.out[55] + + + l2.S3 l6.S8 l8.S1 l12.S2 memory.out[56] + + + l2.W3 l6.W8 l8.W1 l12.W2 memory.out[57] + + + l2.N4 l6.N9 l8.N2 l12.N3 memory.out[58] + + + l2.E4 l6.E9 l8.E2 l12.E3 memory.out[59] + + + l2.S4 l6.S9 l8.S2 l12.S3 memory.out[60] + + + l2.W4 l6.W9 l8.W2 l12.W3 memory.out[61] + + + l2.N5 l6.N10 l8.N3 l12.N4 memory.out[62] + + + l2.E5 l6.E10 l8.E3 l12.E4 memory.out[63] + + + l2.S5 l6.S10 l8.S3 l12.S4 memory.out[54] + + + l2.W5 l6.W10 l8.W3 l12.W4 memory.out[55] + + + l3.N0 l6.N11 l8.N4 l12.N5 memory.out[56] + + + l3.E0 l6.E11 l8.E4 l12.E5 memory.out[57] + + + l3.S0 l6.S11 l8.S4 l12.S5 memory.out[58] + + + l3.W0 l6.W11 l8.W4 l12.W5 memory.out[59] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 memory.out[54] + + + l3.E5 l6.E16 l8.E9 l12.E10 memory.out[55] + + + l3.S5 l6.S16 l8.S9 l12.S10 memory.out[56] + + + l3.W5 l6.W16 l8.W9 l12.W10 memory.out[57] + + + l1.N0 l3.N0 l6.N5 l6.N16 memory.out[54] + + + l1.E0 l3.E0 l6.E5 l6.E16 memory.out[55] + + + l1.S0 l3.S0 l6.S5 l6.S16 memory.out[56] + + + l1.W0 l3.W0 l6.W5 l6.W16 memory.out[57] + + + l1.N1 l3.N1 l6.N6 l6.N17 memory.out[58] + + + l1.E1 l3.E1 l6.E6 l6.E17 memory.out[59] + + + l1.S1 l3.S1 l6.S6 l6.S17 memory.out[60] + + + l1.W1 l3.W1 l6.W6 l6.W17 memory.out[61] + + + l1.N2 l3.N2 l6.N7 l6.N18 memory.out[62] + + + l1.E2 l3.E2 l6.E7 l6.E18 memory.out[63] + + + l1.S2 l3.S2 l6.S7 l6.S18 memory.out[54] + + + l1.W2 l3.W2 l6.W7 l6.W18 memory.out[55] + + + l1.N3 l3.N3 l6.N8 l6.N19 memory.out[56] + + + l1.E3 l3.E3 l6.E8 l6.E19 memory.out[57] + + + l1.S3 l3.S3 l6.S8 l6.S19 memory.out[58] + + + l1.W3 l3.W3 l6.W8 l6.W19 memory.out[59] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + memory.we1[0] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + memory.data[54] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + memory.data[60] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + memory.data[50] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + memory.data[56] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + memory.data[62] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + memory.data[52] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + memory.data[58] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + memory.we2[0] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + memory.data[55] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + memory.data[61] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + memory.data[51] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + memory.data[57] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + memory.data[63] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + memory.data[53] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + memory.data[59] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + memory.data[50] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + memory.data[56] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + memory.data[62] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + memory.data[52] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + memory.data[58] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + memory.we1[0] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + memory.data[54] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + memory.data[60] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + memory.data[51] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + memory.data[57] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + memory.data[63] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + memory.data[53] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + memory.data[59] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + memory.we2[0] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + memory.data[55] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + memory.data[61] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + memory.data[52] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + memory.data[58] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + memory.we1[0] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + memory.data[54] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + memory.data[60] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + memory.data[50] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + memory.data[56] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + memory.data[62] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + memory.data[53] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + memory.data[59] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + memory.we2[0] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + memory.data[55] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + memory.data[61] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + memory.data[51] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + memory.data[57] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + memory.data[63] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + memory.out[44] memory.out[52] memory.out[45] memory.out[53] memory.out[46] memory.out[44] memory.out[47] memory.out[45] + + + memory.out[48] memory.out[46] memory.out[49] memory.out[47] memory.out[50] memory.out[48] memory.out[51] memory.out[49] + + + memory.out[44] memory.out[52] memory.out[45] memory.out[53] memory.out[46] memory.out[44] memory.out[47] memory.out[45] + + + memory.out[48] memory.out[46] memory.out[49] memory.out[47] memory.out[50] memory.out[48] memory.out[51] memory.out[49] + + + memory.out[44] memory.out[52] memory.out[45] memory.out[53] memory.out[46] memory.out[44] memory.out[47] memory.out[45] + + + memory.out[48] memory.out[46] memory.out[49] memory.out[47] memory.out[50] memory.out[48] memory.out[51] memory.out[49] + + + memory.out[44] memory.out[52] memory.out[45] memory.out[53] memory.out[46] memory.out[44] memory.out[47] memory.out[45] + + + memory.out[48] memory.out[46] memory.out[49] memory.out[47] memory.out[50] memory.out[48] memory.out[51] memory.out[49] + + + memory.out[44] memory.out[52] memory.out[45] memory.out[53] memory.out[46] memory.out[44] memory.out[47] memory.out[45] + + + memory.out[48] memory.out[46] memory.out[49] memory.out[47] memory.out[50] memory.out[48] memory.out[51] memory.out[49] + + + memory.out[44] memory.out[52] memory.out[45] memory.out[53] memory.out[46] memory.out[44] memory.out[47] memory.out[45] + + + memory.out[48] memory.out[46] memory.out[49] memory.out[47] memory.out[50] memory.out[48] memory.out[51] memory.out[49] + + + memory.out[44] memory.out[52] memory.out[45] memory.out[53] memory.out[46] memory.out[44] memory.out[47] memory.out[45] + + + memory.out[48] memory.out[46] memory.out[49] memory.out[47] memory.out[50] memory.out[48] memory.out[51] memory.out[49] + + + memory.out[44] memory.out[52] memory.out[45] memory.out[53] memory.out[46] memory.out[44] memory.out[47] memory.out[45] + + + memory.out[48] memory.out[46] memory.out[49] memory.out[47] memory.out[50] memory.out[48] memory.out[51] memory.out[49] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 memory.out[44] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 memory.out[45] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 memory.out[46] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 memory.out[47] + + + l1.N1 l6.N1 l6.N18 l8.N11 memory.out[48] + + + l1.E1 l6.E1 l6.E18 l8.E11 memory.out[49] + + + l1.S1 l6.S1 l6.S18 l8.S11 memory.out[50] + + + l1.W1 l6.W1 l6.W18 l8.W11 memory.out[51] + + + l1.N2 l6.N2 l6.N19 l8.N12 memory.out[52] + + + l1.E2 l6.E2 l6.E19 l8.E12 memory.out[53] + + + l1.S2 l6.S2 l6.S19 l8.S12 memory.out[44] + + + l1.W2 l6.W2 l6.W19 l8.W12 memory.out[45] + + + l1.N3 l6.N3 l6.N20 l8.N13 memory.out[46] + + + l1.E3 l6.E3 l6.E20 l8.E13 memory.out[47] + + + l1.S3 l6.S3 l6.S20 l8.S13 memory.out[48] + + + l1.W3 l6.W3 l6.W20 l8.W13 memory.out[49] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 memory.out[44] + + + l2.E3 l6.E8 l8.E1 l12.E2 memory.out[45] + + + l2.S3 l6.S8 l8.S1 l12.S2 memory.out[46] + + + l2.W3 l6.W8 l8.W1 l12.W2 memory.out[47] + + + l2.N4 l6.N9 l8.N2 l12.N3 memory.out[48] + + + l2.E4 l6.E9 l8.E2 l12.E3 memory.out[49] + + + l2.S4 l6.S9 l8.S2 l12.S3 memory.out[50] + + + l2.W4 l6.W9 l8.W2 l12.W3 memory.out[51] + + + l2.N5 l6.N10 l8.N3 l12.N4 memory.out[52] + + + l2.E5 l6.E10 l8.E3 l12.E4 memory.out[53] + + + l2.S5 l6.S10 l8.S3 l12.S4 memory.out[44] + + + l2.W5 l6.W10 l8.W3 l12.W4 memory.out[45] + + + l3.N0 l6.N11 l8.N4 l12.N5 memory.out[46] + + + l3.E0 l6.E11 l8.E4 l12.E5 memory.out[47] + + + l3.S0 l6.S11 l8.S4 l12.S5 memory.out[48] + + + l3.W0 l6.W11 l8.W4 l12.W5 memory.out[49] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 memory.out[44] + + + l3.E5 l6.E16 l8.E9 l12.E10 memory.out[45] + + + l3.S5 l6.S16 l8.S9 l12.S10 memory.out[46] + + + l3.W5 l6.W16 l8.W9 l12.W10 memory.out[47] + + + l1.N0 l3.N0 l6.N5 l6.N16 memory.out[44] + + + l1.E0 l3.E0 l6.E5 l6.E16 memory.out[45] + + + l1.S0 l3.S0 l6.S5 l6.S16 memory.out[46] + + + l1.W0 l3.W0 l6.W5 l6.W16 memory.out[47] + + + l1.N1 l3.N1 l6.N6 l6.N17 memory.out[48] + + + l1.E1 l3.E1 l6.E6 l6.E17 memory.out[49] + + + l1.S1 l3.S1 l6.S6 l6.S17 memory.out[50] + + + l1.W1 l3.W1 l6.W6 l6.W17 memory.out[51] + + + l1.N2 l3.N2 l6.N7 l6.N18 memory.out[52] + + + l1.E2 l3.E2 l6.E7 l6.E18 memory.out[53] + + + l1.S2 l3.S2 l6.S7 l6.S18 memory.out[44] + + + l1.W2 l3.W2 l6.W7 l6.W18 memory.out[45] + + + l1.N3 l3.N3 l6.N8 l6.N19 memory.out[46] + + + l1.E3 l3.E3 l6.E8 l6.E19 memory.out[47] + + + l1.S3 l3.S3 l6.S8 l6.S19 memory.out[48] + + + l1.W3 l3.W3 l6.W8 l6.W19 memory.out[49] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + memory.addr1[12] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + memory.data[40] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + memory.data[46] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + memory.addr1[14] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + memory.data[42] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + memory.data[48] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + memory.addr2[13] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + memory.data[44] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + memory.addr1[13] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + memory.data[41] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + memory.data[47] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + memory.addr2[12] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + memory.data[43] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + memory.data[49] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + memory.addr2[14] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + memory.data[45] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + memory.addr1[14] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + memory.data[42] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + memory.data[48] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + memory.addr2[13] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + memory.data[44] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + memory.addr1[12] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + memory.data[40] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + memory.data[46] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + memory.addr2[12] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + memory.data[43] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + memory.data[49] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + memory.addr2[14] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + memory.data[45] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + memory.addr1[13] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + memory.data[41] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + memory.data[47] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + memory.addr2[13] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + memory.data[44] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + memory.addr1[12] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + memory.data[40] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + memory.data[46] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + memory.addr1[14] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + memory.data[42] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + memory.data[48] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + memory.addr2[14] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + memory.data[45] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + memory.addr1[13] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + memory.data[41] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + memory.data[47] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + memory.addr2[12] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + memory.data[43] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + memory.data[49] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + memory.out[33] memory.out[41] memory.out[34] memory.out[42] memory.out[35] memory.out[43] memory.out[36] memory.out[33] + + + memory.out[37] memory.out[34] memory.out[38] memory.out[35] memory.out[39] memory.out[36] memory.out[40] memory.out[37] + + + memory.out[33] memory.out[41] memory.out[34] memory.out[42] memory.out[35] memory.out[43] memory.out[36] memory.out[33] + + + memory.out[37] memory.out[34] memory.out[38] memory.out[35] memory.out[39] memory.out[36] memory.out[40] memory.out[37] + + + memory.out[33] memory.out[41] memory.out[34] memory.out[42] memory.out[35] memory.out[43] memory.out[36] memory.out[33] + + + memory.out[37] memory.out[34] memory.out[38] memory.out[35] memory.out[39] memory.out[36] memory.out[40] memory.out[37] + + + memory.out[33] memory.out[41] memory.out[34] memory.out[42] memory.out[35] memory.out[43] memory.out[36] memory.out[33] + + + memory.out[37] memory.out[34] memory.out[38] memory.out[35] memory.out[39] memory.out[36] memory.out[40] memory.out[37] + + + memory.out[33] memory.out[41] memory.out[34] memory.out[42] memory.out[35] memory.out[43] memory.out[36] memory.out[33] + + + memory.out[37] memory.out[34] memory.out[38] memory.out[35] memory.out[39] memory.out[36] memory.out[40] memory.out[37] + + + memory.out[33] memory.out[41] memory.out[34] memory.out[42] memory.out[35] memory.out[43] memory.out[36] memory.out[33] + + + memory.out[37] memory.out[34] memory.out[38] memory.out[35] memory.out[39] memory.out[36] memory.out[40] memory.out[37] + + + memory.out[33] memory.out[41] memory.out[34] memory.out[42] memory.out[35] memory.out[43] memory.out[36] memory.out[33] + + + memory.out[37] memory.out[34] memory.out[38] memory.out[35] memory.out[39] memory.out[36] memory.out[40] memory.out[37] + + + memory.out[33] memory.out[41] memory.out[34] memory.out[42] memory.out[35] memory.out[43] memory.out[36] memory.out[33] + + + memory.out[37] memory.out[34] memory.out[38] memory.out[35] memory.out[39] memory.out[36] memory.out[40] memory.out[37] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 memory.out[33] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 memory.out[34] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 memory.out[35] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 memory.out[36] + + + l1.N1 l6.N1 l6.N18 l8.N11 memory.out[37] + + + l1.E1 l6.E1 l6.E18 l8.E11 memory.out[38] + + + l1.S1 l6.S1 l6.S18 l8.S11 memory.out[39] + + + l1.W1 l6.W1 l6.W18 l8.W11 memory.out[40] + + + l1.N2 l6.N2 l6.N19 l8.N12 memory.out[41] + + + l1.E2 l6.E2 l6.E19 l8.E12 memory.out[42] + + + l1.S2 l6.S2 l6.S19 l8.S12 memory.out[43] + + + l1.W2 l6.W2 l6.W19 l8.W12 memory.out[33] + + + l1.N3 l6.N3 l6.N20 l8.N13 memory.out[34] + + + l1.E3 l6.E3 l6.E20 l8.E13 memory.out[35] + + + l1.S3 l6.S3 l6.S20 l8.S13 memory.out[36] + + + l1.W3 l6.W3 l6.W20 l8.W13 memory.out[37] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 memory.out[33] + + + l2.E3 l6.E8 l8.E1 l12.E2 memory.out[34] + + + l2.S3 l6.S8 l8.S1 l12.S2 memory.out[35] + + + l2.W3 l6.W8 l8.W1 l12.W2 memory.out[36] + + + l2.N4 l6.N9 l8.N2 l12.N3 memory.out[37] + + + l2.E4 l6.E9 l8.E2 l12.E3 memory.out[38] + + + l2.S4 l6.S9 l8.S2 l12.S3 memory.out[39] + + + l2.W4 l6.W9 l8.W2 l12.W3 memory.out[40] + + + l2.N5 l6.N10 l8.N3 l12.N4 memory.out[41] + + + l2.E5 l6.E10 l8.E3 l12.E4 memory.out[42] + + + l2.S5 l6.S10 l8.S3 l12.S4 memory.out[43] + + + l2.W5 l6.W10 l8.W3 l12.W4 memory.out[33] + + + l3.N0 l6.N11 l8.N4 l12.N5 memory.out[34] + + + l3.E0 l6.E11 l8.E4 l12.E5 memory.out[35] + + + l3.S0 l6.S11 l8.S4 l12.S5 memory.out[36] + + + l3.W0 l6.W11 l8.W4 l12.W5 memory.out[37] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 memory.out[33] + + + l3.E5 l6.E16 l8.E9 l12.E10 memory.out[34] + + + l3.S5 l6.S16 l8.S9 l12.S10 memory.out[35] + + + l3.W5 l6.W16 l8.W9 l12.W10 memory.out[36] + + + l1.N0 l3.N0 l6.N5 l6.N16 memory.out[33] + + + l1.E0 l3.E0 l6.E5 l6.E16 memory.out[34] + + + l1.S0 l3.S0 l6.S5 l6.S16 memory.out[35] + + + l1.W0 l3.W0 l6.W5 l6.W16 memory.out[36] + + + l1.N1 l3.N1 l6.N6 l6.N17 memory.out[37] + + + l1.E1 l3.E1 l6.E6 l6.E17 memory.out[38] + + + l1.S1 l3.S1 l6.S6 l6.S17 memory.out[39] + + + l1.W1 l3.W1 l6.W6 l6.W17 memory.out[40] + + + l1.N2 l3.N2 l6.N7 l6.N18 memory.out[41] + + + l1.E2 l3.E2 l6.E7 l6.E18 memory.out[42] + + + l1.S2 l3.S2 l6.S7 l6.S18 memory.out[43] + + + l1.W2 l3.W2 l6.W7 l6.W18 memory.out[33] + + + l1.N3 l3.N3 l6.N8 l6.N19 memory.out[34] + + + l1.E3 l3.E3 l6.E8 l6.E19 memory.out[35] + + + l1.S3 l3.S3 l6.S8 l6.S19 memory.out[36] + + + l1.W3 l3.W3 l6.W8 l6.W19 memory.out[37] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + memory.addr1[9] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + memory.data[30] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + memory.data[36] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + memory.addr1[11] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + memory.data[32] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + memory.data[38] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + memory.addr2[10] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + memory.data[34] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + memory.addr1[10] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + memory.data[31] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + memory.data[37] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + memory.addr2[9] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + memory.data[33] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + memory.data[39] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + memory.addr2[11] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + memory.data[35] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + memory.addr1[11] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + memory.data[32] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + memory.data[38] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + memory.addr2[10] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + memory.data[34] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + memory.addr1[9] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + memory.data[30] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + memory.data[36] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + memory.addr2[9] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + memory.data[33] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + memory.data[39] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + memory.addr2[11] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + memory.data[35] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + memory.addr1[10] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + memory.data[31] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + memory.data[37] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + memory.addr2[10] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + memory.data[34] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + memory.addr1[9] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + memory.data[30] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + memory.data[36] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + memory.addr1[11] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + memory.data[32] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + memory.data[38] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + memory.addr2[11] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + memory.data[35] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + memory.addr1[10] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + memory.data[31] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + memory.data[37] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + memory.addr2[9] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + memory.data[33] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + memory.data[39] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + memory.out[22] memory.out[30] memory.out[23] memory.out[31] memory.out[24] memory.out[32] memory.out[25] memory.out[22] + + + memory.out[26] memory.out[23] memory.out[27] memory.out[24] memory.out[28] memory.out[25] memory.out[29] memory.out[26] + + + memory.out[22] memory.out[30] memory.out[23] memory.out[31] memory.out[24] memory.out[32] memory.out[25] memory.out[22] + + + memory.out[26] memory.out[23] memory.out[27] memory.out[24] memory.out[28] memory.out[25] memory.out[29] memory.out[26] + + + memory.out[22] memory.out[30] memory.out[23] memory.out[31] memory.out[24] memory.out[32] memory.out[25] memory.out[22] + + + memory.out[26] memory.out[23] memory.out[27] memory.out[24] memory.out[28] memory.out[25] memory.out[29] memory.out[26] + + + memory.out[22] memory.out[30] memory.out[23] memory.out[31] memory.out[24] memory.out[32] memory.out[25] memory.out[22] + + + memory.out[26] memory.out[23] memory.out[27] memory.out[24] memory.out[28] memory.out[25] memory.out[29] memory.out[26] + + + memory.out[22] memory.out[30] memory.out[23] memory.out[31] memory.out[24] memory.out[32] memory.out[25] memory.out[22] + + + memory.out[26] memory.out[23] memory.out[27] memory.out[24] memory.out[28] memory.out[25] memory.out[29] memory.out[26] + + + memory.out[22] memory.out[30] memory.out[23] memory.out[31] memory.out[24] memory.out[32] memory.out[25] memory.out[22] + + + memory.out[26] memory.out[23] memory.out[27] memory.out[24] memory.out[28] memory.out[25] memory.out[29] memory.out[26] + + + memory.out[22] memory.out[30] memory.out[23] memory.out[31] memory.out[24] memory.out[32] memory.out[25] memory.out[22] + + + memory.out[26] memory.out[23] memory.out[27] memory.out[24] memory.out[28] memory.out[25] memory.out[29] memory.out[26] + + + memory.out[22] memory.out[30] memory.out[23] memory.out[31] memory.out[24] memory.out[32] memory.out[25] memory.out[22] + + + memory.out[26] memory.out[23] memory.out[27] memory.out[24] memory.out[28] memory.out[25] memory.out[29] memory.out[26] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 memory.out[22] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 memory.out[23] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 memory.out[24] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 memory.out[25] + + + l1.N1 l6.N1 l6.N18 l8.N11 memory.out[26] + + + l1.E1 l6.E1 l6.E18 l8.E11 memory.out[27] + + + l1.S1 l6.S1 l6.S18 l8.S11 memory.out[28] + + + l1.W1 l6.W1 l6.W18 l8.W11 memory.out[29] + + + l1.N2 l6.N2 l6.N19 l8.N12 memory.out[30] + + + l1.E2 l6.E2 l6.E19 l8.E12 memory.out[31] + + + l1.S2 l6.S2 l6.S19 l8.S12 memory.out[32] + + + l1.W2 l6.W2 l6.W19 l8.W12 memory.out[22] + + + l1.N3 l6.N3 l6.N20 l8.N13 memory.out[23] + + + l1.E3 l6.E3 l6.E20 l8.E13 memory.out[24] + + + l1.S3 l6.S3 l6.S20 l8.S13 memory.out[25] + + + l1.W3 l6.W3 l6.W20 l8.W13 memory.out[26] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 memory.out[22] + + + l2.E3 l6.E8 l8.E1 l12.E2 memory.out[23] + + + l2.S3 l6.S8 l8.S1 l12.S2 memory.out[24] + + + l2.W3 l6.W8 l8.W1 l12.W2 memory.out[25] + + + l2.N4 l6.N9 l8.N2 l12.N3 memory.out[26] + + + l2.E4 l6.E9 l8.E2 l12.E3 memory.out[27] + + + l2.S4 l6.S9 l8.S2 l12.S3 memory.out[28] + + + l2.W4 l6.W9 l8.W2 l12.W3 memory.out[29] + + + l2.N5 l6.N10 l8.N3 l12.N4 memory.out[30] + + + l2.E5 l6.E10 l8.E3 l12.E4 memory.out[31] + + + l2.S5 l6.S10 l8.S3 l12.S4 memory.out[32] + + + l2.W5 l6.W10 l8.W3 l12.W4 memory.out[22] + + + l3.N0 l6.N11 l8.N4 l12.N5 memory.out[23] + + + l3.E0 l6.E11 l8.E4 l12.E5 memory.out[24] + + + l3.S0 l6.S11 l8.S4 l12.S5 memory.out[25] + + + l3.W0 l6.W11 l8.W4 l12.W5 memory.out[26] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 memory.out[22] + + + l3.E5 l6.E16 l8.E9 l12.E10 memory.out[23] + + + l3.S5 l6.S16 l8.S9 l12.S10 memory.out[24] + + + l3.W5 l6.W16 l8.W9 l12.W10 memory.out[25] + + + l1.N0 l3.N0 l6.N5 l6.N16 memory.out[22] + + + l1.E0 l3.E0 l6.E5 l6.E16 memory.out[23] + + + l1.S0 l3.S0 l6.S5 l6.S16 memory.out[24] + + + l1.W0 l3.W0 l6.W5 l6.W16 memory.out[25] + + + l1.N1 l3.N1 l6.N6 l6.N17 memory.out[26] + + + l1.E1 l3.E1 l6.E6 l6.E17 memory.out[27] + + + l1.S1 l3.S1 l6.S6 l6.S17 memory.out[28] + + + l1.W1 l3.W1 l6.W6 l6.W17 memory.out[29] + + + l1.N2 l3.N2 l6.N7 l6.N18 memory.out[30] + + + l1.E2 l3.E2 l6.E7 l6.E18 memory.out[31] + + + l1.S2 l3.S2 l6.S7 l6.S18 memory.out[32] + + + l1.W2 l3.W2 l6.W7 l6.W18 memory.out[22] + + + l1.N3 l3.N3 l6.N8 l6.N19 memory.out[23] + + + l1.E3 l3.E3 l6.E8 l6.E19 memory.out[24] + + + l1.S3 l3.S3 l6.S8 l6.S19 memory.out[25] + + + l1.W3 l3.W3 l6.W8 l6.W19 memory.out[26] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + memory.addr1[6] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + memory.data[20] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + memory.data[26] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + memory.addr1[8] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + memory.data[22] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + memory.data[28] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + memory.addr2[7] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + memory.data[24] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + memory.addr1[7] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + memory.data[21] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + memory.data[27] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + memory.addr2[6] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + memory.data[23] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + memory.data[29] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + memory.addr2[8] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + memory.data[25] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + memory.addr1[8] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + memory.data[22] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + memory.data[28] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + memory.addr2[7] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + memory.data[24] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + memory.addr1[6] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + memory.data[20] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + memory.data[26] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + memory.addr2[6] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + memory.data[23] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + memory.data[29] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + memory.addr2[8] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + memory.data[25] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + memory.addr1[7] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + memory.data[21] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + memory.data[27] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + memory.addr2[7] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + memory.data[24] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + memory.addr1[6] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + memory.data[20] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + memory.data[26] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + memory.addr1[8] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + memory.data[22] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + memory.data[28] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + memory.addr2[8] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + memory.data[25] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + memory.addr1[7] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + memory.data[21] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + memory.data[27] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + memory.addr2[6] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + memory.data[23] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + memory.data[29] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + memory.out[11] memory.out[19] memory.out[12] memory.out[20] memory.out[13] memory.out[21] memory.out[14] memory.out[11] + + + memory.out[15] memory.out[12] memory.out[16] memory.out[13] memory.out[17] memory.out[14] memory.out[18] memory.out[15] + + + memory.out[11] memory.out[19] memory.out[12] memory.out[20] memory.out[13] memory.out[21] memory.out[14] memory.out[11] + + + memory.out[15] memory.out[12] memory.out[16] memory.out[13] memory.out[17] memory.out[14] memory.out[18] memory.out[15] + + + memory.out[11] memory.out[19] memory.out[12] memory.out[20] memory.out[13] memory.out[21] memory.out[14] memory.out[11] + + + memory.out[15] memory.out[12] memory.out[16] memory.out[13] memory.out[17] memory.out[14] memory.out[18] memory.out[15] + + + memory.out[11] memory.out[19] memory.out[12] memory.out[20] memory.out[13] memory.out[21] memory.out[14] memory.out[11] + + + memory.out[15] memory.out[12] memory.out[16] memory.out[13] memory.out[17] memory.out[14] memory.out[18] memory.out[15] + + + memory.out[11] memory.out[19] memory.out[12] memory.out[20] memory.out[13] memory.out[21] memory.out[14] memory.out[11] + + + memory.out[15] memory.out[12] memory.out[16] memory.out[13] memory.out[17] memory.out[14] memory.out[18] memory.out[15] + + + memory.out[11] memory.out[19] memory.out[12] memory.out[20] memory.out[13] memory.out[21] memory.out[14] memory.out[11] + + + memory.out[15] memory.out[12] memory.out[16] memory.out[13] memory.out[17] memory.out[14] memory.out[18] memory.out[15] + + + memory.out[11] memory.out[19] memory.out[12] memory.out[20] memory.out[13] memory.out[21] memory.out[14] memory.out[11] + + + memory.out[15] memory.out[12] memory.out[16] memory.out[13] memory.out[17] memory.out[14] memory.out[18] memory.out[15] + + + memory.out[11] memory.out[19] memory.out[12] memory.out[20] memory.out[13] memory.out[21] memory.out[14] memory.out[11] + + + memory.out[15] memory.out[12] memory.out[16] memory.out[13] memory.out[17] memory.out[14] memory.out[18] memory.out[15] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 memory.out[11] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 memory.out[12] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 memory.out[13] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 memory.out[14] + + + l1.N1 l6.N1 l6.N18 l8.N11 memory.out[15] + + + l1.E1 l6.E1 l6.E18 l8.E11 memory.out[16] + + + l1.S1 l6.S1 l6.S18 l8.S11 memory.out[17] + + + l1.W1 l6.W1 l6.W18 l8.W11 memory.out[18] + + + l1.N2 l6.N2 l6.N19 l8.N12 memory.out[19] + + + l1.E2 l6.E2 l6.E19 l8.E12 memory.out[20] + + + l1.S2 l6.S2 l6.S19 l8.S12 memory.out[21] + + + l1.W2 l6.W2 l6.W19 l8.W12 memory.out[11] + + + l1.N3 l6.N3 l6.N20 l8.N13 memory.out[12] + + + l1.E3 l6.E3 l6.E20 l8.E13 memory.out[13] + + + l1.S3 l6.S3 l6.S20 l8.S13 memory.out[14] + + + l1.W3 l6.W3 l6.W20 l8.W13 memory.out[15] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 memory.out[11] + + + l2.E3 l6.E8 l8.E1 l12.E2 memory.out[12] + + + l2.S3 l6.S8 l8.S1 l12.S2 memory.out[13] + + + l2.W3 l6.W8 l8.W1 l12.W2 memory.out[14] + + + l2.N4 l6.N9 l8.N2 l12.N3 memory.out[15] + + + l2.E4 l6.E9 l8.E2 l12.E3 memory.out[16] + + + l2.S4 l6.S9 l8.S2 l12.S3 memory.out[17] + + + l2.W4 l6.W9 l8.W2 l12.W3 memory.out[18] + + + l2.N5 l6.N10 l8.N3 l12.N4 memory.out[19] + + + l2.E5 l6.E10 l8.E3 l12.E4 memory.out[20] + + + l2.S5 l6.S10 l8.S3 l12.S4 memory.out[21] + + + l2.W5 l6.W10 l8.W3 l12.W4 memory.out[11] + + + l3.N0 l6.N11 l8.N4 l12.N5 memory.out[12] + + + l3.E0 l6.E11 l8.E4 l12.E5 memory.out[13] + + + l3.S0 l6.S11 l8.S4 l12.S5 memory.out[14] + + + l3.W0 l6.W11 l8.W4 l12.W5 memory.out[15] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 memory.out[11] + + + l3.E5 l6.E16 l8.E9 l12.E10 memory.out[12] + + + l3.S5 l6.S16 l8.S9 l12.S10 memory.out[13] + + + l3.W5 l6.W16 l8.W9 l12.W10 memory.out[14] + + + l1.N0 l3.N0 l6.N5 l6.N16 memory.out[11] + + + l1.E0 l3.E0 l6.E5 l6.E16 memory.out[12] + + + l1.S0 l3.S0 l6.S5 l6.S16 memory.out[13] + + + l1.W0 l3.W0 l6.W5 l6.W16 memory.out[14] + + + l1.N1 l3.N1 l6.N6 l6.N17 memory.out[15] + + + l1.E1 l3.E1 l6.E6 l6.E17 memory.out[16] + + + l1.S1 l3.S1 l6.S6 l6.S17 memory.out[17] + + + l1.W1 l3.W1 l6.W6 l6.W17 memory.out[18] + + + l1.N2 l3.N2 l6.N7 l6.N18 memory.out[19] + + + l1.E2 l3.E2 l6.E7 l6.E18 memory.out[20] + + + l1.S2 l3.S2 l6.S7 l6.S18 memory.out[21] + + + l1.W2 l3.W2 l6.W7 l6.W18 memory.out[11] + + + l1.N3 l3.N3 l6.N8 l6.N19 memory.out[12] + + + l1.E3 l3.E3 l6.E8 l6.E19 memory.out[13] + + + l1.S3 l3.S3 l6.S8 l6.S19 memory.out[14] + + + l1.W3 l3.W3 l6.W8 l6.W19 memory.out[15] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + memory.addr1[3] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + memory.data[10] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + memory.data[16] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + memory.addr1[5] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + memory.data[12] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + memory.data[18] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + memory.addr2[4] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + memory.data[14] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + memory.addr1[4] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + memory.data[11] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + memory.data[17] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + memory.addr2[3] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + memory.data[13] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + memory.data[19] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + memory.addr2[5] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + memory.data[15] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + memory.addr1[5] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + memory.data[12] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + memory.data[18] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + memory.addr2[4] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + memory.data[14] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + memory.addr1[3] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + memory.data[10] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + memory.data[16] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + memory.addr2[3] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + memory.data[13] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + memory.data[19] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + memory.addr2[5] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + memory.data[15] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + memory.addr1[4] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + memory.data[11] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + memory.data[17] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + memory.addr2[4] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + memory.data[14] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + memory.addr1[3] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + memory.data[10] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + memory.data[16] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + memory.addr1[5] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + memory.data[12] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + memory.data[18] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + memory.addr2[5] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + memory.data[15] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + memory.addr1[4] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + memory.data[11] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + memory.data[17] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + memory.addr2[3] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + memory.data[13] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + memory.data[19] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + memory.out[0] memory.out[8] memory.out[1] memory.out[9] memory.out[2] memory.out[10] memory.out[3] memory.out[0] + + + memory.out[4] memory.out[1] memory.out[5] memory.out[2] memory.out[6] memory.out[3] memory.out[7] memory.out[4] + + + memory.out[0] memory.out[8] memory.out[1] memory.out[9] memory.out[2] memory.out[10] memory.out[3] memory.out[0] + + + memory.out[4] memory.out[1] memory.out[5] memory.out[2] memory.out[6] memory.out[3] memory.out[7] memory.out[4] + + + memory.out[0] memory.out[8] memory.out[1] memory.out[9] memory.out[2] memory.out[10] memory.out[3] memory.out[0] + + + memory.out[4] memory.out[1] memory.out[5] memory.out[2] memory.out[6] memory.out[3] memory.out[7] memory.out[4] + + + memory.out[0] memory.out[8] memory.out[1] memory.out[9] memory.out[2] memory.out[10] memory.out[3] memory.out[0] + + + memory.out[4] memory.out[1] memory.out[5] memory.out[2] memory.out[6] memory.out[3] memory.out[7] memory.out[4] + + + memory.out[0] memory.out[8] memory.out[1] memory.out[9] memory.out[2] memory.out[10] memory.out[3] memory.out[0] + + + memory.out[4] memory.out[1] memory.out[5] memory.out[2] memory.out[6] memory.out[3] memory.out[7] memory.out[4] + + + memory.out[0] memory.out[8] memory.out[1] memory.out[9] memory.out[2] memory.out[10] memory.out[3] memory.out[0] + + + memory.out[4] memory.out[1] memory.out[5] memory.out[2] memory.out[6] memory.out[3] memory.out[7] memory.out[4] + + + memory.out[0] memory.out[8] memory.out[1] memory.out[9] memory.out[2] memory.out[10] memory.out[3] memory.out[0] + + + memory.out[4] memory.out[1] memory.out[5] memory.out[2] memory.out[6] memory.out[3] memory.out[7] memory.out[4] + + + memory.out[0] memory.out[8] memory.out[1] memory.out[9] memory.out[2] memory.out[10] memory.out[3] memory.out[0] + + + memory.out[4] memory.out[1] memory.out[5] memory.out[2] memory.out[6] memory.out[3] memory.out[7] memory.out[4] + + + l1.N0 l6.N0 l6.N17 l8.N10 l12.N11 memory.out[0] + + + l1.E0 l6.E0 l6.E17 l8.E10 l12.E11 memory.out[1] + + + l1.S0 l6.S0 l6.S17 l8.S10 l12.S11 memory.out[2] + + + l1.W0 l6.W0 l6.W17 l8.W10 l12.W11 memory.out[3] + + + l1.N1 l6.N1 l6.N18 l8.N11 memory.out[4] + + + l1.E1 l6.E1 l6.E18 l8.E11 memory.out[5] + + + l1.S1 l6.S1 l6.S18 l8.S11 memory.out[6] + + + l1.W1 l6.W1 l6.W18 l8.W11 memory.out[7] + + + l1.N2 l6.N2 l6.N19 l8.N12 memory.out[8] + + + l1.E2 l6.E2 l6.E19 l8.E12 memory.out[9] + + + l1.S2 l6.S2 l6.S19 l8.S12 memory.out[10] + + + l1.W2 l6.W2 l6.W19 l8.W12 memory.out[0] + + + l1.N3 l6.N3 l6.N20 l8.N13 memory.out[1] + + + l1.E3 l6.E3 l6.E20 l8.E13 memory.out[2] + + + l1.S3 l6.S3 l6.S20 l8.S13 memory.out[3] + + + l1.W3 l6.W3 l6.W20 l8.W13 memory.out[4] + + + l1.N4 l6.N4 l6.N21 l8.N14 omux-0 + + + l1.E4 l6.E4 l6.E21 l8.E14 omux-1 + + + l1.S4 l6.S4 l6.S21 l8.S14 omux-2 + + + l1.W4 l6.W4 l6.W21 l8.W14 omux-3 + + + l2.N0 l6.N5 l6.N22 l8.N15 omux-4 + + + l2.E0 l6.E5 l6.E22 l8.E15 omux-5 + + + l2.S0 l6.S5 l6.S22 l8.S15 omux-6 + + + l2.W0 l6.W5 l6.W22 l8.W15 omux-7 + + + l2.N1 l6.N6 l6.N23 l12.N0 omux-8 + + + l2.E1 l6.E6 l6.E23 l12.E0 omux-9 + + + l2.S1 l6.S6 l6.S23 l12.S0 omux-10 + + + l2.W1 l6.W6 l6.W23 l12.W0 omux-11 + + + l2.N2 l6.N7 l8.N0 l12.N1 omux-12 + + + l2.E2 l6.E7 l8.E0 l12.E1 omux-13 + + + l2.S2 l6.S7 l8.S0 l12.S1 omux-14 + + + l2.W2 l6.W7 l8.W0 l12.W1 omux-15 + + + l2.N3 l6.N8 l8.N1 l12.N2 memory.out[0] + + + l2.E3 l6.E8 l8.E1 l12.E2 memory.out[1] + + + l2.S3 l6.S8 l8.S1 l12.S2 memory.out[2] + + + l2.W3 l6.W8 l8.W1 l12.W2 memory.out[3] + + + l2.N4 l6.N9 l8.N2 l12.N3 memory.out[4] + + + l2.E4 l6.E9 l8.E2 l12.E3 memory.out[5] + + + l2.S4 l6.S9 l8.S2 l12.S3 memory.out[6] + + + l2.W4 l6.W9 l8.W2 l12.W3 memory.out[7] + + + l2.N5 l6.N10 l8.N3 l12.N4 memory.out[8] + + + l2.E5 l6.E10 l8.E3 l12.E4 memory.out[9] + + + l2.S5 l6.S10 l8.S3 l12.S4 memory.out[10] + + + l2.W5 l6.W10 l8.W3 l12.W4 memory.out[0] + + + l3.N0 l6.N11 l8.N4 l12.N5 memory.out[1] + + + l3.E0 l6.E11 l8.E4 l12.E5 memory.out[2] + + + l3.S0 l6.S11 l8.S4 l12.S5 memory.out[3] + + + l3.W0 l6.W11 l8.W4 l12.W5 memory.out[4] + + + l3.N1 l6.N12 l8.N5 l12.N6 omux-0 + + + l3.E1 l6.E12 l8.E5 l12.E6 omux-1 + + + l3.S1 l6.S12 l8.S5 l12.S6 omux-2 + + + l3.W1 l6.W12 l8.W5 l12.W6 omux-3 + + + l3.N2 l6.N13 l8.N6 l12.N7 omux-4 + + + l3.E2 l6.E13 l8.E6 l12.E7 omux-5 + + + l3.S2 l6.S13 l8.S6 l12.S7 omux-6 + + + l3.W2 l6.W13 l8.W6 l12.W7 omux-7 + + + l3.N3 l6.N14 l8.N7 l12.N8 omux-8 + + + l3.E3 l6.E14 l8.E7 l12.E8 omux-9 + + + l3.S3 l6.S14 l8.S7 l12.S8 omux-10 + + + l3.W3 l6.W14 l8.W7 l12.W8 omux-11 + + + l3.N4 l6.N15 l8.N8 l12.N9 omux-12 + + + l3.E4 l6.E15 l8.E8 l12.E9 omux-13 + + + l3.S4 l6.S15 l8.S8 l12.S9 omux-14 + + + l3.W4 l6.W15 l8.W8 l12.W9 omux-15 + + + l3.N5 l6.N16 l8.N9 l12.N10 memory.out[0] + + + l3.E5 l6.E16 l8.E9 l12.E10 memory.out[1] + + + l3.S5 l6.S16 l8.S9 l12.S10 memory.out[2] + + + l3.W5 l6.W16 l8.W9 l12.W10 memory.out[3] + + + l1.N0 l3.N0 l6.N5 l6.N16 memory.out[0] + + + l1.E0 l3.E0 l6.E5 l6.E16 memory.out[1] + + + l1.S0 l3.S0 l6.S5 l6.S16 memory.out[2] + + + l1.W0 l3.W0 l6.W5 l6.W16 memory.out[3] + + + l1.N1 l3.N1 l6.N6 l6.N17 memory.out[4] + + + l1.E1 l3.E1 l6.E6 l6.E17 memory.out[5] + + + l1.S1 l3.S1 l6.S6 l6.S17 memory.out[6] + + + l1.W1 l3.W1 l6.W6 l6.W17 memory.out[7] + + + l1.N2 l3.N2 l6.N7 l6.N18 memory.out[8] + + + l1.E2 l3.E2 l6.E7 l6.E18 memory.out[9] + + + l1.S2 l3.S2 l6.S7 l6.S18 memory.out[10] + + + l1.W2 l3.W2 l6.W7 l6.W18 memory.out[0] + + + l1.N3 l3.N3 l6.N8 l6.N19 memory.out[1] + + + l1.E3 l3.E3 l6.E8 l6.E19 memory.out[2] + + + l1.S3 l3.S3 l6.S8 l6.S19 memory.out[3] + + + l1.W3 l3.W3 l6.W8 l6.W19 memory.out[4] + + + l1.N4 l3.N4 l6.N9 l6.N20 omux-0 + + + l1.E4 l3.E4 l6.E9 l6.E20 omux-1 + + + l1.S4 l3.S4 l6.S9 l6.S20 omux-2 + + + l1.W4 l3.W4 l6.W9 l6.W20 omux-3 + + + + + memory.addr1[0] + mux-0 mux-1 mux-2 mux-3 mux-4 mux-5 + + + memory.data[0] + mux-8 mux-9 mux-10 mux-11 mux-12 mux-13 + + + memory.data[6] + mux-16 mux-17 mux-18 mux-19 mux-20 mux-21 + + + memory.addr1[2] + mux-24 mux-25 mux-26 mux-27 mux-28 mux-29 + + + memory.data[2] + mux-32 mux-33 mux-34 mux-35 mux-36 mux-37 + + + memory.data[8] + mux-40 mux-41 mux-42 mux-43 mux-44 mux-45 + + + memory.addr2[1] + mux-48 mux-49 mux-50 mux-51 mux-52 mux-53 + + + memory.data[4] + mux-56 mux-57 mux-58 mux-59 mux-60 mux-61 + + + memory.addr1[1] + mux-64 mux-65 mux-66 mux-67 mux-_0 mux-_1 + + + memory.data[1] + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 + + + memory.data[7] + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 + + + memory.addr2[0] + mux-1 mux-2 mux-3 mux-4 mux-5 mux-6 + + + memory.data[3] + mux-9 mux-10 mux-11 mux-12 mux-13 mux-14 + + + memory.data[9] + mux-17 mux-18 mux-19 mux-20 mux-21 mux-22 + + + memory.addr2[2] + mux-25 mux-26 mux-27 mux-28 mux-29 mux-30 + + + memory.data[5] + mux-33 mux-34 mux-35 mux-36 mux-37 mux-38 + + + memory.addr1[2] + mux-41 mux-42 mux-43 mux-44 mux-45 mux-46 + + + memory.data[2] + mux-49 mux-50 mux-51 mux-52 mux-53 mux-54 + + + memory.data[8] + mux-57 mux-58 mux-59 mux-60 mux-61 mux-62 + + + memory.addr2[1] + mux-65 mux-66 mux-67 mux-_0 mux-_1 mux-_2 + + + memory.data[4] + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 + + + memory.addr1[0] + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 + + + memory.data[0] + mux-2 mux-3 mux-4 mux-5 mux-6 mux-7 + + + memory.data[6] + mux-10 mux-11 mux-12 mux-13 mux-14 mux-15 + + + memory.addr2[0] + mux-18 mux-19 mux-20 mux-21 mux-22 mux-23 + + + memory.data[3] + mux-26 mux-27 mux-28 mux-29 mux-30 mux-31 + + + memory.data[9] + mux-34 mux-35 mux-36 mux-37 mux-38 mux-39 + + + memory.addr2[2] + mux-42 mux-43 mux-44 mux-45 mux-46 mux-47 + + + memory.data[5] + mux-50 mux-51 mux-52 mux-53 mux-54 mux-55 + + + memory.addr1[1] + mux-58 mux-59 mux-60 mux-61 mux-62 mux-63 + + + memory.data[1] + mux-66 mux-67 mux-_0 mux-_1 mux-_2 mux-_3 + + + memory.data[7] + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 + + + memory.addr2[1] + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 + + + memory.data[4] + mux-3 mux-4 mux-5 mux-6 mux-7 mux-0 + + + memory.addr1[0] + mux-11 mux-12 mux-13 mux-14 mux-15 mux-8 + + + memory.data[0] + mux-19 mux-20 mux-21 mux-22 mux-23 mux-16 + + + memory.data[6] + mux-27 mux-28 mux-29 mux-30 mux-31 mux-24 + + + memory.addr1[2] + mux-35 mux-36 mux-37 mux-38 mux-39 mux-32 + + + memory.data[2] + mux-43 mux-44 mux-45 mux-46 mux-47 mux-40 + + + memory.data[8] + mux-51 mux-52 mux-53 mux-54 mux-55 mux-48 + + + memory.addr2[2] + mux-59 mux-60 mux-61 mux-62 mux-63 mux-56 + + + memory.data[5] + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 mux-64 + + + memory.addr1[1] + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 + + + memory.data[1] + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 + + + memory.data[7] + mux-4 mux-5 mux-6 mux-7 mux-0 mux-1 + + + memory.addr2[0] + mux-12 mux-13 mux-14 mux-15 mux-8 mux-9 + + + memory.data[3] + mux-20 mux-21 mux-22 mux-23 mux-16 mux-17 + + + memory.data[9] + mux-28 mux-29 mux-30 mux-31 mux-24 mux-25 + + + l1.N0 + mux-0 mux-1 mux-2 mux-3 mux-4 omux-0 + + + l1.N1 + mux-8 mux-9 mux-10 mux-11 mux-12 omux-1 + + + l1.N2 + mux-16 mux-17 mux-18 mux-19 mux-20 omux-2 + + + l1.N3 + mux-24 mux-25 mux-26 mux-27 mux-28 omux-3 + + + l1.N4 + mux-32 mux-33 mux-34 mux-35 mux-36 omux-4 + + + l2.N0 + mux-40 mux-41 mux-42 mux-43 mux-44 omux-5 + + + l2.N2 + mux-48 mux-49 mux-50 mux-51 mux-52 omux-6 + + + l2.N4 + mux-56 mux-57 mux-58 mux-59 mux-60 omux-7 + + + l3.N0 + mux-64 mux-65 mux-66 mux-67 mux-_0 omux-8 + + + l3.N3 + mux-_4 mux-_5 mux-_6 mux-_7 mux-_8 omux-9 + + + l6.N0 + mux-_12 mux-_13 mux-_14 mux-_15 mux-_16 omux-10 + + + l6.N6 + mux-1 mux-2 mux-3 mux-4 mux-5 omux-11 + + + l6.N12 + mux-9 mux-10 mux-11 mux-12 mux-13 omux-12 + + + l6.N18 + mux-17 mux-18 mux-19 mux-20 mux-21 omux-13 + + + l8.N0 + mux-25 mux-26 mux-27 mux-28 mux-29 omux-14 + + + l8.N8 + mux-33 mux-34 mux-35 mux-36 mux-37 omux-15 + + + l12.N0 + mux-41 mux-42 mux-43 mux-44 mux-45 omux-0 + + + l1.E0 + mux-49 mux-50 mux-51 mux-52 mux-53 omux-1 + + + l1.E1 + mux-57 mux-58 mux-59 mux-60 mux-61 omux-2 + + + l1.E2 + mux-65 mux-66 mux-67 mux-_0 mux-_1 omux-3 + + + l1.E3 + mux-_5 mux-_6 mux-_7 mux-_8 mux-_9 omux-4 + + + l1.E4 + mux-_13 mux-_14 mux-_15 mux-_16 mux-_17 omux-5 + + + l2.E0 + mux-2 mux-3 mux-4 mux-5 mux-6 omux-6 + + + l2.E2 + mux-10 mux-11 mux-12 mux-13 mux-14 omux-7 + + + l2.E4 + mux-18 mux-19 mux-20 mux-21 mux-22 omux-8 + + + l3.E0 + mux-26 mux-27 mux-28 mux-29 mux-30 omux-9 + + + l3.E3 + mux-34 mux-35 mux-36 mux-37 mux-38 omux-10 + + + l6.E0 + mux-42 mux-43 mux-44 mux-45 mux-46 omux-11 + + + l6.E6 + mux-50 mux-51 mux-52 mux-53 mux-54 omux-12 + + + l6.E12 + mux-58 mux-59 mux-60 mux-61 mux-62 omux-13 + + + l6.E18 + mux-66 mux-67 mux-_0 mux-_1 mux-_2 omux-14 + + + l8.E0 + mux-_6 mux-_7 mux-_8 mux-_9 mux-_10 omux-15 + + + l8.E8 + mux-_14 mux-_15 mux-_16 mux-_17 mux-_18 omux-0 + + + l12.E0 + mux-3 mux-4 mux-5 mux-6 mux-7 omux-1 + + + l1.S0 + mux-11 mux-12 mux-13 mux-14 mux-15 omux-2 + + + l1.S1 + mux-19 mux-20 mux-21 mux-22 mux-23 omux-3 + + + l1.S2 + mux-27 mux-28 mux-29 mux-30 mux-31 omux-4 + + + l1.S3 + mux-35 mux-36 mux-37 mux-38 mux-39 omux-5 + + + l1.S4 + mux-43 mux-44 mux-45 mux-46 mux-47 omux-6 + + + l2.S0 + mux-51 mux-52 mux-53 mux-54 mux-55 omux-7 + + + l2.S2 + mux-59 mux-60 mux-61 mux-62 mux-63 omux-8 + + + l2.S4 + mux-67 mux-_0 mux-_1 mux-_2 mux-_3 omux-9 + + + l3.S0 + mux-_7 mux-_8 mux-_9 mux-_10 mux-_11 omux-10 + + + l3.S3 + mux-_15 mux-_16 mux-_17 mux-_18 mux-_19 omux-11 + + + l6.S0 + mux-4 mux-5 mux-6 mux-7 mux-0 omux-12 + + + l6.S6 + mux-12 mux-13 mux-14 mux-15 mux-8 omux-13 + + + l6.S12 + mux-20 mux-21 mux-22 mux-23 mux-16 omux-14 + + + l6.S18 + mux-28 mux-29 mux-30 mux-31 mux-24 omux-15 + + + l8.S0 + mux-36 mux-37 mux-38 mux-39 mux-32 omux-0 + + + l8.S8 + mux-44 mux-45 mux-46 mux-47 mux-40 omux-1 + + + l12.S0 + mux-52 mux-53 mux-54 mux-55 mux-48 omux-2 + + + l1.W0 + mux-60 mux-61 mux-62 mux-63 mux-56 omux-3 + + + l1.W1 + mux-_0 mux-_1 mux-_2 mux-_3 mux-64 omux-4 + + + l1.W2 + mux-_8 mux-_9 mux-_10 mux-_11 mux-_4 omux-5 + + + l1.W3 + mux-_16 mux-_17 mux-_18 mux-_19 mux-_12 omux-6 + + + l1.W4 + mux-5 mux-6 mux-7 mux-0 mux-1 omux-7 + + + l2.W0 + mux-13 mux-14 mux-15 mux-8 mux-9 omux-8 + + + l2.W2 + mux-21 mux-22 mux-23 mux-16 mux-17 omux-9 + + + l2.W4 + mux-29 mux-30 mux-31 mux-24 mux-25 omux-10 + + + l3.W0 + mux-37 mux-38 mux-39 mux-32 mux-33 omux-11 + + + l3.W3 + mux-45 mux-46 mux-47 mux-40 mux-41 omux-12 + + + l6.W0 + mux-53 mux-54 mux-55 mux-48 mux-49 omux-13 + + + l6.W6 + mux-61 mux-62 mux-63 mux-56 mux-57 omux-14 + + + l6.W12 + mux-_1 mux-_2 mux-_3 mux-64 mux-65 omux-15 + + + l6.W18 + mux-_9 mux-_10 mux-_11 mux-_4 mux-_5 omux-0 + + + l8.W0 + mux-_17 mux-_18 mux-_19 mux-_12 mux-_13 omux-1 + + + l8.W8 + mux-6 mux-7 mux-0 mux-1 mux-2 omux-2 + + + l12.W0 + mux-14 mux-15 mux-8 mux-9 mux-10 omux-3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml b/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml new file mode 100644 index 00000000000..6e97691e42d --- /dev/null +++ b/vtr_flow/arch/timing/k4_N4_tileable_90nm.xml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/arch/timing/k4_N4_tileable_perimeter_cb_90nm.xml b/vtr_flow/arch/timing/k4_N4_tileable_perimeter_cb_90nm.xml new file mode 100644 index 00000000000..33eab099c51 --- /dev/null +++ b/vtr_flow/arch/timing/k4_N4_tileable_perimeter_cb_90nm.xml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + io.outpad io.inpad io.clock + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 + 1 + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + 2.253000e-10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml b/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml new file mode 100644 index 00000000000..6b027fe6a21 --- /dev/null +++ b/vtr_flow/arch/timing/k4_frac_N4_fracff_localClkGen_40nm.xml @@ -0,0 +1,635 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + io.outpad io.inpad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 1 1 1 1 + 1 1 1 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 235e-12 + 235e-12 + 235e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 261e-12 + 261e-12 + 261e-12 + 261e-12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt new file mode 100755 index 00000000000..44cfb179384 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/config.txt @@ -0,0 +1,29 @@ +############################################ +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/blif/4 + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=diffeq.blif +circuit_list_add=ex5p.blif +circuit_list_add=s298.blif + +# Add architectures to list to sweep +arch_list_add=k4_N4_tileable_90nm.xml + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements.txt + +script_params=-starting_stage vpr -track_memory_usage + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt new file mode 100644 index 00000000000..c7b87ad96d9 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph/config/golden_results.txt @@ -0,0 +1,4 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time initial_placed_wirelength_est placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time initial_placed_CPD_est placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time ap_mem ap_time ap_full_legalizer_mem ap_full_legalizer_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_num_rr_graph_nodes crit_path_num_rr_graph_edges crit_path_collapsed_nodes crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_create_rr_graph_time crit_path_create_intra_cluster_rr_graph_time crit_path_tile_lookahead_computation_time crit_path_router_lookahead_computation_time crit_path_total_timing_analysis_time crit_path_total_sta_time +k4_N4_tileable_90nm.xml diffeq.blif common 19.81 vpr 62.34 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 438 64 -1 -1 success v8.0.0-13020-g9db19397a Release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T14:55:09 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 63832 64 39 1935 1974 1 1077 541 23 23 529 clb auto 23.1 MiB 0.33 23383 10407 130518 33236 93921 3361 62.3 MiB 1.45 0.02 13.1424 7.01436 -1186.76 -7.01436 7.01436 0.50 0.00278506 0.0021586 0.190009 0.145283 -1 -1 -1 -1 30 18484 49 983127 976439 713134. 1348.08 14.21 1.06254 0.869067 34090 111051 -1 19671 23 8649 30686 3584279 1256529 7.46036 7.46036 -1487.87 -7.46036 0 0 855979. 1618.11 0.12 1.13 -1 -1 -1 0.12 0.186892 0.161913 +k4_N4_tileable_90nm.xml ex5p.blif common 24.85 vpr 58.21 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 366 8 -1 -1 success v8.0.0-13020-g9db19397a Release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T14:55:09 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 59604 8 63 1072 1135 0 894 437 22 22 484 clb auto 19.0 MiB 0.26 20320 12148 81630 19773 60101 1756 58.1 MiB 0.95 0.02 7.91494 5.42874 -233.124 -5.42874 nan 0.46 0.00184499 0.00135252 0.103436 0.0807114 -1 -1 -1 -1 58 21250 25 891726 815929 1.11794e+06 2309.80 19.84 0.848059 0.694504 50136 186432 -1 21807 22 9790 33292 5274291 1466232 5.49118 nan -247.625 -5.49118 0 0 1.39817e+06 2888.78 0.20 1.26 -1 -1 -1 0.20 0.112893 0.0976021 +k4_N4_tileable_90nm.xml s298.blif common 37.04 vpr 68.60 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 580 4 -1 -1 success v8.0.0-13020-g9db19397a Release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T14:55:09 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 70248 4 6 1942 1948 1 1169 590 27 27 729 clb auto 25.2 MiB 0.34 27324 13487 148970 40500 107491 979 68.6 MiB 1.82 0.03 15.4416 9.90815 -77.1816 -9.90815 9.90815 0.71 0.0031275 0.00242 0.2127 0.16542 -1 -1 -1 -1 34 24602 39 1.39333e+06 1.29301e+06 1.12707e+06 1546.05 28.80 1.56376 1.28245 50282 172723 -1 28340 30 12129 58881 11129903 3411240 10.0924 10.0924 -86.1832 -10.0924 0 0 1.32680e+06 1820.03 0.17 2.63 -1 -1 -1 0.17 0.220331 0.186786 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_perimeter_cb/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_perimeter_cb/config/config.txt new file mode 100755 index 00000000000..f06f65791c8 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_perimeter_cb/config/config.txt @@ -0,0 +1,29 @@ +############################################ +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/blif/4 + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=diffeq.blif +circuit_list_add=ex5p.blif +circuit_list_add=s298.blif + +# Add architectures to list to sweep +arch_list_add=k4_N4_tileable_perimeter_cb_90nm.xml + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements.txt + +script_params=-starting_stage vpr -track_memory_usage + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_perimeter_cb/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_perimeter_cb/config/golden_results.txt new file mode 100644 index 00000000000..1ec53c07bd8 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_perimeter_cb/config/golden_results.txt @@ -0,0 +1,4 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time initial_placed_wirelength_est placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time initial_placed_CPD_est placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time ap_mem ap_time ap_full_legalizer_mem ap_full_legalizer_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_num_rr_graph_nodes crit_path_num_rr_graph_edges crit_path_collapsed_nodes crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_create_rr_graph_time crit_path_create_intra_cluster_rr_graph_time crit_path_tile_lookahead_computation_time crit_path_router_lookahead_computation_time crit_path_total_timing_analysis_time crit_path_total_sta_time +k4_N4_tileable_perimeter_cb_90nm.xml diffeq.blif common 16.22 vpr 62.53 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 438 64 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 64028 64 39 1935 1974 1 1077 541 23 23 529 clb auto 23.3 MiB 0.32 23383 10252 119503 28618 87175 3710 62.5 MiB 1.34 0.02 13.2048 7.13924 -1172.08 -7.13924 7.13924 0.57 0.00281932 0.00216196 0.173313 0.133573 -1 -1 -1 -1 34 18135 26 983127 976439 897386. 1696.38 10.12 0.961872 0.787041 40002 146231 -1 21299 25 9235 32958 4733492 1780844 7.08492 7.08492 -1502.77 -7.08492 0 0 1.04845e+06 1981.94 0.15 1.39 -1 -1 -1 0.15 0.189526 0.162735 +k4_N4_tileable_perimeter_cb_90nm.xml ex5p.blif common 42.06 vpr 58.25 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 366 8 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 59644 8 63 1072 1135 0 894 437 22 22 484 clb auto 19.0 MiB 0.26 20320 11946 86601 21898 63026 1677 58.2 MiB 1.03 0.02 7.95072 5.28278 -233.118 -5.28278 nan 0.52 0.00193544 0.00142786 0.111869 0.0866652 -1 -1 -1 -1 50 20745 26 891726 815929 1.11061e+06 2294.66 36.41 0.787577 0.645158 48048 178976 -1 23642 21 10261 34242 6998785 2421199 5.87736 nan -261.019 -5.87736 0 0 1.28980e+06 2664.87 0.17 1.62 -1 -1 -1 0.17 0.107382 0.0929134 +k4_N4_tileable_perimeter_cb_90nm.xml s298.blif common 53.44 vpr 72.91 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 580 4 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 74664 4 6 1942 1948 1 1169 590 27 27 729 clb auto 25.0 MiB 0.35 27324 13425 148970 41008 107008 954 72.9 MiB 1.84 0.03 15.2089 9.65839 -76.0928 -9.65839 9.65839 0.81 0.00340369 0.00261843 0.224099 0.175111 -1 -1 -1 -1 34 24976 25 1.39333e+06 1.29301e+06 1.24041e+06 1701.52 45.33 1.66978 1.36439 54994 199951 -1 28059 24 11455 54709 8763089 2638513 10.1962 10.1962 -84.1267 -10.1962 0 0 1.45188e+06 1991.60 0.18 2.10 -1 -1 -1 0.18 0.186815 0.160845 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify/config/config.txt new file mode 100755 index 00000000000..a37a1a874db --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify/config/config.txt @@ -0,0 +1,30 @@ +############################################ +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/blif/4 + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=diffeq.blif +circuit_list_add=ex5p.blif +circuit_list_add=s298.blif + +# Add architectures to list to sweep +arch_list_add=k4_N4_tileable_90nm.xml + +# Parse info and how to parse +parse_file=vpr_fixed_chan_width.txt +parse_file=vpr_parse_second_file.txt + +# How to parse QoR info +qor_parse_file=qor_rr_graph.txt + +# Pass requirements +pass_requirements_file=pass_requirements_verify_rr_graph.txt + +script_params=-starting_stage vpr -verify_rr_graph --route_chan_width 60 -track_memory_usage + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify/config/golden_results.txt new file mode 100644 index 00000000000..bb617d6fd9e --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify/config/golden_results.txt @@ -0,0 +1,4 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time initial_placed_wirelength_est placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time initial_placed_CPD_est placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time ap_mem ap_time ap_full_legalizer_mem ap_full_legalizer_time routed_wirelength avg_routed_wirelength routed_wiresegment avg_routed_wiresegment total_nets_routed total_connections_routed total_heap_pushes total_heap_pops logic_block_area_total logic_block_area_used routing_area_total routing_area_per_tile crit_path_route_success_iteration num_rr_graph_nodes num_rr_graph_edges collapsed_nodes critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS create_rr_graph_time create_intra_cluster_rr_graph_time adding_internal_edges route_mem crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time router_lookahead_mem tile_lookahead_computation_time router_lookahead_computation_time +k4_N4_tileable_90nm.xml diffeq.blif common 7.52 vpr 62.34 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 438 64 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 63840 64 39 1935 1974 1 1077 541 23 23 529 clb auto 23.1 MiB 0.32 23383 10407 130518 33236 93921 3361 62.3 MiB 1.44 0.02 13.1424 7.01436 -1186.76 -7.01436 7.01436 0.00 0.00273601 0.00211561 0.187595 0.143154 -1 -1 -1 -1 19245 17.8857 11150 10.3625 7421 25910 2845736 884425 983127 976439 1.22961e+06 2324.41 18 45530 160719 -1 7.37964 7.37964 -1373.48 -7.37964 0 0 -1 -1 -1 62.3 MiB 0.87 0.347277 0.281856 62.3 MiB -1 0.18 +k4_N4_tileable_90nm.xml ex5p.blif common 9.78 vpr 58.43 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 366 8 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 59836 8 63 1072 1135 0 894 437 22 22 484 clb auto 18.8 MiB 0.26 20320 12148 81630 19773 60101 1756 58.4 MiB 0.96 0.02 7.91494 5.42874 -233.124 -5.42874 nan 0.00 0.00181633 0.00135353 0.102123 0.0792786 -1 -1 -1 -1 24636 27.5570 13596 15.2081 10695 36592 10880255 4591282 891726 815929 1.11756e+06 2309.00 31 41484 146288 -1 6.67754 nan -291.675 -6.67754 0 0 -1 -1 -1 58.4 MiB 2.78 0.258159 0.213053 58.4 MiB -1 0.17 +k4_N4_tileable_90nm.xml s298.blif common 11.27 vpr 65.08 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 580 4 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 66640 4 6 1942 1948 1 1169 590 27 27 729 clb auto 25.0 MiB 0.35 27324 13487 148970 40500 107491 979 65.1 MiB 1.82 0.03 15.4416 9.90815 -77.1816 -9.90815 9.90815 0.00 0.00345442 0.0027131 0.213085 0.165433 -1 -1 -1 -1 28424 24.3356 15352 13.1438 11036 53711 8284367 2041058 1.39333e+06 1.29301e+06 1.73135e+06 2374.97 22 63594 225223 -1 9.88398 9.88398 -83.5517 -9.88398 0 0 -1 -1 -1 65.1 MiB 2.28 0.435186 0.358877 65.1 MiB -1 0.26 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify_bin/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify_bin/config/config.txt new file mode 100755 index 00000000000..aa015e865a0 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify_bin/config/config.txt @@ -0,0 +1,30 @@ +############################################ +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/blif/4 + +# Path to directory of architectures to use +archs_dir=arch/timing + +# Add circuits to list to sweep +circuit_list_add=diffeq.blif +circuit_list_add=ex5p.blif +circuit_list_add=s298.blif + +# Add architectures to list to sweep +arch_list_add=k4_N4_tileable_90nm.xml + +# Parse info and how to parse +parse_file=vpr_fixed_chan_width.txt +parse_file=vpr_parse_second_file.txt + +# How to parse QoR info +qor_parse_file=qor_rr_graph.txt + +# Pass requirements +pass_requirements_file=pass_requirements_verify_rr_graph.txt + +script_params=-starting_stage vpr -verify_rr_graph -rr_graph_ext .bin --route_chan_width 100 -track_memory_usage + diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify_bin/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify_bin/config/golden_results.txt new file mode 100644 index 00000000000..8a8cf8f4f29 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify_bin/config/golden_results.txt @@ -0,0 +1,4 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time initial_placed_wirelength_est placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time initial_placed_CPD_est placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time ap_mem ap_time ap_full_legalizer_mem ap_full_legalizer_time routed_wirelength avg_routed_wirelength routed_wiresegment avg_routed_wiresegment total_nets_routed total_connections_routed total_heap_pushes total_heap_pops logic_block_area_total logic_block_area_used routing_area_total routing_area_per_tile crit_path_route_success_iteration num_rr_graph_nodes num_rr_graph_edges collapsed_nodes critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS create_rr_graph_time create_intra_cluster_rr_graph_time adding_internal_edges route_mem crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time router_lookahead_mem tile_lookahead_computation_time router_lookahead_computation_time +k4_N4_tileable_90nm.xml diffeq.blif common 7.48 vpr 65.96 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 438 64 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 67540 64 39 1935 1974 1 1077 541 23 23 529 clb auto 22.9 MiB 0.32 23383 10873 130518 33024 93628 3866 66.0 MiB 1.45 0.02 13.0763 6.99652 -1162.99 -6.99652 6.99652 0.00 0.00285671 0.00221991 0.189815 0.145412 -1 -1 -1 -1 18849 17.5177 10317 9.58829 6567 21821 1854731 449487 983127 976439 2.00514e+06 3790.43 20 70170 268455 -1 7.25476 7.25476 -1304.75 -7.25476 0 0 -1 -1 -1 66.0 MiB 0.75 0.363502 0.296117 66.0 MiB -1 0.31 +k4_N4_tileable_90nm.xml ex5p.blif common 6.79 vpr 58.62 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 366 8 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 60028 8 63 1072 1135 0 894 437 22 22 484 clb auto 18.8 MiB 0.26 20320 12136 81630 20496 59555 1579 58.6 MiB 0.96 0.02 7.72232 5.21275 -226.687 -5.21275 nan 0.00 0.00182646 0.00137253 0.108622 0.0845891 -1 -1 -1 -1 21866 24.4586 10468 11.7092 8501 29146 3873168 891811 891726 815929 1.82197e+06 3764.41 19 63912 244296 -1 5.56516 nan -246.308 -5.56516 0 0 -1 -1 -1 58.6 MiB 1.05 0.222148 0.183769 58.6 MiB -1 0.28 +k4_N4_tileable_90nm.xml s298.blif common 9.95 vpr 85.18 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 580 4 -1 -1 success v8.0.0-13021-g345d251e3-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 13.3.0 on Linux-6.8.0-57-generic x86_64 2025-04-20T19:05:16 agent-3 /home/mohagh18/vtr-verilog-to-routing/vtr_flow/tasks 87228 4 6 1942 1948 1 1169 590 27 27 729 clb auto 25.0 MiB 0.35 27324 13569 146497 39933 105616 948 85.2 MiB 1.82 0.03 15.2069 10.0243 -77.5741 -10.0243 10.0243 0.00 0.00351567 0.00274926 0.230835 0.183363 -1 -1 -1 -1 24793 21.2269 12639 10.8211 7296 37518 4021317 704688 1.39333e+06 1.29301e+06 2.82552e+06 3875.88 19 98122 376471 -1 10.1962 10.1962 -80.531 -10.1962 0 0 -1 -1 -1 85.2 MiB 1.40 0.430119 0.358207 85.2 MiB -1 0.45 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vib/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vib/config/config.txt new file mode 100644 index 00000000000..ab137e3b7da --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vib/config/config.txt @@ -0,0 +1,27 @@ +# +############################################ +# Configuration file for running experiments +############################################## + +# Path to directory of circuits to use +circuits_dir=benchmarks/blif + +# Path to directory of architectures to use +archs_dir=arch/VIB + +# Add circuits to list to sweep +circuit_list_add=tseng.blif + +# Add architectures to list to sweep +arch_list_add=vib_test_arch.xml + +# Parse info and how to parse +parse_file=vpr_standard.txt + +# How to parse QoR info +qor_parse_file=qor_standard.txt + +# Pass requirements +pass_requirements_file=pass_requirements.txt + +script_params=-starting_stage vpr --route_chan_width 138 --max_router_iterations 400 --device ultimate diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vib/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vib/config/golden_results.txt new file mode 100644 index 00000000000..a512510d787 --- /dev/null +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_vib/config/golden_results.txt @@ -0,0 +1,2 @@ +arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_num_rr_graph_nodes crit_path_num_rr_graph_edges crit_path_collapsed_nodes crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_create_rr_graph_time crit_path_create_intra_cluster_rr_graph_time crit_path_tile_lookahead_computation_time crit_path_router_lookahead_computation_time crit_path_total_timing_analysis_time crit_path_total_sta_time +vib_test_arch.xml tseng.blif common 5.38 vpr 65.71 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 135 52 0 0 success unkown release IPO VTR_ASSERT_LEVEL=2 GNU 11.4.0 on Linux-6.8.0-49-generic x86_64 2025-01-16T17:23:42 yuanqiwang-virtual-machine /home/yuanqiwang/Wang/vtr/vtr-patch-1-old/vtr-verilog-to-routing-patch-1/vtr_flow/tasks 67284 52 122 1483 1605 1 1099 309 20 14 280 -1 ultimate 27.7 MiB 0.28 7177 63993 16752 32956 14285 65.7 MiB 0.35 0.01 3.57193 -517.324 -3.57193 3.57193 1.08 0.000907145 0.000734596 0.0683261 0.0559363 -1 9148 14 1.31729e+07 7.27569e+06 2.24313e+06 8011.19 0.60 0.126578 0.108805 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt index 47b1ac1911e..643dfe54c7f 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt @@ -101,9 +101,14 @@ regression_tests/vtr_reg_strong/koios_test regression_tests/vtr_reg_strong/koios_test_no_hb regression_tests/vtr_reg_strong/strong_timing_fail regression_tests/vtr_reg_strong/strong_timing_no_fail +regression_tests/vtr_reg_strong/strong_tileable_rr_graph +regression_tests/vtr_reg_strong/strong_tileable_rr_graph_perimeter_cb +regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify +regression_tests/vtr_reg_strong/strong_tileable_rr_graph_verify_bin regression_tests/vtr_reg_strong/strong_noc regression_tests/vtr_reg_strong/strong_flat_router regression_tests/vtr_reg_strong/strong_routing_constraints +regression_tests/vtr_reg_strong/strong_vib regression_tests/vtr_reg_strong/strong_3d/3d_cb regression_tests/vtr_reg_strong/strong_3d/3d_sb regression_tests/vtr_reg_strong/strong_xilinx_simple