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..41a84dc7e6f --- /dev/null +++ b/doc/src/VIB.rst @@ -0,0 +1,254 @@ +.. _VIB: + +VIB Architecture +============ +The VIB architecture adds modeling support for double-level MUX topology and bent wires. In past, switch blocks have only one level of routing MUXes, whose inputs are 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 original tags of 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 detaild 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 attibute ``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..a7ed928692c 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`` tileable routing resource graph generator. + + 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 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-rectangle 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 perimeter programmable block (mainly I/Os). This is designed to enhance routability of I/Os on perimeter. Also strongly recommended when 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. + By default, it is ``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 cannot be reachable. 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. + By default, it is ``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. + By default, it is ``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. + By default, it is ``false``. + + .. warning:: Please enable this option if you are looking for device support which is created by any release which is before v1.1.541!!! + + .. _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 VPR capability + If not specified, the pass tracks will 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 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 +~~~~~~~~~~~~~~~~ + +OpenFPGA suggests users to give explicit names for each routing segement 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, OpenFPGA 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 OpenFPGA 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 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The direct connection can also drive routing multiplexers of connection blocks. When such connection occures in a connection block, it is called 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, VPR architecture defines feedback connections like: + +.. 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 VPR architecture: + +.. code-block:: xml + + + + + +In OpenFPGA 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 his point to point connection, a truth table with our new parameters id 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..db30862f41d 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(); @@ -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++) { @@ -1048,9 +1051,9 @@ 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 auto& 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 auto& 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) { diff --git a/libs/libarchfpga/src/parse_switchblocks.cpp b/libs/libarchfpga/src/parse_switchblocks.cpp index 3ef9ec8e155..e3558020456 100644 --- a/libs/libarchfpga/src/parse_switchblocks.cpp +++ b/libs/libarchfpga/src/parse_switchblocks.cpp @@ -201,7 +201,7 @@ t_wire_switchpoints parse_wireconn_from_to_node(pugi::xml_node node, const pugiu 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, ",")) { + for (const auto& point_str : vtr::StringToken(points_str).split(",")) { int switchpoint = vtr::atoi(point_str); wire_switchpoints.switchpoints.push_back(switchpoint); } @@ -229,7 +229,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) { - auto types = vtr::split(ch, ","); + auto types = vtr::StringToken(ch).split(","); if (types.empty()) { archfpga_throw(__FILE__, __LINE__, "parse_comma_separated_wire_types: found empty wireconn wire type entry\n"); @@ -245,7 +245,7 @@ static void parse_comma_separated_wire_types(const char* ch, std::vector& wire_switchpoints) { - auto points = vtr::split(ch, ","); + auto points = vtr::StringToken(ch).split(","); if (points.empty()) { archfpga_throw(__FILE__, __LINE__, "parse_comma_separated_wire_points: found empty wireconn wire point entry\n"); } diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index a3ca918d68e..6a60c902097 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -42,6 +42,8 @@ #include "logic_types.h" #include "clock_types.h" +#include "vib_inf.h" + //Forward declarations struct t_clock_network; struct t_power_arch; @@ -1821,6 +1823,24 @@ struct t_segment_inf { */ 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 @@ -2262,7 +2282,7 @@ 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.*/ @@ -2272,12 +2292,43 @@ struct t_arch { /// Secure hash digest of the architecture file to uniquely identify this architecture char* architecture_id; + // Options for tileable routing architectures + + /// 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; + + // End of tileable architecture options + t_chan_width_dist Chans; enum e_switch_block_type SBType; + enum e_switch_block_type SBSubType; std::vector switchblocks; float R_minW_nmos; float R_minW_pmos; int Fs; + int sub_fs; float grid_logic_tile_area; std::vector Segments; @@ -2323,21 +2374,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; + /// VIB grid layouts + std::vector vib_grid_layouts; + 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; + + // added for vib + std::vector vib_infs; }; diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp index 53760953a7a..928854b5bd9 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, " "); + auto 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, "."); + auto parts = vtr::StringToken(ep).split("."); auto bel = parts[0]; auto pin = parts[1]; @@ -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 (auto 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 (auto other_ep : vtr::StringToken(is_backward ? other_ic->output_string : other_ic->input_string).split(" ")) { found |= other_ep == ep; } diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index dc76bff281e..51241fa17a6 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,7 +294,6 @@ 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); /** @@ -388,6 +375,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, std::vector& list, std::vector& part_len, bool& is_bend, const int len, const pugiutil::loc_data& loc_data); + /* * * @@ -447,6 +436,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); @@ -485,6 +480,12 @@ void xml_read_arch(const char* ArchFile, arch->directs = process_directs(Next, arch->switches, loc_data); } + /* Process vib_arch */ + Next = get_single_child(architecture, "vib_arch", loc_data, ReqOpt::OPTIONAL); + if (Next) { + process_vib_arch(Next, PhysicalTileTypes, arch, loc_data); + } + /* Process Clock Networks */ Next = get_single_child(architecture, "clocknetworks", loc_data, ReqOpt::OPTIONAL); if (Next) { @@ -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,8 +2919,9 @@ 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; } else if (strcmp(Prop, "universal") == 0) { @@ -2969,9 +2935,31 @@ static void process_device(pugi::xml_node Node, t_arch* arch, t_default_fc_spec& 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()); } + /* Parse attribute 'sub_type', representing the minor connectivity pattern for switch blocks + * If not specified, the 'sub_type' is the same as major type + * This option is only valid for tileable routing resource graph builder + * Note that sub_type does not support custom switch block pattern!!! + * If 'sub_type' is specified, the custom switch block for 'type' is not allowed! + */ + std::string sub_type_str = get_attribute(Cur, "sub_type", loc_data, BoolToReqOpt(false)).as_string(""); + if (!sub_type_str.empty()) { + if (sub_type_str == std::string("wilton")) { + arch->SBSubType = WILTON; + } else if (sub_type_str == std::string("universal")) { + arch->SBSubType = UNIVERSAL; + } else if (sub_type_str == std::string("subset")) { + arch->SBSubType = SUBSET; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Cur), + "Unknown property %s for switch block subtype x\n", sub_type_str.c_str()); + } + } else { + arch->SBSubType = arch->SBType; + } ReqOpt CUSTOM_SWITCHBLOCK_REQD = BoolToReqOpt(!custom_switch_block); arch->Fs = get_attribute(Cur, "fs", loc_data, CUSTOM_SWITCHBLOCK_REQD).as_int(3); + arch->sub_fs = get_attribute(Cur, "sub_fs", loc_data, BoolToReqOpt(false)).as_int(arch->Fs); Cur = get_single_child(Node, "default_fc", loc_data, ReqOpt::OPTIONAL); if (Cur) { @@ -3519,7 +3507,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) { @@ -3933,7 +3921,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,6 +4056,15 @@ static std::vector process_segments(pugi::xml_node Parent, process_cb_sb(SubElem, Segs[i].sb, loc_data); } + /* 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].bend, Segs[i].part_len, Segs[i].is_bend, (length - 1), loc_data); + } + } + /*Store the index of this segment in Segs vector*/ Segs[i].seg_index = i; /* Get next Node */ @@ -4081,6 +4080,71 @@ static std::vector process_segments(pugi::xml_node Parent, return Segs; } +static void process_bend(pugi::xml_node Node, std::vector& list, std::vector& part_len, bool& is_bend, const int len, const pugiutil::loc_data& loc_data) { + + 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()); + } + + 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); 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..d722212a682 --- /dev/null +++ b/libs/libarchfpga/src/read_xml_arch_file_vib.cpp @@ -0,0 +1,474 @@ +#include "read_xml_arch_file_vib.h" + +#include "vtr_log.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, std::vector& PhysicalTileTypes, t_arch* arch, const pugiutil::loc_data& loc_data); + +static void process_first_stage(pugi::xml_node Stage_node, std::vector& PhysicalTileTypes, std::vector& first_stages, const pugiutil::loc_data& loc_data); + +static void process_second_stage(pugi::xml_node Stage_node, std::vector& PhysicalTileTypes, std::vector& second_stages, 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, std::vector& PhysicalTileTypes, 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, PhysicalTileTypes, arch, loc_data); + Node = Node.next_sibling(Node.name()); + } +} + +static void process_vib(pugi::xml_node Vib_node, std::vector& PhysicalTileTypes, 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()) { + std::string str_tmp; + str_tmp = tmp; + vib.set_switch_name(str_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_vib::BOTH_DIR; /*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_vib::X; + } else if (tmp == "y") { + seg_group.axis = e_parallel_axis_vib::Y; + } else { + archfpga_throw(loc_data.filename_c_str(), loc_data.line(Node), "Unsopported parralel 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 SubElem = get_single_child(Node, "first_stage", loc_data); + if (SubElem) { + std::vector first_stages; + process_first_stage(SubElem, PhysicalTileTypes, first_stages, loc_data); + + for (auto first_stage : first_stages) { + vib.push_first_stage(first_stage); + } + } + + SubElem = get_single_child(Node, "second_stage", loc_data); + if (SubElem) { + std::vector second_stages; + process_second_stage(SubElem, PhysicalTileTypes, second_stages, loc_data); + + for (auto second_stage : second_stages) { + vib.push_second_stage(second_stage); + } + } + + arch->vib_infs.push_back(vib); +} + +static void process_first_stage(pugi::xml_node Stage_node, std::vector& /*PhysicalTileTypes*/, std::vector& first_stages, const pugiutil::loc_data& loc_data) { + 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 SubElem = 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(SubElem.child_value()).split(" \t\n"); + first_stage_mux.from_tokens.push_back(from_tokens); + SubElem = SubElem.next_sibling(SubElem.name()); + } + first_stages.push_back(first_stage_mux); + + Node = Node.next_sibling(Node.name()); + } +} + +static void process_second_stage(pugi::xml_node Stage_node, std::vector& /*PhysicalTileTypes*/, std::vector& second_stages, const pugiutil::loc_data& loc_data) { + 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 SubElem = 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(SubElem.child_value()).split(" \t\n"); + VTR_ASSERT(to_tokens.size() == 1); + second_stage_mux.to_tokens = to_tokens; + + SubElem = 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(SubElem.child_value()).split(" \t\n"); + second_stage_mux.from_tokens.push_back(from_tokens); + SubElem = SubElem.next_sibling(SubElem.name()); + } + + second_stages.push_back(second_stage_mux); + + Node = Node.next_sibling(Node.name()); + } +} + +/* 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 (layout_type_tag.name() == std::string("auto_layout")) { + ++auto_layout_cnt; + } else if (layout_type_tag.name() == std::string("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 (layout_type_tag.name() == std::string("auto_layout")) { + //expect_only_attributes(layout_type_tag, {"aspect_ratio"}, loc_data); + + grid_def.grid_type = VibGridDefType::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; + } + } + //grid_def.aspect_ratio = get_attribute(layout_type_tag, "aspect_ratio", loc_data, ReqOpt::OPTIONAL).as_float(1.); + + } else if (layout_type_tag.name() == std::string("fixed_layout")) { + expect_only_attributes(layout_type_tag, {"name"}, loc_data); + + grid_def.grid_type = VibGridDefType::VIB_FIXED; + //grid_def.width = get_attribute(layout_type_tag, "width", loc_data).as_int(); + //grid_def.height = get_attribute(layout_type_tag, "height", loc_data).as_int(); + 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()) { + auto 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 == std::string("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 auto& 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 == std::string("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 auto& 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 == std::string("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 == std::string("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 == std::string("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 == std::string("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 == std::string("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); + } + } +} 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..5d7ac7c6b53 --- /dev/null +++ b/libs/libarchfpga/src/read_xml_arch_file_vib.h @@ -0,0 +1,11 @@ +#include +#include "physical_types.h" + +#include "pugixml.hpp" +#include "pugixml_util.hpp" + +void process_vib_arch(pugi::xml_node Parent, std::vector& PhysicalTileTypes, 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..a4bb1b10f23 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; + 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; +} diff --git a/libs/libarchfpga/src/read_xml_util.h b/libs/libarchfpga/src/read_xml_util.h index e9cf49d1e18..e3f4e7a451f 100644 --- a/libs/libarchfpga/src/read_xml_util.h +++ b/libs/libarchfpga/src/read_xml_util.h @@ -23,3 +23,19 @@ 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); + +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..f3baf83b068 --- /dev/null +++ b/libs/libarchfpga/src/vib_inf.h @@ -0,0 +1,277 @@ +#pragma once + +#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 The parallel axis of the VIB segment group. + */ +enum class e_parallel_axis_vib { + X, + Y, + BOTH_DIR +}; + +/** + * @brief Segment group information. + */ +struct t_seg_group { + std::string name; + e_parallel_axis_vib 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 +}; + +/* VibInf is used to reserve the VIB information. * + * For example, a VIB is described: * + * + * + * + * + * + * + * + * L1.E0 L1.E1 + * clb.O[0] L1.E2 + * + * + * clb.I[0] MUX0 MUX1 + * L1.N0 MUX0 MUX1 + * + * + * + * + * 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] + * +-------------+ + * + * Attributes: + * name_: The name of the VIB type, "vib0" in the example. + * pbtype_name_: The pbtype of the VIB, "clb" in the example. + * seg_group_num_: The number of segment groups. + * seg_groups_: The segments applied in the VIB. Their names correspond to segment names in . + * switch_name_:The name of the switch type used in the VIB, "mux0" in the example. + * switch_idx_: The index of corresponding switch in . + * first_stages_: The info of first stage MUXes, including the names of the MUXes and their from info. + * second_stages_: The info of second stage MUXes, including the names of the MUXes and their from/to info.*/ + +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: + std::string name_; /* vib name */ + std::string pbtype_name_; /* pbtype name of vib */ + int seg_group_num_; /* seg group number of vib */ + int switch_idx_; /* vib switch index */ + std::string switch_name_; /* vib switch name */ + std::vector seg_groups_; + std::vector first_stages_; + 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)) {} + + std::string start_expr; //Starting position (inclusive) + std::string end_expr; //Ending position (inclusive) + + std::string repeat_expr; //Distance between repeated + // region instances + + std::string incr_expr; //Distance between block instantiations + // with the region +}; + +enum VibGridDefType { + 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 { + VibGridDefType grid_type = VibGridDefType::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 62cbb3f5903..6b89f79298c 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, @@ -403,6 +406,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) { @@ -416,7 +420,7 @@ void check_rr_node(const RRGraphView& rr_graph, break; case e_rr_type::CHANX: - if (xlow < 1 || xhigh > int(grid.width()) - 2 || yhigh > int(grid.height()) - 2 || yhigh != ylow) { + if (xhigh > int(grid.width()) - 1 || yhigh > int(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); } @@ -427,7 +431,7 @@ void check_rr_node(const RRGraphView& rr_graph, break; case e_rr_type::CHANY: - if (xhigh > int(grid.width()) - 2 || ylow < 1 || yhigh > int(grid.height()) - 2 || xlow != xhigh) { + if (xhigh > int(grid.width()) - 2 || yhigh > int(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); } @@ -446,6 +450,14 @@ 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); + 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) { @@ -463,7 +475,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 828438faab3..0d601149d3f 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); @@ -15,6 +16,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..829e88cc33f 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) { + enum 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) { 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..02fefb68628 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); + enum 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 173914241d3..6a4dde3bb9d 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_ptc_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); @@ -55,6 +68,28 @@ 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_ptc_nums_.emplace_back(); + RRNodeId new_node = RRNodeId(node_storage_.size() - 1); + node_storage_.set_node_layer(new_node, layer); + node_storage_.set_node_type(new_node, type); + node_storage_.set_node_coordinates(new_node, x, y, x, y); + node_storage_.set_node_ptc_num(new_node, ptc); + if (e_rr_type::OPIN == type || e_rr_type::IPIN == type) { + node_storage_.add_node_side(new_node, node_side); + } + /* Special for CHANX, being consistent with the rule in find_node() */ + 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); } @@ -66,10 +101,15 @@ void RRGraphBuilder::clear_temp_storage() { void RRGraphBuilder::clear() { node_lookup_.clear(); node_storage_.clear(); + node_in_edges_.clear(); + node_ptc_nums_.clear(); rr_node_metadata_.clear(); rr_edge_metadata_.clear(); rr_segments_.clear(); rr_switch_inf_.clear(); + edges_to_build_.clear(); + is_edge_dirty_ = true; + is_incoming_edge_dirty_ = true; } void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_nodes_algorithm, @@ -141,3 +181,132 @@ void RRGraphBuilder::reorder_nodes(e_rr_node_reorder_algorithm reorder_rr_graph_ std::get<2>(edge)); }); } + +void RRGraphBuilder::create_edge(RRNodeId src, RRNodeId dest, RRSwitchId edge_switch, bool remapped) { + edges_to_build_.emplace_back(src, dest, size_t(edge_switch), remapped); + is_edge_dirty_ = true; /* Adding a new edge revokes the flag */ + is_incoming_edge_dirty_ = true; +} + +void RRGraphBuilder::build_edges(const bool& uniquify) { + if (uniquify) { + std::sort(edges_to_build_.begin(), edges_to_build_.end()); + edges_to_build_.erase(std::unique(edges_to_build_.begin(), edges_to_build_.end()), edges_to_build_.end()); + } + alloc_and_load_edges(&edges_to_build_); + edges_to_build_.clear(); + is_edge_dirty_ = false; +} + +void RRGraphBuilder::build_in_edges() { + VTR_ASSERT(validate()); + node_in_edges_.clear(); + node_in_edges_.resize(node_storage_.size()); + + for (RRNodeId src_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + for (auto iedge : node_storage_.edges(src_node)) { + VTR_ASSERT(src_node == node_storage_.edge_source_node(node_storage_.edge_id(src_node, iedge))); + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, iedge)); + node_in_edges_[des_node].push_back(node_storage_.edge_id(src_node, iedge)); + } + } + is_incoming_edge_dirty_ = false; +} + +std::vector RRGraphBuilder::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (is_incoming_edge_dirty_) { + VTR_LOG_ERROR("Incoming edges are not built yet in routing resource graph. Please call build_in_edges()."); + return std::vector(); + } + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +void RRGraphBuilder::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_ptc_nums_.size()); + node_ptc_nums_[node].resize(ptc_tokens.size()); + for (size_t iptc = 0; iptc < ptc_tokens.size(); iptc++) { + node_ptc_nums_[node][iptc] = std::stoi(ptc_tokens[iptc]); + } + } +} + +std::string RRGraphBuilder::node_ptc_nums_to_string(RRNodeId node) const { + if (node_ptc_nums_.empty()) { + return std::to_string(size_t(node_storage_.node_ptc_num(node))); + } + VTR_ASSERT(size_t(node) < node_ptc_nums_.size()); + if (node_ptc_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_ptc_nums_[node].size(); iptc++) { + ret += std::to_string(size_t(node_ptc_nums_[node][iptc])) + ","; + } + /* Remove the last comma */ + ret.pop_back(); + return ret; +} + +bool RRGraphBuilder::node_contain_multiple_ptc(RRNodeId node) const { + if (node_ptc_nums_.empty()) { + return false; + } + return node_ptc_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_ptc_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_ptc_nums_[node].size()) { + node_ptc_nums_[node].resize(node_length + 1); + } + + size_t offset = node_offset.x() - node_storage_.node_xlow(node) + node_offset.y() - node_storage_.node_ylow(node); + VTR_ASSERT(offset < node_ptc_nums_[node].size()); + + node_ptc_nums_[node][offset] = track_id; +} + +void RRGraphBuilder::add_track_node_to_lookup(RRNodeId node) { + VTR_ASSERT_MSG(node_storage_.node_type(node) == 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 + * FIXME: Special case on assigning CHANX (x,y) should be changed to a natural way! + */ + if (e_rr_type::CHANX == node_type || e_rr_type::CHANY == node_type) { + ptc = node_type == e_rr_type::CHANX ? node_ptc_nums_[node][x - node_storage_.node_xlow(node)] : node_ptc_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..77b41fafaa2 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 -- */ @@ -43,6 +44,10 @@ class RRGraphBuilder { MetadataStorage& rr_node_metadata(); /** @brief Return a writable object for the meta data on the edge */ MetadataStorage>& rr_edge_metadata(); + /** @brief Return a writable object fo the incoming edge storage */ + vtr::vector>& node_in_edge_storage(); + /** @brief Return a writable object of the node ptc storage (for tileable routing resource graph) */ + vtr::vector>& node_ptc_storage(); /** @brief Return the size for rr_node_metadata */ inline size_t rr_node_metadata_size() const { @@ -122,10 +127,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 +193,22 @@ 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 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 +241,60 @@ class RRGraphBuilder { node_storage_.set_node_track_num(id, new_track_num); } - /** @brief set_ node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ + /** @brief Add a track id for a given node base on the offset in coordinate, applicable only to CHANX and CHANY nodes. + * This API is used by tileable routing resource graph generator, which requires each routing track has a different + * track id depending their location in FPGA fabric. + */ + void add_node_track_num(RRNodeId node, vtr::Point node_offset, short track_id); + + /** @brief Update the node_lookup for a track node. This is applicable to tileable routing graph */ + void add_track_node_to_lookup(RRNodeId node); + + /** @brief set_node_class_num() is designed for routing source and sinks, which are SOURCE and SINK nodes */ inline void set_node_class_num(RRNodeId id, int new_class_num) { node_storage_.set_node_class_num(id, new_class_num); } + /** @brief set_node_mux_num() is designed for routing mux nodes */ + inline void set_node_mux_num(RRNodeId id, int new_class_num) { + node_storage_.set_node_mux_num(id, new_class_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. Not suggested for internal builder!!! */ + 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. Not suggested for internal builder!!! */ + std::string node_ptc_nums_to_string(RRNodeId node) const; + + /** @brief Identify if a node contains multiple ptc numbers. Mainly used by I/O reader only. Not suggest for internal builder */ + 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(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; + /** @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); @@ -301,6 +370,9 @@ class RRGraphBuilder { return node_storage_.count_rr_switches(arch_switch_inf, arch_switch_fanins); } + /* Unlock storage; required to modify an routing resource graph after edge is read */ + inline void unlock_storage() { node_storage_.edges_read_ = false; node_storage_.partitioned_ = false; node_storage_.clear_node_first_edge();} + /** @brief Reserve the lists of nodes, edges, switches etc. to be memory efficient. * This function is mainly used to reserve memory space inside RRGraph, * when adding a large number of nodes/edge/switches/segments, @@ -319,19 +391,24 @@ 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. Do not use for internal builder */ + inline void resize_node_ptc_nums(size_t size) { + node_ptc_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. Also there are no edges left to be built! * @note This function is used to validate the correctness of the routing resource graph in terms * of graph attributes. Strongly recommend to call it when you finish the building a routing resource * graph. If you need more advance checks, which are related to architecture features, you should * consider to use the check_rr_graph() function or build your own check_rr_graph() function. */ inline bool validate() const { - return node_storage_.validate(rr_switch_inf_); + return node_storage_.validate(rr_switch_inf_) && edges_to_build_.empty(); } /** @brief Sorts edge data such that configurable edges appears before @@ -378,6 +455,14 @@ class RRGraphBuilder { /* Fast look-up for rr nodes */ RRSpatialLookup node_lookup_; + /* A cache for edge-related information, required to build edges for routing resource nodes. + * It is used when building a routing resource graph by considering memory efficiency. + * It will be clear up after calling build_edges(). + * + * .. warning:: This is a temporary data which is used to collect edges to be built for nodes + */ + t_rr_edge_info_set edges_to_build_; + /** Wire segment types in RR graph * - Each rr_segment contains the detailed information of a routing track, which is denoted by a node in CHANX or CHANY type. * - We use a fly-weight data structure here, in the same philosophy as the rr_indexed_data. See detailed explanation in the t_segment_inf data structure @@ -391,6 +476,29 @@ class RRGraphBuilder { /* Detailed information about the switches, which are used in the RRGraph */ vtr::vector rr_switch_inf_; + /** A list of incoming edges for each routing resource node. This can be built optionally, as required by applications. + * By default, it is empty! Call build_in_edges() to construct it!!! */ + vtr::vector> node_in_edges_; + + /* Extra ptc number for each routing resource node. This is required by tileable routing resource graph. + * In a tileable routing architecture, routing tracks, e.g., CHANX and CHANY, follows a staggered organization. + * Hence, a routing track may appear in different routing channels, representing different ptc/track id. + * Here is an illustrative example of a X-direction routing track (CHANX) in INC direction, which is organized in staggered way. + * + * Coord(x,y) (1,0) (2,0) (3,0) (4,0) Another track (node) + * ptc=0 ------> ------> + * \ / + * ptc=1 ------> / + * \ / + * ptc=2 ------> / + * \ / + * ptc=3 -------> + * ^ ^ + * | | + * starting point ending point + */ + vtr::vector> node_ptc_nums_; + /** @warning The Metadata should stay as an independent data structure 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 @@ -415,4 +523,9 @@ class RRGraphBuilder { * value: map of */ MetadataStorage> rr_edge_metadata_; + + /** @brief a flag to mark the status of edge storage + * dirty means that the edge storage is not complete, should call related APIs to build */ + bool is_edge_dirty_; + bool is_incoming_edge_dirty_; }; 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 fe23bbc248c..4dd57faf165 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; } @@ -696,6 +711,17 @@ static int get_node_class_num( 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_), @@ -714,6 +740,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; @@ -723,7 +755,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; @@ -741,10 +773,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; @@ -795,16 +835,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_); @@ -819,7 +866,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), @@ -844,7 +893,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]; } @@ -860,8 +909,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..20f475dc4bc 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,14 @@ class t_rr_graph_storage { return node_storage_[id].yhigh_; } + 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 +229,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 +333,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 +364,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 +373,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]; } @@ -400,17 +412,22 @@ class t_rr_graph_storage { } /** @brief Get the source node for the specified edge. */ - RRNodeId edge_src_node(const RREdgeId& edge) const { + 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 { + RRNodeId edge_sink_node(const RREdgeId edge) const { VTR_ASSERT_DEBUG(edge.is_valid()); return edge_dest_node_[edge]; } + // Get the source node for the specified edge. + RRNodeId edge_source_node(const RREdgeId edge) const { + return edge_src_node_[edge]; + } + /** @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++) { @@ -424,12 +441,17 @@ class t_rr_graph_storage { * 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)); } + // 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 { + short edge_switch(const RREdgeId edge) const { return edge_switch_[edge]; } @@ -438,7 +460,7 @@ class t_rr_graph_storage { * 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)); } @@ -492,6 +514,8 @@ class t_rr_graph_storage { node_ptc_.reserve(node_storage_.capacity()); node_ptc_.resize(node_storage_.size()); node_layer_.resize(node_storage_.size()); + node_bend_start_.resize(node_storage_.size()); + node_bend_end_.resize(node_storage_.size()); } /** @brief Reserve storage for RR nodes. */ @@ -501,6 +525,8 @@ class t_rr_graph_storage { node_storage_.reserve(size); node_ptc_.reserve(size); node_layer_.reserve(size); + node_bend_start_.reserve(size); + node_bend_end_.reserve(size); } /** @brief Resize node storage to accomidate size RR nodes. */ @@ -510,6 +536,8 @@ class t_rr_graph_storage { node_storage_.resize(size); node_ptc_.resize(size); node_layer_.resize(size); + node_bend_start_.resize(size); + node_bend_end_.resize(size); } /** @brief Number of RR nodes that can be accessed. */ @@ -531,6 +559,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(); @@ -566,6 +596,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 +612,8 @@ class t_rr_graph_storage { node_storage_.emplace_back(); node_ptc_.emplace_back(); node_layer_.emplace_back(); + 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 +630,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 +792,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'", @@ -766,6 +804,7 @@ class t_rr_graph_storage { return side_tt[size_t(side)]; } + public: inline void clear_node_first_edge() { node_first_edge_.clear(); } @@ -874,6 +913,14 @@ class t_rr_graph_storage { */ vtr::vector edge_remapped_; + /** @brief + * 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. + */ + vtr::vector node_bend_start_; + vtr::vector node_bend_end_; + /*************** * State flags * ***************/ @@ -927,7 +974,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 +986,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 +1036,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 +1194,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..f5654a9c221 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_ptc_nums) : node_storage_(node_storage) , node_lookup_(node_lookup) , rr_node_metadata_(rr_node_metadata) @@ -17,5 +19,117 @@ RRGraphView::RRGraphView(const t_rr_graph_storage& node_storage, , rr_indexed_data_(rr_indexed_data) , rr_rc_data_(rr_rc_data) , rr_segments_(rr_segments) - , rr_switch_inf_(rr_switch_inf) { + , rr_switch_inf_(rr_switch_inf) + , node_in_edges_(node_in_edges) + , node_ptc_nums_(node_ptc_nums) { } + +std::vector RRGraphView::node_in_edges(RRNodeId node) const { + VTR_ASSERT(size_t(node) < node_storage_.size()); + if (node_in_edges_.empty()) { + return std::vector(); + } + return node_in_edges_[node]; +} + +std::vector RRGraphView::node_configurable_in_edges(RRNodeId node) const { + /* Note: This is not efficient in runtime, should sort edges by configurability when allocating the array! */ + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (rr_switch_inf_[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: This is not efficient in runtime, should sort edges by configurability when allocating the array! */ + VTR_ASSERT(size_t(node) < node_storage_.size()); + std::vector ret_edges; + if (node_in_edges_.empty()) { + return ret_edges; + } + for (const RREdgeId& edge : node_in_edges_[node]) { + if (!rr_switch_inf_[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 (auto edge_list : node_in_edges_) { + edge_count += edge_list.size(); + } + return edge_count; +} + +bool RRGraphView::validate_in_edges() const { + size_t num_err = 0; + /* For each edge, validate that + * - The source node is in the fan-in edge list of the destination node + * - The sink node is in the fan-out edge list of the source node + */ + for (RRNodeId curr_node : vtr::StrongIdRange(RRNodeId(0), RRNodeId(node_storage_.size()))) { + /* curr_node ---> des_node + * <-?- check if the incoming edge is correct or not + */ + for (auto iedge : node_storage_.edges(curr_node)) { + RRNodeId des_node = node_storage_.edge_sink_node(node_storage_.edge_id(curr_node, iedge)); + std::vector des_fanin_nodes; + for (auto next_edge : node_in_edges(des_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_source_node(next_edge); + des_fanin_nodes.push_back(prev_edge_des_node); + } + if (des_fanin_nodes.end() == std::find(des_fanin_nodes.begin(), des_fanin_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-in edges of Node '%s', while does drive it in its fan-out list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(des_node).c_str()); + num_err++; + } + } + /* src_node -?-> curr_node + * <--- check if the fan-out edge is correct or not + */ + for (auto iedge : node_in_edges(curr_node)) { + RRNodeId src_node = node_storage_.edge_source_node(iedge); + std::vector src_fanout_nodes; + for (auto prev_edge : node_storage_.edges(src_node)) { + RRNodeId prev_edge_des_node = node_storage_.edge_sink_node(node_storage_.edge_id(src_node, prev_edge)); + src_fanout_nodes.push_back(prev_edge_des_node); + } + if (src_fanout_nodes.end() == std::find(src_fanout_nodes.begin(), src_fanout_nodes.end(), curr_node)) { + VTR_LOG_ERROR("Node '%s' does not appear in the fan-out edges of Node '%s', while does exist in its fan-in list\n", + node_coordinate_to_string(curr_node).c_str(), node_coordinate_to_string(src_node).c_str()); + num_err++; + } + } + } + if (num_err) { + VTR_LOG_ERROR("Found %ld errors when validating incoming edges for routing resource graph\n", num_err); + return false; + } + return true; +} + + diff --git a/libs/librrgraph/src/base/rr_graph_view.h b/libs/librrgraph/src/base/rr_graph_view.h index 0fe36d8cef0..df5ac062d36 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 @@ -71,19 +76,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: @@ -222,6 +229,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. */ @@ -428,6 +453,22 @@ 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. + * 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 @@ -438,6 +479,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 { @@ -450,12 +500,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 @@ -465,6 +526,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 @@ -480,6 +548,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 { @@ -529,6 +607,25 @@ 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 + * Require 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 + */ + std::vector node_configurable_in_edges(RRNodeId node) const; + + /** + * @brief Return non-configurable incoming edges for a given routing resource node + */ + 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. @@ -603,6 +700,21 @@ class RRGraphView { return node_storage_.validate_node(node_id, rr_switch_inf_); } + /** @brief Check if the node id is a valid one in storage */ + inline bool valid_node(RRNodeId node_id) const { + return size_t(node_id) < node_storage_.size(); + } + + /** @brief Check if the switch is a valid one in storage */ + inline bool valid_switch(RRSwitchId switch_id) const { + return (size_t(switch_id) < rr_switch_inf_.size()); + } + + /** @brief Validate if all the fan-in edge lists are valid */ + bool validate_in_edges() const; + /** @brief Count the number of incoming edges for all the nodes */ + size_t in_edges_count() const; + /* -- Internal data storage -- */ /* Note: only read-only object or data structures are allowed!!! */ private: @@ -645,4 +757,11 @@ 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. See details in RRGraphBuilder class */ + const vtr::vector>& node_ptc_nums_; }; diff --git a/libs/librrgraph/src/base/rr_node_types.h b/libs/librrgraph/src/base/rr_node_types.h index 4df21fdb3d2..4cdf5af0859 100644 --- a/libs/librrgraph/src/base/rr_node_types.h +++ b/libs/librrgraph/src/base/rr_node_types.h @@ -29,14 +29,15 @@ 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::OPIN, e_rr_type::CHANX, e_rr_type::CHANY, e_rr_type::MUX}}; -constexpr vtr::array rr_node_typename {"SOURCE", "SINK", "IPIN", "OPIN", "CHANX", "CHANY"}; +constexpr vtr::array rr_node_typename {"SOURCE", "SINK", "IPIN", "OPIN", "CHANX", "CHANY", "MUX"}; /** * @enum Direction diff --git a/libs/librrgraph/src/base/rr_spatial_lookup.cpp b/libs/librrgraph/src/base/rr_spatial_lookup.cpp index 3b53404e6f8..72574328dc0 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 bd7ff068198..af5d6228e13 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", "SOURCE", "SINK", "OPIN", "IPIN"}; +constexpr const char *lookup_node_type[] = {"UXSD_INVALID", "CHANX", "CHANY", "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"}; @@ -2460,7 +2460,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); @@ -2471,7 +2471,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 */ @@ -3410,7 +3410,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); @@ -3596,8 +3596,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; @@ -3606,8 +3604,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 0b0b50ab8ff..d0e05adb356 100644 --- a/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h +++ b/libs/librrgraph/src/io/gen/rr_graph_uxsdcxx_capnp.h @@ -762,6 +762,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); } @@ -835,7 +836,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 70a2aae0d07..da06ab12b74 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, SOURCE, SINK, OPIN, IPIN}; +enum class enum_node_type {UXSD_INVALID = 0, CHANX, CHANY, 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 a958fc35aba..b6f415ca4f5 100644 --- a/libs/librrgraph/src/io/rr_graph.xsd +++ b/libs/librrgraph/src/io/rr_graph.xsd @@ -233,6 +233,7 @@ + @@ -281,7 +282,7 @@ - + diff --git a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h index 2f23c13d77c..ca523b5a4d1 100644 --- a/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h +++ b/libs/librrgraph/src/io/rr_graph_uxsdcxx_serializer.h @@ -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()); @@ -865,6 +871,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 @@ -1821,10 +1828,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_); @@ -1844,7 +1853,14 @@ class RrGraphSerializer final : public uxsd::RrGraphBase { /* 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()); + } } } @@ -2004,6 +2020,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", @@ -2024,6 +2042,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 001c03fe898..dbc13f6cd92 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 @@ -348,6 +348,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. @@ -743,6 +744,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..28e5e09c992 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); + auto 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); + auto 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..a3a50acdffe 100644 --- a/libs/libvtrutil/src/vtr_util.cpp +++ b/libs/libvtrutil/src/vtr_util.cpp @@ -21,56 +21,6 @@ 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 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 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 - - //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); @@ -416,7 +366,7 @@ 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. @@ -428,4 +378,167 @@ 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 + ***********************************************************************/ +/* Get the data string */ +std::string StringToken::data() const { + return data_; +} + +/* Split the string using a given delim */ +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; +} + +/* Split the string using a given delim */ +std::vector StringToken::split(const char& delim) const { + /* Create delims */ + std::string delims(1, delim); + + return split(delims); +} + +/* Split the string using a given delim */ +std::vector StringToken::split(const char* delim) const { + /* Create delims */ + std::string delims(delim); + + return split(delims); +} + +/* Split the string using a given delim */ +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); +} + +/* Split the string */ +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; +} + +/* Add a delima to the list */ +void StringToken::add_delim(const char& delim) { + delims_.push_back(delim); +} + +/* Remove the string repeated at the beginning of string */ +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); +} + +/* Remove the string repeated at the end of string */ +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..cfb20b9b407 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); @@ -148,4 +140,72 @@ bool exactly_k_conditions(int k, Conditions... conditions) { 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 */ + 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); + void add_delim(const char& delim); + void ltrim(const std::string& sensitive_word); + 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..203732f1c57 100644 --- a/utils/fasm/src/fasm.cpp +++ b/utils/fasm/src/fasm.cpp @@ -67,7 +67,7 @@ void FasmWriterVisitor::visit_clb_impl(ClusterBlockId blk_id, const t_pb* clb) { VTR_ASSERT(value != nullptr); // Parse placeholder definition - std::vector tag_defs = vtr::split(value->front().as_string().get(strings_), "\n"); + std::vector tag_defs = vtr::StringToken(value->front().as_string().get(strings_)).split("\n"); for (auto& tag_def: tag_defs) { auto parts = split_fasm_entry(tag_def, "=:", "\t "); if (parts.empty()) { @@ -93,7 +93,7 @@ void FasmWriterVisitor::visit_clb_impl(ClusterBlockId blk_id, const t_pb* clb) { auto* 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, @@ -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__, @@ -624,7 +624,7 @@ 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")) { + for(const auto& param : vtr::StringToken(fasm_params_str).split("\n")) { auto param_parts = split_fasm_entry(param, "=", "\t "); if(param_parts.empty()) { continue; @@ -732,7 +732,7 @@ void FasmWriterVisitor::output_fasm_mux(std::string_view fasm_mux_str, 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"); + auto mux_inputs = vtr::StringToken(fasm_mux_str).split("\n"); bool have_prefix = false; std::string clb_prefix; @@ -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], "."); + auto 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"); + auto 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 6411dd97f06..4ce20a17ed4 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..2859fa3b75f 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -84,6 +84,10 @@ int binary_search_place_and_route(const Netlist<>& placement_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); + /* 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); } diff --git a/vpr/src/base/read_blif.cpp b/vpr/src/base/read_blif.cpp index 653b7bf01db..42b58499f0a 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" @@ -688,18 +689,15 @@ bool is_binary_param(const std::string& param) { } bool is_real_param(const std::string& param) { - const std::string chars = "012345678."; - /* Must be non-empty */ if (param.empty()) { return false; } - /* The string mustn't contain any other chars that the expected ones */ - for (size_t i = 0; i < param.length(); ++i) { - if (chars.find(param[i]) == std::string::npos) { - return false; - } + /* The string must match the regular expression */ + const std::regex real_number_expr("[+-]?([0-9]*\\.[0-9]+)|([0-9]+\\.[0-9]*)"); + if (!std::regex_match(param, real_number_expr)) { + return false; } /* This is a real number param */ diff --git a/vpr/src/base/read_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 28c15debac2..e272ea3d40c 100644 --- a/vpr/src/base/read_route.cpp +++ b/vpr/src/base/read_route.cpp @@ -79,7 +79,7 @@ bool read_route(const char* route_file, const t_router_opts& router_opts, bool v 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" @@ -104,7 +104,7 @@ bool read_route(const char* route_file, const t_router_opts& router_opts, bool v 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 ", @@ -151,7 +151,7 @@ static void process_route(const Netlist<>& net_list, std::ifstream& fp, const ch 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] == '#') { @@ -228,7 +228,7 @@ static void process_nodes(const Netlist<>& net_list, std::ifstream& fp, ClusterN ++lineno; tokens.clear(); - tokens = vtr::split(input); + tokens = vtr::StringToken(input).split(" \t\n"); if (tokens.empty()) { continue; /*Skip blank lines*/ @@ -416,7 +416,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*/ @@ -619,6 +619,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..d7b556a2de3 100644 --- a/vpr/src/base/setup_clocks.cpp +++ b/vpr/src/base/setup_clocks.cpp @@ -136,7 +136,7 @@ void setup_clock_connections(const t_arch& Arch, FormulaParser& p) { 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, "."); + auto 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, "."); + auto 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, "."); + auto to_names = vtr::StringToken(clock_connection_arch.to).split("."); + auto 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]); 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/setup_vib_grid.cpp b/vpr/src/base/setup_vib_grid.cpp new file mode 100644 index 00000000000..26b45d27465 --- /dev/null +++ b/vpr/src/base/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 == VibGridDefType::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 +#ifdef VERBOSE + VTR_LOG("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->name, max_priority_type_loc.x, max_priority_type_loc.y, + max_priority_type_loc.priority, priority); +#endif + 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/setup_vib_grid.h b/vpr/src/base/setup_vib_grid.h new file mode 100644 index 00000000000..770f94a7b80 --- /dev/null +++ b/vpr/src/base/setup_vib_grid.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include "physical_types.h" + +/** + * @brief Create a VIB device grid + * @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/SetupVPR.cpp b/vpr/src/base/setup_vpr.cpp similarity index 72% rename from vpr/src/base/SetupVPR.cpp rename to vpr/src/base/setup_vpr.cpp index 651ae5ccd14..c1ccbbda852 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,37 @@ #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); +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); + +static void setup_vib_inf(const std::vector& PhysicalTileTypes, + const std::vector& Switches, + const std::vector& Segments, + std::vector& vib_infs); + +static void process_from_or_to_tokens(const std::vector Tokens, const std::vector& PhysicalTileTypes, 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); /** * @brief Identify which switch must be used for *track* to *IPIN* connections based on architecture file specification. @@ -111,7 +119,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 +151,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 +190,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 +208,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 +236,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 +353,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 +368,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 +425,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) { +static void setup_routing_arch(const t_arch& Arch, + t_det_routing_arch& RoutingArch) { RoutingArch.switch_block_type = Arch.SBType; + RoutingArch.switch_block_subtype = Arch.SBSubType; RoutingArch.R_minW_nmos = Arch.R_minW_nmos; RoutingArch.R_minW_pmos = Arch.R_minW_pmos; RoutingArch.Fs = Arch.Fs; + RoutingArch.sub_fs = Arch.sub_fs; RoutingArch.directionality = BI_DIRECTIONAL; if (!Arch.Segments.empty()) { RoutingArch.directionality = Arch.Segments[0].directionality; @@ -426,9 +440,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 +485,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; @@ -518,8 +542,8 @@ static void SetupRouterOpts(const t_options& Options, t_router_opts* RouterOpts) RouterOpts->with_timing_analysis = Options.timing_analysis; } -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"); @@ -554,8 +578,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(); @@ -576,8 +600,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; @@ -607,7 +631,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; @@ -623,7 +647,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 = STAGE_DO; } @@ -707,7 +731,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 = STAGE_DO; } @@ -731,8 +755,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; @@ -756,7 +780,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; @@ -780,7 +804,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; } @@ -812,20 +836,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; @@ -844,13 +868,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, @@ -876,13 +900,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); @@ -918,19 +942,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; } @@ -944,7 +968,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++) { @@ -1008,10 +1032,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); @@ -1021,3 +1045,229 @@ static void do_reachability_analysis(t_physical_tile_type* physical_tile, } } } + +static void setup_vib_inf(const std::vector& PhysicalTileTypes, + 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, PhysicalTileTypes, 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, PhysicalTileTypes, 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, PhysicalTileTypes, Segments, second_stage.froms); + } + } + vib_inf.set_second_stages(second_stages); + } +} + +static void process_from_or_to_tokens(const std::vector Tokens, const std::vector& PhysicalTileTypes, const std::vector segments, std::vector& froms) { + for (int i_token = 0; i_token < (int)Tokens.size(); i_token++) { + std::string Token = Tokens[i_token]; + const char* Token_char = Token.c_str(); + std::vector token = vtr::StringToken(Token).split("."); + if (token.size() == 1) { + t_from_or_to_inf from_inf; + from_inf.type_name = token[0]; + from_inf.from_type = e_multistage_mux_from_or_to_type::MUX; + froms.push_back(from_inf); + } else if (token.size() == 2) { + std::string from_type_name = token[0]; + e_multistage_mux_from_or_to_type from_type; + for (int i_phy_type = 0; i_phy_type < (int)PhysicalTileTypes.size(); i_phy_type++) { + if (from_type_name == PhysicalTileTypes[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 : PhysicalTileTypes[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 = token[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'", 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; + } + } +} 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/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index a40c7597877..5e1c7bd4ada 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. " @@ -1177,6 +1181,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..b6c67e3d6b0 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,22 @@ struct DeviceContext : public Context { /* A read-only view of routing resource graph to be the ONLY database * for client functions: GUI, placer, router, timing analyzer etc. */ - RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch()}; + RRGraphView rr_graph{rr_graph_builder.rr_nodes(), + rr_graph_builder.node_lookup(), + rr_graph_builder.rr_node_metadata(), + rr_graph_builder.rr_edge_metadata(), + rr_indexed_data, + rr_rc_data, + rr_graph_builder.rr_segments(), + rr_graph_builder.rr_switch(), + rr_graph_builder.node_in_edge_storage(), + rr_graph_builder.node_ptc_storage()}; + + /* Track ids for each rr_node in the rr_graph. + * This is used by drawer for tileable routing resource graph + */ + std::map> rr_node_track_ids; + std::vector arch_switch_inf; // [0..(num_arch_switches-1)] std::map all_sw_inf; 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 71bf0c3458e..e7faaad7e87 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1407,6 +1407,40 @@ struct t_det_routing_arch { /// the CUSTOM switch block type. See comment at top of SRC/route/build_switchblocks.c std::vector switchblocks; + /// Sub type and Fs are applied to pass tracks + int sub_fs; + + /// Subtype of switch blocks. + enum e_switch_block_type switch_block_subtype; + + // Following options are used only for tileable routing architecture + + /// Whether the routing architecture is tileable + bool tileable; + + /// 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 0061fad8034..e490af5fb3e 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 (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 57ee96a1a7f..426f968f5e6 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 fa159289e26..827b5c1e774 100644 --- a/vpr/src/route/check_route.cpp +++ b/vpr/src/route/check_route.cpp @@ -357,7 +357,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 @@ -432,6 +432,8 @@ static bool check_adjacent(RRNodeId from_node, RRNodeId to_node, bool is_flat) { } } else if (to_type == e_rr_type::CHANY) { num_adj += chanx_chany_adjacent(from_node, to_node); + } else if (to_type == e_rr_type::MUX) { + num_adj += 1; } else { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "in check_adjacent: %d and %d are not adjacent", from_node, to_node); @@ -464,12 +466,27 @@ static bool check_adjacent(RRNodeId from_node, RRNodeId to_node, bool is_flat) { } } else if (to_type == e_rr_type::CHANX) { num_adj += chanx_chany_adjacent(to_node, from_node); + } else if (to_type == e_rr_type::MUX) { + num_adj += 1; } else { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "in check_adjacent: %d and %d are not adjacent", from_node, to_node); } 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: break; } @@ -607,6 +624,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 53952af47f7..eb91001f2fb 100644 --- a/vpr/src/route/route.cpp +++ b/vpr/src/route/route.cpp @@ -41,6 +41,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_map.cpp b/vpr/src/route/router_lookahead/router_lookahead_map.cpp index 2f49b439617..9cf19ede97e 100644 --- a/vpr/src/route/router_lookahead/router_lookahead_map.cpp +++ b/vpr/src/route/router_lookahead/router_lookahead_map.cpp @@ -727,7 +727,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_chan(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) { @@ -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.h b/vpr/src/route/rr_graph_generation/clb2clb_directs.h new file mode 100644 index 00000000000..987fd1f6536 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/clb2clb_directs.h @@ -0,0 +1,16 @@ +#pragma once + +#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 +}; diff --git a/vpr/src/route/rr_graph_generation/rr_graph.cpp b/vpr/src/route/rr_graph_generation/rr_graph.cpp index 9ab6c86dc07..283f5e2883b 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; @@ -564,14 +556,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); @@ -586,27 +570,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); @@ -802,28 +767,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. @@ -853,6 +844,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, @@ -1494,10 +1486,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); @@ -1571,10 +1565,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); @@ -1655,13 +1651,13 @@ void build_tile_rr_graph(RRGraphBuilder& rr_graph_builder, * and count how many different fan-ins exist for each arch switch. * Then we create these rr switches and update the switch indices * of rr_nodes to index into the rr_switch_inf array. */ -static void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, - std::vector>& switch_fanin_remap, - const std::map& arch_sw_inf, - const float R_minW_nmos, - const float R_minW_pmos, - const int wire_to_arch_ipin_switch, - int* wire_to_rr_ipin_switch) { +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map& arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch) { /* we will potentially be creating a couple of versions of each arch switch where * each version corresponds to a different fan-in. We will need to fill device_ctx.rr_switch_inf * with this expanded list of switches. @@ -1843,11 +1839,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; @@ -1939,15 +1935,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); @@ -2732,6 +2728,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(); @@ -4274,8 +4272,8 @@ static void build_unidir_rr_opins(RRGraphBuilder& rr_graph_builder, * This data structure supplements the the info in the "directs" data structure * TODO: The function that does this parsing in placement is poorly done because it lacks generality on heterogeniety, should replace with this one */ -static std::vector alloc_and_load_clb_to_clb_directs(const std::vector& directs, - int delayless_switch) { +std::vector alloc_and_load_clb_to_clb_directs(const std::vector& directs, int delayless_switch) { + auto& device_ctx = g_vpr_ctx.device(); const int num_directs = directs.size(); diff --git a/vpr/src/route/rr_graph_generation/rr_graph.h b/vpr/src/route/rr_graph_generation/rr_graph.h index c0bb1f1bb36..694e8287d45 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,33 @@ void free_rr_graph(); t_rr_switch_inf create_rr_switch_from_arch_switch(const t_arch_switch_inf& arch_sw_inf, const float R_minW_nmos, const float R_minW_pmos); + +void alloc_and_load_rr_switch_inf(RRGraphBuilder& rr_graph_builder, + std::vector>& switch_fanin_remap, + const std::map& arch_sw_inf, + const float R_minW_nmos, + const float R_minW_pmos, + const int wire_to_arch_ipin_switch, + int* wire_to_rr_ipin_switch); + +void rr_graph_externals(const std::vector& segment_inf, + const std::vector& segment_inf_x, + const std::vector& segment_inf_y, + int wire_to_rr_ipin_switch, + enum e_base_cost_type base_cost_type); + +std::vector alloc_and_load_clb_to_clb_directs(const std::vector& directs, const int delayless_switch); + +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_graph_area.cpp b/vpr/src/route/rr_graph_generation/rr_graph_area.cpp index 784125a437b..ea6d985f29b 100644 --- a/vpr/src/route/rr_graph_generation/rr_graph_area.cpp +++ b/vpr/src/route/rr_graph_generation/rr_graph_area.cpp @@ -376,6 +376,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: num_edges = rr_graph.num_edges(RRNodeId(from_node)); @@ -391,6 +392,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: if (!chan_node_switch_done[size_t(to_node)]) { @@ -464,7 +466,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 4c309597796..97f0681305a 100644 --- a/vpr/src/route/rr_graph_generation/rr_node_indices.cpp +++ b/vpr/src/route/rr_graph_generation/rr_node_indices.cpp @@ -518,8 +518,7 @@ bool verify_rr_node_indices(const DeviceGrid& grid, y, 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) { - // Sources have co-ordinates covering the entire block they are in, but not sinks + } 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) { 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", rr_graph.node_xlow(inode), 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..7b631e799ca --- /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..c8a3b0dd66d --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/device_grid_annotation.h @@ -0,0 +1,39 @@ +#pragma once + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include +#include "vtr_geometry.h" +#include "vtr_ndmatrix.h" +#include "physical_types.h" +#include "device_grid.h" + +/** @brief This is the data structure to provide additional data for the device grid: + * - Border of the device grid (check where the empty types cover the perimeters) + */ +class DeviceGridAnnotation { + public: /* Constructor */ + DeviceGridAnnotation(const DeviceGrid& grid, 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..99bad8645ef --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_chan.cpp @@ -0,0 +1,190 @@ +/************************************************************************ + * Member functions for class RRChan + ***********************************************************************/ +#include "vtr_log.h" +#include "vtr_assert.h" +#include "rr_chan.h" + +/************************************************************************ + * Constructors + ***********************************************************************/ +/* default constructor */ +RRChan::RRChan() { + type_ = 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; + + /* make sure a clean start */ + seg_list.clear(); + + /* Traverse node_segments */ + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + std::vector::iterator it; + /* Try to find the node_segment id in the list */ + it = find(seg_list.begin(), seg_list.end(), node_segments_[inode]); + if (it == seg_list.end()) { + /* Not found, add it to the list */ + seg_list.push_back(node_segments_[inode]); + } + } + + return seg_list; +} + +/* Get a list of nodes whose segment_id is specified */ +std::vector RRChan::get_node_ids_by_segment_ids(const RRSegmentId& seg_id) const { + std::vector node_list; + + /* make sure a clean start */ + node_list.clear(); + + /* Traverse node_segments */ + for (size_t inode = 0; inode < get_chan_width(); ++inode) { + /* Try to find the node_segment id in the list */ + if (seg_id == node_segments_[inode]) { + node_list.push_back(inode); + } + } + + return node_list; +} + +/************************************************************************ + * Mutators + ***********************************************************************/ +void RRChan::set(const RRChan& rr_chan) { + /* Ensure a clean start */ + this->clear(); + /* Assign type of this routing channel */ + this->type_ = rr_chan.get_type(); + /* Copy node and node_segments */ + this->nodes_.resize(rr_chan.get_chan_width()); + this->node_segments_.resize(rr_chan.get_chan_width()); + for (size_t inode = 0; inode < rr_chan.get_chan_width(); ++inode) { + this->nodes_[inode] = rr_chan.get_node(inode); + this->node_segments_[inode] = rr_chan.get_node_segment(inode); + } + return; +} + +/* modify type */ +void RRChan::set_type(const e_rr_type& type) { + VTR_ASSERT(valid_type(type)); + type_ = type; +} + +/* Reserve node list */ +void RRChan::reserve_node(const size_t& node_size) { + nodes_.reserve(node_size); /* reserve to the maximum */ + node_segments_.reserve(node_size); /* reserve to the maximum */ +} + +/* add a node to the array */ +void RRChan::add_node(const RRGraphView& rr_graph, const RRNodeId& node, const RRSegmentId& node_segment) { + /* fill the dedicated element in the vector */ + nodes_.push_back(node); + node_segments_.push_back(node_segment); + + if (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::valid_type(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 { + valid_type(rr_graph.node_type(node)); + if (e_rr_type::NUM_RR_TYPES == type_) { + return true; + } + valid_type(type_); + if (type_ != rr_graph.node_type(node)) { + return false; + } + return true; +} + +/* check if the node id is valid */ +bool RRChan::valid_node_id(const size_t& node_id) const { + if (node_id < nodes_.size()) { + return true; + } + + return false; +} diff --git a/vpr/src/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..59016689ec9 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_chan.h @@ -0,0 +1,80 @@ +#pragma once + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from vpr library */ +#include "rr_graph_view.h" + +/******************************************************************** + * RRChan object aim to describe a routing channel in a routing resource graph + * - What are the nodes in the RRGraph object, for each routing track + * - What are routing segments used by each node in the channel + * - What are the directions of each routing channel + * being either X-direction or Y-direction + * + * Note : + * - This is a collection of rr_nodes from the RRGraph Object + * It does not rebuild or contruct any connects between rr_nodes + * It is just an annotation on an existing RRGraph + * ------------- ------ + * | | | | + * | | | Y | + * | CLB | | Chan | + * | | | | + * | | | | + * ------------- ------ + * ------------- + * | X | + * | Channel | + * ------------- + *******************************************************************/ +class RRChan { + public: /* Constructors */ + RRChan(); + + public: /* Accessors */ + e_rr_type get_type() const; + size_t get_chan_width() const; /* get the number of tracks in this channel */ + int get_node_track_id(const RRNodeId& node) const; /* get the track_id of a node */ + RRNodeId get_node(const size_t& track_num) const; /* get the rr_node with the track_id */ + RRSegmentId get_node_segment(const RRNodeId& node) const; + RRSegmentId get_node_segment(const size_t& track_num) const; + std::vector get_segment_ids() const; /* Get a list of segments used in this routing channel */ + std::vector get_node_ids_by_segment_ids(const RRSegmentId& seg_id) const; /* Get a list of segments used in this routing channel */ + public: /* Mutators */ + /* copy */ + void set(const RRChan&); + + /* modify the type of routing channel */ + void set_type(const e_rr_type& type); + + /* reseve a number of nodes to the array */ + void reserve_node(const size_t& node_size); + + /* add a node to the routing channel */ + void add_node(const RRGraphView& rr_graph, const RRNodeId& node, const RRSegmentId& node_segment); + + /* clear the content */ + void clear(); + + private: /* internal functions */ + /* For the type of a routing channel, only valid type is CHANX and CHANY */ + bool valid_type(const e_rr_type& type) const; + + /* Check each node, see if the node type is consistent with the type of routing channel */ + bool valid_node_type(const RRGraphView& rr_graph, const RRNodeId& node) const; + + /* Validate if the track number in the range */ + bool valid_node_id(const size_t& node_id) const; + + private: /* Internal Data */ + 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..bb3bbe7d076 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_builder_utils.cpp @@ -0,0 +1,651 @@ +/************************************************************************ + * 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; +} + +/************************************************************************ + * Get a list of pin_index for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +std::vector get_grid_side_pins(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const e_pin_type& pin_type, + const e_side& pin_side, + const int& pin_width, + const int& pin_height) { + std::vector pin_list; + /* Make sure a clear start */ + pin_list.clear(); + + t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(t_physical_tile_loc(x, y, layer)); + for (int ipin = 0; ipin < phy_tile_type->num_pins; ++ipin) { + int class_id = phy_tile_type->pin_class[ipin]; + if ((1 == phy_tile_type->pinloc[pin_width][pin_height][pin_side][ipin]) + && (pin_type == phy_tile_type->class_inf[class_id].type)) { + pin_list.push_back(ipin); + } + } + return pin_list; +} + +/************************************************************************ + * Get the number of pins for a grid (either OPIN or IPIN) + * For IO_TYPE, only one side will be used, we consider one side of pins + * For others, we consider all the sides + ***********************************************************************/ +size_t get_grid_num_pins(const DeviceGrid& grids, + const size_t& layer, + const size_t& x, + const size_t& y, + const e_pin_type& pin_type, + const 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; +} + +/************************************************************************ + * Idenfity if a X-direction routing channel exist in the fabric + * This could be entirely possible that a routig channel + * is in the middle of a multi-width and multi-height grid + * + * As the chanx always locates on top of a grid with the same coord + * + * +----------+ + * | CHANX | + * | [x][y] | + * +----------+ + * + * +----------+ + * | Grid | height_offset = height - 1 + * | [x][y] | + * +----------+ + * + * +----------+ + * | Grid | height_offset = height - 2 + * | [x][y-1] | + * +----------+ + * If the CHANX is in the middle of a multi-width and multi-height grid + * it should locate at a grid whose height_offset is lower than the its height defined in physical_tile + * When height_offset == height - 1, it means that the grid is at the top side of this multi-width and multi-height block + ***********************************************************************/ +bool is_chanx_exist(const DeviceGrid& grids, + const size_t& layer, + const vtr::Point& chanx_coord, + const bool& 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; +} + +/************************************************************************ + * Find the minimum fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_min_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + short min_fan_in = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + min_fan_in = std::min(rr_graph.node_fan_in(node), min_fan_in); + } + + return min_fan_in; +} + +/************************************************************************ + * Find the average fan-in for a given class of nodes + * in a routing resource graph + ************************************************************************/ +short find_rr_graph_average_fan_in(const RRGraph& rr_graph, + const std::vector& node_types) { + /* Get the maximum SB mux size */ + size_t sum = 0; + size_t counter = 0; + + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass the nodes not in the class */ + if (node_types.end() == std::find(node_types.begin(), node_types.end(), rr_graph.node_type(node))) { + continue; + } + + sum += rr_graph.node_fan_in(node); + counter++; + } + + return sum / counter; +} + +/************************************************************************ + * Print statistics of multiplexers in a routing resource graph + ************************************************************************/ +void print_rr_graph_mux_stats(const RRGraph& rr_graph) { + /* Print MUX size distribution */ + std::vector sb_node_types; + sb_node_types.push_back(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..825cd02c458 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_graph_builder_utils.h @@ -0,0 +1,123 @@ +#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); + +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); + +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..6111bd23540 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/rr_gsb.h @@ -0,0 +1,289 @@ +#pragma once + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +#include "rr_chan.h" +#include "rr_graph_view.h" + +/******************************************************************** + * Object Generic Switch Block + * This block contains + * 1. A switch block + * 2. A X-direction Connection block locates at the left side of the switch block + * 2. A Y-direction Connection block locates at the top side of the switch block + * + * 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..a2cc1e53b06 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_builder.cpp @@ -0,0 +1,323 @@ +/************************************************************************ + * 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(); + + /* 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, X_AXIS, true); + std::vector segment_inf_y = get_parallel_segs(segment_inf, segment_index_map, 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..e5f4ac64743 --- /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(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(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(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(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..bdd137ffdc9 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_gsb.cpp @@ -0,0 +1,2113 @@ +/************************************************************************ + * 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(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(chan_node, ipin_node, rr_node_driver_switches[ipin_node], false); + edge_count++; + } + } + } + + /* 3. create edges between CHANX|CHANY and CHANX|CHANY, using track2track_map */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); + for (const RRNodeId& track_node : track2track_map[gsb_side][inode]) { + rr_graph_builder.create_edge(chan_node, track_node, rr_node_driver_switches[track_node], false); + edge_count++; + } + } + } + // /* Create edges between bend nodes */ + // for (auto iter = sb_bend_conn.begin(); iter != sb_bend_conn.end(); ++iter) { + // rr_graph_builder.create_edge(iter->first, iter->second, rr_node_driver_switches[iter->second], 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(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(chan_node, ipin_node, rr_node_driver_switches[ipin_node], false); + edge_count++; + } + } + } + + /* 3. create edges between CHANX|CHANY and CHANX|CHANY, using track2track_map */ + for (size_t inode = 0; inode < rr_gsb.get_chan_width(gsb_side); ++inode) { + const RRNodeId& chan_node = rr_gsb.get_chan_node(gsb_side, inode); + for (const RRNodeId& track_node : track2track_map[gsb_side][inode]) { + rr_graph_builder.create_edge(chan_node, track_node, rr_node_driver_switches[track_node], false); + edge_count++; + } + } + } + /* Create edges between bend nodes */ + for (auto iter = sb_bend_conn.begin(); iter != sb_bend_conn.end(); ++iter) { + rr_graph_builder.create_edge(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(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(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(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..c74a5435fd7 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_node_builder.cpp @@ -0,0 +1,1359 @@ +/************************************************************************ + * This file contains functions that are used to allocate nodes + * for the tileable routing resource graph builder + ***********************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" +#include "vtr_geometry.h" + +/* Headers from openfpgautil library */ +#include "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" + +/************************************************************************ + * 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; +} + +/************************************************************************ + * Estimate the number of CHANY rr_nodes for Y-direction routing channels + * The technical rationale is very similar to the X-direction routing channel + * Refer to the detailed explanation there + ***********************************************************************/ +static size_t estimate_num_chany_rr_nodes(const DeviceGrid& grids, + const size_t& layer, + const size_t& chan_width, + const std::vector& segment_infs, + const DeviceGridAnnotation& device_grid_annotation, + const bool& shrink_boundary, + const bool& 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; +} + +/************************************************************************ + * 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; +} + +/************************************************************************ + * Allocate rr_nodes to a rr_graph object + * This function just allocate the memory and ensure its efficiency + * It will NOT fill detailed information for each node!!! + * + * Note: ensure that there are NO nodes in the rr_graph + ***********************************************************************/ +void alloc_tileable_rr_graph_nodes(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + const DeviceGrid& grids, + const 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); +} + +/************************************************************************ + * Configure OPIN rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_opin_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const 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 */ +} + +/************************************************************************ + * Configure IPIN rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_ipin_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const 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 */ +} + +/************************************************************************ + * Configure SOURCE rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_source_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const 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 */ +} + +/************************************************************************ + * Configure SINK rr_nodes for this grid + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, ptc_num (pin_num), + * + * Note: this function should be applied ONLY to grid with 0 width offset and 0 height offset!!! + ***********************************************************************/ +static void load_one_grid_sink_nodes_basic_info(RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::vector& rr_rc_data, + const size_t& layer, + const vtr::Point& grid_coordinate, + const DeviceGrid& grids, + const 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))); + } +} + +/************************************************************************ + * 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]); + } + } + } +} + +/************************************************************************ + * Initialize the basic information of routing track rr_nodes + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + ***********************************************************************/ +static void load_one_chan_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + 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 ? X_AXIS : Y_AXIS; + size_t parallel_seg_id = find_parallel_seg_index(seg_id, seg_index_map, wanted_axis); + rr_node_driver_switches[node] = RRSwitchId(segment_infs[parallel_seg_id].arch_opin_switch); + + /* Update chan_details with node_id */ + chan_details.set_track_node_id(itrack, size_t(node)); + + /* cost index depends on the segment index */ + rr_graph_builder.set_node_cost_index(node, RRIndexedDataId(cost_index_offset + parallel_seg_id)); + + 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 ? X_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 */ + } +} + +/************************************************************************ + * Initialize the basic information of X-channel rr_nodes: + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + * grid_info : pb_graph_pin + ***********************************************************************/ +static void load_chanx_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + 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(); + } + } +} + +/************************************************************************ + * Initialize the basic information of Y-channel rr_nodes: + * coordinates: xlow, ylow, xhigh, yhigh, + * features: capacity, track_ids, ptc_num, direction + ***********************************************************************/ +static void load_chany_rr_nodes_basic_info(const RRGraphView& rr_graph, + RRGraphBuilder& rr_graph_builder, + vtr::vector& rr_node_driver_switches, + std::map>& rr_node_track_ids, + 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(); + } + } +} + +/************************************************************************ + * Reverse the track_ids of CHANX and CHANY nodes in DEC_DIRECTION + * This is required as the track ids are allocated in the sequence + * of incrementing x and y + * However, DEC direction routing tracks should have a reversed sequence in + * track ids + ***********************************************************************/ +static void reverse_dec_chan_rr_node_track_ids(const RRGraphView& rr_graph, + std::map>& rr_node_track_ids) { + // this should call rr_graph_builder to do the job + for (const RRNodeId& node : rr_graph.nodes()) { + /* Bypass condition: only focus on CHANX and CHANY in DEC_DIRECTION */ + if (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()); + } +} + +/************************************************************************ + * 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..4ab4074d2fe --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_node_builder.h @@ -0,0 +1,54 @@ +#pragma once + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from readarch library */ +#include "physical_types.h" + +/* Headers from vpr library */ +#include "device_grid.h" +#include "device_grid_annotation.h" +#include "rr_node_types.h" +#include "rr_graph_type.h" +#include "rr_graph_view.h" +#include "rr_graph_builder.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +void alloc_tileable_rr_graph_nodes(RRGraphBuilder& rr_graph_builder, + vtr::vector& driver_switches, + const DeviceGrid& grids, + const 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..638213dc300 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_types.h @@ -0,0 +1,20 @@ +#pragma once + +/******************************************************************** + * Data types required by routing resource graph (RRGraph) definition + *******************************************************************/ + +/******************************************************************** + * Directionality of a routing track (node type CHANX and CHANY) in + * a routing resource graph + *******************************************************************/ +enum e_direction : unsigned char { + INC_DIRECTION = 0, + DEC_DIRECTION = 1, + BI_DIRECTION = 2, + NO_DIRECTION = 3, + NUM_DIRECTIONS +}; + +/* Xifan Tang - string used in describe_rr_node() and write_xml_rr_graph_obj() */ +constexpr std::array DIRECTION_STRING_WRITE_XML = {{"INC_DIR", "DEC_DIR", "BI_DIR", "NO_DIR"}}; 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..f758ce5a3c1 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_utils.cpp @@ -0,0 +1,201 @@ +/******************************************************************** + * This file includes most utilized functions for the rr_graph + * data structure in the OpenFPGA context + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "tileable_rr_graph_utils.h" +#include "tileable_rr_graph_types.h" +#include "rr_graph_view.h" +#include "vtr_geometry.h" + +/************************************************************************ + * Get the coordinator of a starting point of a routing track + * For routing tracks in INC_DIRECTION + * (xlow, ylow) should be the starting point + * + * For routing tracks in DEC_DIRECTION + * (xhigh, yhigh) should be the starting point + ***********************************************************************/ +vtr::Point get_track_rr_node_start_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((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; +} + +/************************************************************************ + * Get the coordinator of a end point of a routing track + * For routing tracks in INC_DIRECTION + * (xhigh, yhigh) should be the starting point + * + * For routing tracks in DEC_DIRECTION + * (xlow, ylow) should be the starting point + ***********************************************************************/ +vtr::Point get_track_rr_node_end_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node) { + /* Make sure we have CHANX or CHANY */ + VTR_ASSERT((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; +} + +/************************************************************************ + * Find the driver switches for a node in the rr_graph + * This function only return unique driver switches + ***********************************************************************/ +std::vector get_rr_graph_driver_switches(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_switches; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + if (driver_switches.end() == std::find(driver_switches.begin(), driver_switches.end(), RRSwitchId(rr_graph.edge_switch(edge)))) { + driver_switches.push_back(RRSwitchId(rr_graph.edge_switch(edge))); + } + } + + return driver_switches; +} + +/************************************************************************ + * Find the driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Find the configurable driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + /* Bypass non-configurable edges */ + if (false == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Find the configurable driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + /* Bypass configurable edges */ + if (true == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Check if an OPIN of a rr_graph is directly driving an IPIN + * To meet this requirement, the OPIN must: + * - Have only 1 fan-out + * - The only fan-out is an IPIN + ***********************************************************************/ +bool is_opin_direct_connected_ipin(const RRGraphView& rr_graph, + const RRNodeId& node) { + /* We only accept OPIN */ + VTR_ASSERT(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; +} + +/************************************************************************ + * Check if an IPIN of a rr_graph is directly connected to an OPIN + * To meet this requirement, the IPIN must: + * - Have only 1 fan-in + * - The only fan-in is an OPIN + ***********************************************************************/ +bool is_ipin_direct_connected_opin(const RRGraphView& rr_graph, + const RRNodeId& node) { + /* We only accept IPIN */ + VTR_ASSERT(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; +} + +/** @brief Get a side of a given node in a routing resource graph. + * Note that this function expect one valid side to be got. Otherwise, it will fail! */ +e_side get_rr_graph_single_node_side(const RRGraphView& rr_graph, + const RRNodeId& node) { + e_side node_side = NUM_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..42237d9d3e3 --- /dev/null +++ b/vpr/src/route/rr_graph_generation/tileable_rr_graph/tileable_rr_graph_utils.h @@ -0,0 +1,42 @@ +#pragma once + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_geometry.h" + +/* Headers from vpr library */ +#include "rr_graph_obj.h" +#include "rr_graph_view.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +vtr::Point get_track_rr_node_start_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node); + +vtr::Point get_track_rr_node_end_coordinate(const RRGraphView& rr_graph, + const RRNodeId& track_rr_node); + +std::vector get_rr_graph_driver_switches(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraphView& rr_graph, + const RRNodeId& node); + +bool is_opin_direct_connected_ipin(const RRGraphView& rr_graph, + const RRNodeId& node); + +bool is_ipin_direct_connected_opin(const RRGraphView& rr_graph, + const RRNodeId& node); + +e_side get_rr_graph_single_node_side(const RRGraphView& rr_graph, + const RRNodeId& node); 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 a45818da484..5fc9f686ed0 100644 --- a/vpr/src/util/vpr_utils.cpp +++ b/vpr/src/util/vpr_utils.cpp @@ -1355,7 +1355,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); } @@ -1645,7 +1645,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) { + if (node_type == e_rr_type::CHANX || node_type == e_rr_type::CHANY || 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 ed1879c6dd1..962cdc11dc3 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 @@ -100,9 +100,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