Merge branch 'lnis-uofu:master' into master
|
@ -37,7 +37,7 @@ jobs:
|
|||
docker_repo: ${{ steps.changes.outputs.docker_repo }}
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
@ -120,7 +120,7 @@ jobs:
|
|||
CXX: ${{ matrix.config.cxx }}
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
@ -153,7 +153,7 @@ jobs:
|
|||
- name: Check ccache size
|
||||
run: ccache -s
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.config.cc == 'gcc-11'}}
|
||||
with:
|
||||
name: openfpga
|
||||
|
@ -216,7 +216,7 @@ jobs:
|
|||
CXX: ${{ matrix.config.cxx }}
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
@ -262,7 +262,7 @@ jobs:
|
|||
CXX: ${{ matrix.config.cxx }}
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
@ -308,7 +308,7 @@ jobs:
|
|||
CXX: ${{ matrix.config.cxx }}
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
@ -360,7 +360,7 @@ jobs:
|
|||
CXX: ${{ matrix.config.cxx }}
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
@ -397,14 +397,14 @@ jobs:
|
|||
needs: [linux_build, change_detect]
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
- name: Checkout OpenFPGA repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Download a built artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: openfpga
|
||||
- name: Set up QEMU
|
||||
|
@ -450,14 +450,14 @@ jobs:
|
|||
- name: tcl_reg_test
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
- name: Checkout OpenFPGA repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Download a built artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: openfpga
|
||||
- name: chmod
|
||||
|
@ -475,7 +475,7 @@ jobs:
|
|||
shell: bash
|
||||
run: source openfpga.sh && source openfpga_flow/regression_test_scripts/${{matrix.config.name}}.sh --debug --show_thread_logs
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
name: failed_${{matrix.config.name}}_regression_log
|
||||
|
@ -508,7 +508,7 @@ jobs:
|
|||
- name: tcl_reg_test
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
@ -524,7 +524,7 @@ jobs:
|
|||
unset OPENFPGA_PATH
|
||||
source openfpga.sh && source openfpga_flow/regression_test_scripts/${{matrix.config.name}}.sh --debug --show_thread_logs
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
name: failed_${{matrix.config.name}}_regression_log
|
||||
|
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
dependency_version: "ubuntu22p04"
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
uses: styfle/cancel-workflow-action@0.12.1
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.2.2262
|
||||
1.2.2520
|
||||
|
|
|
@ -88,6 +88,23 @@ Layout
|
|||
|
||||
.. warning:: Do NOT enable ``shrink_boundary`` if you are not using the tileable routing resource graph generator!
|
||||
|
||||
.. option:: perimeter_cb="<bool>"
|
||||
|
||||
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:: ./figures/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="<bool>"
|
||||
|
||||
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.
|
||||
|
|
|
@ -79,6 +79,7 @@ For subtile port merge support (see an illustrative example in :numref:`fig_subt
|
|||
|
||||
.. note:: When defined, the given port of all the subtiles of a tile will be merged into one port. For example, a tile consists of 8 subtile ``A`` and 6 subtile ``B`` and all the subtiles have a port ``clk``, in the FPGA fabric, all the ``clk`` of the subtiles ``A`` and ``B`` will be wired to a common port ``clk`` at tile level.
|
||||
|
||||
.. note:: Note that when a dedicated clock network is defined, the size of the global port will follow the ``global_port`` defined in the clock network description file (See details in :ref:`file_formats_clock_network`)
|
||||
|
||||
.. note:: When merged, the port will have a default side of ``TOP`` and index of ``0`` on all the attributes, such as width, height etc.
|
||||
|
||||
|
@ -99,6 +100,8 @@ For global port support:
|
|||
|
||||
- ``clock_arch_tree_name="<string>"`` defines the name of the programmable clock network, which the global port will drive. The name of the programmable clock network must be a valid name (See details in :ref:`file_formats_clock_network`)
|
||||
|
||||
.. note:: ``clock_arch_tree_name`` is applicable to clock, reset and set signals.
|
||||
|
||||
- ``is_reset="<bool>"`` define if the global port is a reset port at the top-level FPGA fabric. An operating reset port will be driven by proper signals in testbenches.
|
||||
|
||||
- ``is_set="<bool>"`` define if the global port is a set port at the top-level FPGA fabric. An operating set port will be driven by proper signals in testbenches.
|
||||
|
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 119 KiB |
After Width: | Height: | Size: 5.3 MiB |
|
@ -14,6 +14,8 @@ Using the clock network description language, users can define multiple clock ne
|
|||
- A number of switch points which interconnects clock spines using programmable routing switches. See details in :ref:`file_formats_clock_network_switch_point`.
|
||||
- A number of tap points which connect the clock spines to programmable blocks, e.g., CLBs. See details in :ref:`file_formats_clock_network_tap_point`.
|
||||
|
||||
The entry point of a clock tree must be at a valid connection block.
|
||||
|
||||
.. note:: Please note that the levels of a clock network will be automatically inferred from the clock spines and switch points. Clock network will be **only** built based on the width and the number of levels, as well as the tap points.
|
||||
|
||||
.. note:: The switch points and clock spines will be used to route a clock network. The switch points will not impact the physical clock network but only impact the configuration of the programmable routing switches in the physical clock network.
|
||||
|
@ -22,17 +24,40 @@ Using the clock network description language, users can define multiple clock ne
|
|||
|
||||
.. code-block:: xml
|
||||
|
||||
<clock_networks default_segment="<string>" default_switch="<string>">
|
||||
<clock_network name="<string>" width="<int>">
|
||||
<clock_networks default_segment="<string>" default_tap_switch="<string>" default_driver_switch="<string>">
|
||||
<clock_network name="<string>" global_port="<int>">
|
||||
<spine name="<string>" start_x="<int>" start_y="<int>" end_x="<int>" end_y="<int>">
|
||||
<switch_point tap="<string>" x="<int>" y="<int>"/>
|
||||
<switch_point tap="<string>" x="<int>" y="<int>">
|
||||
<internal_driver from_pin="<string>" to_pin="<string>"/>
|
||||
</switch_point>
|
||||
</spine>
|
||||
<taps>
|
||||
<tap tile_pin="<string>"/>
|
||||
<all from_pin="<string>" to_pin="<string>"/>
|
||||
<region from_pin="<string>" to_pin="<string>" start_x="<int>" start_y="<int>" end_x="<int>" end_y="<int>" repeat_x="<int>" repeat_y="<int>"/>
|
||||
<single from_pin="<string>" to_pin="<string>" x="<int>" y="<int>"/>
|
||||
</taps>
|
||||
</clock_network>
|
||||
</clock_networks>
|
||||
|
||||
.. _fig_prog_clock_network_example_2x2:
|
||||
|
||||
.. figure:: figures/prog_clk_network_example_2x2.png
|
||||
:width: 100%
|
||||
:alt: An example of programmable clock network considering a 2x2 FPGA fabric
|
||||
|
||||
An example of programmable clock network considering a 2x2 FPGA fabric
|
||||
|
||||
Note that when the ``perimeter_cb`` is enabled for routing architecture (See details in :ref:`addon_vpr_syntax`), clock entry point can be indeed at the fringe of FPGA fabrics. See example in :numref:`prog_clock_network_example_2x2_perimeter_cb`.
|
||||
|
||||
.. _fig_prog_clock_network_example_2x2_perimeter_cb:
|
||||
|
||||
.. figure:: figures/prog_clk_network_example_2x2_perimeter_cb.png
|
||||
:width: 100%
|
||||
:alt: An example of programmable clock network considering a 2x2 FPGA fabric with perimeter cb
|
||||
|
||||
An example of programmable clock network considering a 2x2 FPGA fabric with perimeter cb
|
||||
|
||||
|
||||
General Settings
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -40,7 +65,7 @@ The following syntax are applicable to the XML definition under the root node ``
|
|||
|
||||
.. option:: default_segment="<string>"
|
||||
|
||||
Define the default routing segment to be used when building the routing tracks for the clock network. Must be a valid routing segment defined in the VPR architecture file. For example,
|
||||
Define the default routing segment to be used when building the routing tracks for the clock network. The routing segments are used to build the spines of clock networks as shown in :numref:`fig_prog_clock_network_example_2x2`. Must be a valid routing segment defined in the VPR architecture file. For example,
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
|
@ -56,23 +81,30 @@ where the segment is defined in the VPR architecture file:
|
|||
|
||||
.. note:: Currently, clock network requires only length-1 wire segment to be used!
|
||||
|
||||
.. option:: default_switch="<string>"
|
||||
.. option:: default_tap_switch="<string>"
|
||||
|
||||
Define the default routing switch to be used when interconnects the routing tracks in the clock network. Must be a valid routing switch defined in the VPR architecture file. For example,
|
||||
Define the default routing switch to be used when interconnects the routing tracks to the input pins of programmable blocks in the clock network. The tap switches are used to build the taps of clock networks as shown in :numref:`fig_prog_clock_network_example_2x2`. Must be a valid routing switch defined in the VPR architecture file. See the example in the ``default_driver_switch``.
|
||||
|
||||
.. option:: default_driver_switch="<string>"
|
||||
|
||||
.. note:: For internal drivers, suggest to use the same driver switch for the output pins of a programmable block as defined in VPR architecture.
|
||||
|
||||
Define the default routing switch to be used when interconnects the routing tracks in the clock network. The driver switches are used to build the switch points of clock networks as shown in :numref:`fig_prog_clock_network_example_2x2`. Must be a valid routing switch defined in the VPR architecture file. For example,
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
default_switch="clk_mux"
|
||||
default_tap_switch="cb_mux" default_driver_switch="sb_clk_mux"
|
||||
|
||||
where the switch is defined in the VPR architecture file:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<switchlist>
|
||||
<switch type="mux" name="clk_mux" R="551" Cin=".77e-15" Cout="4e-15" Tdel="58e-12" mux_trans_size="2.630740" buf_size="27.645901"/>
|
||||
<switch type="mux" name="cb_mux" R="551" Cin=".77e-15" Cout="4e-15" Tdel="58e-12" mux_trans_size="2.630740" buf_size="27.645901"/>
|
||||
<switch type="mux" name="sb_clk_mux" R="55" Cin=".7e-15" Cout="4e-15" Tdel="58e-12" mux_trans_size="2.630740" buf_size="27.645901"/>
|
||||
</switchlist>
|
||||
|
||||
.. note:: Currently, clock network only supports one type of routing switch, which means all the programmable routing switch in the clock network will be in the same type and circuit design topology.
|
||||
.. note:: Currently, clock network only supports the default types of routing switch, which means all the programmable routing switch in the clock network will be in the same type and circuit design topology.
|
||||
|
||||
Clock Network Settings
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -94,13 +126,18 @@ where the clock network is used to drive the global clock pin ``clk0`` in OpenFP
|
|||
|
||||
<tile_annotations>
|
||||
<global_port name="clk0" is_clock="true" clock_arch_tree_name="clk_tree_0" default_val="0">
|
||||
<tile name="clb" port="clk[0:1]"
|
||||
<tile name="clb" port="clk[0:0]"/>
|
||||
</global_port>
|
||||
</tile_annotations>
|
||||
|
||||
.. option:: width="<int>"
|
||||
.. option:: global_port="<string>"
|
||||
|
||||
The maximum number of clock pins that a clock network can drive.
|
||||
.. note:: When programmable clock network is specified for a global port in OpenFPGA architecure description file, the width of clock tree will be the final size of the global port.
|
||||
|
||||
Define the source port of the clock network. For example, ``clk[0:7]``. Note that the global port name should match
|
||||
|
||||
- the ``from_pin`` when defining the tap points (See details in :ref:`file_formats_clock_network_clock_tap_point`).
|
||||
- the ``name`` of global port definition in OpenFPGA architecture description file
|
||||
|
||||
.. _file_formats_clock_network_clock_spine:
|
||||
|
||||
|
@ -110,6 +147,8 @@ Clock Spine Settings
|
|||
The following syntax are applicable to the XML definition tagged by ``spine``.
|
||||
Note that a number of clock spines can be defined under the node ``clock_network``.
|
||||
|
||||
.. note:: Use coordinates of connection blocks to define the starting and ending points of clock spines.
|
||||
|
||||
.. option:: name="<string>"
|
||||
|
||||
The unique name of the clock spine. It will be used to build switch points between other clock spines.
|
||||
|
@ -136,7 +175,7 @@ For example,
|
|||
|
||||
<spine name="spine0" start_x="1" start_y="1" end_x="2" end_y="1"/>
|
||||
|
||||
where a horizental clock spine ``spine0`` is defined which spans from (1, 1) to (2, 1)
|
||||
where a horizental clock spine ``spine0`` is defined which spans from (1, 1) to (2, 1), as highlighted in orange in the :numref:`fig_prog_clock_network_example_2x2`
|
||||
|
||||
.. note:: We only support clock spines in horizental and vertical directions. Diagonal clock spine is not supported!
|
||||
|
||||
|
@ -148,6 +187,8 @@ Switch Point Settings
|
|||
The following syntax are applicable to the XML definition tagged by ``switch_point``.
|
||||
Note that a number of switch points can be defined under each clock spine ``spine``.
|
||||
|
||||
.. note:: Use the coordinate of switch block to define switching points!
|
||||
|
||||
.. option:: tap="<string>"
|
||||
|
||||
Define which clock spine will be tapped from the current clock spine.
|
||||
|
@ -168,40 +209,121 @@ For example,
|
|||
<switch_point tap="spine1" x="1" y="1"/>
|
||||
<spine>
|
||||
|
||||
where clock spine ``spine0`` will drive another clock spine ``spine1`` at (1, 1).
|
||||
where clock spine ``spine0`` will drive another clock spine ``spine1`` at (1, 1), as highlighted in blue in the :numref:`fig_prog_clock_network_example_2x2`
|
||||
|
||||
For each switch point, outputs of neighbouring programmable blocks are allowed to drive the spine at next level, through syntax ``internal_driver``.
|
||||
|
||||
.. option:: from_pin="<string>"
|
||||
|
||||
Define the pin of a programmable block as an internal driver to a clock network. The pin must be a valid pin defined in the VPR architecture description file.
|
||||
|
||||
.. option:: to_pin="<string>"
|
||||
|
||||
Define the source pin of a clock network. The pin must be a valid pin of the global ports defined in the tile_annotation part of OpenFPGA architecture description file.
|
||||
|
||||
For example,
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<clock_network name="clk_tree_0" global_port="clk[0:1]">
|
||||
<!-- Some clock spines -->
|
||||
<spine name="spine0" start_x="1" start_y="1" end_x="2" end_y="1">
|
||||
<switch_point tap="spine1" x="1" y="1">
|
||||
<internal_driver from_pin="clb.O[0:1]" to_pin="clk[0:0]"/>
|
||||
</switch_point>
|
||||
<spine>
|
||||
</clock_network>
|
||||
|
||||
where the clock routing can be driven at (x=1,y=1) by the output pins ``O[0:3]`` of tile ``clb`` in a VPR architecture description file:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<tile name="clb">
|
||||
<sub_tile name="clb">
|
||||
<output name="O" num_pins="8"/>
|
||||
</sub_tile>
|
||||
</tile>
|
||||
|
||||
|
||||
.. _file_formats_clock_network_tap_point:
|
||||
|
||||
Tap Point Settings
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following syntax are applicable to the XML definition tagged by ``tap``.
|
||||
The following syntax are applicable to the XML definition tagged by ``all``, ``region`` and ``single``.
|
||||
Note that a number of tap points can be defined under the node ``taps``.
|
||||
|
||||
.. option:: tile_pin="<string>"
|
||||
.. option:: from_pin="<string>"
|
||||
|
||||
Define the pin of a programmable block to be tapped by a clock network. The pin must be a valid pin defined in the VPR architecture description file.
|
||||
Define the source pin of a programmable block to be tapped by a clock network. The pin must be a valid pin of the global ports defined in the tile_annotation part of OpenFPGA architecture description file.
|
||||
|
||||
.. option:: to_pin="<string>"
|
||||
|
||||
Define the destination pin of a programmable block to be tapped by a clock network. The pin must be a valid pin defined in the VPR architecture description file.
|
||||
|
||||
.. note:: Only the leaf clock spine (not switch points to drive other clock spine) can tap pins of programmable blocks.
|
||||
|
||||
.. note:: Each coordinate must be a valid integer within the device height and width that are defined in VPR architecture!!!
|
||||
|
||||
.. warning:: The following syntax are only applicable to ``single`` tap mode.
|
||||
|
||||
.. option:: x="<int>"
|
||||
|
||||
Define the x coordinate of the tap point, which is applied to the destination pin ``to_pin``
|
||||
|
||||
.. option:: y="<int>"
|
||||
|
||||
Define the y coordinate of the tap point, which is applied to the destination pin ``to_pin``
|
||||
|
||||
.. warning:: The following syntax are only applicable to ``region`` tap mode.
|
||||
|
||||
.. option:: start_x="<int>"
|
||||
|
||||
Define the starting x coordinate of the tap region, which is applied to the destination pin ``to_pin``
|
||||
|
||||
.. option:: start_y="<int>"
|
||||
|
||||
Define the starting y coordinate of the tap region, which is applied to the destination pin ``to_pin``
|
||||
|
||||
.. option:: end_x="<int>"
|
||||
|
||||
Define the ending x coordinate of the tap region, which is applied to the destination pin ``to_pin``
|
||||
|
||||
.. option:: end_y="<int>"
|
||||
|
||||
Define the ending y coordinate of the tap region, which is applied to the destination pin ``to_pin``
|
||||
|
||||
.. option:: repeat_x="<int>"
|
||||
|
||||
Define the repeating factor on x coordinate of the tap region, which is applied to the destination pin ``to_pin``
|
||||
|
||||
.. option:: repeat_y="<int>"
|
||||
|
||||
Define the repeating factor on y coordinate of the tap region, which is applied to the destination pin ``to_pin``
|
||||
|
||||
For example,
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<clock_network name="clk_tree_0" width="1">
|
||||
<clock_network name="clk_tree_0" global_port="clk[0:1]">
|
||||
<!-- Some clock spines -->
|
||||
<taps>
|
||||
<tap tile_pin="clb.clk"/>
|
||||
<all from_pin="clk[0:0]" to_pin="clb.clk[0:0]"/>
|
||||
<region from_pin="clk[1:1]" to_pin="clb.clk[1:1]" start_x="1" start_y="1" end_x="4" end_y="4" repeat_x="2" repeat_y="2"/>
|
||||
<single from_pin="clk[1:1]" to_pin="clb.clk[1:1]" x="2" y="2"/>
|
||||
</taps>
|
||||
</clock_network>
|
||||
|
||||
where all the clock spines of the clock network ``clk_tree_0`` tap the clock pins ``clk`` of tile ``clb`` in a VPR architecture description file:
|
||||
|
||||
.. note:: Use the name of ``subtile`` in the ``to_pin`` when there are a number of subtiles in your tile!
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<tile name="clb">
|
||||
<sub_tile name="clb">
|
||||
<clock name="clk" num_pins="1"/>
|
||||
<clock name="clk" num_pins="2"/>
|
||||
</sub_tile>
|
||||
</tile>
|
||||
|
||||
|
||||
|
|
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 89 KiB |
|
@ -24,7 +24,7 @@ Detailed syntax are presented as follows.
|
|||
|
||||
Specify the style of tile organization. Can be [``top_left`` | ``top_right`` | ``bottom_left`` | ``bottom_right`` | ``custom``]
|
||||
|
||||
.. warning:: Currently, only ``top_left`` is supported!
|
||||
.. warning:: Currently, only ``top_left`` and ``bottom_left`` are supported!
|
||||
|
||||
The ``top_left`` is a shortcut to define the organization for all the tiles. :numref:`fig_tile_style_top_left` shows an example of tiles in the top-left sytle, where the programmable block locates in the top-left corner of all the tiles, surrounded by two connection blocks and one switch blocks.
|
||||
|
||||
|
@ -37,3 +37,14 @@ Detailed syntax are presented as follows.
|
|||
An example of top-left style of a tile in FPGA fabric
|
||||
|
||||
|
||||
The ``bottom_left`` is a shortcut to define the organization for all the tiles. :numref:`fig_tile_style_bottom_left` shows an example of tiles in the bottom-left sytle, where the programmable block locates in the bottom-left corner of all the tiles, surrounded by two connection blocks and one switch blocks.
|
||||
|
||||
.. _fig_tile_style_bottom_left:
|
||||
|
||||
.. figure:: ./figures/tile_style_bottom_left.png
|
||||
:width: 100%
|
||||
:alt: An example of bottom-left style of tile
|
||||
|
||||
An example of bottom-left style of a tile in FPGA fabric
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,14 @@ write_fabric_verilog
|
|||
|
||||
Specify the output directory for the Verilog netlists. For example, ``--file /temp/fabric_netlist/``
|
||||
|
||||
.. option:: --constant_undriven_inputs
|
||||
|
||||
.. note:: This option is automatically enabled when the option ``perimeter_cb`` of tileable routing resource graph is enabled (see details in :ref`addon_vpr_syntax`).
|
||||
|
||||
.. note:: Enable this option may shadow issues in your FPGA architecture, which causes them difficult to be found in design verification.
|
||||
|
||||
Use constant gnd for undriven wires in Verilog netlists. Recommand to enable when there are boundary routing tracks in FPGA fabric.
|
||||
|
||||
.. option:: --default_net_type <string>
|
||||
|
||||
Specify the default net type for the Verilog netlists. Currently, supported types are ``none`` and ``wire``. Default value: ``none``.
|
||||
|
|
|
@ -130,8 +130,17 @@ Clock signals will be auto-detected and routed based on pin constraints which ar
|
|||
|
||||
.. option:: --pin_constraints_file <string> or -pcf <string>
|
||||
|
||||
Specify the *Pin Constraints File* (PCF) when the clock network contains multiple clock pins. For example, ``-pin_constraints_file pin_constraints.xml``
|
||||
Strongly recommend for multi-clock network. See detailed file format about :ref:`file_format_pin_constraints_file`.
|
||||
Specify the *Pin Constraints File* (PCF) when the clock network contains multiple clock pins. For example, ``-pin_constraints_file pin_constraints.xml``. Strongly recommend for multi-clock network. See detailed file format about :ref:`file_format_pin_constraints_file`.
|
||||
|
||||
.. note:: If there is a global net, e.g., ``clk`` or ``reset``, which will be driven by an internal resource, it should also be defined in the PCF file.
|
||||
|
||||
.. option:: --disable_unused_trees
|
||||
|
||||
Disable entire clock trees when they are not used by any clock nets. Useful to reduce clock power
|
||||
|
||||
.. option:: --disable_unused_spines
|
||||
|
||||
Disable part of the clock tree which are used by clock nets. Useful to reduce clock power
|
||||
|
||||
.. option:: --verbose
|
||||
|
||||
|
|
|
@ -89,14 +89,6 @@ static void read_xml_tile_global_port_annotation(
|
|||
get_attribute(xml_tile, "is_clock", loc_data, pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_bool(false));
|
||||
|
||||
/* Get clock tree attributes if this is a clock */
|
||||
if (tile_annotation.global_port_is_clock(tile_global_port_id)) {
|
||||
tile_annotation.set_global_port_clock_arch_tree_name(
|
||||
tile_global_port_id, get_attribute(xml_tile, "clock_arch_tree_name",
|
||||
loc_data, pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_string());
|
||||
}
|
||||
|
||||
/* Get is_set attributes */
|
||||
tile_annotation.set_global_port_is_set(
|
||||
tile_global_port_id,
|
||||
|
@ -109,6 +101,16 @@ static void read_xml_tile_global_port_annotation(
|
|||
get_attribute(xml_tile, "is_reset", loc_data, pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_bool(false));
|
||||
|
||||
/* Get clock tree attributes if this is a clock, reset or set */
|
||||
if (tile_annotation.global_port_is_clock(tile_global_port_id) ||
|
||||
tile_annotation.global_port_is_reset(tile_global_port_id) ||
|
||||
tile_annotation.global_port_is_set(tile_global_port_id)) {
|
||||
tile_annotation.set_global_port_clock_arch_tree_name(
|
||||
tile_global_port_id, get_attribute(xml_tile, "clock_arch_tree_name",
|
||||
loc_data, pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_string());
|
||||
}
|
||||
|
||||
/* Get default_value attributes */
|
||||
tile_annotation.set_global_port_default_value(
|
||||
tile_global_port_id,
|
||||
|
|
|
@ -96,6 +96,11 @@ size_t TileAnnotation::global_port_default_value(
|
|||
return global_port_default_values_[global_port_id];
|
||||
}
|
||||
|
||||
bool TileAnnotation::global_port_thru_dedicated_network(
|
||||
const TileGlobalPortId& global_port_id) const {
|
||||
return !global_port_clock_arch_tree_name(global_port_id).empty();
|
||||
}
|
||||
|
||||
std::string TileAnnotation::global_port_clock_arch_tree_name(
|
||||
const TileGlobalPortId& global_port_id) const {
|
||||
VTR_ASSERT(valid_global_port_id(global_port_id));
|
||||
|
|
|
@ -54,6 +54,8 @@ class TileAnnotation {
|
|||
bool global_port_is_clock(const TileGlobalPortId& global_port_id) const;
|
||||
bool global_port_is_set(const TileGlobalPortId& global_port_id) const;
|
||||
bool global_port_is_reset(const TileGlobalPortId& global_port_id) const;
|
||||
bool global_port_thru_dedicated_network(
|
||||
const TileGlobalPortId& global_port_id) const;
|
||||
std::string global_port_clock_arch_tree_name(
|
||||
const TileGlobalPortId& global_port_id) const;
|
||||
size_t global_port_default_value(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<clock_networks default_segment="seg_len1" default_switch="fast_switch">
|
||||
<clock_network name="example_network" width="8">
|
||||
<clock_networks default_segment="seg_len1" default_tap_switch="fast_switch" default_driver_switch="slow_switch">
|
||||
<clock_network name="example_network" global_port="clk[0:7]">
|
||||
<spine name="spine_lvl3" start_x="0" start_y="2" end_x="2" end_y="2">
|
||||
<switch_point tap="spine_lvl2_upper" x="2" y="2"/>
|
||||
<switch_point tap="spine_lvl2_lower" x="2" y="2"/>
|
||||
|
@ -17,10 +17,10 @@
|
|||
<spine name="rib_lvl1_lower_left" start_x="2" start_y="1" end_x="1" end_y="1"/>
|
||||
<spine name="rib_lvl1_lower_right" start_x="2" start_y="1" end_x="3" end_y="1"/>
|
||||
<taps>
|
||||
<tap tile_pin="io[0:23].clk[0:7]"/>
|
||||
<tap tile_pin="clb[0:0].clk[0:7]"/>
|
||||
<tap tile_pin="dsp[0:0].clk[0:7]"/>
|
||||
<tap tile_pin="bram[0:0].clk[0:7]"/>
|
||||
<all from_pin="clk[0:0]" to_pin="io[0:23].clk[0:7]"/>
|
||||
<all from_pin="clk[0:0]" to_pin="clb[0:0].clk[0:7]"/>
|
||||
<all from_pin="clk[0:0]" to_pin="dsp[0:0].clk[0:7]"/>
|
||||
<all from_pin="clk[0:0]" to_pin="bram[0:0].clk[0:7]"/>
|
||||
</taps>
|
||||
</clock_network>
|
||||
</clock_networks>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<clock_networks default_segment="seg_len1" default_tap_switch="fast_switch" default_driver_switch="slow_switch">
|
||||
<clock_network name="example_network" global_port="clk[0:7]">
|
||||
<spine name="spine_lvl3" start_x="0" start_y="2" end_x="2" end_y="2">
|
||||
<switch_point tap="spine_lvl2_upper" x="2" y="2">
|
||||
<internal_driver from_pin="clb.O[0:3]" to_pin="clk[1:1]"/>
|
||||
</switch_point>
|
||||
<switch_point tap="spine_lvl2_lower" x="2" y="2">
|
||||
<internal_driver from_pin="clb.O[0:3]" to_pin="clk[0:0]"/>
|
||||
</switch_point>
|
||||
</spine>
|
||||
<spine name="spine_lvl2_upper" start_x="2" start_y="2" end_x="2" end_y="3">
|
||||
<switch_point tap="rib_lvl1_upper_left" x="2" y="3"/>
|
||||
<switch_point tap="rib_lvl1_upper_right" x="2" y="3"/>
|
||||
</spine>
|
||||
<spine name="spine_lvl2_lower" start_x="2" start_y="2" end_x="2" end_y="1">
|
||||
<switch_point tap="rib_lvl1_lower_left" x="2" y="1"/>
|
||||
<switch_point tap="rib_lvl1_lower_right" x="2" y="1"/>
|
||||
</spine>
|
||||
<spine name="rib_lvl1_upper_left" start_x="2" start_y="3" end_x="1" end_y="3"/>
|
||||
<spine name="rib_lvl1_upper_right" start_x="2" start_y="3" end_x="3" end_y="3"/>
|
||||
<spine name="rib_lvl1_lower_left" start_x="2" start_y="1" end_x="1" end_y="1"/>
|
||||
<spine name="rib_lvl1_lower_right" start_x="2" start_y="1" end_x="3" end_y="1"/>
|
||||
<taps>
|
||||
<all from_pin="clk[0:0]" to_pin="io[0:23].clk[0:0]"/>
|
||||
<all from_pin="clk[0:0]" to_pin="clb[0:0].clk[1:1]"/>
|
||||
<all from_pin="clk[0:0]" to_pin="dsp[0:0].clk[2:2]"/>
|
||||
<single from_pin="clk[0:0]" to_pin="bram[0:0].clk[3:7]" x="1" y="1"/>
|
||||
<region from_pin="clk[0:0]" to_pin="bram[0:0].clk[3:7]" start_x="1" end_x="10" repeat_x="2" start_y="8" end_y="20" repeat_y="8"/>
|
||||
</taps>
|
||||
</clock_network>
|
||||
</clock_networks>
|
|
@ -18,7 +18,10 @@ namespace openfpga { // Begin namespace openfpga
|
|||
***********************************************************************/
|
||||
ClockNetwork::ClockNetwork() {
|
||||
default_segment_id_ = RRSegmentId::INVALID();
|
||||
default_switch_id_ = RRSwitchId::INVALID();
|
||||
default_tap_switch_id_ = RRSwitchId::INVALID();
|
||||
default_driver_switch_id_ = RRSwitchId::INVALID();
|
||||
/* Set a default invalid bounding box */
|
||||
empty_tap_bb_ = vtr::Rect<size_t>(1, 0, 1, 0);
|
||||
is_dirty_ = true;
|
||||
}
|
||||
|
||||
|
@ -31,6 +34,12 @@ ClockNetwork::clock_tree_range ClockNetwork::trees() const {
|
|||
return vtr::make_range(tree_ids_.begin(), tree_ids_.end());
|
||||
}
|
||||
|
||||
ClockNetwork::clock_internal_driver_range ClockNetwork::internal_drivers()
|
||||
const {
|
||||
return vtr::make_range(internal_driver_ids_.begin(),
|
||||
internal_driver_ids_.end());
|
||||
}
|
||||
|
||||
std::vector<ClockLevelId> ClockNetwork::levels(
|
||||
const ClockTreeId& tree_id) const {
|
||||
std::vector<ClockLevelId> ret;
|
||||
|
@ -172,11 +181,20 @@ RRSegmentId ClockNetwork::default_segment() const {
|
|||
return default_segment_id_;
|
||||
}
|
||||
|
||||
std::string ClockNetwork::default_switch_name() const {
|
||||
return default_switch_name_;
|
||||
std::string ClockNetwork::default_tap_switch_name() const {
|
||||
return default_tap_switch_name_;
|
||||
}
|
||||
|
||||
RRSwitchId ClockNetwork::default_switch() const { return default_switch_id_; }
|
||||
std::string ClockNetwork::default_driver_switch_name() const {
|
||||
return default_driver_switch_name_;
|
||||
}
|
||||
|
||||
RRSwitchId ClockNetwork::default_tap_switch() const {
|
||||
return default_tap_switch_id_;
|
||||
}
|
||||
RRSwitchId ClockNetwork::default_driver_switch() const {
|
||||
return default_driver_switch_id_;
|
||||
}
|
||||
|
||||
std::string ClockNetwork::tree_name(const ClockTreeId& tree_id) const {
|
||||
VTR_ASSERT(valid_tree_id(tree_id));
|
||||
|
@ -199,9 +217,14 @@ size_t ClockNetwork::max_tree_depth() const {
|
|||
return max_size;
|
||||
}
|
||||
|
||||
BasicPort ClockNetwork::tree_global_port(const ClockTreeId& tree_id) const {
|
||||
VTR_ASSERT(valid_tree_id(tree_id));
|
||||
return tree_global_ports_[tree_id];
|
||||
}
|
||||
|
||||
size_t ClockNetwork::tree_width(const ClockTreeId& tree_id) const {
|
||||
VTR_ASSERT(valid_tree_id(tree_id));
|
||||
return tree_widths_[tree_id];
|
||||
return tree_global_ports_[tree_id].get_width();
|
||||
}
|
||||
|
||||
size_t ClockNetwork::tree_depth(const ClockTreeId& tree_id) const {
|
||||
|
@ -315,17 +338,163 @@ vtr::Point<int> ClockNetwork::spine_switch_point(
|
|||
return spine_switch_coords_[spine_id][size_t(switch_point_id)];
|
||||
}
|
||||
|
||||
std::vector<std::string> ClockNetwork::tree_taps(
|
||||
std::vector<ClockSwitchPointId>
|
||||
ClockNetwork::find_spine_switch_points_with_coord(
|
||||
const ClockSpineId& spine_id, const vtr::Point<int>& coord) const {
|
||||
VTR_ASSERT(valid_spine_id(spine_id));
|
||||
std::vector<ClockSwitchPointId> ret;
|
||||
for (size_t i = 0; i < spine_switch_points_[spine_id].size(); ++i) {
|
||||
if (spine_switch_coords_[spine_id][i] == coord) {
|
||||
ret.push_back(ClockSwitchPointId(i));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<ClockInternalDriverId>
|
||||
ClockNetwork::spine_switch_point_internal_drivers(
|
||||
const ClockSpineId& spine_id,
|
||||
const ClockSwitchPointId& switch_point_id) const {
|
||||
VTR_ASSERT(valid_spine_switch_point_id(spine_id, switch_point_id));
|
||||
return spine_switch_internal_drivers_[spine_id][size_t(switch_point_id)];
|
||||
}
|
||||
|
||||
std::string ClockNetwork::internal_driver_from_pin(
|
||||
const ClockInternalDriverId& int_driver_id) const {
|
||||
VTR_ASSERT(valid_internal_driver_id(int_driver_id));
|
||||
return internal_driver_from_pins_[int_driver_id];
|
||||
}
|
||||
|
||||
BasicPort ClockNetwork::internal_driver_to_pin(
|
||||
const ClockInternalDriverId& int_driver_id) const {
|
||||
VTR_ASSERT(valid_internal_driver_id(int_driver_id));
|
||||
return internal_driver_to_pins_[int_driver_id];
|
||||
}
|
||||
|
||||
std::vector<ClockTapId> ClockNetwork::tree_taps(
|
||||
const ClockTreeId& tree_id) const {
|
||||
VTR_ASSERT(valid_tree_id(tree_id));
|
||||
return tree_taps_[tree_id];
|
||||
}
|
||||
|
||||
std::vector<std::string> ClockNetwork::tree_flatten_taps(
|
||||
const ClockTreeId& tree_id, const ClockTreePinId& clk_pin_id) const {
|
||||
BasicPort ClockNetwork::tap_from_port(const ClockTapId& tap_id) const {
|
||||
VTR_ASSERT(valid_tap_id(tap_id));
|
||||
return tap_from_ports_[tap_id];
|
||||
}
|
||||
|
||||
std::string ClockNetwork::tap_to_port(const ClockTapId& tap_id) const {
|
||||
VTR_ASSERT(valid_tap_id(tap_id));
|
||||
return tap_to_ports_[tap_id];
|
||||
}
|
||||
|
||||
ClockNetwork::e_tap_type ClockNetwork::tap_type(
|
||||
const ClockTapId& tap_id) const {
|
||||
VTR_ASSERT(valid_tap_id(tap_id));
|
||||
/* If not a region, it is a default type covering all the coordinates*/
|
||||
if (tap_bbs_[tap_id] == empty_tap_bb_) {
|
||||
return ClockNetwork::e_tap_type::ALL;
|
||||
}
|
||||
/* Now check if this a single point */
|
||||
if (tap_bbs_[tap_id].height() == 0 && tap_bbs_[tap_id].width() == 0) {
|
||||
return ClockNetwork::e_tap_type::SINGLE;
|
||||
}
|
||||
return ClockNetwork::e_tap_type::REGION;
|
||||
}
|
||||
|
||||
size_t ClockNetwork::tap_x(const ClockTapId& tap_id) const {
|
||||
VTR_ASSERT(tap_type(tap_id) == ClockNetwork::e_tap_type::SINGLE);
|
||||
return tap_bbs_[tap_id].xmin();
|
||||
}
|
||||
|
||||
size_t ClockNetwork::tap_y(const ClockTapId& tap_id) const {
|
||||
VTR_ASSERT(tap_type(tap_id) == ClockNetwork::e_tap_type::SINGLE);
|
||||
return tap_bbs_[tap_id].ymin();
|
||||
}
|
||||
|
||||
vtr::Rect<size_t> ClockNetwork::tap_bounding_box(
|
||||
const ClockTapId& tap_id) const {
|
||||
VTR_ASSERT(tap_type(tap_id) == ClockNetwork::e_tap_type::REGION);
|
||||
return tap_bbs_[tap_id];
|
||||
}
|
||||
|
||||
size_t ClockNetwork::tap_step_x(const ClockTapId& tap_id) const {
|
||||
VTR_ASSERT(tap_type(tap_id) == ClockNetwork::e_tap_type::REGION);
|
||||
return tap_bb_steps_[tap_id].x();
|
||||
}
|
||||
|
||||
size_t ClockNetwork::tap_step_y(const ClockTapId& tap_id) const {
|
||||
VTR_ASSERT(tap_type(tap_id) == ClockNetwork::e_tap_type::REGION);
|
||||
return tap_bb_steps_[tap_id].y();
|
||||
}
|
||||
|
||||
bool ClockNetwork::valid_tap_coord_in_bb(
|
||||
const ClockTapId& tap_id, const vtr::Point<size_t>& tap_coord) const {
|
||||
VTR_ASSERT(valid_tap_id(tap_id));
|
||||
if (tap_type(tap_id) == ClockNetwork::e_tap_type::ALL) {
|
||||
return true;
|
||||
}
|
||||
if (tap_type(tap_id) == ClockNetwork::e_tap_type::SINGLE &&
|
||||
tap_bbs_[tap_id].coincident(tap_coord)) {
|
||||
return true;
|
||||
}
|
||||
if (tap_type(tap_id) == ClockNetwork::e_tap_type::REGION &&
|
||||
tap_bbs_[tap_id].coincident(tap_coord)) {
|
||||
/* Check if steps are considered, coords still matches */
|
||||
bool x_in_bb = false;
|
||||
for (size_t ix = tap_bbs_[tap_id].xmin(); ix <= tap_bbs_[tap_id].xmax();
|
||||
ix = ix + tap_bb_steps_[tap_id].x()) {
|
||||
if (tap_coord.x() == ix) {
|
||||
x_in_bb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Early exit */
|
||||
if (!x_in_bb) {
|
||||
return false;
|
||||
}
|
||||
bool y_in_bb = false;
|
||||
for (size_t iy = tap_bbs_[tap_id].ymin(); iy <= tap_bbs_[tap_id].ymax();
|
||||
iy = iy + tap_bb_steps_[tap_id].y()) {
|
||||
if (tap_coord.y() == iy) {
|
||||
y_in_bb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (y_in_bb && x_in_bb) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> ClockNetwork::tree_flatten_tap_to_ports(
|
||||
const ClockTreeId& tree_id, const ClockTreePinId& clk_pin_id,
|
||||
const vtr::Point<size_t>& tap_coord) const {
|
||||
VTR_ASSERT(valid_tree_id(tree_id));
|
||||
std::vector<std::string> flatten_taps;
|
||||
for (const std::string& tap_name : tree_taps_[tree_id]) {
|
||||
for (ClockTapId tap_id : tree_taps_[tree_id]) {
|
||||
VTR_ASSERT(valid_tap_id(tap_id));
|
||||
/* Filter out unmatched from ports. Expect [clk_pin_id:clk_pin_id] */
|
||||
BasicPort from_port = tap_from_ports_[tap_id];
|
||||
if (!from_port.is_valid()) {
|
||||
VTR_LOG_ERROR("Invalid from port name '%s' whose index is not valid\n",
|
||||
from_port.to_verilog_string().c_str());
|
||||
exit(1);
|
||||
}
|
||||
if (from_port.get_width() != 1) {
|
||||
VTR_LOG_ERROR("Invalid from port name '%s' whose width is not 1\n",
|
||||
from_port.to_verilog_string().c_str());
|
||||
exit(1);
|
||||
}
|
||||
if (from_port.get_lsb() != size_t(clk_pin_id)) {
|
||||
continue;
|
||||
}
|
||||
/* Filter out unmatched coordinates */
|
||||
if (!valid_tap_coord_in_bb(tap_id, tap_coord)) {
|
||||
continue;
|
||||
}
|
||||
std::string tap_name = tap_to_ports_[tap_id];
|
||||
StringToken tokenizer(tap_name);
|
||||
std::vector<std::string> pin_tokens = tokenizer.split(".");
|
||||
if (pin_tokens.size() != 2) {
|
||||
|
@ -351,9 +520,6 @@ std::vector<std::string> ClockNetwork::tree_flatten_taps(
|
|||
std::string flatten_tile_str =
|
||||
tile_info.get_name() + "[" + std::to_string(tile_idx) + "]";
|
||||
for (size_t& pin_idx : pin_info.pins()) {
|
||||
if (pin_idx != size_t(clk_pin_id)) {
|
||||
continue;
|
||||
}
|
||||
std::string flatten_pin_str =
|
||||
pin_info.get_name() + "[" + std::to_string(pin_idx) + "]";
|
||||
flatten_taps.push_back(flatten_tile_str + "." + flatten_pin_str);
|
||||
|
@ -363,6 +529,63 @@ std::vector<std::string> ClockNetwork::tree_flatten_taps(
|
|||
return flatten_taps;
|
||||
}
|
||||
|
||||
std::vector<std::string> ClockNetwork::flatten_internal_driver_from_pin(
|
||||
const ClockInternalDriverId& int_driver_id,
|
||||
const ClockTreePinId& clk_pin_id) const {
|
||||
std::vector<std::string> flatten_taps;
|
||||
BasicPort des_pin = internal_driver_to_pin(int_driver_id);
|
||||
if (!des_pin.is_valid()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid internal driver destination port name '%s' whose index is not "
|
||||
"valid\n",
|
||||
des_pin.to_verilog_string().c_str());
|
||||
exit(1);
|
||||
}
|
||||
if (des_pin.get_width() != 1) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid internal driver destination port name '%s' whose width is not "
|
||||
"1\n",
|
||||
des_pin.to_verilog_string().c_str());
|
||||
exit(1);
|
||||
}
|
||||
if (des_pin.get_lsb() != size_t(clk_pin_id)) {
|
||||
return flatten_taps;
|
||||
}
|
||||
|
||||
std::string tap_name = internal_driver_from_pin(int_driver_id);
|
||||
StringToken tokenizer(tap_name);
|
||||
std::vector<std::string> pin_tokens = tokenizer.split(".");
|
||||
if (pin_tokens.size() != 2) {
|
||||
VTR_LOG_ERROR("Invalid pin name '%s'. Expect <tile>.<port>\n",
|
||||
tap_name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
PortParser tile_parser(pin_tokens[0]);
|
||||
BasicPort tile_info = tile_parser.port();
|
||||
PortParser pin_parser(pin_tokens[1]);
|
||||
BasicPort pin_info = pin_parser.port();
|
||||
if (!tile_info.is_valid()) {
|
||||
VTR_LOG_ERROR("Invalid pin name '%s' whose subtile index is not valid\n",
|
||||
tap_name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
if (!pin_info.is_valid()) {
|
||||
VTR_LOG_ERROR("Invalid pin name '%s' whose pin index is not valid\n",
|
||||
tap_name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
for (size_t& tile_idx : tile_info.pins()) {
|
||||
std::string flatten_tile_str =
|
||||
tile_info.get_name() + "[" + std::to_string(tile_idx) + "]";
|
||||
for (size_t& pin_idx : pin_info.pins()) {
|
||||
std::string flatten_pin_str =
|
||||
pin_info.get_name() + "[" + std::to_string(pin_idx) + "]";
|
||||
flatten_taps.push_back(flatten_tile_str + "." + flatten_pin_str);
|
||||
}
|
||||
}
|
||||
return flatten_taps;
|
||||
}
|
||||
|
||||
ClockTreeId ClockNetwork::find_tree(const std::string& name) const {
|
||||
auto result = tree_name2id_map_.find(name);
|
||||
if (result == tree_name2id_map_.end()) {
|
||||
|
@ -400,6 +623,7 @@ void ClockNetwork::reserve_spines(const size_t& num_spines) {
|
|||
spine_track_types_.reserve(num_spines);
|
||||
spine_switch_points_.reserve(num_spines);
|
||||
spine_switch_coords_.reserve(num_spines);
|
||||
spine_switch_internal_drivers_.reserve(num_spines);
|
||||
spine_parents_.reserve(num_spines);
|
||||
spine_children_.reserve(num_spines);
|
||||
spine_parent_trees_.reserve(num_spines);
|
||||
|
@ -408,7 +632,7 @@ void ClockNetwork::reserve_spines(const size_t& num_spines) {
|
|||
void ClockNetwork::reserve_trees(const size_t& num_trees) {
|
||||
tree_ids_.reserve(num_trees);
|
||||
tree_names_.reserve(num_trees);
|
||||
tree_widths_.reserve(num_trees);
|
||||
tree_global_ports_.reserve(num_trees);
|
||||
tree_top_spines_.reserve(num_trees);
|
||||
tree_taps_.reserve(num_trees);
|
||||
}
|
||||
|
@ -417,25 +641,40 @@ void ClockNetwork::set_default_segment(const RRSegmentId& seg_id) {
|
|||
default_segment_id_ = seg_id;
|
||||
}
|
||||
|
||||
void ClockNetwork::set_default_switch(const RRSwitchId& switch_id) {
|
||||
default_switch_id_ = switch_id;
|
||||
void ClockNetwork::set_default_tap_switch(const RRSwitchId& switch_id) {
|
||||
default_tap_switch_id_ = switch_id;
|
||||
}
|
||||
|
||||
void ClockNetwork::set_default_driver_switch(const RRSwitchId& switch_id) {
|
||||
default_driver_switch_id_ = switch_id;
|
||||
}
|
||||
|
||||
void ClockNetwork::set_default_segment_name(const std::string& name) {
|
||||
default_segment_name_ = name;
|
||||
}
|
||||
|
||||
void ClockNetwork::set_default_switch_name(const std::string& name) {
|
||||
default_switch_name_ = name;
|
||||
void ClockNetwork::set_default_tap_switch_name(const std::string& name) {
|
||||
default_tap_switch_name_ = name;
|
||||
}
|
||||
|
||||
ClockTreeId ClockNetwork::create_tree(const std::string& name, size_t width) {
|
||||
void ClockNetwork::set_default_driver_switch_name(const std::string& name) {
|
||||
default_driver_switch_name_ = name;
|
||||
}
|
||||
|
||||
ClockTreeId ClockNetwork::create_tree(const std::string& name,
|
||||
const BasicPort& global_port) {
|
||||
/* Sanity checks */
|
||||
if (!global_port.is_valid()) {
|
||||
VTR_LOG_ERROR("Invalid global port '%s' for clock tree name '%s'\n",
|
||||
global_port.to_verilog_string().c_str(), name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
/* Create a new id */
|
||||
ClockTreeId tree_id = ClockTreeId(tree_ids_.size());
|
||||
|
||||
tree_ids_.push_back(tree_id);
|
||||
tree_names_.push_back(name);
|
||||
tree_widths_.push_back(width);
|
||||
tree_global_ports_.push_back(global_port);
|
||||
tree_depths_.emplace_back();
|
||||
tree_taps_.emplace_back();
|
||||
tree_top_spines_.emplace_back();
|
||||
|
@ -476,6 +715,7 @@ ClockSpineId ClockNetwork::create_spine(const std::string& name) {
|
|||
spine_track_types_.emplace_back(NUM_RR_TYPES);
|
||||
spine_switch_points_.emplace_back();
|
||||
spine_switch_coords_.emplace_back();
|
||||
spine_switch_internal_drivers_.emplace_back();
|
||||
spine_parents_.emplace_back();
|
||||
spine_children_.emplace_back();
|
||||
spine_parent_trees_.emplace_back();
|
||||
|
@ -526,13 +766,14 @@ void ClockNetwork::set_spine_track_type(const ClockSpineId& spine_id,
|
|||
spine_track_types_[spine_id] = type;
|
||||
}
|
||||
|
||||
void ClockNetwork::add_spine_switch_point(const ClockSpineId& spine_id,
|
||||
const ClockSpineId& drive_spine_id,
|
||||
const vtr::Point<int>& coord) {
|
||||
ClockSwitchPointId ClockNetwork::add_spine_switch_point(
|
||||
const ClockSpineId& spine_id, const ClockSpineId& drive_spine_id,
|
||||
const vtr::Point<int>& coord) {
|
||||
VTR_ASSERT(valid_spine_id(spine_id));
|
||||
VTR_ASSERT(valid_spine_id(drive_spine_id));
|
||||
spine_switch_points_[spine_id].push_back(drive_spine_id);
|
||||
spine_switch_coords_[spine_id].push_back(coord);
|
||||
spine_switch_internal_drivers_[spine_id].emplace_back();
|
||||
/* Do not allow any spine has different parents */
|
||||
if (spine_parents_[drive_spine_id]) {
|
||||
VTR_LOG_ERROR(
|
||||
|
@ -545,12 +786,96 @@ void ClockNetwork::add_spine_switch_point(const ClockSpineId& spine_id,
|
|||
}
|
||||
spine_parents_[drive_spine_id] = spine_id;
|
||||
spine_children_[spine_id].push_back(drive_spine_id);
|
||||
return ClockSwitchPointId(spine_switch_points_[spine_id].size() - 1);
|
||||
}
|
||||
|
||||
void ClockNetwork::add_tree_tap(const ClockTreeId& tree_id,
|
||||
const std::string& pin_name) {
|
||||
ClockInternalDriverId ClockNetwork::add_spine_switch_point_internal_driver(
|
||||
const ClockSpineId& spine_id, const ClockSwitchPointId& switch_point_id,
|
||||
const std::string& int_driver_from_port,
|
||||
const std::string& int_driver_to_port) {
|
||||
VTR_ASSERT(valid_spine_id(spine_id));
|
||||
VTR_ASSERT(valid_spine_switch_point_id(spine_id, switch_point_id));
|
||||
/* Parse ports */
|
||||
PortParser to_pin_parser(int_driver_to_port);
|
||||
/* Find any existing id for the driver port */
|
||||
for (ClockInternalDriverId int_driver_id : internal_driver_ids_) {
|
||||
if (internal_driver_from_pins_[int_driver_id] == int_driver_from_port &&
|
||||
internal_driver_to_pins_[int_driver_id] == to_pin_parser.port()) {
|
||||
spine_switch_internal_drivers_[spine_id][size_t(switch_point_id)]
|
||||
.push_back(int_driver_id);
|
||||
return int_driver_id;
|
||||
}
|
||||
}
|
||||
/* Reaching here, no existing id can be reused, create a new one */
|
||||
ClockInternalDriverId int_driver_id =
|
||||
ClockInternalDriverId(internal_driver_ids_.size());
|
||||
internal_driver_ids_.push_back(int_driver_id);
|
||||
internal_driver_from_pins_.push_back(int_driver_from_port);
|
||||
internal_driver_to_pins_.push_back(to_pin_parser.port());
|
||||
spine_switch_internal_drivers_[spine_id][size_t(switch_point_id)].push_back(
|
||||
int_driver_id);
|
||||
return int_driver_id;
|
||||
}
|
||||
|
||||
ClockTapId ClockNetwork::add_tree_tap(const ClockTreeId& tree_id,
|
||||
const BasicPort& from_port,
|
||||
const std::string& to_port) {
|
||||
VTR_ASSERT(valid_tree_id(tree_id));
|
||||
tree_taps_[tree_id].push_back(pin_name);
|
||||
/* TODO: Consider find existing tap template and avoid duplication in storage
|
||||
*/
|
||||
ClockTapId tap_id = ClockTapId(tap_ids_.size());
|
||||
tap_ids_.push_back(tap_id);
|
||||
tap_from_ports_.push_back(from_port);
|
||||
tap_to_ports_.push_back(to_port);
|
||||
tap_bbs_.emplace_back(empty_tap_bb_);
|
||||
tap_bb_steps_.emplace_back(vtr::Point<size_t>(0, 0));
|
||||
tree_taps_[tree_id].push_back(tap_id);
|
||||
return tap_id;
|
||||
}
|
||||
|
||||
bool ClockNetwork::set_tap_bounding_box(const ClockTapId& tap_id,
|
||||
const vtr::Rect<size_t>& bb) {
|
||||
VTR_ASSERT(valid_tap_id(tap_id));
|
||||
/* Check the bounding box, ensure it must be valid */
|
||||
if (bb.xmax() < bb.xmin() || bb.ymax() < bb.ymin()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid bounding box (xlow=%lu, ylow=%lu) -> (xhigh=%lu, yhigh=%lu)! "
|
||||
"Must follow: xlow <= xhigh, ylow <= yhigh!\n",
|
||||
bb.xmin(), bb.ymin(), bb.xmax(), bb.ymax());
|
||||
return false;
|
||||
}
|
||||
tap_bbs_[tap_id] = bb;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClockNetwork::set_tap_step_x(const ClockTapId& tap_id,
|
||||
const size_t& step) {
|
||||
VTR_ASSERT(valid_tap_id(tap_id));
|
||||
/* Must be a valid step >= 1 */
|
||||
if (step == 0) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid x-direction step (=%lu) for any bounding box! Expect an integer "
|
||||
">= 1!\n",
|
||||
step);
|
||||
return false;
|
||||
}
|
||||
tap_bb_steps_[tap_id].set_x(step);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClockNetwork::set_tap_step_y(const ClockTapId& tap_id,
|
||||
const size_t& step) {
|
||||
VTR_ASSERT(valid_tap_id(tap_id));
|
||||
/* Must be a valid step >= 1 */
|
||||
if (step == 0) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid y-direction step (=%lu) for any bounding box! Expect an integer "
|
||||
">= 1!\n",
|
||||
step);
|
||||
return false;
|
||||
}
|
||||
tap_bb_steps_[tap_id].set_y(step);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClockNetwork::link() {
|
||||
|
@ -562,6 +887,25 @@ bool ClockNetwork::link() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ClockNetwork::validate_tree_taps() const {
|
||||
for (ClockTreeId tree_id : trees()) {
|
||||
for (ClockTapId tap_id : tree_taps(tree_id)) {
|
||||
/* The from pin name should match the global port */
|
||||
if (!tree_global_port(tree_id).mergeable(tap_from_port(tap_id)) ||
|
||||
!tree_global_port(tree_id).contained(tap_from_port(tap_id))) {
|
||||
VTR_LOG_ERROR(
|
||||
"Tap point from_port '%s' is not part of the global port '%s' of "
|
||||
"tree '%s'\n",
|
||||
tap_from_port(tap_id).to_verilog_string().c_str(),
|
||||
tree_global_port(tree_id).to_verilog_string().c_str(),
|
||||
tree_name(tree_id).c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClockNetwork::validate_tree() const {
|
||||
for (ClockTreeId tree_id : trees()) {
|
||||
for (ClockSpineId spine_id : spines(tree_id)) {
|
||||
|
@ -622,7 +966,8 @@ bool ClockNetwork::validate_tree() const {
|
|||
|
||||
bool ClockNetwork::validate() const {
|
||||
is_dirty_ = true;
|
||||
if (default_segment_id_ && default_switch_id_ && validate_tree()) {
|
||||
if (default_segment_id_ && default_tap_switch_id_ &&
|
||||
default_driver_switch_id_ && validate_tree() && validate_tree_taps()) {
|
||||
is_dirty_ = false;
|
||||
}
|
||||
return true;
|
||||
|
@ -698,6 +1043,16 @@ bool ClockNetwork::valid_tree_id(const ClockTreeId& tree_id) const {
|
|||
(tree_id == tree_ids_[tree_id]);
|
||||
}
|
||||
|
||||
bool ClockNetwork::valid_internal_driver_id(
|
||||
const ClockInternalDriverId& int_driver_id) const {
|
||||
return (size_t(int_driver_id) < internal_driver_ids_.size()) &&
|
||||
(int_driver_id == internal_driver_ids_[int_driver_id]);
|
||||
}
|
||||
|
||||
bool ClockNetwork::valid_tap_id(const ClockTapId& tap_id) const {
|
||||
return (size_t(tap_id) < tap_ids_.size()) && (tap_id == tap_ids_[tap_id]);
|
||||
}
|
||||
|
||||
bool ClockNetwork::valid_level_id(const ClockTreeId& tree_id,
|
||||
const ClockLevelId& lvl_id) const {
|
||||
return valid_tree_id(tree_id) && (size_t(lvl_id) < tree_depth(tree_id));
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
/* Headers from openfpgautil library */
|
||||
#include "clock_network_fwd.h"
|
||||
#include "openfpga_port.h"
|
||||
#include "rr_graph_fwd.h"
|
||||
#include "rr_node_types.h"
|
||||
|
||||
|
@ -42,6 +43,14 @@ class ClockNetwork {
|
|||
clock_tree_iterator;
|
||||
/* Create range */
|
||||
typedef vtr::Range<clock_tree_iterator> clock_tree_range;
|
||||
typedef vtr::vector<ClockInternalDriverId,
|
||||
ClockInternalDriverId>::const_iterator
|
||||
clock_internal_driver_iterator;
|
||||
/* Create range */
|
||||
typedef vtr::Range<clock_internal_driver_iterator>
|
||||
clock_internal_driver_range;
|
||||
/* Type of tap points */
|
||||
enum class e_tap_type : unsigned char { ALL = 0, SINGLE, REGION, NUM_TYPES };
|
||||
|
||||
public: /* Constructors */
|
||||
ClockNetwork();
|
||||
|
@ -49,6 +58,7 @@ class ClockNetwork {
|
|||
public: /* Accessors: aggregates */
|
||||
size_t num_trees() const;
|
||||
clock_tree_range trees() const;
|
||||
clock_internal_driver_range internal_drivers() const;
|
||||
/* Return the range of clock levels */
|
||||
std::vector<ClockLevelId> levels(const ClockTreeId& tree_id) const;
|
||||
/* Return a list of spine id under a clock tree */
|
||||
|
@ -73,9 +83,12 @@ class ClockNetwork {
|
|||
* information from RRGraph */
|
||||
RRSegmentId default_segment() const;
|
||||
std::string default_segment_name() const;
|
||||
RRSwitchId default_switch() const;
|
||||
std::string default_switch_name() const;
|
||||
RRSwitchId default_tap_switch() const;
|
||||
std::string default_tap_switch_name() const;
|
||||
RRSwitchId default_driver_switch() const;
|
||||
std::string default_driver_switch_name() const;
|
||||
std::string tree_name(const ClockTreeId& tree_id) const;
|
||||
BasicPort tree_global_port(const ClockTreeId& tree_id) const;
|
||||
size_t tree_width(const ClockTreeId& tree_id) const;
|
||||
size_t tree_depth(const ClockTreeId& tree_id) const;
|
||||
size_t max_tree_width() const;
|
||||
|
@ -114,15 +127,51 @@ class ClockNetwork {
|
|||
vtr::Point<int> spine_switch_point(
|
||||
const ClockSpineId& spine_id,
|
||||
const ClockSwitchPointId& switch_point_id) const;
|
||||
|
||||
/* Find all the switching points at a given coordinate */
|
||||
std::vector<ClockSwitchPointId> find_spine_switch_points_with_coord(
|
||||
const ClockSpineId& spine_id, const vtr::Point<int>& coord) const;
|
||||
|
||||
std::vector<ClockInternalDriverId> spine_switch_point_internal_drivers(
|
||||
const ClockSpineId& spine_id,
|
||||
const ClockSwitchPointId& switch_point_id) const;
|
||||
std::string internal_driver_from_pin(
|
||||
const ClockInternalDriverId& int_driver_id) const;
|
||||
std::vector<std::string> flatten_internal_driver_from_pin(
|
||||
const ClockInternalDriverId& int_driver_id,
|
||||
const ClockTreePinId& clk_pin_id) const;
|
||||
BasicPort internal_driver_to_pin(
|
||||
const ClockInternalDriverId& int_driver_id) const;
|
||||
|
||||
/* Return the original list of tap pins that is in storage; useful for parsers
|
||||
*/
|
||||
std::vector<std::string> tree_taps(const ClockTreeId& tree_id) const;
|
||||
std::vector<ClockTapId> tree_taps(const ClockTreeId& tree_id) const;
|
||||
/* Return the source ports for a given tap */
|
||||
BasicPort tap_from_port(const ClockTapId& tap_id) const;
|
||||
/* Return the destination ports for a given tap */
|
||||
std::string tap_to_port(const ClockTapId& tap_id) const;
|
||||
/* Find the type of tap point:
|
||||
* all -> all coordinates in efpga are required to tap
|
||||
* single -> only 1 coordinate is required to tap
|
||||
* region -> coordinates in a region required to tap. Steps in region may be
|
||||
* required
|
||||
*/
|
||||
e_tap_type tap_type(const ClockTapId& tap_id) const;
|
||||
/* Require the type of single */
|
||||
size_t tap_x(const ClockTapId& tap_id) const;
|
||||
size_t tap_y(const ClockTapId& tap_id) const;
|
||||
/* Require the type of region */
|
||||
vtr::Rect<size_t> tap_bounding_box(const ClockTapId& tap_id) const;
|
||||
/* Steps are only available when type is region */
|
||||
size_t tap_step_x(const ClockTapId& tap_id) const;
|
||||
size_t tap_step_y(const ClockTapId& tap_id) const;
|
||||
/* Return the list of flatten tap pins. For example: clb[0:1].clk[2:2] is
|
||||
* flatten to { clb[0].clk[2], clb[1].clk[2] } Useful to build clock routing
|
||||
* resource graph Note that the clk_pin_id limits only 1 clock to be accessed
|
||||
*/
|
||||
std::vector<std::string> tree_flatten_taps(
|
||||
const ClockTreeId& tree_id, const ClockTreePinId& clk_pin_id) const;
|
||||
std::vector<std::string> tree_flatten_tap_to_ports(
|
||||
const ClockTreeId& tree_id, const ClockTreePinId& clk_pin_id,
|
||||
const vtr::Point<size_t>& tap_coord) const;
|
||||
/* Find a spine with a given name, if not found, return an valid id, otherwise
|
||||
* return an invalid one */
|
||||
ClockSpineId find_spine(const std::string& name) const;
|
||||
|
@ -145,12 +194,15 @@ class ClockNetwork {
|
|||
/* Reserve a number of trees to be memory efficent */
|
||||
void reserve_trees(const size_t& num_trees);
|
||||
void set_default_segment(const RRSegmentId& seg_id);
|
||||
void set_default_switch(const RRSwitchId& switch_id);
|
||||
void set_default_tap_switch(const RRSwitchId& switch_id);
|
||||
void set_default_driver_switch(const RRSwitchId& switch_id);
|
||||
void set_default_segment_name(const std::string& name);
|
||||
void set_default_switch_name(const std::string& name);
|
||||
void set_default_tap_switch_name(const std::string& name);
|
||||
void set_default_driver_switch_name(const std::string& name);
|
||||
/* Create a new tree, by default the tree can accomodate only 1 clock signal;
|
||||
* use width to adjust the size */
|
||||
ClockTreeId create_tree(const std::string& name, size_t width = 1);
|
||||
ClockTreeId create_tree(const std::string& name,
|
||||
const BasicPort& global_port);
|
||||
/* Create a new spine, if the spine is already created, return an invalid id
|
||||
*/
|
||||
ClockSpineId create_spine(const std::string& name);
|
||||
|
@ -168,10 +220,20 @@ class ClockNetwork {
|
|||
void set_spine_direction(const ClockSpineId& spine_id, const Direction& dir);
|
||||
void set_spine_track_type(const ClockSpineId& spine_id,
|
||||
const t_rr_type& type);
|
||||
void add_spine_switch_point(const ClockSpineId& spine_id,
|
||||
const ClockSpineId& drive_spine_id,
|
||||
const vtr::Point<int>& coord);
|
||||
void add_tree_tap(const ClockTreeId& tree_id, const std::string& pin_name);
|
||||
ClockSwitchPointId add_spine_switch_point(const ClockSpineId& spine_id,
|
||||
const ClockSpineId& drive_spine_id,
|
||||
const vtr::Point<int>& coord);
|
||||
ClockInternalDriverId add_spine_switch_point_internal_driver(
|
||||
const ClockSpineId& spine_id, const ClockSwitchPointId& switch_point_id,
|
||||
const std::string& internal_driver_from_port,
|
||||
const std::string& internal_driver_to_port);
|
||||
ClockTapId add_tree_tap(const ClockTreeId& tree_id,
|
||||
const BasicPort& from_port,
|
||||
const std::string& to_port);
|
||||
bool set_tap_bounding_box(const ClockTapId& tap_id,
|
||||
const vtr::Rect<size_t>& bb);
|
||||
bool set_tap_step_x(const ClockTapId& tap_id, const size_t& step);
|
||||
bool set_tap_step_y(const ClockTapId& tap_id, const size_t& step);
|
||||
/* Build internal links between clock tree, spines etc. This is also an
|
||||
* validator to verify the correctness of the clock network. Must run before
|
||||
* using the data! */
|
||||
|
@ -208,7 +270,16 @@ class ClockNetwork {
|
|||
private: /* Public invalidators/validators */
|
||||
/* Ensure tree data is clean. All the spines are valid, and switch points are
|
||||
* valid */
|
||||
bool validate_tree_taps() const;
|
||||
bool validate_tree() const;
|
||||
/* Show if the internal driver id is a valid for data queries */
|
||||
bool valid_internal_driver_id(
|
||||
const ClockInternalDriverId& int_driver_id) const;
|
||||
/* Show if the tap id is a valid for data queries */
|
||||
bool valid_tap_id(const ClockTapId& tap_id) const;
|
||||
/* Check if a given coordinate matches the requirements for a tap point */
|
||||
bool valid_tap_coord_in_bb(const ClockTapId& tap_id,
|
||||
const vtr::Point<size_t>& tap_coord) const;
|
||||
|
||||
private: /* Private mutators */
|
||||
/* Build internal links between spines under a given tree */
|
||||
|
@ -226,10 +297,10 @@ class ClockNetwork {
|
|||
/* Basic information of each tree */
|
||||
vtr::vector<ClockTreeId, ClockTreeId> tree_ids_;
|
||||
vtr::vector<ClockTreeId, std::string> tree_names_;
|
||||
vtr::vector<ClockTreeId, size_t> tree_widths_;
|
||||
vtr::vector<ClockTreeId, BasicPort> tree_global_ports_;
|
||||
vtr::vector<ClockTreeId, size_t> tree_depths_;
|
||||
vtr::vector<ClockTreeId, std::vector<ClockSpineId>> tree_top_spines_;
|
||||
vtr::vector<ClockTreeId, std::vector<std::string>> tree_taps_;
|
||||
vtr::vector<ClockTreeId, std::vector<ClockTapId>> tree_taps_;
|
||||
|
||||
/* Basic information of each spine */
|
||||
vtr::vector<ClockSpineId, ClockSpineId> spine_ids_;
|
||||
|
@ -241,22 +312,44 @@ class ClockNetwork {
|
|||
vtr::vector<ClockSpineId, t_rr_type> spine_track_types_;
|
||||
vtr::vector<ClockSpineId, std::vector<ClockSpineId>> spine_switch_points_;
|
||||
vtr::vector<ClockSpineId, std::vector<vtr::Point<int>>> spine_switch_coords_;
|
||||
vtr::vector<ClockSpineId, std::vector<std::vector<ClockInternalDriverId>>>
|
||||
spine_switch_internal_drivers_;
|
||||
vtr::vector<ClockSpineId, ClockSpineId> spine_parents_;
|
||||
vtr::vector<ClockSpineId, std::vector<ClockSpineId>> spine_children_;
|
||||
vtr::vector<ClockSpineId, ClockTreeId> spine_parent_trees_;
|
||||
|
||||
/* Basic Information about internal drivers */
|
||||
vtr::vector<ClockInternalDriverId, ClockInternalDriverId>
|
||||
internal_driver_ids_;
|
||||
vtr::vector<ClockInternalDriverId, std::string> internal_driver_from_pins_;
|
||||
vtr::vector<ClockInternalDriverId, BasicPort> internal_driver_to_pins_;
|
||||
/* Basic information about tap */
|
||||
vtr::vector<ClockTapId, ClockTapId> tap_ids_;
|
||||
vtr::vector<ClockTapId, BasicPort> tap_from_ports_;
|
||||
vtr::vector<ClockTapId, std::string> tap_to_ports_;
|
||||
vtr::vector<ClockTapId, vtr::Rect<size_t>>
|
||||
tap_bbs_; /* Bounding box for tap points, (xlow, ylow) -> (xhigh, yhigh) */
|
||||
vtr::vector<ClockTapId, vtr::Point<size_t>>
|
||||
tap_bb_steps_; /* x() -> x-direction step, y() -> y-direction step */
|
||||
|
||||
/* Default routing resource */
|
||||
std::string default_segment_name_; /* The routing segment representing the
|
||||
clock wires */
|
||||
RRSegmentId default_segment_id_;
|
||||
std::string
|
||||
default_switch_name_; /* The routing switch interconnecting clock wire */
|
||||
RRSwitchId default_switch_id_;
|
||||
std::string default_tap_switch_name_; /* The routing switch interconnecting
|
||||
clock wire */
|
||||
RRSwitchId default_tap_switch_id_;
|
||||
std::string default_driver_switch_name_; /* The routing switch interconnecting
|
||||
clock wire */
|
||||
RRSwitchId default_driver_switch_id_;
|
||||
|
||||
/* Fast lookup */
|
||||
std::map<std::string, ClockTreeId> tree_name2id_map_;
|
||||
std::map<std::string, ClockSpineId> spine_name2id_map_;
|
||||
|
||||
/* Constants */
|
||||
vtr::Rect<size_t> empty_tap_bb_;
|
||||
|
||||
/* Flags */
|
||||
mutable bool is_dirty_;
|
||||
};
|
||||
|
|
|
@ -19,12 +19,16 @@ struct clock_tree_id_tag;
|
|||
struct clock_tree_pin_id_tag;
|
||||
struct clock_spine_id_tag;
|
||||
struct clock_switch_point_id_tag;
|
||||
struct clock_internal_driver_id_tag;
|
||||
struct clock_tap_id_tag;
|
||||
|
||||
typedef vtr::StrongId<clock_level_id_tag> ClockLevelId;
|
||||
typedef vtr::StrongId<clock_tree_id_tag> ClockTreeId;
|
||||
typedef vtr::StrongId<clock_tree_pin_id_tag> ClockTreePinId;
|
||||
typedef vtr::StrongId<clock_spine_id_tag> ClockSpineId;
|
||||
typedef vtr::StrongId<clock_switch_point_id_tag> ClockSwitchPointId;
|
||||
typedef vtr::StrongId<clock_internal_driver_id_tag> ClockInternalDriverId;
|
||||
typedef vtr::StrongId<clock_tap_id_tag> ClockTapId;
|
||||
|
||||
/* Short declaration of class */
|
||||
class ClockNetwork;
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
constexpr const char* XML_CLOCK_NETWORK_ROOT_NAME = "clock_networks";
|
||||
constexpr const char* XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_SEGMENT =
|
||||
"default_segment";
|
||||
constexpr const char* XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_SWITCH =
|
||||
"default_switch";
|
||||
constexpr const char* XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_TAP_SWITCH =
|
||||
"default_tap_switch";
|
||||
constexpr const char* XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_DRIVER_SWITCH =
|
||||
"default_driver_switch";
|
||||
constexpr const char* XML_CLOCK_TREE_NODE_NAME = "clock_network";
|
||||
constexpr const char* XML_CLOCK_TREE_ATTRIBUTE_NAME = "name";
|
||||
constexpr const char* XML_CLOCK_TREE_ATTRIBUTE_WIDTH = "width";
|
||||
constexpr const char* XML_CLOCK_TREE_ATTRIBUTE_GLOBAL_PORT = "global_port";
|
||||
constexpr const char* XML_CLOCK_SPINE_NODE_NAME = "spine";
|
||||
constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_NAME = "name";
|
||||
constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_START_X = "start_x";
|
||||
|
@ -20,11 +22,28 @@ constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_END_Y = "end_y";
|
|||
constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_TYPE = "type";
|
||||
constexpr const char* XML_CLOCK_SPINE_ATTRIBUTE_DIRECTION = "direction";
|
||||
constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME = "switch_point";
|
||||
constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_NODE_NAME =
|
||||
"internal_driver";
|
||||
constexpr const char*
|
||||
XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_ATTRIBUTE_FROM_PIN = "from_pin";
|
||||
constexpr const char*
|
||||
XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_ATTRIBUTE_TO_PIN = "to_pin";
|
||||
constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_ATTRIBUTE_TAP = "tap";
|
||||
constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_ATTRIBUTE_X = "x";
|
||||
constexpr const char* XML_CLOCK_SPINE_SWITCH_POINT_ATTRIBUTE_Y = "y";
|
||||
constexpr const char* XML_CLOCK_TREE_TAPS_NODE_NAME = "taps";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_NODE_NAME = "tap";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_TILE_PIN = "tile_pin";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ALL_NODE_NAME = "all";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_REGION_NODE_NAME = "region";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_SINGLE_NODE_NAME = "single";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_FROM_PIN = "from_pin";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_TO_PIN = "to_pin";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_X = "x";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_Y = "y";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_STARTX = "start_x";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_STARTY = "start_y";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_ENDX = "end_x";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_ENDY = "end_y";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_REPEATX = "repeat_x";
|
||||
constexpr const char* XML_CLOCK_TREE_TAP_ATTRIBUTE_REPEATY = "repeat_y";
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,21 +25,107 @@
|
|||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <tap> to an object of ClockNetwork
|
||||
* Parse XML codes of a <all> to an object of ClockNetwork
|
||||
*******************************************************************/
|
||||
static void read_xml_clock_tree_tap(pugi::xml_node& xml_tap,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
ClockNetwork& clk_ntwk,
|
||||
const ClockTreeId& tree_id) {
|
||||
static void read_xml_clock_tree_tap_type_all(pugi::xml_node& xml_tap,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
ClockNetwork& clk_ntwk,
|
||||
const ClockTreeId& tree_id) {
|
||||
if (!clk_ntwk.valid_tree_id(tree_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_tap),
|
||||
"Invalid id of a clock tree!\n");
|
||||
}
|
||||
|
||||
std::string tile_pin_name =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_TILE_PIN, loc_data)
|
||||
std::string from_pin_name =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_FROM_PIN, loc_data)
|
||||
.as_string();
|
||||
clk_ntwk.add_tree_tap(tree_id, tile_pin_name);
|
||||
std::string to_pin_name =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_TO_PIN, loc_data)
|
||||
.as_string();
|
||||
PortParser from_port_parser(from_pin_name);
|
||||
clk_ntwk.add_tree_tap(tree_id, from_port_parser.port(), to_pin_name);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <single> to an object of ClockNetwork
|
||||
*******************************************************************/
|
||||
static void read_xml_clock_tree_tap_type_single(
|
||||
pugi::xml_node& xml_tap, const pugiutil::loc_data& loc_data,
|
||||
ClockNetwork& clk_ntwk, const ClockTreeId& tree_id) {
|
||||
if (!clk_ntwk.valid_tree_id(tree_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_tap),
|
||||
"Invalid id of a clock tree!\n");
|
||||
}
|
||||
|
||||
std::string from_pin_name =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_FROM_PIN, loc_data)
|
||||
.as_string();
|
||||
std::string to_pin_name =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_TO_PIN, loc_data)
|
||||
.as_string();
|
||||
PortParser from_port_parser(from_pin_name);
|
||||
ClockTapId tap_id =
|
||||
clk_ntwk.add_tree_tap(tree_id, from_port_parser.port(), to_pin_name);
|
||||
|
||||
/* Single tap only require a coordinate */
|
||||
size_t tap_x = get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_X,
|
||||
loc_data, pugiutil::ReqOpt::REQUIRED)
|
||||
.as_int();
|
||||
size_t tap_y = get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_Y,
|
||||
loc_data, pugiutil::ReqOpt::REQUIRED)
|
||||
.as_int();
|
||||
clk_ntwk.set_tap_bounding_box(tap_id,
|
||||
vtr::Rect<size_t>(tap_x, tap_y, tap_x, tap_y));
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <region> to an object of ClockNetwork
|
||||
*******************************************************************/
|
||||
static void read_xml_clock_tree_tap_type_region(
|
||||
pugi::xml_node& xml_tap, const pugiutil::loc_data& loc_data,
|
||||
ClockNetwork& clk_ntwk, const ClockTreeId& tree_id) {
|
||||
if (!clk_ntwk.valid_tree_id(tree_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_tap),
|
||||
"Invalid id of a clock tree!\n");
|
||||
}
|
||||
|
||||
std::string from_pin_name =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_FROM_PIN, loc_data)
|
||||
.as_string();
|
||||
std::string to_pin_name =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_TO_PIN, loc_data)
|
||||
.as_string();
|
||||
PortParser from_port_parser(from_pin_name);
|
||||
ClockTapId tap_id =
|
||||
clk_ntwk.add_tree_tap(tree_id, from_port_parser.port(), to_pin_name);
|
||||
|
||||
/* Region require a bounding box */
|
||||
size_t tap_start_x =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_STARTX, loc_data,
|
||||
pugiutil::ReqOpt::REQUIRED)
|
||||
.as_int();
|
||||
size_t tap_start_y =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_STARTY, loc_data,
|
||||
pugiutil::ReqOpt::REQUIRED)
|
||||
.as_int();
|
||||
size_t tap_end_x = get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_ENDX,
|
||||
loc_data, pugiutil::ReqOpt::REQUIRED)
|
||||
.as_int();
|
||||
size_t tap_end_y = get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_ENDY,
|
||||
loc_data, pugiutil::ReqOpt::REQUIRED)
|
||||
.as_int();
|
||||
clk_ntwk.set_tap_bounding_box(
|
||||
tap_id, vtr::Rect<size_t>(tap_start_x, tap_start_y, tap_end_x, tap_end_y));
|
||||
|
||||
/* Default step is all 1 */
|
||||
size_t tap_step_x =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_REPEATX, loc_data)
|
||||
.as_int(1);
|
||||
size_t tap_step_y =
|
||||
get_attribute(xml_tap, XML_CLOCK_TREE_TAP_ATTRIBUTE_REPEATY, loc_data)
|
||||
.as_int(1);
|
||||
clk_ntwk.set_tap_step_x(tap_id, tap_step_x);
|
||||
clk_ntwk.set_tap_step_y(tap_id, tap_step_y);
|
||||
}
|
||||
|
||||
static void read_xml_clock_tree_taps(pugi::xml_node& xml_taps,
|
||||
|
@ -48,14 +134,50 @@ static void read_xml_clock_tree_taps(pugi::xml_node& xml_taps,
|
|||
const ClockTreeId& tree_id) {
|
||||
for (pugi::xml_node xml_tap : xml_taps.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_tap.name() == std::string(XML_CLOCK_TREE_TAP_NODE_NAME)) {
|
||||
read_xml_clock_tree_tap(xml_tap, loc_data, clk_ntwk, tree_id);
|
||||
if (xml_tap.name() == std::string(XML_CLOCK_TREE_TAP_ALL_NODE_NAME)) {
|
||||
read_xml_clock_tree_tap_type_all(xml_tap, loc_data, clk_ntwk, tree_id);
|
||||
} else if (xml_tap.name() ==
|
||||
std::string(XML_CLOCK_TREE_TAP_REGION_NODE_NAME)) {
|
||||
read_xml_clock_tree_tap_type_region(xml_tap, loc_data, clk_ntwk, tree_id);
|
||||
} else if (xml_tap.name() ==
|
||||
std::string(XML_CLOCK_TREE_TAP_SINGLE_NODE_NAME)) {
|
||||
read_xml_clock_tree_tap_type_single(xml_tap, loc_data, clk_ntwk, tree_id);
|
||||
} else {
|
||||
bad_tag(xml_taps, loc_data, xml_tap, {XML_CLOCK_TREE_TAP_NODE_NAME});
|
||||
bad_tag(
|
||||
xml_taps, loc_data, xml_tap,
|
||||
{XML_CLOCK_TREE_TAP_ALL_NODE_NAME, XML_CLOCK_TREE_TAP_REGION_NODE_NAME,
|
||||
XML_CLOCK_TREE_TAP_SINGLE_NODE_NAME});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <switch_point> to an object of ClockNetwork
|
||||
*******************************************************************/
|
||||
static void read_xml_clock_spine_switch_point_internal_driver(
|
||||
pugi::xml_node& xml_int_driver, const pugiutil::loc_data& loc_data,
|
||||
ClockNetwork& clk_ntwk, const ClockSpineId& spine_id,
|
||||
const ClockSwitchPointId& switch_point_id) {
|
||||
if (!clk_ntwk.valid_spine_id(spine_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_int_driver),
|
||||
"Invalid id of a clock spine!\n");
|
||||
}
|
||||
|
||||
std::string int_driver_from_port_name =
|
||||
get_attribute(
|
||||
xml_int_driver,
|
||||
XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_ATTRIBUTE_FROM_PIN, loc_data)
|
||||
.as_string();
|
||||
std::string int_driver_to_port_name =
|
||||
get_attribute(xml_int_driver,
|
||||
XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_ATTRIBUTE_TO_PIN,
|
||||
loc_data)
|
||||
.as_string();
|
||||
clk_ntwk.add_spine_switch_point_internal_driver(spine_id, switch_point_id,
|
||||
int_driver_from_port_name,
|
||||
int_driver_to_port_name);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <switch_point> to an object of ClockNetwork
|
||||
*******************************************************************/
|
||||
|
@ -90,8 +212,21 @@ static void read_xml_clock_spine_switch_point(
|
|||
XML_CLOCK_SPINE_SWITCH_POINT_ATTRIBUTE_Y, loc_data)
|
||||
.as_int();
|
||||
|
||||
clk_ntwk.add_spine_switch_point(spine_id, tap_spine_id,
|
||||
vtr::Point<int>(tap_x, tap_y));
|
||||
ClockSwitchPointId switch_point_id = clk_ntwk.add_spine_switch_point(
|
||||
spine_id, tap_spine_id, vtr::Point<int>(tap_x, tap_y));
|
||||
|
||||
/* Add internal drivers if possible */
|
||||
for (pugi::xml_node xml_int_driver : xml_switch_point.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_int_driver.name() ==
|
||||
std::string(XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_NODE_NAME)) {
|
||||
read_xml_clock_spine_switch_point_internal_driver(
|
||||
xml_int_driver, loc_data, clk_ntwk, spine_id, switch_point_id);
|
||||
} else {
|
||||
bad_tag(xml_int_driver, loc_data, xml_switch_point,
|
||||
{XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_NODE_NAME});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
@ -212,14 +347,18 @@ static void read_xml_clock_tree(pugi::xml_node& xml_clk_tree,
|
|||
const pugiutil::loc_data& loc_data,
|
||||
ClockNetwork& clk_ntwk) {
|
||||
std::string clk_tree_name =
|
||||
get_attribute(xml_clk_tree, XML_CLOCK_TREE_ATTRIBUTE_NAME, loc_data)
|
||||
get_attribute(xml_clk_tree, XML_CLOCK_TREE_ATTRIBUTE_NAME, loc_data,
|
||||
pugiutil::ReqOpt::REQUIRED)
|
||||
.as_string();
|
||||
std::string clk_global_port_str =
|
||||
get_attribute(xml_clk_tree, XML_CLOCK_TREE_ATTRIBUTE_GLOBAL_PORT, loc_data,
|
||||
pugiutil::ReqOpt::REQUIRED)
|
||||
.as_string();
|
||||
int clk_tree_width =
|
||||
get_attribute(xml_clk_tree, XML_CLOCK_TREE_ATTRIBUTE_WIDTH, loc_data)
|
||||
.as_int();
|
||||
|
||||
/* Create a new tree in the storage */
|
||||
ClockTreeId tree_id = clk_ntwk.create_tree(clk_tree_name, clk_tree_width);
|
||||
PortParser gport_parser(clk_global_port_str);
|
||||
ClockTreeId tree_id =
|
||||
clk_ntwk.create_tree(clk_tree_name, gport_parser.port());
|
||||
|
||||
if (false == clk_ntwk.valid_tree_id(tree_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_clk_tree),
|
||||
|
@ -263,11 +402,17 @@ ClockNetwork read_xml_clock_network(const char* fname) {
|
|||
.as_string();
|
||||
clk_ntwk.set_default_segment_name(default_segment_name);
|
||||
|
||||
std::string default_switch_name =
|
||||
get_attribute(xml_root, XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_SWITCH,
|
||||
std::string default_tap_switch_name =
|
||||
get_attribute(xml_root, XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_TAP_SWITCH,
|
||||
loc_data)
|
||||
.as_string();
|
||||
clk_ntwk.set_default_switch_name(default_switch_name);
|
||||
clk_ntwk.set_default_tap_switch_name(default_tap_switch_name);
|
||||
|
||||
std::string default_driver_switch_name =
|
||||
get_attribute(xml_root, XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_DRIVER_SWITCH,
|
||||
loc_data)
|
||||
.as_string();
|
||||
clk_ntwk.set_default_driver_switch_name(default_driver_switch_name);
|
||||
|
||||
size_t num_trees =
|
||||
std::distance(xml_root.children().begin(), xml_root.children().end());
|
||||
|
|
|
@ -28,14 +28,66 @@ static int write_xml_clock_tree_taps(std::fstream& fp,
|
|||
const ClockTreeId& tree_id) {
|
||||
openfpga::write_tab_to_file(fp, 3);
|
||||
fp << "<" << XML_CLOCK_TREE_TAPS_NODE_NAME << ">\n";
|
||||
for (const std::string& tile_pin_name : clk_ntwk.tree_taps(tree_id)) {
|
||||
openfpga::write_tab_to_file(fp, 4);
|
||||
fp << "<" << XML_CLOCK_TREE_TAP_NODE_NAME << "";
|
||||
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_TILE_PIN,
|
||||
tile_pin_name.c_str());
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
/* Depends on the type */
|
||||
for (ClockTapId tap_id : clk_ntwk.tree_taps(tree_id)) {
|
||||
switch (clk_ntwk.tap_type(tap_id)) {
|
||||
case ClockNetwork::e_tap_type::ALL: {
|
||||
openfpga::write_tab_to_file(fp, 4);
|
||||
fp << "<" << XML_CLOCK_TREE_TAP_ALL_NODE_NAME << "";
|
||||
write_xml_attribute(
|
||||
fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_FROM_PIN,
|
||||
clk_ntwk.tap_from_port(tap_id).to_verilog_string().c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_TO_PIN,
|
||||
clk_ntwk.tap_to_port(tap_id).c_str());
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
break;
|
||||
}
|
||||
case ClockNetwork::e_tap_type::SINGLE: {
|
||||
openfpga::write_tab_to_file(fp, 4);
|
||||
fp << "<" << XML_CLOCK_TREE_TAP_SINGLE_NODE_NAME << "";
|
||||
write_xml_attribute(
|
||||
fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_FROM_PIN,
|
||||
clk_ntwk.tap_from_port(tap_id).to_verilog_string().c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_TO_PIN,
|
||||
clk_ntwk.tap_to_port(tap_id).c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_X,
|
||||
clk_ntwk.tap_x(tap_id));
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_Y,
|
||||
clk_ntwk.tap_y(tap_id));
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
break;
|
||||
}
|
||||
case ClockNetwork::e_tap_type::REGION: {
|
||||
openfpga::write_tab_to_file(fp, 4);
|
||||
fp << "<" << XML_CLOCK_TREE_TAP_REGION_NODE_NAME << "";
|
||||
write_xml_attribute(
|
||||
fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_FROM_PIN,
|
||||
clk_ntwk.tap_from_port(tap_id).to_verilog_string().c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_TO_PIN,
|
||||
clk_ntwk.tap_to_port(tap_id).c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_STARTX,
|
||||
clk_ntwk.tap_bounding_box(tap_id).xmin());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_STARTY,
|
||||
clk_ntwk.tap_bounding_box(tap_id).ymin());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_ENDX,
|
||||
clk_ntwk.tap_bounding_box(tap_id).xmax());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_ENDY,
|
||||
clk_ntwk.tap_bounding_box(tap_id).ymax());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_REPEATX,
|
||||
clk_ntwk.tap_step_x(tap_id));
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_TAP_ATTRIBUTE_REPEATY,
|
||||
clk_ntwk.tap_step_y(tap_id));
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
VTR_LOG_ERROR("Invalid type of tap point!\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, 3);
|
||||
|
@ -60,8 +112,32 @@ static int write_xml_clock_spine_switch_point(
|
|||
write_xml_attribute(fp, XML_CLOCK_SPINE_SWITCH_POINT_ATTRIBUTE_X, coord.x());
|
||||
write_xml_attribute(fp, XML_CLOCK_SPINE_SWITCH_POINT_ATTRIBUTE_Y, coord.y());
|
||||
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
/* Optional: internal drivers */
|
||||
if (clk_ntwk.spine_switch_point_internal_drivers(spine_id, switch_point_id)
|
||||
.empty()) {
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
} else {
|
||||
fp << ">"
|
||||
<< "\n";
|
||||
for (ClockInternalDriverId int_driver_id :
|
||||
clk_ntwk.spine_switch_point_internal_drivers(spine_id,
|
||||
switch_point_id)) {
|
||||
openfpga::write_tab_to_file(fp, 4);
|
||||
fp << "<" << XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_NODE_NAME;
|
||||
write_xml_attribute(
|
||||
fp, XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_ATTRIBUTE_FROM_PIN,
|
||||
clk_ntwk.internal_driver_from_pin(int_driver_id).c_str());
|
||||
write_xml_attribute(
|
||||
fp, XML_CLOCK_SPINE_SWITCH_POINT_INTERNAL_DRIVER_ATTRIBUTE_TO_PIN,
|
||||
clk_ntwk.internal_driver_to_pin(int_driver_id)
|
||||
.to_verilog_string()
|
||||
.c_str());
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
}
|
||||
fp << "</" << XML_CLOCK_SPINE_SWITCH_POINT_NODE_NAME << ">\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -126,8 +202,9 @@ static int write_xml_clock_tree(std::fstream& fp, const ClockNetwork& clk_ntwk,
|
|||
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_ATTRIBUTE_NAME,
|
||||
clk_ntwk.tree_name(tree_id).c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_TREE_ATTRIBUTE_WIDTH,
|
||||
clk_ntwk.tree_width(tree_id));
|
||||
write_xml_attribute(
|
||||
fp, XML_CLOCK_TREE_ATTRIBUTE_GLOBAL_PORT,
|
||||
clk_ntwk.tree_global_port(tree_id).to_verilog_string().c_str());
|
||||
fp << ">"
|
||||
<< "\n";
|
||||
|
||||
|
@ -168,8 +245,10 @@ int write_xml_clock_network(const char* fname, const ClockNetwork& clk_ntwk) {
|
|||
fp << "<" << XML_CLOCK_NETWORK_ROOT_NAME;
|
||||
write_xml_attribute(fp, XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_SEGMENT,
|
||||
clk_ntwk.default_segment_name().c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_SWITCH,
|
||||
clk_ntwk.default_switch_name().c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_TAP_SWITCH,
|
||||
clk_ntwk.default_tap_switch_name().c_str());
|
||||
write_xml_attribute(fp, XML_CLOCK_NETWORK_ATTRIBUTE_DEFAULT_DRIVER_SWITCH,
|
||||
clk_ntwk.default_driver_switch_name().c_str());
|
||||
fp << ">"
|
||||
<< "\n";
|
||||
|
||||
|
|
|
@ -22,7 +22,10 @@ static int link_clock_network_rr_segments(ClockNetwork& clk_ntwk,
|
|||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
VTR_LOG_ERROR(
|
||||
"Unable to find the default segement '%s' in VPR architecture "
|
||||
"description!\n",
|
||||
default_segment_name.c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
|
@ -30,19 +33,46 @@ static int link_clock_network_rr_segments(ClockNetwork& clk_ntwk,
|
|||
* Link all the switches that are defined in a routing resource graph to a given
|
||||
*clock network
|
||||
*******************************************************************/
|
||||
static int link_clock_network_rr_switches(ClockNetwork& clk_ntwk,
|
||||
const RRGraphView& rr_graph) {
|
||||
/* default switch id */
|
||||
std::string default_switch_name = clk_ntwk.default_switch_name();
|
||||
static int link_clock_network_tap_rr_switches(ClockNetwork& clk_ntwk,
|
||||
const RRGraphView& rr_graph) {
|
||||
/* default tap switch id */
|
||||
std::string default_tap_switch_name = clk_ntwk.default_tap_switch_name();
|
||||
for (size_t rr_switch_id = 0; rr_switch_id < rr_graph.num_rr_switches();
|
||||
++rr_switch_id) {
|
||||
if (std::string(rr_graph.rr_switch_inf(RRSwitchId(rr_switch_id)).name) ==
|
||||
default_switch_name) {
|
||||
clk_ntwk.set_default_switch(RRSwitchId(rr_switch_id));
|
||||
default_tap_switch_name) {
|
||||
clk_ntwk.set_default_tap_switch(RRSwitchId(rr_switch_id));
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
}
|
||||
VTR_LOG_ERROR(
|
||||
"Unable to find the default tap switch '%s' in VPR architecture "
|
||||
"description!\n",
|
||||
default_tap_switch_name.c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Link all the switches that are defined in a routing resource graph to a given
|
||||
*clock network
|
||||
*******************************************************************/
|
||||
static int link_clock_network_driver_rr_switches(ClockNetwork& clk_ntwk,
|
||||
const RRGraphView& rr_graph) {
|
||||
/* default driver switch id */
|
||||
std::string default_driver_switch_name =
|
||||
clk_ntwk.default_driver_switch_name();
|
||||
for (size_t rr_switch_id = 0; rr_switch_id < rr_graph.num_rr_switches();
|
||||
++rr_switch_id) {
|
||||
if (std::string(rr_graph.rr_switch_inf(RRSwitchId(rr_switch_id)).name) ==
|
||||
default_driver_switch_name) {
|
||||
clk_ntwk.set_default_driver_switch(RRSwitchId(rr_switch_id));
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
}
|
||||
VTR_LOG_ERROR(
|
||||
"Unable to find the default driver switch '%s' in VPR architecture "
|
||||
"description!\n",
|
||||
default_driver_switch_name.c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
|
@ -54,7 +84,11 @@ int link_clock_network_rr_graph(ClockNetwork& clk_ntwk,
|
|||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
status = link_clock_network_rr_switches(clk_ntwk, rr_graph);
|
||||
status = link_clock_network_tap_rr_switches(clk_ntwk, rr_graph);
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
status = link_clock_network_driver_rr_switches(clk_ntwk, rr_graph);
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
|
@ -62,4 +96,39 @@ int link_clock_network_rr_graph(ClockNetwork& clk_ntwk,
|
|||
return status;
|
||||
}
|
||||
|
||||
/** Check for each global ports in tile annotation
|
||||
* If a clock tree is required for a global port, the global port name define
|
||||
* in the tile annotation should match the one in clock clock
|
||||
*/
|
||||
int check_clock_network_tile_annotation(const ClockNetwork& clk_ntwk,
|
||||
const TileAnnotation& tile_annotation) {
|
||||
for (const TileGlobalPortId& gport_id : tile_annotation.global_ports()) {
|
||||
if (!tile_annotation.global_port_thru_dedicated_network(gport_id)) {
|
||||
continue;
|
||||
}
|
||||
std::string gport_name = tile_annotation.global_port_name(gport_id);
|
||||
std::string clk_tree_name =
|
||||
tile_annotation.global_port_clock_arch_tree_name(gport_id);
|
||||
ClockTreeId clk_tree_id = clk_ntwk.find_tree(clk_tree_name);
|
||||
if (!clk_ntwk.valid_tree_id(clk_tree_id)) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid clock tree name '%s' defined for global port '%s' in tile "
|
||||
"annotation! Must be a valid name defined in the clock network "
|
||||
"description!\n",
|
||||
clk_tree_name.c_str(), gport_name.c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (clk_ntwk.tree_global_port(clk_tree_id).get_name() != gport_name) {
|
||||
VTR_LOG_ERROR(
|
||||
"Global port '%s' of clock tree name '%s' must match the name of "
|
||||
"assoicated global port '%s' in tile annotation! Must be a valid name "
|
||||
"defined in the clock network description!\n",
|
||||
clk_ntwk.tree_global_port(clk_tree_id).to_verilog_string().c_str(),
|
||||
clk_tree_name.c_str(), gport_name.c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*******************************************************************/
|
||||
#include "clock_network.h"
|
||||
#include "rr_graph_view.h"
|
||||
#include "tile_annotation.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
|
@ -16,6 +17,9 @@ namespace openfpga { // Begin namespace openfpga
|
|||
int link_clock_network_rr_graph(ClockNetwork& clk_ntwk,
|
||||
const RRGraphView& rr_graph);
|
||||
|
||||
int check_clock_network_tile_annotation(const ClockNetwork& clk_ntwk,
|
||||
const TileAnnotation& tile_annotation);
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
||||
|
|
|
@ -22,6 +22,10 @@ int main(int argc, const char** argv) {
|
|||
|
||||
/* Validate before write out */
|
||||
if (!clk_ntwk.link()) {
|
||||
VTR_LOG_ERROR("Invalid clock network when linking.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!clk_ntwk.validate()) {
|
||||
VTR_LOG_ERROR("Invalid clock network.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
const vtr::Point<size_t>& gsb_range,
|
||||
const size_t& layer,
|
||||
const vtr::Point<size_t>& gsb_coord,
|
||||
const bool& include_clock) {
|
||||
const bool& perimeter_cb, const bool& include_clock) {
|
||||
/* Create an object to return */
|
||||
RRGSB rr_gsb;
|
||||
|
||||
|
@ -126,7 +126,6 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
|
||||
switch (side) {
|
||||
case TOP: /* TOP = 0 */
|
||||
/* For the border, we should take special care */
|
||||
if (gsb_coord.y() == gsb_range.y()) {
|
||||
rr_gsb.clear_one_side(side_manager.get_side());
|
||||
break;
|
||||
|
@ -157,7 +156,6 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
|
||||
break;
|
||||
case RIGHT: /* RIGHT = 1 */
|
||||
/* For the border, we should take special care */
|
||||
if (gsb_coord.x() == gsb_range.x()) {
|
||||
rr_gsb.clear_one_side(side_manager.get_side());
|
||||
break;
|
||||
|
@ -189,8 +187,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
gsb_coord.x() + 1, gsb_coord.y(), OPIN, opin_grid_side[1]);
|
||||
break;
|
||||
case BOTTOM: /* BOTTOM = 2*/
|
||||
/* For the border, we should take special care */
|
||||
if (gsb_coord.y() == 0) {
|
||||
if (!perimeter_cb && gsb_coord.y() == 0) {
|
||||
rr_gsb.clear_one_side(side_manager.get_side());
|
||||
break;
|
||||
}
|
||||
|
@ -220,8 +217,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
gsb_coord.y(), OPIN, opin_grid_side[1]);
|
||||
break;
|
||||
case LEFT: /* LEFT = 3 */
|
||||
/* For the border, we should take special care */
|
||||
if (gsb_coord.x() == 0) {
|
||||
if (!perimeter_cb && gsb_coord.x() == 0) {
|
||||
rr_gsb.clear_one_side(side_manager.get_side());
|
||||
break;
|
||||
}
|
||||
|
@ -333,11 +329,11 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
case RIGHT: /* RIGHT = 1 */
|
||||
/* For the bording, we should take special care */
|
||||
/* Check if TOP side chan width is 0 or not */
|
||||
chan_side = TOP;
|
||||
chan_side = BOTTOM;
|
||||
/* Build the connection block: ipin and ipin_grid_side */
|
||||
/* LEFT side INPUT Pins of Grid[x+1][y+1] */
|
||||
/* LEFT side INPUT Pins of Grid[x+1][y] */
|
||||
ix = rr_gsb.get_sb_x() + 1;
|
||||
iy = rr_gsb.get_sb_y() + 1;
|
||||
iy = rr_gsb.get_sb_y();
|
||||
ipin_rr_node_grid_side = LEFT;
|
||||
break;
|
||||
case BOTTOM: /* BOTTOM = 2*/
|
||||
|
@ -353,11 +349,11 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
case LEFT: /* LEFT = 3 */
|
||||
/* For the bording, we should take special care */
|
||||
/* Check if left side chan width is 0 or not */
|
||||
chan_side = TOP;
|
||||
chan_side = BOTTOM;
|
||||
/* Build the connection block: ipin and ipin_grid_side */
|
||||
/* RIGHT side INPUT Pins of Grid[x][y+1] */
|
||||
/* RIGHT side INPUT Pins of Grid[x][y] */
|
||||
ix = rr_gsb.get_sb_x();
|
||||
iy = rr_gsb.get_sb_y() + 1;
|
||||
iy = rr_gsb.get_sb_y();
|
||||
ipin_rr_node_grid_side = RIGHT;
|
||||
break;
|
||||
default:
|
||||
|
@ -420,6 +416,9 @@ void annotate_device_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
*/
|
||||
vtr::Point<size_t> gsb_range(vpr_device_ctx.grid.width() - 1,
|
||||
vpr_device_ctx.grid.height() - 1);
|
||||
if (vpr_device_ctx.arch->perimeter_cb) {
|
||||
gsb_range.set(vpr_device_ctx.grid.width(), vpr_device_ctx.grid.height());
|
||||
}
|
||||
device_rr_gsb.reserve(gsb_range);
|
||||
|
||||
VTR_LOGV(verbose_output, "Start annotation GSB up to [%lu][%lu]\n",
|
||||
|
@ -434,11 +433,11 @@ void annotate_device_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
* the GSBs at the borderside correctly sort drive_rr_nodes should be
|
||||
* called if required by users
|
||||
*/
|
||||
const RRGSB& rr_gsb =
|
||||
build_rr_gsb(vpr_device_ctx,
|
||||
vtr::Point<size_t>(vpr_device_ctx.grid.width() - 2,
|
||||
vpr_device_ctx.grid.height() - 2),
|
||||
layer, vtr::Point<size_t>(ix, iy), include_clock);
|
||||
vtr::Point<size_t> sub_gsb_range(vpr_device_ctx.grid.width() - 1,
|
||||
vpr_device_ctx.grid.height() - 1);
|
||||
const RRGSB& rr_gsb = build_rr_gsb(
|
||||
vpr_device_ctx, sub_gsb_range, layer, vtr::Point<size_t>(ix, iy),
|
||||
vpr_device_ctx.arch->perimeter_cb, include_clock);
|
||||
/* Add to device_rr_gsb */
|
||||
vtr::Point<size_t> gsb_coordinate = rr_gsb.get_sb_coordinate();
|
||||
device_rr_gsb.add_rr_gsb(gsb_coordinate, rr_gsb);
|
||||
|
|
|
@ -45,17 +45,25 @@ static size_t estimate_clock_rr_graph_num_chan_nodes(
|
|||
*******************************************************************/
|
||||
static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids,
|
||||
const size_t& layer,
|
||||
const bool& perimeter_cb,
|
||||
const bool& through_channel,
|
||||
const ClockNetwork& clk_ntwk) {
|
||||
size_t num_nodes = 0;
|
||||
vtr::Rect<size_t> chanx_bb(1, 0, grids.width() - 1, grids.height() - 1);
|
||||
if (perimeter_cb) {
|
||||
chanx_bb.set_xmin(0);
|
||||
chanx_bb.set_xmax(grids.width());
|
||||
chanx_bb.set_ymin(0);
|
||||
chanx_bb.set_ymax(grids.height());
|
||||
}
|
||||
/* Check the number of CHANX nodes required */
|
||||
for (size_t iy = 0; iy < grids.height() - 1; ++iy) {
|
||||
for (size_t ix = 1; ix < grids.width() - 1; ++ix) {
|
||||
for (size_t iy = chanx_bb.ymin(); iy < chanx_bb.ymax(); ++iy) {
|
||||
for (size_t ix = chanx_bb.xmin(); ix < chanx_bb.xmax(); ++ix) {
|
||||
vtr::Point<size_t> 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))) {
|
||||
(false == is_chanx_exist(grids, layer, chanx_coord, perimeter_cb))) {
|
||||
continue;
|
||||
}
|
||||
/* Estimate the routing tracks required by clock routing only */
|
||||
|
@ -63,13 +71,21 @@ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids,
|
|||
}
|
||||
}
|
||||
|
||||
for (size_t ix = 0; ix < grids.width() - 1; ++ix) {
|
||||
for (size_t iy = 1; iy < grids.height() - 1; ++iy) {
|
||||
vtr::Rect<size_t> chany_bb(0, 1, grids.width() - 1, grids.height() - 1);
|
||||
if (perimeter_cb) {
|
||||
chany_bb.set_xmin(0);
|
||||
chany_bb.set_xmax(grids.width());
|
||||
chany_bb.set_ymin(0);
|
||||
chany_bb.set_ymax(grids.height());
|
||||
}
|
||||
|
||||
for (size_t ix = chany_bb.xmin(); ix < chany_bb.xmax(); ++ix) {
|
||||
for (size_t iy = chany_bb.ymin(); iy < chany_bb.ymax(); ++iy) {
|
||||
vtr::Point<size_t> 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))) {
|
||||
(false == is_chany_exist(grids, layer, chany_coord, perimeter_cb))) {
|
||||
continue;
|
||||
}
|
||||
/* Estimate the routing tracks required by clock routing only */
|
||||
|
@ -151,56 +167,59 @@ static void add_rr_graph_block_clock_nodes(
|
|||
static void add_rr_graph_clock_nodes(
|
||||
RRGraphBuilder& rr_graph_builder, RRClockSpatialLookup& clk_rr_lookup,
|
||||
const RRGraphView& rr_graph_view, const DeviceGrid& grids,
|
||||
const size_t& layer, const bool& through_channel,
|
||||
const size_t& layer, const bool& perimeter_cb, const bool& through_channel,
|
||||
const ClockNetwork& clk_ntwk, const bool& verbose) {
|
||||
/* Pre-allocate memory: Must do otherwise data will be messed up! */
|
||||
clk_rr_lookup.reserve_nodes(grids.width(), grids.height(),
|
||||
clk_ntwk.num_trees(), clk_ntwk.max_tree_depth(),
|
||||
clk_ntwk.max_tree_width());
|
||||
|
||||
vtr::Rect<size_t> chanx_bb(1, 0, grids.width() - 1, grids.height() - 1);
|
||||
if (perimeter_cb) {
|
||||
chanx_bb.set_xmin(0);
|
||||
chanx_bb.set_xmax(grids.width());
|
||||
chanx_bb.set_ymin(0);
|
||||
chanx_bb.set_ymax(grids.height());
|
||||
}
|
||||
/* Add X-direction clock nodes */
|
||||
for (size_t iy = 0; iy < grids.height() - 1; ++iy) {
|
||||
for (size_t ix = 1; ix < grids.width() - 1; ++ix) {
|
||||
for (size_t iy = chanx_bb.ymin(); iy < chanx_bb.ymax(); ++iy) {
|
||||
for (size_t ix = chanx_bb.xmin(); ix < chanx_bb.xmax(); ++ix) {
|
||||
vtr::Point<size_t> 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))) {
|
||||
(false == is_chanx_exist(grids, layer, chanx_coord, perimeter_cb))) {
|
||||
continue;
|
||||
}
|
||||
add_rr_graph_block_clock_nodes(
|
||||
rr_graph_builder, clk_rr_lookup, rr_graph_view, clk_ntwk, layer,
|
||||
chanx_coord, CHANX, CHANX_COST_INDEX_START, verbose);
|
||||
VTR_ASSERT(rr_graph_view.valid_node(
|
||||
clk_rr_lookup.find_node(1, 0, ClockTreeId(0), ClockLevelId(0),
|
||||
ClockTreePinId(0), Direction::INC)));
|
||||
}
|
||||
}
|
||||
|
||||
VTR_ASSERT(rr_graph_view.valid_node(clk_rr_lookup.find_node(
|
||||
1, 0, ClockTreeId(0), ClockLevelId(0), ClockTreePinId(0), Direction::INC)));
|
||||
|
||||
/* Add Y-direction clock nodes */
|
||||
for (size_t ix = 0; ix < grids.width() - 1; ++ix) {
|
||||
for (size_t iy = 1; iy < grids.height() - 1; ++iy) {
|
||||
vtr::Rect<size_t> chany_bb(0, 1, grids.width() - 1, grids.height() - 1);
|
||||
if (perimeter_cb) {
|
||||
chany_bb.set_xmin(0);
|
||||
chany_bb.set_xmax(grids.width());
|
||||
chany_bb.set_ymin(0);
|
||||
chany_bb.set_ymax(grids.height());
|
||||
}
|
||||
for (size_t ix = chany_bb.xmin(); ix < chany_bb.xmax(); ++ix) {
|
||||
for (size_t iy = chany_bb.ymin(); iy < chany_bb.ymax(); ++iy) {
|
||||
vtr::Point<size_t> 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))) {
|
||||
(false == is_chany_exist(grids, layer, chany_coord, perimeter_cb))) {
|
||||
continue;
|
||||
}
|
||||
add_rr_graph_block_clock_nodes(
|
||||
rr_graph_builder, clk_rr_lookup, rr_graph_view, clk_ntwk, layer,
|
||||
chany_coord, CHANY,
|
||||
CHANX_COST_INDEX_START + rr_graph_view.num_rr_segments(), verbose);
|
||||
VTR_ASSERT(rr_graph_view.valid_node(
|
||||
clk_rr_lookup.find_node(1, 0, ClockTreeId(0), ClockLevelId(0),
|
||||
ClockTreePinId(0), Direction::INC)));
|
||||
}
|
||||
}
|
||||
VTR_ASSERT(rr_graph_view.valid_node(clk_rr_lookup.find_node(
|
||||
1, 0, ClockTreeId(0), ClockLevelId(0), ClockTreePinId(0), Direction::INC)));
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
@ -396,19 +415,23 @@ static void try_find_and_add_clock_track2ipin_node(
|
|||
const RRGraphView& rr_graph_view, const size_t& layer,
|
||||
const vtr::Point<size_t>& grid_coord, const e_side& pin_side,
|
||||
const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree,
|
||||
const ClockTreePinId& clk_pin) {
|
||||
const ClockTreePinId& clk_pin, const bool& verbose) {
|
||||
t_physical_tile_type_ptr grid_type = grids.get_physical_type(
|
||||
t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer));
|
||||
for (std::string tap_pin_name :
|
||||
clk_ntwk.tree_flatten_taps(clk_tree, clk_pin)) {
|
||||
clk_ntwk.tree_flatten_tap_to_ports(clk_tree, clk_pin, grid_coord)) {
|
||||
VTR_LOGV(verbose, "Checking tap pin name: %s\n", tap_pin_name.c_str());
|
||||
/* tap pin name could be 'io[5:5].a2f[0]' */
|
||||
int grid_pin_idx = find_physical_tile_pin_index(grid_type, tap_pin_name);
|
||||
if (grid_pin_idx == grid_type->num_pins) {
|
||||
continue;
|
||||
}
|
||||
VTR_LOGV(verbose, "Found a valid pin (index=%d) in physical tile\n",
|
||||
grid_pin_idx);
|
||||
RRNodeId des_node = rr_graph_view.node_lookup().find_node(
|
||||
layer, grid_coord.x(), grid_coord.y(), IPIN, grid_pin_idx, pin_side);
|
||||
if (rr_graph_view.valid_node(des_node)) {
|
||||
VTR_LOGV(verbose, "Found a valid pin in rr graph\n");
|
||||
des_nodes.push_back(des_node);
|
||||
}
|
||||
}
|
||||
|
@ -444,34 +467,35 @@ static std::vector<RRNodeId> find_clock_track2ipin_node(
|
|||
const DeviceGrid& grids, const RRGraphView& rr_graph_view,
|
||||
const t_rr_type& chan_type, const size_t& layer,
|
||||
const vtr::Point<size_t>& chan_coord, const ClockNetwork& clk_ntwk,
|
||||
const ClockTreeId& clk_tree, const ClockTreePinId& clk_pin) {
|
||||
const ClockTreeId& clk_tree, const ClockTreePinId& clk_pin,
|
||||
const bool& verbose) {
|
||||
std::vector<RRNodeId> des_nodes;
|
||||
|
||||
if (chan_type == CHANX) {
|
||||
/* Get the clock IPINs at the BOTTOM side of adjacent grids [x][y+1] */
|
||||
vtr::Point<size_t> bot_grid_coord(chan_coord.x(), chan_coord.y() + 1);
|
||||
try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view,
|
||||
layer, bot_grid_coord, BOTTOM,
|
||||
clk_ntwk, clk_tree, clk_pin);
|
||||
try_find_and_add_clock_track2ipin_node(
|
||||
des_nodes, grids, rr_graph_view, layer, bot_grid_coord, BOTTOM, clk_ntwk,
|
||||
clk_tree, clk_pin, verbose);
|
||||
|
||||
/* Get the clock IPINs at the TOP side of adjacent grids [x][y] */
|
||||
vtr::Point<size_t> top_grid_coord(chan_coord.x(), chan_coord.y());
|
||||
try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view,
|
||||
layer, top_grid_coord, TOP, clk_ntwk,
|
||||
clk_tree, clk_pin);
|
||||
clk_tree, clk_pin, verbose);
|
||||
} else {
|
||||
VTR_ASSERT(chan_type == CHANY);
|
||||
/* Get the clock IPINs at the LEFT side of adjacent grids [x][y+1] */
|
||||
vtr::Point<size_t> left_grid_coord(chan_coord.x() + 1, chan_coord.y());
|
||||
try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view,
|
||||
layer, left_grid_coord, LEFT,
|
||||
clk_ntwk, clk_tree, clk_pin);
|
||||
try_find_and_add_clock_track2ipin_node(
|
||||
des_nodes, grids, rr_graph_view, layer, left_grid_coord, LEFT, clk_ntwk,
|
||||
clk_tree, clk_pin, verbose);
|
||||
|
||||
/* Get the clock IPINs at the RIGHT side of adjacent grids [x][y] */
|
||||
vtr::Point<size_t> right_grid_coord(chan_coord.x(), chan_coord.y());
|
||||
try_find_and_add_clock_track2ipin_node(des_nodes, grids, rr_graph_view,
|
||||
layer, right_grid_coord, RIGHT,
|
||||
clk_ntwk, clk_tree, clk_pin);
|
||||
try_find_and_add_clock_track2ipin_node(
|
||||
des_nodes, grids, rr_graph_view, layer, right_grid_coord, RIGHT, clk_ntwk,
|
||||
clk_tree, clk_pin, verbose);
|
||||
}
|
||||
|
||||
return des_nodes;
|
||||
|
@ -525,8 +549,8 @@ static void add_rr_graph_block_clock_edges(
|
|||
chan_coord, itree, ilvl, ClockTreePinId(ipin), node_dir)) {
|
||||
/* Create edges */
|
||||
VTR_ASSERT(rr_graph_view.valid_node(des_node));
|
||||
rr_graph_builder.create_edge(src_node, des_node,
|
||||
clk_ntwk.default_switch(), false);
|
||||
rr_graph_builder.create_edge(
|
||||
src_node, des_node, clk_ntwk.default_driver_switch(), false);
|
||||
edge_count++;
|
||||
}
|
||||
VTR_LOGV(verbose, "\tWill add %lu edges to other clock nodes\n",
|
||||
|
@ -538,14 +562,14 @@ static void add_rr_graph_block_clock_edges(
|
|||
size_t curr_edge_count = edge_count;
|
||||
for (RRNodeId des_node : find_clock_track2ipin_node(
|
||||
grids, rr_graph_view, chan_type, layer, chan_coord, clk_ntwk,
|
||||
itree, ClockTreePinId(ipin))) {
|
||||
itree, ClockTreePinId(ipin), verbose)) {
|
||||
/* Create edges */
|
||||
VTR_ASSERT(rr_graph_view.valid_node(des_node));
|
||||
rr_graph_builder.create_edge(src_node, des_node,
|
||||
clk_ntwk.default_switch(), false);
|
||||
rr_graph_builder.create_edge(
|
||||
src_node, des_node, clk_ntwk.default_tap_switch(), false);
|
||||
edge_count++;
|
||||
}
|
||||
VTR_LOGV(verbose, "\tWill add %lu edges to other IPIN\n",
|
||||
VTR_LOGV(verbose, "\tWill add %lu edges to IPINs\n",
|
||||
edge_count - curr_edge_count);
|
||||
}
|
||||
}
|
||||
|
@ -557,6 +581,155 @@ static void add_rr_graph_block_clock_edges(
|
|||
num_edges_to_create += edge_count;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Try to find an OPIN of a grid which satisfy the requirement of clock pins
|
||||
* that has been defined in clock network. If the OPIN does exist in a
|
||||
* routing resource graph, add it to the node list
|
||||
*******************************************************************/
|
||||
static void try_find_and_add_clock_opin2track_node(
|
||||
std::vector<RRNodeId>& opin_nodes, const DeviceGrid& grids,
|
||||
const RRGraphView& rr_graph_view, const size_t& layer,
|
||||
const vtr::Point<int>& grid_coord, const e_side& pin_side,
|
||||
const ClockNetwork& clk_ntwk, const ClockTreePinId& clk_pin,
|
||||
const ClockInternalDriverId& int_driver_id) {
|
||||
t_physical_tile_type_ptr grid_type = grids.get_physical_type(
|
||||
t_physical_tile_loc(grid_coord.x(), grid_coord.y(), layer));
|
||||
for (std::string tap_pin_name :
|
||||
clk_ntwk.flatten_internal_driver_from_pin(int_driver_id, clk_pin)) {
|
||||
/* tap pin name could be 'io[5:5].a2f[0]' */
|
||||
int grid_pin_idx = find_physical_tile_pin_index(grid_type, tap_pin_name);
|
||||
if (grid_pin_idx == grid_type->num_pins) {
|
||||
continue;
|
||||
}
|
||||
RRNodeId opin_node = rr_graph_view.node_lookup().find_node(
|
||||
layer, grid_coord.x(), grid_coord.y(), OPIN, grid_pin_idx, pin_side);
|
||||
if (rr_graph_view.valid_node(opin_node)) {
|
||||
opin_nodes.push_back(opin_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Find the source OPIN nodes as internal drivers for a clock node
|
||||
* For example
|
||||
* clk0_lvl1_chany[1][1]
|
||||
* ^
|
||||
* |
|
||||
* internal_driver OPIN[0] -->-------+
|
||||
* ^
|
||||
* |
|
||||
* internal_driver OPIN[1]
|
||||
*
|
||||
* Coordinate system:
|
||||
*
|
||||
* +----------+----------+------------+
|
||||
* | Grid | CBy | Grid |
|
||||
* | [x][y+1] | [x][y+1] | [x+1][y+1] |
|
||||
* +----------+----------+------------+
|
||||
* | CBx | SB | CBx |
|
||||
* | [x][y] | [x][y] | [x+1][y] |
|
||||
* +----------+----------+------------+
|
||||
* | Grid | CBy | Grid |
|
||||
* | [x][y] | [x][y] | [x+1][y] |
|
||||
* +----------+----------+------------+
|
||||
*******************************************************************/
|
||||
static std::vector<RRNodeId> find_clock_opin2track_node(
|
||||
const DeviceGrid& grids, const RRGraphView& rr_graph_view,
|
||||
const size_t& layer, const vtr::Point<int>& sb_coord,
|
||||
const ClockNetwork& clk_ntwk, const ClockTreePinId& clk_pin,
|
||||
const std::vector<ClockInternalDriverId>& int_driver_ids) {
|
||||
std::vector<RRNodeId> opin_nodes;
|
||||
/* Find opins from
|
||||
* - Grid[x][y+1] on right and bottom sides
|
||||
* - Grid[x+1][y+1] on left and bottom sides
|
||||
* - Grid[x][y] on right and top sides
|
||||
* - Grid[x+1][y] on left and top sides
|
||||
*/
|
||||
std::array<vtr::Point<int>, 4> grid_coords;
|
||||
std::array<std::array<e_side, 2>, 4> grid_sides;
|
||||
grid_coords[0] = vtr::Point<int>(sb_coord.x(), sb_coord.y() + 1);
|
||||
grid_sides[0] = {RIGHT, BOTTOM};
|
||||
grid_coords[1] = vtr::Point<int>(sb_coord.x() + 1, sb_coord.y() + 1);
|
||||
grid_sides[1] = {LEFT, BOTTOM};
|
||||
grid_coords[2] = vtr::Point<int>(sb_coord.x(), sb_coord.y());
|
||||
grid_sides[2] = {RIGHT, TOP};
|
||||
grid_coords[3] = vtr::Point<int>(sb_coord.x() + 1, sb_coord.y());
|
||||
grid_sides[3] = {LEFT, TOP};
|
||||
for (size_t igrid = 0; igrid < 4; igrid++) {
|
||||
vtr::Point<int> grid_coord = grid_coords[igrid];
|
||||
for (e_side grid_side : grid_sides[igrid]) {
|
||||
for (ClockInternalDriverId int_driver_id : int_driver_ids) {
|
||||
try_find_and_add_clock_opin2track_node(
|
||||
opin_nodes, grids, rr_graph_view, layer, grid_coord, grid_side,
|
||||
clk_ntwk, clk_pin, int_driver_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return opin_nodes;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Add edges between OPIN of programmable blocks and clock routing tracks
|
||||
* Note that such edges only occur at the switching points of spines
|
||||
* Different from add_rr_graph_block_clock_edges(), we follow the clock spines
|
||||
*here By expanding on switching points, internal drivers will be added
|
||||
*******************************************************************/
|
||||
static int add_rr_graph_opin2clk_edges(
|
||||
RRGraphBuilder& rr_graph_builder, size_t& num_edges_to_create,
|
||||
const RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view,
|
||||
const DeviceGrid& grids, const size_t& layer, const ClockNetwork& clk_ntwk,
|
||||
const bool& verbose) {
|
||||
size_t edge_count = 0;
|
||||
for (ClockTreeId clk_tree : clk_ntwk.trees()) {
|
||||
for (ClockSpineId ispine : clk_ntwk.spines(clk_tree)) {
|
||||
VTR_LOGV(verbose, "Finding internal drivers on spine '%s'...\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
for (auto ipin : clk_ntwk.pins(clk_tree)) {
|
||||
for (ClockSwitchPointId switch_point_id :
|
||||
clk_ntwk.spine_switch_points(ispine)) {
|
||||
if (clk_ntwk
|
||||
.spine_switch_point_internal_drivers(ispine, switch_point_id)
|
||||
.empty()) {
|
||||
continue; /* We only focus on switching points containing internal
|
||||
drivers */
|
||||
}
|
||||
size_t curr_edge_count = edge_count;
|
||||
/* Get the rr node of destination spine */
|
||||
ClockSpineId des_spine =
|
||||
clk_ntwk.spine_switch_point_tap(ispine, switch_point_id);
|
||||
vtr::Point<int> des_coord = clk_ntwk.spine_start_point(des_spine);
|
||||
Direction des_spine_direction = clk_ntwk.spine_direction(des_spine);
|
||||
ClockLevelId des_spine_level = clk_ntwk.spine_level(des_spine);
|
||||
RRNodeId des_node =
|
||||
clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree,
|
||||
des_spine_level, ipin, des_spine_direction);
|
||||
/* Walk through each qualified OPIN, build edges */
|
||||
vtr::Point<int> src_coord =
|
||||
clk_ntwk.spine_switch_point(ispine, switch_point_id);
|
||||
std::vector<ClockInternalDriverId> int_driver_ids =
|
||||
clk_ntwk.spine_switch_point_internal_drivers(ispine,
|
||||
switch_point_id);
|
||||
for (RRNodeId src_node : find_clock_opin2track_node(
|
||||
grids, rr_graph_view, layer, src_coord, clk_ntwk, ipin,
|
||||
int_driver_ids)) {
|
||||
/* Create edges */
|
||||
VTR_ASSERT(rr_graph_view.valid_node(des_node));
|
||||
rr_graph_builder.create_edge(
|
||||
src_node, des_node, clk_ntwk.default_driver_switch(), false);
|
||||
edge_count++;
|
||||
}
|
||||
VTR_LOGV(verbose, "\tWill add %lu edges to OPINs at (x=%lu, y=%lu)\n",
|
||||
edge_count - curr_edge_count, des_coord.x(), des_coord.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Allocate edges */
|
||||
rr_graph_builder.build_edges(true);
|
||||
num_edges_to_create += edge_count;
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Add edges to interconnect clock nodes
|
||||
* Walk through the routing tracks in each connection block (driver nodes)
|
||||
|
@ -581,16 +754,24 @@ static void add_rr_graph_block_clock_edges(
|
|||
static void add_rr_graph_clock_edges(
|
||||
RRGraphBuilder& rr_graph_builder, size_t& num_edges_to_create,
|
||||
const RRClockSpatialLookup& clk_rr_lookup, const RRGraphView& rr_graph_view,
|
||||
const DeviceGrid& grids, const size_t& layer, const bool& through_channel,
|
||||
const ClockNetwork& clk_ntwk, const bool& verbose) {
|
||||
const DeviceGrid& grids, const size_t& layer, const bool& perimeter_cb,
|
||||
const bool& through_channel, const ClockNetwork& clk_ntwk,
|
||||
const bool& verbose) {
|
||||
vtr::Rect<size_t> chanx_bb(1, 0, grids.width() - 1, grids.height() - 1);
|
||||
if (perimeter_cb) {
|
||||
chanx_bb.set_xmin(0);
|
||||
chanx_bb.set_xmax(grids.width());
|
||||
chanx_bb.set_ymin(0);
|
||||
chanx_bb.set_ymax(grids.height());
|
||||
}
|
||||
/* Add edges which is driven by X-direction clock routing tracks */
|
||||
for (size_t iy = 0; iy < grids.height() - 1; ++iy) {
|
||||
for (size_t ix = 1; ix < grids.width() - 1; ++ix) {
|
||||
for (size_t iy = chanx_bb.ymin(); iy < chanx_bb.ymax(); ++iy) {
|
||||
for (size_t ix = chanx_bb.xmin(); ix < chanx_bb.xmax(); ++ix) {
|
||||
vtr::Point<size_t> 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))) {
|
||||
(false == is_chanx_exist(grids, layer, chanx_coord, perimeter_cb))) {
|
||||
continue;
|
||||
}
|
||||
add_rr_graph_block_clock_edges(rr_graph_builder, num_edges_to_create,
|
||||
|
@ -600,13 +781,20 @@ static void add_rr_graph_clock_edges(
|
|||
}
|
||||
|
||||
/* Add edges which is driven by Y-direction clock routing tracks */
|
||||
for (size_t ix = 0; ix < grids.width() - 1; ++ix) {
|
||||
for (size_t iy = 1; iy < grids.height() - 1; ++iy) {
|
||||
vtr::Rect<size_t> chany_bb(0, 1, grids.width() - 1, grids.height() - 1);
|
||||
if (perimeter_cb) {
|
||||
chany_bb.set_xmin(0);
|
||||
chany_bb.set_xmax(grids.width());
|
||||
chany_bb.set_ymin(0);
|
||||
chany_bb.set_ymax(grids.height());
|
||||
}
|
||||
for (size_t ix = chany_bb.xmin(); ix < chany_bb.xmax(); ++ix) {
|
||||
for (size_t iy = chany_bb.ymin(); iy < chany_bb.ymax(); ++iy) {
|
||||
vtr::Point<size_t> 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))) {
|
||||
(false == is_chany_exist(grids, layer, chany_coord, perimeter_cb))) {
|
||||
continue;
|
||||
}
|
||||
add_rr_graph_block_clock_edges(rr_graph_builder, num_edges_to_create,
|
||||
|
@ -614,6 +802,10 @@ static void add_rr_graph_clock_edges(
|
|||
clk_ntwk, chany_coord, CHANY, verbose);
|
||||
}
|
||||
}
|
||||
/* Add edges between OPIN (internal driver) and clock routing tracks */
|
||||
add_rr_graph_opin2clk_edges(rr_graph_builder, num_edges_to_create,
|
||||
clk_rr_lookup, rr_graph_view, grids, layer,
|
||||
clk_ntwk, verbose);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
@ -638,18 +830,11 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx,
|
|||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/* Report any clock structure we do not support yet! */
|
||||
if (clk_ntwk.num_trees() > 1) {
|
||||
VTR_LOG(
|
||||
"Currently only support 1 clock tree in programmable clock "
|
||||
"architecture\nPlease update your clock architecture definition\n");
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/* Estimate the number of nodes and pre-allocate */
|
||||
size_t orig_num_nodes = vpr_device_ctx.rr_graph.num_nodes();
|
||||
size_t num_clock_nodes = estimate_clock_rr_graph_num_nodes(
|
||||
vpr_device_ctx.grid, 0, vpr_device_ctx.arch->through_channel, clk_ntwk);
|
||||
vpr_device_ctx.grid, 0, vpr_device_ctx.arch->perimeter_cb,
|
||||
vpr_device_ctx.arch->through_channel, clk_ntwk);
|
||||
vpr_device_ctx.rr_graph_builder.unlock_storage();
|
||||
vpr_device_ctx.rr_graph_builder.reserve_nodes(num_clock_nodes +
|
||||
orig_num_nodes);
|
||||
|
@ -659,10 +844,10 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx,
|
|||
num_clock_nodes, (float)(num_clock_nodes / orig_num_nodes));
|
||||
|
||||
/* Add clock nodes */
|
||||
add_rr_graph_clock_nodes(vpr_device_ctx.rr_graph_builder, clk_rr_lookup,
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, 0,
|
||||
vpr_device_ctx.arch->through_channel, clk_ntwk,
|
||||
verbose);
|
||||
add_rr_graph_clock_nodes(
|
||||
vpr_device_ctx.rr_graph_builder, clk_rr_lookup, vpr_device_ctx.rr_graph,
|
||||
vpr_device_ctx.grid, 0, vpr_device_ctx.arch->perimeter_cb,
|
||||
vpr_device_ctx.arch->through_channel, clk_ntwk, verbose);
|
||||
VTR_LOGV(verbose,
|
||||
"Added %lu clock nodes to routing "
|
||||
"resource graph.\n",
|
||||
|
@ -676,7 +861,8 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx,
|
|||
vpr_device_ctx.rr_graph_builder, num_clock_edges,
|
||||
static_cast<const RRClockSpatialLookup&>(clk_rr_lookup),
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, 0,
|
||||
vpr_device_ctx.arch->through_channel, clk_ntwk, verbose);
|
||||
vpr_device_ctx.arch->perimeter_cb, vpr_device_ctx.arch->through_channel,
|
||||
clk_ntwk, verbose);
|
||||
VTR_LOGV(verbose,
|
||||
"Added %lu clock edges to routing "
|
||||
"resource graph.\n",
|
||||
|
|
|
@ -8,10 +8,102 @@
|
|||
#include "old_traceback.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Create a mapping between each rr_node and its mapped nets
|
||||
* - Only applicable to global nets for dedicated clock routing purpose
|
||||
* - Note that this function is different than annotate_vpr_rr_nodes()
|
||||
* Please do not annotate global nets in vpr_routing_annotation!
|
||||
*******************************************************************/
|
||||
vtr::vector<RRNodeId, ClusterNetId> annotate_rr_node_global_net(
|
||||
const DeviceContext& device_ctx, const ClusteredNetlist& cluster_nlist,
|
||||
const PlacementContext& placement_ctx,
|
||||
const VprClusteringAnnotation& clustering_annotation, const bool& verbose) {
|
||||
vtr::vector<RRNodeId, ClusterNetId> rr_node_nets;
|
||||
|
||||
size_t counter = 0;
|
||||
vtr::ScopedStartFinishTimer timer("Annotating rr_node with global nets");
|
||||
|
||||
const auto& rr_graph = device_ctx.rr_graph;
|
||||
|
||||
rr_node_nets.resize(rr_graph.num_nodes(), ClusterNetId::INVALID());
|
||||
|
||||
size_t layer = 0;
|
||||
|
||||
for (ClusterNetId net_id : cluster_nlist.nets()) {
|
||||
if (!cluster_nlist.net_is_ignored(net_id)) {
|
||||
continue;
|
||||
}
|
||||
/* Walk through all the sinks */
|
||||
for (ClusterPinId pin_id : cluster_nlist.net_pins(net_id)) {
|
||||
ClusterBlockId block_id = cluster_nlist.pin_block(pin_id);
|
||||
t_block_loc blk_loc = get_block_loc(block_id, false);
|
||||
int phy_pin = placement_ctx.physical_pins[pin_id];
|
||||
t_physical_tile_type_ptr phy_tile = device_ctx.grid.get_physical_type(
|
||||
t_physical_tile_loc(blk_loc.loc.x, blk_loc.loc.y, 0));
|
||||
int node_pin_num = phy_tile->num_pins;
|
||||
/* Note that the phy_pin may not reflect the actual pin index at the
|
||||
* top-level physical tile type. It could be one of the random pin to the
|
||||
* same pin class. So here, we have to find an exact match of the pin
|
||||
* index from the clustering results! */
|
||||
int subtile_idx = blk_loc.loc.sub_tile;
|
||||
auto logical_block = cluster_nlist.block_type(block_id);
|
||||
for (int j = 0; j < logical_block->pb_type->num_pins; j++) {
|
||||
/* Find the net mapped to this pin in clustering results*/
|
||||
ClusterNetId cluster_net_id = cluster_nlist.block_net(block_id, j);
|
||||
/* Get the actual net id because it may be renamed during routing */
|
||||
if (true == clustering_annotation.is_net_renamed(block_id, j)) {
|
||||
cluster_net_id = clustering_annotation.net(block_id, j);
|
||||
}
|
||||
/* Bypass unmatched pins */
|
||||
if (cluster_net_id != net_id) {
|
||||
continue;
|
||||
}
|
||||
int curr_pin_num = get_physical_pin_at_sub_tile_location(
|
||||
phy_tile, logical_block, subtile_idx, j);
|
||||
if (phy_tile->pin_class[curr_pin_num] != phy_tile->pin_class[phy_pin]) {
|
||||
continue;
|
||||
}
|
||||
node_pin_num = curr_pin_num;
|
||||
break;
|
||||
}
|
||||
VTR_ASSERT(node_pin_num < phy_tile->num_pins);
|
||||
t_rr_type rr_pin_type = IPIN;
|
||||
if (phy_tile->class_inf[phy_tile->pin_class[node_pin_num]].type ==
|
||||
RECEIVER) {
|
||||
rr_pin_type = IPIN;
|
||||
} else if (phy_tile->class_inf[phy_tile->pin_class[node_pin_num]].type ==
|
||||
DRIVER) {
|
||||
rr_pin_type = OPIN;
|
||||
} else {
|
||||
VTR_LOG_ERROR(
|
||||
"When annotating global net '%s', invalid rr node pin type for '%s' "
|
||||
"pin '%d'\n",
|
||||
cluster_nlist.net_name(net_id).c_str(), phy_tile->name, node_pin_num);
|
||||
exit(1);
|
||||
}
|
||||
std::vector<RRNodeId> curr_rr_nodes =
|
||||
rr_graph.node_lookup().find_nodes_at_all_sides(
|
||||
layer, blk_loc.loc.x, blk_loc.loc.y, rr_pin_type, node_pin_num);
|
||||
for (RRNodeId curr_rr_node : curr_rr_nodes) {
|
||||
VTR_LOGV(verbose, "on '%s' pin '%d'\n",
|
||||
cluster_nlist.net_name(net_id).c_str(), phy_tile->name,
|
||||
node_pin_num);
|
||||
rr_node_nets[curr_rr_node] = net_id;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VTR_LOGV(verbose, "Done with %d nodes mapping\n", counter);
|
||||
|
||||
return rr_node_nets;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Create a mapping between each rr_node and its mapped nets
|
||||
* based on VPR routing results
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include "openfpga_context.h"
|
||||
#include "vpr_clustering_annotation.h"
|
||||
#include "vpr_context.h"
|
||||
#include "vpr_routing_annotation.h"
|
||||
|
||||
|
@ -15,6 +16,11 @@
|
|||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
vtr::vector<RRNodeId, ClusterNetId> annotate_rr_node_global_net(
|
||||
const DeviceContext& device_ctx, const ClusteredNetlist& cluster_nlist,
|
||||
const PlacementContext& placement_ctx,
|
||||
const VprClusteringAnnotation& clustering_annotation, const bool& verbose);
|
||||
|
||||
void annotate_vpr_rr_node_nets(const DeviceContext& device_ctx,
|
||||
const ClusteringContext& clustering_ctx,
|
||||
const RoutingContext& routing_ctx,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "route_clock_rr_graph.h"
|
||||
|
||||
#include "command_exit_codes.h"
|
||||
#include "openfpga_atom_netlist_utils.h"
|
||||
#include "openfpga_annotate_routing.h"
|
||||
#include "openfpga_clustered_netlist_utils.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_geometry.h"
|
||||
#include "vtr_log.h"
|
||||
|
@ -21,27 +22,30 @@ namespace openfpga {
|
|||
static int build_clock_tree_net_map(
|
||||
std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map,
|
||||
const ClusteredNetlist& cluster_nlist, const PinConstraints& pin_constraints,
|
||||
const std::vector<std::string>& clk_names, const ClockNetwork& clk_ntwk,
|
||||
const std::vector<ClusterNetId>& gnets, const ClockNetwork& clk_ntwk,
|
||||
const ClockTreeId clk_tree, const bool& verbose) {
|
||||
BasicPort tree_gport = clk_ntwk.tree_global_port(clk_tree);
|
||||
/* Find the pin id for each clock name, error out if there is any mismatch */
|
||||
if (clk_names.size() == 1 && clk_ntwk.tree_width(clk_tree) == 1) {
|
||||
if (clk_ntwk.num_trees() == 1 && gnets.size() == 1 &&
|
||||
clk_ntwk.tree_width(clk_tree) == 1) {
|
||||
/* Find cluster net id */
|
||||
ClusterNetId clk_net = cluster_nlist.find_net(clk_names[0]);
|
||||
if (!cluster_nlist.valid_net_id(clk_net)) {
|
||||
VTR_LOG_ERROR("Invalid clock name '%s'! Cannot found from netlists!\n",
|
||||
clk_names[0].c_str());
|
||||
if (!cluster_nlist.valid_net_id(gnets[0])) {
|
||||
VTR_LOG_ERROR("Invalid clock name '%s'! Cannot be found from netlists!\n",
|
||||
cluster_nlist.net_name(gnets[0]).c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
tree2clk_pin_map[ClockTreePinId(0)] = clk_net;
|
||||
tree2clk_pin_map[ClockTreePinId(0)] = gnets[0];
|
||||
} else {
|
||||
for (std::string clk_name : clk_names) {
|
||||
for (ClusterNetId gnet : gnets) {
|
||||
/* Find the pin information that the net should be mapped to */
|
||||
BasicPort tree_pin = pin_constraints.net_pin(clk_name);
|
||||
std::string gnet_name = cluster_nlist.net_name(gnet);
|
||||
/* The pin should match be global port name of the tree */
|
||||
BasicPort tree_pin = pin_constraints.net_pin(gnet_name);
|
||||
if (!tree_pin.is_valid()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid tree pin for clock '%s'! Clock name may not be valid "
|
||||
"(mismatched with netlists)!\n",
|
||||
clk_name.c_str());
|
||||
"Global net '%s' is not mapped to a valid pin '%s' in pin "
|
||||
"constraints!\n",
|
||||
gnet_name.c_str(), tree_pin.to_verilog_string().c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (tree_pin.get_width() != 1) {
|
||||
|
@ -49,25 +53,28 @@ static int build_clock_tree_net_map(
|
|||
"Invalid tree pin %s[%lu:%lu] for clock '%s'! Clock pin must have "
|
||||
"only a width of 1!\n",
|
||||
tree_pin.get_name().c_str(), tree_pin.get_lsb(), tree_pin.get_msb(),
|
||||
clk_name.c_str());
|
||||
gnet_name.c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (tree_pin.get_lsb() >= clk_ntwk.tree_width(clk_tree)) {
|
||||
if (tree_gport.get_name() != tree_pin.get_name()) {
|
||||
continue;
|
||||
}
|
||||
if (!tree_gport.contained(tree_pin)) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid tree pin %s[%lu] is out of range of clock tree size '%lu'\n",
|
||||
tree_pin.get_name().c_str(), tree_pin.get_lsb(),
|
||||
clk_ntwk.tree_width(clk_tree));
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
/* Find cluster net id */
|
||||
ClusterNetId clk_net = cluster_nlist.find_net(clk_name);
|
||||
if (!cluster_nlist.valid_net_id(clk_net)) {
|
||||
VTR_LOG_ERROR("Invalid clock name '%s'! Cannot found from netlists!\n",
|
||||
clk_name.c_str());
|
||||
"Invalid pin constraint port '%s' which is out of range of the "
|
||||
"global port '%s' of clock tree '%s'\n",
|
||||
tree_pin.to_verilog_string().c_str(),
|
||||
tree_gport.to_verilog_string().c_str(),
|
||||
clk_ntwk.tree_name(clk_tree).c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
/* TODO: Check the tree_pin.get_name(), see if matches the tree from ports
|
||||
*/
|
||||
/* Register the pin mapping */
|
||||
tree2clk_pin_map[ClockTreePinId(tree_pin.get_lsb())] = clk_net;
|
||||
tree2clk_pin_map[ClockTreePinId(tree_pin.get_lsb())] = gnet;
|
||||
VTR_LOGV(verbose, "Mapped net '%s' to pin '%s' of clock tree '%s'.\n",
|
||||
gnet_name.c_str(), tree_pin.to_verilog_string().c_str(),
|
||||
clk_ntwk.tree_name(clk_tree).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +84,343 @@ static int build_clock_tree_net_map(
|
|||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Route a switching points between spines
|
||||
* - connect between two routing tracks (left or right turns)
|
||||
* - connect internal driver to routing track
|
||||
*******************************************************************/
|
||||
static int route_clock_spine_switch_point(
|
||||
VprRoutingAnnotation& vpr_routing_annotation, const RRGraphView& rr_graph,
|
||||
const RRClockSpatialLookup& clk_rr_lookup,
|
||||
const vtr::vector<RRNodeId, ClusterNetId>& rr_node_gnets,
|
||||
const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map,
|
||||
const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree,
|
||||
const ClockSpineId& ispine, const ClockTreePinId& ipin,
|
||||
const ClockSwitchPointId& switch_point_id, const bool& verbose) {
|
||||
VTR_LOGV(verbose, "Routing switch points of spine '%s'...\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
vtr::Point<int> src_coord =
|
||||
clk_ntwk.spine_switch_point(ispine, switch_point_id);
|
||||
ClockSpineId des_spine =
|
||||
clk_ntwk.spine_switch_point_tap(ispine, switch_point_id);
|
||||
vtr::Point<int> des_coord = clk_ntwk.spine_start_point(des_spine);
|
||||
Direction src_spine_direction = clk_ntwk.spine_direction(ispine);
|
||||
Direction des_spine_direction = clk_ntwk.spine_direction(des_spine);
|
||||
ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine);
|
||||
ClockLevelId des_spine_level = clk_ntwk.spine_level(des_spine);
|
||||
RRNodeId src_node =
|
||||
clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree,
|
||||
src_spine_level, ipin, src_spine_direction);
|
||||
RRNodeId des_node =
|
||||
clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree,
|
||||
des_spine_level, ipin, des_spine_direction);
|
||||
VTR_ASSERT(rr_graph.valid_node(src_node));
|
||||
VTR_ASSERT(rr_graph.valid_node(des_node));
|
||||
/* Internal drivers may appear at the switch point. Check if there are
|
||||
* any defined and related rr_node found as incoming edges. If the
|
||||
* global net is mapped to the internal driver, use it as the previous
|
||||
* node */
|
||||
size_t use_int_driver = 0;
|
||||
if (!clk_ntwk.spine_switch_point_internal_drivers(ispine, switch_point_id)
|
||||
.empty() &&
|
||||
tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) {
|
||||
for (RREdgeId cand_edge : rr_graph.node_in_edges(des_node)) {
|
||||
RRNodeId opin_node = rr_graph.edge_src_node(cand_edge);
|
||||
if (OPIN != rr_graph.node_type(opin_node)) {
|
||||
continue;
|
||||
}
|
||||
if (rr_node_gnets[opin_node] != tree2clk_pin_map.at(ipin)) {
|
||||
continue;
|
||||
}
|
||||
/* This is the opin node we need, use it as the internal driver */
|
||||
vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node,
|
||||
opin_node);
|
||||
vpr_routing_annotation.set_rr_node_net(opin_node,
|
||||
tree2clk_pin_map.at(ipin));
|
||||
vpr_routing_annotation.set_rr_node_net(des_node,
|
||||
tree2clk_pin_map.at(ipin));
|
||||
use_int_driver++;
|
||||
VTR_LOGV(verbose,
|
||||
"Routed switch points of spine '%s' at the switching point "
|
||||
"(%lu, %lu) using internal driver\n",
|
||||
clk_ntwk.spine_name(ispine).c_str(), src_coord.x(),
|
||||
src_coord.y());
|
||||
}
|
||||
}
|
||||
if (use_int_driver > 1) {
|
||||
VTR_LOG_ERROR(
|
||||
"Found %lu internal drivers for the switching point (%lu, %lu) for "
|
||||
"spine '%s'!\n Expect only 1!\n",
|
||||
use_int_driver, src_coord.x(), src_coord.y(),
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (use_int_driver == 1) {
|
||||
return CMD_EXEC_SUCCESS; /* Used internal driver, early pass */
|
||||
}
|
||||
VTR_LOGV(verbose,
|
||||
"Routed switch points of spine '%s' from (x=%lu, y=%lu) to spine "
|
||||
"'%s' at (x=%lu, y=%lu)\n",
|
||||
clk_ntwk.spine_name(ispine).c_str(), src_coord.x(), src_coord.y(),
|
||||
clk_ntwk.spine_name(des_spine).c_str(), des_coord.x(),
|
||||
des_coord.y());
|
||||
vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, src_node);
|
||||
/* It could happen that there is no net mapped some clock pin, skip the
|
||||
* net mapping */
|
||||
if (tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) {
|
||||
vpr_routing_annotation.set_rr_node_net(src_node, tree2clk_pin_map.at(ipin));
|
||||
vpr_routing_annotation.set_rr_node_net(des_node, tree2clk_pin_map.at(ipin));
|
||||
}
|
||||
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Route a spine to its tap points
|
||||
* - Only connect to tap points which are mapped by a global net
|
||||
*******************************************************************/
|
||||
static int route_spine_taps(
|
||||
VprRoutingAnnotation& vpr_routing_annotation, bool& spine_usage,
|
||||
const RRGraphView& rr_graph, const RRClockSpatialLookup& clk_rr_lookup,
|
||||
const vtr::vector<RRNodeId, ClusterNetId>& rr_node_gnets,
|
||||
const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map,
|
||||
const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree,
|
||||
const ClockSpineId& ispine, const ClockTreePinId& ipin, const bool& verbose) {
|
||||
size_t spine_tap_cnt = 0;
|
||||
/* Route the spine-to-IPIN connections (only for the last level) */
|
||||
if (clk_ntwk.is_last_level(ispine)) {
|
||||
VTR_LOGV(verbose,
|
||||
"Routing clock taps of spine '%s' for pin '%d' of tree '%s'...\n",
|
||||
clk_ntwk.spine_name(ispine).c_str(), size_t(ipin),
|
||||
clk_ntwk.tree_name(clk_tree).c_str());
|
||||
std::vector<vtr::Point<int>> spine_coords =
|
||||
clk_ntwk.spine_coordinates(ispine);
|
||||
/* Connect to any fan-out node which is IPIN */
|
||||
for (size_t icoord = 0; icoord < spine_coords.size(); ++icoord) {
|
||||
vtr::Point<int> src_coord = spine_coords[icoord];
|
||||
Direction src_spine_direction = clk_ntwk.spine_direction(ispine);
|
||||
ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine);
|
||||
RRNodeId src_node =
|
||||
clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree,
|
||||
src_spine_level, ipin, src_spine_direction);
|
||||
for (RREdgeId edge : rr_graph.edge_range(src_node)) {
|
||||
RRNodeId des_node = rr_graph.edge_sink_node(edge);
|
||||
if (rr_graph.node_type(des_node) == IPIN) {
|
||||
VTR_LOGV(verbose, "Trying to route to IPIN '%s'\n",
|
||||
rr_graph.node_coordinate_to_string(des_node).c_str());
|
||||
/* Check if the IPIN is mapped, if not, do not connect */
|
||||
/* if the IPIN is mapped, only connect when net mapping is
|
||||
* expected */
|
||||
if (tree2clk_pin_map.find(ipin) == tree2clk_pin_map.end()) {
|
||||
VTR_LOGV(verbose,
|
||||
"Skip routing clock tap of spine '%s' as the tree is "
|
||||
"not used\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
continue;
|
||||
}
|
||||
if (!rr_node_gnets[des_node]) {
|
||||
VTR_LOGV(verbose,
|
||||
"Skip routing clock tap of spine '%s' as the IPIN is "
|
||||
"not mapped\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
continue;
|
||||
}
|
||||
if (rr_node_gnets[des_node] != tree2clk_pin_map.at(ipin)) {
|
||||
VTR_LOGV(verbose,
|
||||
"Skip routing clock tap of spine '%s' as the net "
|
||||
"mapping does not match clock net\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
continue;
|
||||
}
|
||||
VTR_ASSERT(rr_graph.valid_node(src_node));
|
||||
VTR_ASSERT(rr_graph.valid_node(des_node));
|
||||
VTR_LOGV(verbose, "Routed clock tap of spine '%s'\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node,
|
||||
src_node);
|
||||
vpr_routing_annotation.set_rr_node_net(src_node,
|
||||
tree2clk_pin_map.at(ipin));
|
||||
vpr_routing_annotation.set_rr_node_net(des_node,
|
||||
tree2clk_pin_map.at(ipin));
|
||||
/* Increment upon any required tap */
|
||||
spine_tap_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spine_tap_cnt) {
|
||||
spine_usage = true;
|
||||
}
|
||||
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Recursively route a clock spine on an existing routing resource graph
|
||||
* The strategy is to route spine one by one
|
||||
* - route the spine from the ending point to starting point (straight line)
|
||||
* - for each stops on the staight line, route the spine-to-spine switching
|
||||
points
|
||||
* - for each switching point (des_spine_top|bottom), go recursively
|
||||
* - If the downstream spine at any switching point is not used, disconnect
|
||||
* - If any stop on the spine (straght line) is not used, disconnect
|
||||
* - route the spine-to-IPIN connections (only for the last level)
|
||||
*
|
||||
* des_spine_top[0...N]
|
||||
* ^ ^ ^ ^
|
||||
* | | | |
|
||||
* spine_start ---->+---->+---->+---->+->spine_end
|
||||
* | | | |
|
||||
* v v v v
|
||||
* des_spine_bottom[0...N]
|
||||
*
|
||||
* <-------------------------------------------- direction to walk through
|
||||
*
|
||||
*
|
||||
* On each stop, we expand the spine to switch points and tap points
|
||||
* - If the previous stop is used (connection to des_spines are required), then
|
||||
the current stop should be connected to the previous stop
|
||||
* - If previous stop is not used, while the des_spines are required to
|
||||
connect, then the current stop should be connected to the previous stop
|
||||
* - Only when previous stops and des_spines are not used, the current stop
|
||||
will be NOT connected to the previous stop
|
||||
*
|
||||
* des_spine_top[i]
|
||||
* ^
|
||||
* |
|
||||
* spine_curr_stop ---->+->spine_prev_stop
|
||||
* |
|
||||
* v
|
||||
* des_spine_bottom[i]
|
||||
|
||||
*
|
||||
*******************************************************************/
|
||||
static int rec_expand_and_route_clock_spine(
|
||||
VprRoutingAnnotation& vpr_routing_annotation, bool& spine_usage,
|
||||
const RRGraphView& rr_graph, const RRClockSpatialLookup& clk_rr_lookup,
|
||||
const vtr::vector<RRNodeId, ClusterNetId>& rr_node_gnets,
|
||||
const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map,
|
||||
const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree,
|
||||
const ClockSpineId& curr_spine, const ClockTreePinId& curr_pin,
|
||||
const bool& disable_unused_spines, const bool& verbose) {
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
bool curr_spine_usage = false;
|
||||
bool curr_tap_usage = false;
|
||||
/* For last level, we just connect tap points */
|
||||
status = route_spine_taps(vpr_routing_annotation, curr_tap_usage, rr_graph,
|
||||
clk_rr_lookup, rr_node_gnets, tree2clk_pin_map,
|
||||
clk_ntwk, clk_tree, curr_spine, curr_pin, verbose);
|
||||
if (CMD_EXEC_SUCCESS != status) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (curr_tap_usage) {
|
||||
curr_spine_usage = true;
|
||||
}
|
||||
|
||||
std::vector<vtr::Point<int>> spine_coords =
|
||||
clk_ntwk.spine_coordinates(curr_spine);
|
||||
/* We expand from the the ending point to starting point on the straight line.
|
||||
* As such, it is easy to turn off spines by any stop.
|
||||
* The spine should go in a straight line, connect all the stops on the line
|
||||
*/
|
||||
bool prev_stop_usage = false;
|
||||
std::reverse(spine_coords.begin(), spine_coords.end());
|
||||
for (size_t icoord = 0; icoord < spine_coords.size(); ++icoord) {
|
||||
vtr::Point<int> switch_point_coord = spine_coords[icoord];
|
||||
bool curr_stop_usage = false;
|
||||
if (icoord == 0) {
|
||||
prev_stop_usage = true; /* The first stop is always used */
|
||||
}
|
||||
/* Expand on the switching point here */
|
||||
for (ClockSwitchPointId switch_point_id :
|
||||
clk_ntwk.find_spine_switch_points_with_coord(curr_spine,
|
||||
switch_point_coord)) {
|
||||
ClockSpineId des_spine =
|
||||
clk_ntwk.spine_switch_point_tap(curr_spine, switch_point_id);
|
||||
/* Go recursively for the destination spine */
|
||||
bool curr_branch_usage = false;
|
||||
status = rec_expand_and_route_clock_spine(
|
||||
vpr_routing_annotation, curr_branch_usage, rr_graph, clk_rr_lookup,
|
||||
rr_node_gnets, tree2clk_pin_map, clk_ntwk, clk_tree, des_spine,
|
||||
curr_pin, disable_unused_spines, verbose);
|
||||
if (CMD_EXEC_SUCCESS != status) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
/* Connect only when the destination spine is used */
|
||||
if (disable_unused_spines && !curr_branch_usage) {
|
||||
VTR_LOGV(verbose,
|
||||
"Disconnect switching from spine '%s' to spine '%s' as "
|
||||
"downstream is not used\n",
|
||||
clk_ntwk.spine_name(curr_spine).c_str(),
|
||||
clk_ntwk.spine_name(des_spine).c_str());
|
||||
continue;
|
||||
}
|
||||
curr_stop_usage = true;
|
||||
/* Now connect to next spine, internal drivers may join */
|
||||
status = route_clock_spine_switch_point(
|
||||
vpr_routing_annotation, rr_graph, clk_rr_lookup, rr_node_gnets,
|
||||
tree2clk_pin_map, clk_ntwk, clk_tree, curr_spine, curr_pin,
|
||||
switch_point_id, verbose);
|
||||
if (CMD_EXEC_SUCCESS != status) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
if (disable_unused_spines && !curr_stop_usage && !prev_stop_usage) {
|
||||
VTR_LOGV(verbose,
|
||||
"Disconnect backbone of spine '%s' at (x=%lu, y=%lu) as "
|
||||
"downstream is not used\n",
|
||||
clk_ntwk.spine_name(curr_spine).c_str(), switch_point_coord.x(),
|
||||
switch_point_coord.y());
|
||||
continue;
|
||||
}
|
||||
/* Skip the first stop */
|
||||
if (icoord == spine_coords.size() - 1) {
|
||||
continue;
|
||||
}
|
||||
/* Connect only when next stop is used */
|
||||
vtr::Point<int> src_coord = spine_coords[icoord + 1];
|
||||
vtr::Point<int> des_coord = spine_coords[icoord];
|
||||
VTR_LOGV(verbose,
|
||||
"(icoord=%lu) Expanding on backbone of spine '%s' from (x=%lu, "
|
||||
"y=%lu) to (x=%lu, y=%lu)...\n",
|
||||
icoord, clk_ntwk.spine_name(curr_spine).c_str(), src_coord.x(),
|
||||
src_coord.y(), des_coord.x(), des_coord.y());
|
||||
Direction src_spine_direction = clk_ntwk.spine_direction(curr_spine);
|
||||
Direction des_spine_direction = clk_ntwk.spine_direction(curr_spine);
|
||||
ClockLevelId src_spine_level = clk_ntwk.spine_level(curr_spine);
|
||||
ClockLevelId des_spine_level = clk_ntwk.spine_level(curr_spine);
|
||||
RRNodeId src_node =
|
||||
clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree,
|
||||
src_spine_level, curr_pin, src_spine_direction);
|
||||
RRNodeId des_node =
|
||||
clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree,
|
||||
des_spine_level, curr_pin, des_spine_direction);
|
||||
VTR_ASSERT(rr_graph.valid_node(src_node));
|
||||
VTR_ASSERT(rr_graph.valid_node(des_node));
|
||||
VTR_LOGV(verbose,
|
||||
"Routed backbone of spine '%s' from (x=%lu, y=%lu) to (x=%lu, "
|
||||
"y=%lu)...\n",
|
||||
clk_ntwk.spine_name(curr_spine).c_str(), src_coord.x(),
|
||||
src_coord.y(), des_coord.x(), des_coord.y());
|
||||
vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node, src_node);
|
||||
/* It could happen that there is no net mapped some clock pin, skip the
|
||||
* net mapping */
|
||||
if (tree2clk_pin_map.find(curr_pin) != tree2clk_pin_map.end()) {
|
||||
vpr_routing_annotation.set_rr_node_net(src_node,
|
||||
tree2clk_pin_map.at(curr_pin));
|
||||
vpr_routing_annotation.set_rr_node_net(des_node,
|
||||
tree2clk_pin_map.at(curr_pin));
|
||||
}
|
||||
|
||||
prev_stop_usage = true;
|
||||
curr_spine_usage = true;
|
||||
}
|
||||
/* Update status */
|
||||
spine_usage = curr_spine_usage;
|
||||
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Route a clock tree on an existing routing resource graph
|
||||
* The strategy is to route spine one by one
|
||||
|
@ -87,99 +431,35 @@ static int build_clock_tree_net_map(
|
|||
static int route_clock_tree_rr_graph(
|
||||
VprRoutingAnnotation& vpr_routing_annotation, const RRGraphView& rr_graph,
|
||||
const RRClockSpatialLookup& clk_rr_lookup,
|
||||
const vtr::vector<RRNodeId, ClusterNetId>& rr_node_gnets,
|
||||
const std::map<ClockTreePinId, ClusterNetId>& tree2clk_pin_map,
|
||||
const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree,
|
||||
const bool& disable_unused_trees, const bool& disable_unused_spines,
|
||||
const bool& verbose) {
|
||||
for (auto ispine : clk_ntwk.spines(clk_tree)) {
|
||||
VTR_LOGV(verbose, "Routing spine '%s'...\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
for (auto ipin : clk_ntwk.pins(clk_tree)) {
|
||||
/* Route the spine from starting point to ending point */
|
||||
std::vector<vtr::Point<int>> spine_coords =
|
||||
clk_ntwk.spine_coordinates(ispine);
|
||||
VTR_LOGV(verbose, "Routing backbone of spine '%s'...\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
for (size_t icoord = 0; icoord < spine_coords.size() - 1; ++icoord) {
|
||||
vtr::Point<int> src_coord = spine_coords[icoord];
|
||||
vtr::Point<int> des_coord = spine_coords[icoord + 1];
|
||||
Direction src_spine_direction = clk_ntwk.spine_direction(ispine);
|
||||
Direction des_spine_direction = clk_ntwk.spine_direction(ispine);
|
||||
ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine);
|
||||
ClockLevelId des_spine_level = clk_ntwk.spine_level(ispine);
|
||||
RRNodeId src_node =
|
||||
clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree,
|
||||
src_spine_level, ipin, src_spine_direction);
|
||||
RRNodeId des_node =
|
||||
clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree,
|
||||
des_spine_level, ipin, des_spine_direction);
|
||||
VTR_ASSERT(rr_graph.valid_node(src_node));
|
||||
VTR_ASSERT(rr_graph.valid_node(des_node));
|
||||
vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node,
|
||||
src_node);
|
||||
}
|
||||
/* Route the spine-to-spine switching points */
|
||||
VTR_LOGV(verbose, "Routing switch points of spine '%s'...\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
for (ClockSwitchPointId switch_point_id :
|
||||
clk_ntwk.spine_switch_points(ispine)) {
|
||||
vtr::Point<int> src_coord =
|
||||
clk_ntwk.spine_switch_point(ispine, switch_point_id);
|
||||
ClockSpineId des_spine =
|
||||
clk_ntwk.spine_switch_point_tap(ispine, switch_point_id);
|
||||
vtr::Point<int> des_coord = clk_ntwk.spine_start_point(des_spine);
|
||||
Direction src_spine_direction = clk_ntwk.spine_direction(ispine);
|
||||
Direction des_spine_direction = clk_ntwk.spine_direction(des_spine);
|
||||
ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine);
|
||||
ClockLevelId des_spine_level = clk_ntwk.spine_level(des_spine);
|
||||
RRNodeId src_node =
|
||||
clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree,
|
||||
src_spine_level, ipin, src_spine_direction);
|
||||
RRNodeId des_node =
|
||||
clk_rr_lookup.find_node(des_coord.x(), des_coord.y(), clk_tree,
|
||||
des_spine_level, ipin, des_spine_direction);
|
||||
VTR_ASSERT(rr_graph.valid_node(src_node));
|
||||
VTR_ASSERT(rr_graph.valid_node(des_node));
|
||||
vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node,
|
||||
src_node);
|
||||
/* It could happen that there is no net mapped some clock pin, skip the
|
||||
* net mapping */
|
||||
if (tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) {
|
||||
vpr_routing_annotation.set_rr_node_net(src_node,
|
||||
tree2clk_pin_map.at(ipin));
|
||||
vpr_routing_annotation.set_rr_node_net(des_node,
|
||||
tree2clk_pin_map.at(ipin));
|
||||
}
|
||||
}
|
||||
/* Route the spine-to-IPIN connections (only for the last level) */
|
||||
if (clk_ntwk.is_last_level(ispine)) {
|
||||
VTR_LOGV(verbose, "Routing clock taps of spine '%s'...\n",
|
||||
clk_ntwk.spine_name(ispine).c_str());
|
||||
/* Connect to any fan-out node which is IPIN */
|
||||
for (size_t icoord = 0; icoord < spine_coords.size(); ++icoord) {
|
||||
vtr::Point<int> src_coord = spine_coords[icoord];
|
||||
Direction src_spine_direction = clk_ntwk.spine_direction(ispine);
|
||||
ClockLevelId src_spine_level = clk_ntwk.spine_level(ispine);
|
||||
RRNodeId src_node =
|
||||
clk_rr_lookup.find_node(src_coord.x(), src_coord.y(), clk_tree,
|
||||
src_spine_level, ipin, src_spine_direction);
|
||||
for (RREdgeId edge : rr_graph.edge_range(src_node)) {
|
||||
RRNodeId des_node = rr_graph.edge_sink_node(edge);
|
||||
if (rr_graph.node_type(des_node) == IPIN) {
|
||||
VTR_ASSERT(rr_graph.valid_node(src_node));
|
||||
VTR_ASSERT(rr_graph.valid_node(des_node));
|
||||
vpr_routing_annotation.set_rr_node_prev_node(rr_graph, des_node,
|
||||
src_node);
|
||||
if (tree2clk_pin_map.find(ipin) != tree2clk_pin_map.end()) {
|
||||
vpr_routing_annotation.set_rr_node_net(
|
||||
src_node, tree2clk_pin_map.at(ipin));
|
||||
vpr_routing_annotation.set_rr_node_net(
|
||||
des_node, tree2clk_pin_map.at(ipin));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto ipin : clk_ntwk.pins(clk_tree)) {
|
||||
/* Do not route unused clock spines */
|
||||
if (disable_unused_trees &&
|
||||
tree2clk_pin_map.find(ipin) == tree2clk_pin_map.end()) {
|
||||
VTR_LOGV(verbose, "Skip routing unused tree '%s' pin '%lu'...\n",
|
||||
clk_ntwk.tree_name(clk_tree).c_str(), size_t(ipin));
|
||||
continue;
|
||||
}
|
||||
/* Start with the top-level spines. Recursively walk through coordinates and
|
||||
* expand on switch points */
|
||||
bool tree_usage = false;
|
||||
for (auto top_spine : clk_ntwk.tree_top_spines(clk_tree)) {
|
||||
int status = rec_expand_and_route_clock_spine(
|
||||
vpr_routing_annotation, tree_usage, rr_graph, clk_rr_lookup,
|
||||
rr_node_gnets, tree2clk_pin_map, clk_ntwk, clk_tree, top_spine, ipin,
|
||||
disable_unused_spines, verbose);
|
||||
if (CMD_EXEC_SUCCESS != status) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
if (!tree_usage) {
|
||||
VTR_LOGV(verbose, "Detect unused tree '%s' pin '%lu'...\n",
|
||||
clk_ntwk.tree_name(clk_tree).c_str(), size_t(ipin));
|
||||
}
|
||||
}
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
@ -190,15 +470,14 @@ static int route_clock_tree_rr_graph(
|
|||
* - configure the routing annotation w.r.t. the clock node connections
|
||||
* - quick check to ensure routing is valid
|
||||
*******************************************************************/
|
||||
int route_clock_rr_graph(VprRoutingAnnotation& vpr_routing_annotation,
|
||||
const DeviceContext& vpr_device_ctx,
|
||||
const AtomContext& atom_ctx,
|
||||
const ClusteredNetlist& cluster_nlist,
|
||||
const VprNetlistAnnotation& netlist_annotation,
|
||||
const RRClockSpatialLookup& clk_rr_lookup,
|
||||
const ClockNetwork& clk_ntwk,
|
||||
const PinConstraints& pin_constraints,
|
||||
const bool& verbose) {
|
||||
int route_clock_rr_graph(
|
||||
VprRoutingAnnotation& vpr_routing_annotation,
|
||||
const VprClusteringAnnotation& vpr_clustering_annotation,
|
||||
const DeviceContext& vpr_device_ctx, const ClusteredNetlist& cluster_nlist,
|
||||
const PlacementContext& vpr_place_ctx,
|
||||
const RRClockSpatialLookup& clk_rr_lookup, const ClockNetwork& clk_ntwk,
|
||||
const PinConstraints& pin_constraints, const bool& disable_unused_trees,
|
||||
const bool& disable_unused_spines, const bool& verbose) {
|
||||
vtr::ScopedStartFinishTimer timer(
|
||||
"Route programmable clock network based on routing resource graph");
|
||||
|
||||
|
@ -210,41 +489,40 @@ int route_clock_rr_graph(VprRoutingAnnotation& vpr_routing_annotation,
|
|||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/* Report any clock structure we do not support yet! */
|
||||
if (clk_ntwk.num_trees() > 1) {
|
||||
VTR_LOG(
|
||||
"Currently only support 1 clock tree in programmable clock "
|
||||
"architecture\nPlease update your clock architecture definition\n");
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/* If there are multiple clock signals from the netlist, require pin
|
||||
/* If there are multiple global signals from the netlist, require pin
|
||||
* constraints */
|
||||
std::vector<std::string> clock_net_names =
|
||||
find_atom_netlist_clock_port_names(atom_ctx.nlist, netlist_annotation);
|
||||
if (clock_net_names.empty()) {
|
||||
std::vector<ClusterNetId> gnets =
|
||||
find_clustered_netlist_global_nets(cluster_nlist);
|
||||
if (gnets.empty()) {
|
||||
VTR_LOG(
|
||||
"Skip due to 0 clocks found from netlist\nDouble check your HDL design "
|
||||
"Skip due to 0 global nets found from netlist\nDouble check your HDL "
|
||||
"design "
|
||||
"if this is unexpected\n");
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
if (clock_net_names.size() > 1 && pin_constraints.empty()) {
|
||||
if (gnets.size() > 1 && pin_constraints.empty()) {
|
||||
VTR_LOG(
|
||||
"There is %lu clock nets (more than 1). Require pin constraints to be "
|
||||
"There is %lu global nets (more than 1). Require pin constraints to be "
|
||||
"specified\n",
|
||||
clock_net_names.size());
|
||||
gnets.size());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/* Build rr_node-to-net mapping for global nets */
|
||||
vtr::vector<RRNodeId, ClusterNetId> rr_node_gnets =
|
||||
annotate_rr_node_global_net(vpr_device_ctx, cluster_nlist, vpr_place_ctx,
|
||||
vpr_clustering_annotation, verbose);
|
||||
|
||||
/* Route spines one by one */
|
||||
for (auto itree : clk_ntwk.trees()) {
|
||||
VTR_LOGV(verbose, "Build clock name to clock tree '%s' pin mapping...\n",
|
||||
VTR_LOGV(verbose,
|
||||
"Build global net name to clock tree '%s' pin mapping...\n",
|
||||
clk_ntwk.tree_name(itree).c_str());
|
||||
std::map<ClockTreePinId, ClusterNetId> tree2clk_pin_map;
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
status =
|
||||
build_clock_tree_net_map(tree2clk_pin_map, cluster_nlist, pin_constraints,
|
||||
clock_net_names, clk_ntwk, itree, verbose);
|
||||
gnets, clk_ntwk, itree, verbose);
|
||||
if (status == CMD_EXEC_FATAL_ERROR) {
|
||||
return status;
|
||||
}
|
||||
|
@ -253,7 +531,8 @@ int route_clock_rr_graph(VprRoutingAnnotation& vpr_routing_annotation,
|
|||
clk_ntwk.tree_name(itree).c_str());
|
||||
status = route_clock_tree_rr_graph(
|
||||
vpr_routing_annotation, vpr_device_ctx.rr_graph, clk_rr_lookup,
|
||||
tree2clk_pin_map, clk_ntwk, itree, verbose);
|
||||
rr_node_gnets, tree2clk_pin_map, clk_ntwk, itree, disable_unused_trees,
|
||||
disable_unused_spines, verbose);
|
||||
if (status == CMD_EXEC_FATAL_ERROR) {
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
#include "clock_network.h"
|
||||
#include "pin_constraints.h"
|
||||
#include "rr_clock_spatial_lookup.h"
|
||||
#include "vpr_clustering_annotation.h"
|
||||
#include "vpr_context.h"
|
||||
#include "vpr_netlist_annotation.h"
|
||||
#include "vpr_routing_annotation.h"
|
||||
|
||||
/********************************************************************
|
||||
|
@ -18,15 +18,14 @@
|
|||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
int route_clock_rr_graph(VprRoutingAnnotation& vpr_routing_annotation,
|
||||
const DeviceContext& vpr_device_ctx,
|
||||
const AtomContext& atom_ctx,
|
||||
const ClusteredNetlist& cluster_nlist,
|
||||
const VprNetlistAnnotation& netlist_annotation,
|
||||
const RRClockSpatialLookup& clk_rr_lookup,
|
||||
const ClockNetwork& clk_ntwk,
|
||||
const PinConstraints& pin_constraints,
|
||||
const bool& verbose);
|
||||
int route_clock_rr_graph(
|
||||
VprRoutingAnnotation& vpr_routing_annotation,
|
||||
const VprClusteringAnnotation& vpr_clustering_annotation,
|
||||
const DeviceContext& vpr_device_ctx, const ClusteredNetlist& cluster_nlist,
|
||||
const PlacementContext& vpr_place_ctx,
|
||||
const RRClockSpatialLookup& clk_rr_lookup, const ClockNetwork& clk_ntwk,
|
||||
const PinConstraints& pin_constraints, const bool& disable_unused_trees,
|
||||
const bool& disable_unused_spines, const bool& verbose);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
|
|
|
@ -219,6 +219,9 @@ int route_clock_rr_graph_template(T& openfpga_ctx, const Command& cmd,
|
|||
|
||||
/* add an option '--pin_constraints_file in short '-pcf' */
|
||||
CommandOptionId opt_pcf = cmd.option("pin_constraints_file");
|
||||
CommandOptionId opt_disable_unused_trees = cmd.option("disable_unused_trees");
|
||||
CommandOptionId opt_disable_unused_spines =
|
||||
cmd.option("disable_unused_spines");
|
||||
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||
|
||||
/* If pin constraints are enabled by command options, read the file */
|
||||
|
@ -229,10 +232,12 @@ int route_clock_rr_graph_template(T& openfpga_ctx, const Command& cmd,
|
|||
}
|
||||
|
||||
return route_clock_rr_graph(
|
||||
openfpga_ctx.mutable_vpr_routing_annotation(), g_vpr_ctx.device(),
|
||||
g_vpr_ctx.atom(), g_vpr_ctx.clustering().clb_nlist,
|
||||
openfpga_ctx.vpr_netlist_annotation(), openfpga_ctx.clock_rr_lookup(),
|
||||
openfpga_ctx.clock_arch(), pin_constraints,
|
||||
openfpga_ctx.mutable_vpr_routing_annotation(),
|
||||
openfpga_ctx.vpr_clustering_annotation(), g_vpr_ctx.device(),
|
||||
g_vpr_ctx.clustering().clb_nlist, g_vpr_ctx.placement(),
|
||||
openfpga_ctx.clock_rr_lookup(), openfpga_ctx.clock_arch(), pin_constraints,
|
||||
cmd_context.option_enable(cmd, opt_disable_unused_trees),
|
||||
cmd_context.option_enable(cmd, opt_disable_unused_spines),
|
||||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
}
|
||||
|
||||
|
|
|
@ -59,8 +59,8 @@ static void update_cluster_pin_with_post_routing_results(
|
|||
VTR_ASSERT(class_inf.type == RECEIVER);
|
||||
rr_node_type = IPIN;
|
||||
}
|
||||
std::vector<e_side> pin_sides =
|
||||
find_physical_tile_pin_side(physical_tile, physical_pin, border_side);
|
||||
std::vector<e_side> pin_sides = find_physical_tile_pin_side(
|
||||
physical_tile, physical_pin, border_side, device_ctx.arch->perimeter_cb);
|
||||
/* As some grid has height/width offset, we may not have the pin on any side
|
||||
*/
|
||||
if (0 == pin_sides.size()) {
|
||||
|
|
|
@ -236,9 +236,23 @@ int read_openfpga_clock_arch_template(T& openfpga_context, const Command& cmd,
|
|||
openfpga_context.mutable_clock_arch() =
|
||||
read_xml_clock_network(arch_file_name.c_str());
|
||||
/* Build internal links */
|
||||
openfpga_context.mutable_clock_arch().link();
|
||||
link_clock_network_rr_graph(openfpga_context.mutable_clock_arch(),
|
||||
g_vpr_ctx.device().rr_graph);
|
||||
if (!openfpga_context.mutable_clock_arch().link()) {
|
||||
VTR_LOG_ERROR("Link clock network failed!");
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (CMD_EXEC_SUCCESS !=
|
||||
link_clock_network_rr_graph(openfpga_context.mutable_clock_arch(),
|
||||
g_vpr_ctx.device().rr_graph)) {
|
||||
VTR_LOG_ERROR("Link clock network to routing architecture failed!");
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (CMD_EXEC_SUCCESS != check_clock_network_tile_annotation(
|
||||
openfpga_context.clock_arch(),
|
||||
openfpga_context.arch().tile_annotations)) {
|
||||
VTR_LOG_ERROR(
|
||||
"Check clock network consistency with tile annotation failed!");
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
/* Ensure clean data */
|
||||
openfpga_context.clock_arch().validate();
|
||||
if (!openfpga_context.clock_arch().is_valid()) {
|
||||
|
|
|
@ -710,6 +710,12 @@ ShellCommandId add_route_clock_rr_graph_command_template(
|
|||
shell_cmd.set_option_short_name(opt_file, "pcf");
|
||||
shell_cmd.set_option_require_value(opt_file, openfpga::OPT_STRING);
|
||||
|
||||
shell_cmd.add_option("disable_unused_trees", false,
|
||||
"Disable entire clock trees when they are not used by "
|
||||
"any clock nets. Useful to reduce clock power");
|
||||
shell_cmd.add_option("disable_unused_spines", false,
|
||||
"Disable part of the clock tree which are used by clock "
|
||||
"nets. Useful to reduce clock power");
|
||||
/* Add an option '--verbose' */
|
||||
shell_cmd.add_option("verbose", false, "Show verbose outputs");
|
||||
|
||||
|
|
|
@ -30,6 +30,12 @@ ShellCommandId add_write_fabric_verilog_command_template(
|
|||
shell_cmd.set_option_short_name(output_opt, "f");
|
||||
shell_cmd.set_option_require_value(output_opt, openfpga::OPT_STRING);
|
||||
|
||||
/* Add an option '--constant_undriven_inputs' */
|
||||
shell_cmd.add_option(
|
||||
"constant_undriven_inputs", false,
|
||||
"Use constant gnd for undriven wires in Verilog netlists. Recommand to "
|
||||
"enable when there are boundary routing tracks in FPGA fabric");
|
||||
|
||||
/* Add an option '--explicit_port_mapping' */
|
||||
shell_cmd.add_option("explicit_port_mapping", false,
|
||||
"Use explicit port mapping in Verilog netlists");
|
||||
|
|
|
@ -28,6 +28,8 @@ int write_fabric_verilog_template(T& openfpga_ctx, const Command& cmd,
|
|||
CommandOptionId opt_output_dir = cmd.option("file");
|
||||
CommandOptionId opt_explicit_port_mapping =
|
||||
cmd.option("explicit_port_mapping");
|
||||
CommandOptionId opt_constant_undriven_inputs =
|
||||
cmd.option("constant_undriven_inputs");
|
||||
CommandOptionId opt_include_timing = cmd.option("include_timing");
|
||||
CommandOptionId opt_print_user_defined_template =
|
||||
cmd.option("print_user_defined_template");
|
||||
|
@ -56,6 +58,17 @@ int write_fabric_verilog_template(T& openfpga_ctx, const Command& cmd,
|
|||
}
|
||||
options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose));
|
||||
options.set_compress_routing(openfpga_ctx.flow_manager().compress_routing());
|
||||
/* For perimeter cb, enable the constant undriven inputs, unless it is off by
|
||||
* user */
|
||||
if (g_vpr_ctx.device().arch->perimeter_cb) {
|
||||
options.set_constant_undriven_inputs(true);
|
||||
VTR_LOG(
|
||||
"Automatically enable the constant_undriven_input option as perimeter "
|
||||
"connection blocks are seen in FPGA fabric\n");
|
||||
} else {
|
||||
options.set_constant_undriven_inputs(
|
||||
cmd_context.option_enable(cmd, opt_constant_undriven_inputs));
|
||||
}
|
||||
|
||||
return fpga_fabric_verilog(
|
||||
openfpga_ctx.mutable_module_graph(),
|
||||
|
|
|
@ -128,7 +128,8 @@ int build_device_module_graph(
|
|||
openfpga_ctx.device_rr_gsb(), vpr_device_ctx.rr_graph,
|
||||
openfpga_ctx.arch().tile_annotations, openfpga_ctx.arch().circuit_lib,
|
||||
sram_model, openfpga_ctx.arch().config_protocol.type(),
|
||||
name_module_using_index, frame_view, verbose);
|
||||
name_module_using_index, vpr_device_ctx.arch->perimeter_cb, frame_view,
|
||||
verbose);
|
||||
}
|
||||
|
||||
/* Build FPGA fabric top-level module */
|
||||
|
@ -141,7 +142,8 @@ int build_device_module_graph(
|
|||
openfpga_ctx.arch().arch_direct, openfpga_ctx.arch().config_protocol,
|
||||
sram_model, fabric_tile, name_module_using_index, frame_view,
|
||||
compress_routing, duplicate_grid_pin, fabric_key,
|
||||
generate_random_fabric_key, group_config_block, verbose);
|
||||
generate_random_fabric_key, group_config_block,
|
||||
vpr_device_ctx.arch->perimeter_cb, verbose);
|
||||
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
|
|
|
@ -22,10 +22,175 @@
|
|||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Build tiles by following the top-level style.
|
||||
* With a given coordinate of a grid, find an existing fabric tile
|
||||
* or create a new fabric tile
|
||||
* - A grid may never exist in any fabric tile (no coordinate matches)
|
||||
* Create a new one
|
||||
* - A grid already in another fabric tile (occur in heterogeneous blocks)
|
||||
* Find the existing one
|
||||
*******************************************************************/
|
||||
static int find_or_create_one_fabric_tile_from_grid(
|
||||
FabricTile& fabric_tile, FabricTileId& curr_tile_id, const DeviceGrid& grids,
|
||||
const t_physical_tile_loc& tile_loc, const bool& verbose) {
|
||||
t_physical_tile_type_ptr phy_tile_type = grids.get_physical_type(tile_loc);
|
||||
vtr::Point<size_t> curr_tile_coord(tile_loc.x, tile_loc.y);
|
||||
vtr::Point<size_t> curr_gsb_coord(tile_loc.x, tile_loc.y);
|
||||
|
||||
bool skip_add_pb = false;
|
||||
/* For EMPTY grid, routing blocks may still be required if there is a gsb
|
||||
*/
|
||||
if (true == is_empty_type(phy_tile_type)) {
|
||||
return CMD_EXEC_SUCCESS;
|
||||
} else if ((0 < grids.get_width_offset(tile_loc)) ||
|
||||
(0 < grids.get_height_offset(tile_loc))) {
|
||||
/* Skip width, height > 1 tiles (mostly heterogeneous blocks) */
|
||||
/* Find the root of this grid, the instance id should be valid.
|
||||
* We just copy it here
|
||||
*/
|
||||
vtr::Point<size_t> root_tile_coord(
|
||||
curr_tile_coord.x() - grids.get_width_offset(tile_loc),
|
||||
curr_tile_coord.y() - grids.get_height_offset(tile_loc));
|
||||
skip_add_pb = true;
|
||||
VTR_LOGV(verbose,
|
||||
"Tile[%lu][%lu] contains a heterogeneous block which is "
|
||||
"rooted from tile[%lu][%lu]\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y(), root_tile_coord.x(),
|
||||
root_tile_coord.y());
|
||||
curr_tile_id = fabric_tile.find_tile(root_tile_coord);
|
||||
/* Update the coordinates of the pb in tiles */
|
||||
size_t root_pb_idx_in_curr_tile =
|
||||
fabric_tile.find_pb_index_in_tile(curr_tile_id, root_tile_coord);
|
||||
int status_code = fabric_tile.set_pb_max_coordinate(
|
||||
curr_tile_id, root_pb_idx_in_curr_tile, curr_tile_coord);
|
||||
if (status_code != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* Need to create a new tile here */
|
||||
VTR_LOGV(verbose, "Create a regular tile[%lu][%lu]\n", curr_tile_coord.x(),
|
||||
curr_tile_coord.y());
|
||||
curr_tile_id = fabric_tile.create_tile(curr_tile_coord);
|
||||
}
|
||||
|
||||
/* Ensure that we have a valid id */
|
||||
if (!fabric_tile.valid_tile_id(curr_tile_id)) {
|
||||
VTR_LOG_ERROR("Failed to get a valid id for tile[%lu][%lu]!\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/* Add components: pb, cbx, cby, and sb if exists */
|
||||
if (!skip_add_pb) {
|
||||
fabric_tile.add_pb_coordinate(curr_tile_id, curr_tile_coord,
|
||||
curr_gsb_coord);
|
||||
}
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Build tiles by following the bottom style.
|
||||
* - The programmble block, e.g., clb, is placed on the bottom-left corner
|
||||
* - The connection blocks and switch block are placed on the top and bottom
|
||||
*sides
|
||||
* This is exactly how GSB is organized. Just need to transfer data from one GSB
|
||||
* The gsb coordinate is the same as the grid coordinate when the
|
||||
* bottom-left style is considered
|
||||
*
|
||||
* ------------------------------
|
||||
* +----------+ +----------+ ^
|
||||
* | CBx | | SB | |
|
||||
* | [x][y] | | [x][y] | GSB[x][y]
|
||||
* +----------+ +----------+ |
|
||||
* +----------+ +----------+ |
|
||||
* | Grid | | CBy | |
|
||||
* | [x][y] | | [x][y] | |
|
||||
* +----------+ +----------+ v
|
||||
* ------------------------------
|
||||
*
|
||||
*******************************************************************/
|
||||
static int build_fabric_tile_style_bottom_left(FabricTile& fabric_tile,
|
||||
const DeviceGrid& grids,
|
||||
const size_t& layer,
|
||||
const RRGraphView& rr_graph,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const bool& verbose) {
|
||||
int status_code = CMD_EXEC_SUCCESS;
|
||||
|
||||
/* Walk through all the device rr_gsb and create tile one by one */
|
||||
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);
|
||||
FabricTileId curr_tile_id = FabricTileId::INVALID();
|
||||
status_code = find_or_create_one_fabric_tile_from_grid(
|
||||
fabric_tile, curr_tile_id, grids, tile_loc, verbose);
|
||||
if (status_code != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
/* If no tile is created for the pb, check if routing exists */
|
||||
vtr::Point<size_t> curr_tile_coord(tile_loc.x, tile_loc.y);
|
||||
vtr::Point<size_t> curr_gsb_coord(ix, iy);
|
||||
if (!fabric_tile.valid_tile_id(curr_tile_id)) {
|
||||
if (!device_rr_gsb.is_gsb_exist(rr_graph, curr_gsb_coord)) {
|
||||
VTR_LOGV(verbose, "Skip tile[%lu][%lu] as it is empty\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y());
|
||||
continue;
|
||||
}
|
||||
/* Need to create a new tile here */
|
||||
VTR_LOGV(verbose,
|
||||
"Create tile[%lu][%lu] which only has routing but not a "
|
||||
"programmable block\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y());
|
||||
curr_tile_id = fabric_tile.create_tile(curr_tile_coord);
|
||||
}
|
||||
if (fabric_tile.valid_tile_id(curr_tile_id) &&
|
||||
!device_rr_gsb.is_gsb_exist(rr_graph, curr_gsb_coord)) {
|
||||
VTR_LOGV(
|
||||
verbose,
|
||||
"Skip to add routing to tile[%lu][%lu] as it is not required\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y());
|
||||
continue;
|
||||
}
|
||||
const RRGSB& curr_rr_gsb = device_rr_gsb.get_gsb(curr_gsb_coord);
|
||||
for (t_rr_type cb_type : {CHANX, CHANY}) {
|
||||
if (curr_rr_gsb.is_cb_exist(cb_type)) {
|
||||
fabric_tile.add_cb_coordinate(curr_tile_id, cb_type,
|
||||
curr_rr_gsb.get_sb_coordinate());
|
||||
VTR_LOGV(
|
||||
verbose, "Added %s connection block [%lu][%lu] to tile[%lu][%lu]\n",
|
||||
cb_type == CHANX ? "x-" : "y-", curr_rr_gsb.get_cb_x(cb_type),
|
||||
curr_rr_gsb.get_cb_y(cb_type), ix, iy);
|
||||
}
|
||||
}
|
||||
if (curr_rr_gsb.is_sb_exist(rr_graph)) {
|
||||
fabric_tile.add_sb_coordinate(curr_tile_id,
|
||||
curr_rr_gsb.get_sb_coordinate());
|
||||
VTR_LOGV(verbose, "Added switch block [%lu][%lu] to tile[%lu][%lu]\n",
|
||||
curr_rr_gsb.get_sb_x(), curr_rr_gsb.get_sb_y(), ix, iy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status_code;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Build tiles by following the top-left style.
|
||||
* - The programmble block, e.g., clb, is placed on the top-left corner
|
||||
* - The connection blocks and switch block are placed on the right and bottom
|
||||
*sides
|
||||
* Tile[x][y]
|
||||
* ------------------------------
|
||||
* +----------+ +----------+ ^
|
||||
* | Grid | | CBy | GSB[x][y]
|
||||
* | [x][y] | | [x][y] | |
|
||||
* +----------+ +----------+ v
|
||||
* ------------------------------
|
||||
* +----------+ +----------+ ^
|
||||
* | CBx | | SB | |
|
||||
* | [x][y-1] | | [x][y-1] | GSB[x][y-1]
|
||||
* +----------+ +----------+ |
|
||||
* ------------------------------
|
||||
|
||||
*******************************************************************/
|
||||
static int build_fabric_tile_style_top_left(FabricTile& fabric_tile,
|
||||
const DeviceGrid& grids,
|
||||
|
@ -39,17 +204,34 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile,
|
|||
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);
|
||||
t_physical_tile_type_ptr phy_tile_type =
|
||||
grids.get_physical_type(tile_loc);
|
||||
bool skip_add_pb = false;
|
||||
vtr::Point<size_t> curr_tile_coord(ix, iy);
|
||||
vtr::Point<size_t> curr_gsb_coord(ix, iy - 1);
|
||||
FabricTileId curr_tile_id = FabricTileId::INVALID();
|
||||
/* For EMPTY grid, routing blocks may still be required if there is a gsb
|
||||
*/
|
||||
if (true == is_empty_type(phy_tile_type)) {
|
||||
skip_add_pb = true;
|
||||
if (!device_rr_gsb.is_gsb_exist(rr_graph, curr_gsb_coord)) {
|
||||
status_code = find_or_create_one_fabric_tile_from_grid(
|
||||
fabric_tile, curr_tile_id, grids, tile_loc, verbose);
|
||||
if (status_code != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
/* If no valid tile is created/found by the pb, check if there is any
|
||||
* routing inside */
|
||||
vtr::Point<size_t> curr_tile_coord(tile_loc.x, tile_loc.y);
|
||||
vtr::Point<size_t> curr_gsb_coord(ix, iy);
|
||||
vtr::Point<size_t> neighbor_gsb_coord(ix, iy - 1);
|
||||
if (!fabric_tile.valid_tile_id(curr_tile_id)) {
|
||||
bool routing_exist = false;
|
||||
if (device_rr_gsb.is_gsb_exist(rr_graph, curr_gsb_coord)) {
|
||||
const RRGSB& routing_rr_gsb = device_rr_gsb.get_gsb(curr_gsb_coord);
|
||||
if (routing_rr_gsb.is_cb_exist(CHANY)) {
|
||||
routing_exist = true;
|
||||
}
|
||||
}
|
||||
if (device_rr_gsb.is_gsb_exist(rr_graph, neighbor_gsb_coord)) {
|
||||
const RRGSB& routing_rr_gsb =
|
||||
device_rr_gsb.get_gsb(neighbor_gsb_coord);
|
||||
if (routing_rr_gsb.is_cb_exist(CHANX) ||
|
||||
routing_rr_gsb.is_sb_exist(rr_graph)) {
|
||||
routing_exist = true;
|
||||
}
|
||||
}
|
||||
if (!routing_exist) {
|
||||
VTR_LOGV(verbose, "Skip tile[%lu][%lu] as it is empty\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y());
|
||||
continue;
|
||||
|
@ -60,74 +242,38 @@ static int build_fabric_tile_style_top_left(FabricTile& fabric_tile,
|
|||
"programmable block\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y());
|
||||
curr_tile_id = fabric_tile.create_tile(curr_tile_coord);
|
||||
} else if ((0 < grids.get_width_offset(tile_loc)) ||
|
||||
(0 < grids.get_height_offset(tile_loc))) {
|
||||
/* Skip width, height > 1 tiles (mostly heterogeneous blocks) */
|
||||
/* Find the root of this grid, the instance id should be valid.
|
||||
* We just copy it here
|
||||
*/
|
||||
vtr::Point<size_t> root_tile_coord(
|
||||
ix - grids.get_width_offset(tile_loc),
|
||||
iy - grids.get_height_offset(tile_loc));
|
||||
skip_add_pb = true;
|
||||
VTR_LOGV(verbose,
|
||||
"Tile[%lu][%lu] contains a heterogeneous block which is "
|
||||
"rooted from tile[%lu][%lu]\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y(), root_tile_coord.x(),
|
||||
root_tile_coord.y());
|
||||
curr_tile_id = fabric_tile.find_tile(root_tile_coord);
|
||||
/* Update the coordinates of the pb in tiles */
|
||||
size_t root_pb_idx_in_curr_tile =
|
||||
fabric_tile.find_pb_index_in_tile(curr_tile_id, root_tile_coord);
|
||||
status_code = fabric_tile.set_pb_max_coordinate(
|
||||
curr_tile_id, root_pb_idx_in_curr_tile, vtr::Point<size_t>(ix, iy));
|
||||
if (status_code != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* Need to create a new tile here */
|
||||
VTR_LOGV(verbose, "Create a regular tile[%lu][%lu]\n",
|
||||
curr_tile_coord.x(), curr_tile_coord.y());
|
||||
curr_tile_id = fabric_tile.create_tile(curr_tile_coord);
|
||||
}
|
||||
|
||||
/* Ensure that we have a valid id */
|
||||
if (!fabric_tile.valid_tile_id(curr_tile_id)) {
|
||||
VTR_LOG_ERROR("Failed to get a valid id for tile[%lu][%lu]!\n", ix, iy);
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
/* Add components: pb, cbx, cby, and sb if exists */
|
||||
if (!skip_add_pb) {
|
||||
fabric_tile.add_pb_coordinate(curr_tile_id, curr_tile_coord,
|
||||
curr_gsb_coord);
|
||||
}
|
||||
/* The gsb coordinate is different than the grid coordinate when the
|
||||
* top-left style is considered
|
||||
*
|
||||
* +----------+ +----------+
|
||||
* | Grid | | CBx |
|
||||
* | [x][y] | | [x][y] |
|
||||
* +----------+ +----------+
|
||||
* +----------+ +----------+
|
||||
* | CBy | | SB |
|
||||
* | [x][y-1] | | [x][y-1] |
|
||||
* +----------+ +----------+
|
||||
*
|
||||
*/
|
||||
if (!device_rr_gsb.is_gsb_exist(rr_graph, curr_gsb_coord)) {
|
||||
continue;
|
||||
}
|
||||
const RRGSB& curr_rr_gsb = device_rr_gsb.get_gsb(curr_gsb_coord);
|
||||
for (t_rr_type cb_type : {CHANX, CHANY}) {
|
||||
if (curr_rr_gsb.is_cb_exist(cb_type)) {
|
||||
fabric_tile.add_cb_coordinate(curr_tile_id, cb_type,
|
||||
/* For the cby in the same gsb */
|
||||
if (device_rr_gsb.is_gsb_exist(rr_graph, curr_gsb_coord)) {
|
||||
const RRGSB& curr_rr_gsb = device_rr_gsb.get_gsb(curr_gsb_coord);
|
||||
if (curr_rr_gsb.is_cb_exist(CHANY)) {
|
||||
fabric_tile.add_cb_coordinate(curr_tile_id, CHANY,
|
||||
curr_rr_gsb.get_sb_coordinate());
|
||||
VTR_LOGV(
|
||||
verbose, "Added y- connection block [%lu][%lu] to tile[%lu][%lu]\n",
|
||||
curr_rr_gsb.get_cb_x(CHANY), curr_rr_gsb.get_cb_y(CHANY), ix, iy);
|
||||
}
|
||||
}
|
||||
if (curr_rr_gsb.is_sb_exist(rr_graph)) {
|
||||
fabric_tile.add_sb_coordinate(curr_tile_id,
|
||||
curr_rr_gsb.get_sb_coordinate());
|
||||
/* For the cbx and sb in the neighbour gsb */
|
||||
if (device_rr_gsb.is_gsb_exist(rr_graph, neighbor_gsb_coord)) {
|
||||
const RRGSB& neighbor_rr_gsb =
|
||||
device_rr_gsb.get_gsb(neighbor_gsb_coord);
|
||||
if (neighbor_rr_gsb.is_cb_exist(CHANX)) {
|
||||
fabric_tile.add_cb_coordinate(curr_tile_id, CHANX,
|
||||
neighbor_rr_gsb.get_sb_coordinate());
|
||||
|
||||
VTR_LOGV(verbose,
|
||||
"Added x- connection block [%lu][%lu] to tile[%lu][%lu]\n",
|
||||
neighbor_rr_gsb.get_cb_x(CHANX),
|
||||
neighbor_rr_gsb.get_cb_y(CHANX), ix, iy);
|
||||
}
|
||||
if (neighbor_rr_gsb.is_sb_exist(rr_graph)) {
|
||||
fabric_tile.add_sb_coordinate(curr_tile_id,
|
||||
neighbor_rr_gsb.get_sb_coordinate());
|
||||
VTR_LOGV(verbose, "Added switch block [%lu][%lu] to tile[%lu][%lu]\n",
|
||||
neighbor_rr_gsb.get_sb_x(), neighbor_rr_gsb.get_sb_y(), ix,
|
||||
iy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,6 +298,10 @@ int build_fabric_tile(FabricTile& fabric_tile, const TileConfig& tile_config,
|
|||
if (tile_config.style() == TileConfig::e_style::TOP_LEFT) {
|
||||
status_code = build_fabric_tile_style_top_left(
|
||||
fabric_tile, grids, 0, rr_graph, device_rr_gsb, verbose);
|
||||
} else if (tile_config.style() == TileConfig::e_style::BOTTOM_LEFT) {
|
||||
status_code = build_fabric_tile_style_bottom_left(
|
||||
fabric_tile, grids, 0, rr_graph, device_rr_gsb, verbose);
|
||||
|
||||
} else {
|
||||
/* Error out for styles that are not supported yet! */
|
||||
VTR_LOG_ERROR("Tile style '%s' is not supported yet!\n",
|
||||
|
|
|
@ -55,7 +55,8 @@ void add_grid_module_duplicated_pb_type_ports(
|
|||
ModuleManager& module_manager, const ModuleId& grid_module,
|
||||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor,
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side) {
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side,
|
||||
const bool& perimeter_cb) {
|
||||
/* Ensure that we have a valid grid_type_descriptor */
|
||||
VTR_ASSERT(false == is_empty_type(grid_type_descriptor));
|
||||
|
||||
|
@ -65,8 +66,8 @@ void add_grid_module_duplicated_pb_type_ports(
|
|||
* Otherwise, we will iterate all the 4 sides
|
||||
*/
|
||||
if (true == is_io_type(grid_type_descriptor)) {
|
||||
grid_pin_sides =
|
||||
find_grid_module_pin_sides(grid_type_descriptor, border_side);
|
||||
grid_pin_sides = find_grid_module_pin_sides(grid_type_descriptor,
|
||||
border_side, perimeter_cb);
|
||||
} else {
|
||||
grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT};
|
||||
}
|
||||
|
@ -172,7 +173,8 @@ static void add_grid_module_net_connect_duplicated_pb_graph_pin(
|
|||
const size_t& child_inst_subtile_index,
|
||||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor, t_pb_graph_pin* pb_graph_pin,
|
||||
const e_side& border_side, const e_pin2pin_interc_type& pin2pin_interc_type) {
|
||||
const e_side& border_side, const bool& perimeter_cb,
|
||||
const e_pin2pin_interc_type& pin2pin_interc_type) {
|
||||
/* Make sure this is ONLY applied to output pins */
|
||||
VTR_ASSERT(OUTPUT2OUTPUT_INTERC == pin2pin_interc_type);
|
||||
|
||||
|
@ -182,8 +184,8 @@ static void add_grid_module_net_connect_duplicated_pb_graph_pin(
|
|||
* Otherwise, we will iterate all the 4 sides
|
||||
*/
|
||||
if (true == is_io_type(grid_type_descriptor)) {
|
||||
grid_pin_sides =
|
||||
find_grid_module_pin_sides(grid_type_descriptor, border_side);
|
||||
grid_pin_sides = find_grid_module_pin_sides(grid_type_descriptor,
|
||||
border_side, perimeter_cb);
|
||||
} else {
|
||||
grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT};
|
||||
}
|
||||
|
@ -316,7 +318,8 @@ void add_grid_module_nets_connect_duplicated_pb_type_ports(
|
|||
const ModuleId& child_module, const size_t& child_instance,
|
||||
const t_sub_tile& sub_tile, const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor,
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side) {
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side,
|
||||
const bool& perimeter_cb) {
|
||||
/* Ensure that we have a valid grid_type_descriptor */
|
||||
VTR_ASSERT(false == is_empty_type(grid_type_descriptor));
|
||||
|
||||
|
@ -334,7 +337,7 @@ void add_grid_module_nets_connect_duplicated_pb_type_ports(
|
|||
module_manager, grid_module, child_module, child_instance,
|
||||
child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor,
|
||||
tile_annotation, &(top_pb_graph_node->input_pins[iport][ipin]),
|
||||
border_side, INPUT2INPUT_INTERC);
|
||||
border_side, perimeter_cb, INPUT2INPUT_INTERC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,7 +348,7 @@ void add_grid_module_nets_connect_duplicated_pb_type_ports(
|
|||
module_manager, grid_module, child_module, child_instance,
|
||||
child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor,
|
||||
&(top_pb_graph_node->output_pins[iport][ipin]), border_side,
|
||||
OUTPUT2OUTPUT_INTERC);
|
||||
perimeter_cb, OUTPUT2OUTPUT_INTERC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,7 +359,7 @@ void add_grid_module_nets_connect_duplicated_pb_type_ports(
|
|||
module_manager, grid_module, child_module, child_instance,
|
||||
child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor,
|
||||
tile_annotation, &(top_pb_graph_node->clock_pins[iport][ipin]),
|
||||
border_side, INPUT2INPUT_INTERC);
|
||||
border_side, perimeter_cb, INPUT2INPUT_INTERC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,16 @@ void add_grid_module_duplicated_pb_type_ports(
|
|||
ModuleManager& module_manager, const ModuleId& grid_module,
|
||||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor,
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side);
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side,
|
||||
const bool& perimeter_cb);
|
||||
|
||||
void add_grid_module_nets_connect_duplicated_pb_type_ports(
|
||||
ModuleManager& module_manager, const ModuleId& grid_module,
|
||||
const ModuleId& child_module, const size_t& child_instance,
|
||||
const t_sub_tile& sub_tile, const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor,
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side);
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side,
|
||||
const bool& perimeter_cb);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ namespace openfpga {
|
|||
* 5. I/O grids in the center part of FPGA can have ports on any side
|
||||
*******************************************************************/
|
||||
std::vector<e_side> find_grid_module_pin_sides(
|
||||
t_physical_tile_type_ptr grid_type_descriptor, const e_side& border_side) {
|
||||
t_physical_tile_type_ptr grid_type_descriptor, const e_side& border_side,
|
||||
const bool& perimeter_cb) {
|
||||
/* We must have an regular (non-I/O) type here */
|
||||
VTR_ASSERT(true == is_io_type(grid_type_descriptor));
|
||||
SideManager side_manager(border_side);
|
||||
|
@ -33,7 +34,19 @@ std::vector<e_side> find_grid_module_pin_sides(
|
|||
return {TOP, RIGHT, BOTTOM, LEFT};
|
||||
}
|
||||
|
||||
return std::vector<e_side>(1, side_manager.get_opposite());
|
||||
if (!perimeter_cb) {
|
||||
return std::vector<e_side>(1, side_manager.get_opposite());
|
||||
}
|
||||
/* For cbs on perimeter, exclude the border side. All the other 3 sides are ok
|
||||
*/
|
||||
std::vector<e_side> pin_sides;
|
||||
pin_sides.reserve(3);
|
||||
for (e_side pin_side : {TOP, RIGHT, BOTTOM, LEFT}) {
|
||||
if (pin_side != border_side) {
|
||||
pin_sides.push_back(pin_side);
|
||||
}
|
||||
}
|
||||
return pin_sides;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
@ -47,15 +60,16 @@ void add_grid_module_net_connect_pb_graph_pin(
|
|||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor,
|
||||
const TileAnnotation& tile_annotation, t_pb_graph_pin* pb_graph_pin,
|
||||
const e_side& border_side, const e_pin2pin_interc_type& pin2pin_interc_type) {
|
||||
const e_side& border_side, const bool& perimeter_cb,
|
||||
const e_pin2pin_interc_type& pin2pin_interc_type) {
|
||||
/* Find the pin side for I/O grids*/
|
||||
std::vector<e_side> grid_pin_sides;
|
||||
/* For I/O grids, we care only one side
|
||||
* Otherwise, we will iterate all the 4 sides
|
||||
*/
|
||||
if (true == is_io_type(grid_type_descriptor)) {
|
||||
grid_pin_sides =
|
||||
find_grid_module_pin_sides(grid_type_descriptor, border_side);
|
||||
grid_pin_sides = find_grid_module_pin_sides(grid_type_descriptor,
|
||||
border_side, perimeter_cb);
|
||||
} else {
|
||||
grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT};
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
namespace openfpga {
|
||||
|
||||
std::vector<e_side> find_grid_module_pin_sides(
|
||||
t_physical_tile_type_ptr grid_type_descriptor, const e_side& border_side);
|
||||
t_physical_tile_type_ptr grid_type_descriptor, const e_side& border_side,
|
||||
const bool& perimeter_cb);
|
||||
|
||||
void add_grid_module_net_connect_pb_graph_pin(
|
||||
ModuleManager& module_manager, const ModuleId& grid_module,
|
||||
|
@ -28,7 +29,7 @@ void add_grid_module_net_connect_pb_graph_pin(
|
|||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor,
|
||||
const TileAnnotation& tile_annotation, t_pb_graph_pin* pb_graph_pin,
|
||||
const e_side& border_side,
|
||||
const e_side& border_side, const bool& perimeter_cb,
|
||||
const enum e_pin2pin_interc_type& pin2pin_interc_type);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -42,7 +42,8 @@ static void add_grid_module_pb_type_ports(
|
|||
ModuleManager& module_manager, const ModuleId& grid_module,
|
||||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor,
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side) {
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side,
|
||||
const bool& perimeter_cb) {
|
||||
/* Ensure that we have a valid grid_type_descriptor */
|
||||
VTR_ASSERT(nullptr != grid_type_descriptor);
|
||||
|
||||
|
@ -52,8 +53,8 @@ static void add_grid_module_pb_type_ports(
|
|||
* Otherwise, we will iterate all the 4 sides
|
||||
*/
|
||||
if (true == is_io_type(grid_type_descriptor)) {
|
||||
grid_pin_sides =
|
||||
find_grid_module_pin_sides(grid_type_descriptor, border_side);
|
||||
grid_pin_sides = find_grid_module_pin_sides(grid_type_descriptor,
|
||||
border_side, perimeter_cb);
|
||||
} else {
|
||||
grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT};
|
||||
}
|
||||
|
@ -125,7 +126,8 @@ static void add_grid_module_nets_connect_pb_type_ports(
|
|||
const ModuleId& child_module, const size_t& child_instance,
|
||||
const t_sub_tile& sub_tile, const VprDeviceAnnotation& vpr_device_annotation,
|
||||
t_physical_tile_type_ptr grid_type_descriptor,
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side) {
|
||||
const TileAnnotation& tile_annotation, const e_side& border_side,
|
||||
const bool& perimeter_cb) {
|
||||
/* Ensure that we have a valid grid_type_descriptor */
|
||||
VTR_ASSERT(nullptr != grid_type_descriptor);
|
||||
|
||||
|
@ -144,7 +146,7 @@ static void add_grid_module_nets_connect_pb_type_ports(
|
|||
module_manager, grid_module, child_module, child_instance,
|
||||
child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor,
|
||||
tile_annotation, &(top_pb_graph_node->input_pins[iport][ipin]),
|
||||
border_side, INPUT2INPUT_INTERC);
|
||||
border_side, perimeter_cb, INPUT2INPUT_INTERC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,7 +157,7 @@ static void add_grid_module_nets_connect_pb_type_ports(
|
|||
module_manager, grid_module, child_module, child_instance,
|
||||
child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor,
|
||||
tile_annotation, &(top_pb_graph_node->output_pins[iport][ipin]),
|
||||
border_side, OUTPUT2OUTPUT_INTERC);
|
||||
border_side, perimeter_cb, OUTPUT2OUTPUT_INTERC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +168,7 @@ static void add_grid_module_nets_connect_pb_type_ports(
|
|||
module_manager, grid_module, child_module, child_instance,
|
||||
child_inst_subtile_index, vpr_device_annotation, grid_type_descriptor,
|
||||
tile_annotation, &(top_pb_graph_node->clock_pins[iport][ipin]),
|
||||
border_side, INPUT2INPUT_INTERC);
|
||||
border_side, perimeter_cb, INPUT2INPUT_INTERC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1168,7 +1170,7 @@ static int build_physical_tile_module(
|
|||
const TileAnnotation& tile_annotation, const e_side& border_side,
|
||||
const QLMemoryBankConfigSetting* ql_memory_bank_config_setting,
|
||||
const bool& duplicate_grid_pin, const bool& group_config_block,
|
||||
const bool& verbose) {
|
||||
const bool& perimeter_cb, const bool& verbose) {
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
/* Create a Module for the top-level physical block, and add to module manager
|
||||
*/
|
||||
|
@ -1247,7 +1249,7 @@ static int build_physical_tile_module(
|
|||
/* Default way to add these ports by following the definition in pb_types */
|
||||
add_grid_module_pb_type_ports(module_manager, grid_module,
|
||||
vpr_device_annotation, phy_block_type,
|
||||
tile_annotation, border_side);
|
||||
tile_annotation, border_side, perimeter_cb);
|
||||
/* Add module nets to connect the pb_type ports to sub modules */
|
||||
for (const t_sub_tile& sub_tile : phy_block_type->sub_tiles) {
|
||||
VTR_ASSERT(sub_tile.equivalent_sites.size() == 1);
|
||||
|
@ -1264,7 +1266,8 @@ static int build_physical_tile_module(
|
|||
module_manager.child_module_instances(grid_module, pb_module)) {
|
||||
add_grid_module_nets_connect_pb_type_ports(
|
||||
module_manager, grid_module, pb_module, child_instance, sub_tile,
|
||||
vpr_device_annotation, phy_block_type, tile_annotation, border_side);
|
||||
vpr_device_annotation, phy_block_type, tile_annotation, border_side,
|
||||
perimeter_cb);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1272,7 +1275,7 @@ static int build_physical_tile_module(
|
|||
/* Add these ports with duplication */
|
||||
add_grid_module_duplicated_pb_type_ports(
|
||||
module_manager, grid_module, vpr_device_annotation, phy_block_type,
|
||||
tile_annotation, border_side);
|
||||
tile_annotation, border_side, perimeter_cb);
|
||||
|
||||
/* Add module nets to connect the duplicated pb_type ports to sub modules */
|
||||
for (const t_sub_tile& sub_tile : phy_block_type->sub_tiles) {
|
||||
|
@ -1290,7 +1293,8 @@ static int build_physical_tile_module(
|
|||
module_manager.child_module_instances(grid_module, pb_module)) {
|
||||
add_grid_module_nets_connect_duplicated_pb_type_ports(
|
||||
module_manager, grid_module, pb_module, child_instance, sub_tile,
|
||||
vpr_device_annotation, phy_block_type, tile_annotation, border_side);
|
||||
vpr_device_annotation, phy_block_type, tile_annotation, border_side,
|
||||
perimeter_cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1436,7 +1440,7 @@ int build_grid_modules(
|
|||
module_manager, decoder_lib, device_annotation, circuit_lib,
|
||||
sram_orgz_type, sram_model, &physical_tile, tile_annotation,
|
||||
io_type_side, ql_memory_bank_config_setting, duplicate_grid_pin,
|
||||
group_config_block, verbose);
|
||||
group_config_block, device_ctx.arch->perimeter_cb, verbose);
|
||||
if (status != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
@ -1447,7 +1451,7 @@ int build_grid_modules(
|
|||
module_manager, decoder_lib, device_annotation, circuit_lib,
|
||||
sram_orgz_type, sram_model, &physical_tile, tile_annotation, NUM_SIDES,
|
||||
ql_memory_bank_config_setting, duplicate_grid_pin, group_config_block,
|
||||
verbose);
|
||||
device_ctx.arch->perimeter_cb, verbose);
|
||||
if (status != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
|
|
@ -732,13 +732,13 @@ static int build_tile_module_port_and_nets_between_sb_and_cb(
|
|||
* is_cb_exist() FOr RIGHT and BOTTOM side, find the adjacent RRGSB and then
|
||||
* use is_cb_exist()
|
||||
*/
|
||||
if (TOP == side_manager.get_side() || LEFT == side_manager.get_side()) {
|
||||
if (BOTTOM == side_manager.get_side() || LEFT == side_manager.get_side()) {
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (RIGHT == side_manager.get_side() || BOTTOM == side_manager.get_side()) {
|
||||
if (RIGHT == side_manager.get_side() || TOP == side_manager.get_side()) {
|
||||
const RRGSB& adjacent_gsb =
|
||||
device_rr_gsb.get_gsb(module_gsb_cb_coordinate);
|
||||
if (false == adjacent_gsb.is_cb_exist(cb_type)) {
|
||||
|
@ -1175,7 +1175,7 @@ static int build_tile_port_and_nets_from_pb(
|
|||
const TileAnnotation& tile_annotation, const vtr::Point<size_t>& pb_coord,
|
||||
const std::vector<size_t>& pb_instances, const FabricTile& fabric_tile,
|
||||
const FabricTileId& curr_fabric_tile_id, const size_t& ipb,
|
||||
const bool& frame_view, const bool& verbose) {
|
||||
const bool& perimeter_cb, const bool& frame_view, const bool& verbose) {
|
||||
size_t pb_instance = pb_instances[ipb];
|
||||
t_physical_tile_type_ptr phy_tile = grids.get_physical_type(
|
||||
t_physical_tile_loc(pb_coord.x(), pb_coord.y(), layer));
|
||||
|
@ -1201,7 +1201,8 @@ static int build_tile_port_and_nets_from_pb(
|
|||
* Otherwise, we will iterate all the 4 sides
|
||||
*/
|
||||
if (true == is_io_type(phy_tile)) {
|
||||
grid_pin_sides = find_grid_module_pin_sides(phy_tile, grid_side);
|
||||
grid_pin_sides =
|
||||
find_grid_module_pin_sides(phy_tile, grid_side, perimeter_cb);
|
||||
} else {
|
||||
grid_pin_sides = {TOP, RIGHT, BOTTOM, LEFT};
|
||||
}
|
||||
|
@ -1378,7 +1379,7 @@ static int build_tile_module_ports_and_nets(
|
|||
const FabricTileId& fabric_tile_id, const std::vector<size_t>& pb_instances,
|
||||
const std::map<t_rr_type, std::vector<size_t>>& cb_instances,
|
||||
const std::vector<size_t>& sb_instances, const bool& name_module_using_index,
|
||||
const bool& frame_view, const bool& verbose) {
|
||||
const bool& perimeter_cb, const bool& frame_view, const bool& verbose) {
|
||||
int status_code = CMD_EXEC_SUCCESS;
|
||||
|
||||
/* Get the submodule of Switch blocks one by one, build connections between sb
|
||||
|
@ -1441,7 +1442,7 @@ static int build_tile_module_ports_and_nets(
|
|||
status_code = build_tile_port_and_nets_from_pb(
|
||||
module_manager, tile_module, grids, layer, vpr_device_annotation,
|
||||
rr_graph_view, tile_annotation, pb_coord, pb_instances, fabric_tile,
|
||||
fabric_tile_id, ipb, frame_view, verbose);
|
||||
fabric_tile_id, ipb, perimeter_cb, frame_view, verbose);
|
||||
if (status_code != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
@ -1487,8 +1488,8 @@ static int build_tile_module(
|
|||
const TileAnnotation& tile_annotation, const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& sram_model,
|
||||
const e_config_protocol_type& sram_orgz_type,
|
||||
const bool& name_module_using_index, const bool& frame_view,
|
||||
const bool& verbose) {
|
||||
const bool& name_module_using_index, const bool& perimeter_cb,
|
||||
const bool& frame_view, const bool& verbose) {
|
||||
int status_code = CMD_EXEC_SUCCESS;
|
||||
|
||||
/* Create the module */
|
||||
|
@ -1635,7 +1636,7 @@ static int build_tile_module(
|
|||
module_manager, tile_module, grids, layer, vpr_device_annotation,
|
||||
device_rr_gsb, rr_graph_view, tile_annotation, fabric_tile, fabric_tile_id,
|
||||
pb_instances, cb_instances, sb_instances, name_module_using_index,
|
||||
frame_view, verbose);
|
||||
perimeter_cb, frame_view, verbose);
|
||||
|
||||
/* Add global ports to the pb_module:
|
||||
* This is a much easier job after adding sub modules (instances),
|
||||
|
@ -1698,18 +1699,16 @@ static int build_tile_module(
|
|||
/********************************************************************
|
||||
* Build all the tile modules
|
||||
*******************************************************************/
|
||||
int build_tile_modules(ModuleManager& module_manager,
|
||||
DecoderLibrary& decoder_lib,
|
||||
const FabricTile& fabric_tile, const DeviceGrid& grids,
|
||||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const RRGraphView& rr_graph_view,
|
||||
const TileAnnotation& tile_annotation,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& sram_model,
|
||||
const e_config_protocol_type& sram_orgz_type,
|
||||
const bool& name_module_using_index,
|
||||
const bool& frame_view, const bool& verbose) {
|
||||
int build_tile_modules(
|
||||
ModuleManager& module_manager, DecoderLibrary& decoder_lib,
|
||||
const FabricTile& fabric_tile, const DeviceGrid& grids,
|
||||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph_view,
|
||||
const TileAnnotation& tile_annotation, const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& sram_model,
|
||||
const e_config_protocol_type& sram_orgz_type,
|
||||
const bool& name_module_using_index, const bool& perimeter_cb,
|
||||
const bool& frame_view, const bool& verbose) {
|
||||
vtr::ScopedStartFinishTimer timer("Build tile modules for the FPGA fabric");
|
||||
|
||||
int status_code = CMD_EXEC_SUCCESS;
|
||||
|
@ -1722,7 +1721,7 @@ int build_tile_modules(ModuleManager& module_manager,
|
|||
module_manager, decoder_lib, fabric_tile, fabric_tile_id, grids, layer,
|
||||
vpr_device_annotation, device_rr_gsb, rr_graph_view, tile_annotation,
|
||||
circuit_lib, sram_model, sram_orgz_type, name_module_using_index,
|
||||
frame_view, verbose);
|
||||
perimeter_cb, frame_view, verbose);
|
||||
if (status_code != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
|
|
@ -25,18 +25,16 @@
|
|||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
int build_tile_modules(ModuleManager& module_manager,
|
||||
DecoderLibrary& decoder_lib,
|
||||
const FabricTile& fabric_tile, const DeviceGrid& grids,
|
||||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const RRGraphView& rr_graph_view,
|
||||
const TileAnnotation& tile_annotation,
|
||||
const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& sram_model,
|
||||
const e_config_protocol_type& sram_orgz_type,
|
||||
const bool& name_module_using_index,
|
||||
const bool& frame_view, const bool& verbose);
|
||||
int build_tile_modules(
|
||||
ModuleManager& module_manager, DecoderLibrary& decoder_lib,
|
||||
const FabricTile& fabric_tile, const DeviceGrid& grids,
|
||||
const VprDeviceAnnotation& vpr_device_annotation,
|
||||
const DeviceRRGSB& device_rr_gsb, const RRGraphView& rr_graph_view,
|
||||
const TileAnnotation& tile_annotation, const CircuitLibrary& circuit_lib,
|
||||
const CircuitModelId& sram_model,
|
||||
const e_config_protocol_type& sram_orgz_type,
|
||||
const bool& name_module_using_index, const bool& perimeter_cb,
|
||||
const bool& frame_view, const bool& verbose);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
|
|
|
@ -58,7 +58,8 @@ int build_top_module(
|
|||
const bool& name_module_using_index, const bool& frame_view,
|
||||
const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin,
|
||||
const FabricKey& fabric_key, const bool& generate_random_fabric_key,
|
||||
const bool& group_config_block, const bool& verbose) {
|
||||
const bool& group_config_block, const bool& perimeter_cb,
|
||||
const bool& verbose) {
|
||||
vtr::ScopedStartFinishTimer timer("Build FPGA fabric module");
|
||||
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
|
@ -79,7 +80,7 @@ int build_top_module(
|
|||
rr_clock_lookup, vpr_device_annotation, grids, layer, tile_annotation,
|
||||
rr_graph, device_rr_gsb, tile_direct, arch_direct, config_protocol,
|
||||
sram_model, frame_view, compact_routing_hierarchy, duplicate_grid_pin,
|
||||
fabric_key, group_config_block);
|
||||
fabric_key, group_config_block, perimeter_cb, verbose);
|
||||
} else {
|
||||
/* Build the tile instances under the top module */
|
||||
status = build_top_module_tile_child_instances(
|
||||
|
@ -87,7 +88,7 @@ int build_top_module(
|
|||
rr_clock_lookup, vpr_device_annotation, grids, layer, tile_annotation,
|
||||
rr_graph, device_rr_gsb, tile_direct, arch_direct, fabric_tile,
|
||||
config_protocol, sram_model, fabric_key, group_config_block,
|
||||
name_module_using_index, frame_view, verbose);
|
||||
name_module_using_index, perimeter_cb, frame_view, verbose);
|
||||
}
|
||||
|
||||
if (status != CMD_EXEC_SUCCESS) {
|
||||
|
|
|
@ -45,7 +45,8 @@ int build_top_module(
|
|||
const bool& name_module_using_index, const bool& frame_view,
|
||||
const bool& compact_routing_hierarchy, const bool& duplicate_grid_pin,
|
||||
const FabricKey& fabric_key, const bool& generate_random_fabric_key,
|
||||
const bool& group_config_block, const bool& verbose);
|
||||
const bool& group_config_block, const bool& perimeter_cb,
|
||||
const bool& verbose);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ static vtr::Matrix<size_t> add_top_module_switch_block_instances(
|
|||
static vtr::Matrix<size_t> add_top_module_connection_block_instances(
|
||||
ModuleManager& module_manager, const ModuleId& top_module,
|
||||
const DeviceRRGSB& device_rr_gsb, const t_rr_type& cb_type,
|
||||
const bool& compact_routing_hierarchy) {
|
||||
const bool& compact_routing_hierarchy, const bool& verbose) {
|
||||
vtr::ScopedStartFinishTimer timer(
|
||||
"Add connection block instances to top module");
|
||||
|
||||
|
@ -261,9 +261,15 @@ static vtr::Matrix<size_t> add_top_module_connection_block_instances(
|
|||
* We will skip those modules
|
||||
*/
|
||||
const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy);
|
||||
VTR_LOGV(verbose, "Try to add %s connnection block at (%lu, %lu)\n",
|
||||
cb_type == CHANX ? "X-" : "Y-", ix, iy);
|
||||
vtr::Point<size_t> cb_coordinate(rr_gsb.get_cb_x(cb_type),
|
||||
rr_gsb.get_cb_y(cb_type));
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
VTR_LOGV(
|
||||
verbose,
|
||||
"Skip %s connnection block at (%lu, %lu) as it does not exist\n",
|
||||
cb_type == CHANX ? "X-" : "Y-", cb_coordinate.x(), cb_coordinate.y());
|
||||
continue;
|
||||
}
|
||||
/* If we use compact routing hierarchy, we should instanciate the unique
|
||||
|
@ -295,6 +301,9 @@ static vtr::Matrix<size_t> add_top_module_connection_block_instances(
|
|||
top_module, cb_module,
|
||||
cb_instance_ids[rr_gsb.get_cb_x(cb_type)][rr_gsb.get_cb_y(cb_type)],
|
||||
cb_instance_name);
|
||||
VTR_LOGV(verbose, "Added %s connnection block '%s' (module '%s')\n",
|
||||
cb_type == CHANX ? "X-" : "Y-", cb_instance_name.c_str(),
|
||||
cb_module_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -445,7 +454,8 @@ int build_top_module_fine_grained_child_instances(
|
|||
const ConfigProtocol& config_protocol, const CircuitModelId& sram_model,
|
||||
const bool& frame_view, const bool& compact_routing_hierarchy,
|
||||
const bool& duplicate_grid_pin, const FabricKey& fabric_key,
|
||||
const bool& group_config_block) {
|
||||
const bool& group_config_block, const bool& perimeter_cb,
|
||||
const bool& verbose) {
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
std::map<t_rr_type, vtr::Matrix<size_t>> cb_instance_ids;
|
||||
|
||||
|
@ -459,11 +469,11 @@ int build_top_module_fine_grained_child_instances(
|
|||
compact_routing_hierarchy);
|
||||
/* Add all the CBX and CBYs across the fabric */
|
||||
cb_instance_ids[CHANX] = add_top_module_connection_block_instances(
|
||||
module_manager, top_module, device_rr_gsb, CHANX,
|
||||
compact_routing_hierarchy);
|
||||
module_manager, top_module, device_rr_gsb, CHANX, compact_routing_hierarchy,
|
||||
verbose);
|
||||
cb_instance_ids[CHANY] = add_top_module_connection_block_instances(
|
||||
module_manager, top_module, device_rr_gsb, CHANY,
|
||||
compact_routing_hierarchy);
|
||||
module_manager, top_module, device_rr_gsb, CHANY, compact_routing_hierarchy,
|
||||
verbose);
|
||||
|
||||
/* Update I/O children list */
|
||||
add_top_module_io_children(module_manager, top_module, grids, layer,
|
||||
|
@ -492,7 +502,7 @@ int build_top_module_fine_grained_child_instances(
|
|||
status = add_top_module_global_ports_from_grid_modules(
|
||||
module_manager, top_module, tile_annotation, vpr_device_annotation, grids,
|
||||
layer, rr_graph, device_rr_gsb, cb_instance_ids, grid_instance_ids,
|
||||
clk_ntwk, rr_clock_lookup);
|
||||
clk_ntwk, rr_clock_lookup, perimeter_cb);
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,8 @@ int build_top_module_fine_grained_child_instances(
|
|||
const ConfigProtocol& config_protocol, const CircuitModelId& sram_model,
|
||||
const bool& frame_view, const bool& compact_routing_hierarchy,
|
||||
const bool& duplicate_grid_pin, const FabricKey& fabric_key,
|
||||
const bool& group_config_block);
|
||||
const bool& group_config_block, const bool& perimeter_cb,
|
||||
const bool& verbose);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
|
|
|
@ -802,13 +802,13 @@ static int build_top_module_tile_nets_between_sb_and_cb(
|
|||
* is_cb_exist() FOr RIGHT and BOTTOM side, find the adjacent RRGSB and then
|
||||
* use is_cb_exist()
|
||||
*/
|
||||
if (TOP == side_manager.get_side() || LEFT == side_manager.get_side()) {
|
||||
if (BOTTOM == side_manager.get_side() || LEFT == side_manager.get_side()) {
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (RIGHT == side_manager.get_side() || BOTTOM == side_manager.get_side()) {
|
||||
if (RIGHT == side_manager.get_side() || TOP == side_manager.get_side()) {
|
||||
const RRGSB& adjacent_gsb =
|
||||
device_rr_gsb.get_gsb(module_gsb_cb_coordinate);
|
||||
if (false == adjacent_gsb.is_cb_exist(cb_type)) {
|
||||
|
@ -1295,7 +1295,7 @@ static int build_top_module_global_net_for_given_tile_module(
|
|||
const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids,
|
||||
const size_t& layer, const vtr::Point<size_t>& grid_coordinate,
|
||||
const e_side& border_side, const vtr::Matrix<size_t>& tile_instance_ids,
|
||||
const FabricTile& fabric_tile) {
|
||||
const FabricTile& fabric_tile, const bool& perimeter_cb) {
|
||||
/* Get the tile module and instance */
|
||||
FabricTileId curr_fabric_tile_id =
|
||||
fabric_tile.find_tile_by_pb_coordinate(grid_coordinate);
|
||||
|
@ -1392,7 +1392,7 @@ static int build_top_module_global_net_for_given_tile_module(
|
|||
size_t grid_pin_height =
|
||||
physical_tile->pin_height_offset[grid_pin_index];
|
||||
std::vector<e_side> pin_sides = find_physical_tile_pin_side(
|
||||
physical_tile, grid_pin_index, border_side);
|
||||
physical_tile, grid_pin_index, border_side, perimeter_cb);
|
||||
|
||||
BasicPort grid_pin_info =
|
||||
vpr_device_annotation.physical_tile_pin_port_info(physical_tile,
|
||||
|
@ -1452,7 +1452,7 @@ static int build_top_module_global_net_from_tile_modules(
|
|||
const TileGlobalPortId& tile_global_port,
|
||||
const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids,
|
||||
const size_t& layer, const vtr::Matrix<size_t>& tile_instance_ids,
|
||||
const FabricTile& fabric_tile) {
|
||||
const FabricTile& fabric_tile, const bool& perimeter_cb) {
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
|
||||
std::map<e_side, std::vector<vtr::Point<size_t>>> io_coordinates =
|
||||
|
@ -1530,8 +1530,8 @@ static int build_top_module_global_net_from_tile_modules(
|
|||
status = build_top_module_global_net_for_given_tile_module(
|
||||
module_manager, top_module, top_module_port, tile_annotation,
|
||||
tile_global_port, tile_port, vpr_device_annotation, grids, layer,
|
||||
vtr::Point<size_t>(ix, iy), NUM_SIDES, tile_instance_ids,
|
||||
fabric_tile);
|
||||
vtr::Point<size_t>(ix, iy), NUM_SIDES, tile_instance_ids, fabric_tile,
|
||||
perimeter_cb);
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
|
@ -1578,7 +1578,7 @@ static int build_top_module_global_net_from_tile_modules(
|
|||
status = build_top_module_global_net_for_given_tile_module(
|
||||
module_manager, top_module, top_module_port, tile_annotation,
|
||||
tile_global_port, tile_port, vpr_device_annotation, grids, layer,
|
||||
io_coordinate, io_side, tile_instance_ids, fabric_tile);
|
||||
io_coordinate, io_side, tile_instance_ids, fabric_tile, perimeter_cb);
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
|
@ -1600,7 +1600,8 @@ static int add_top_module_global_ports_from_tile_modules(
|
|||
const size_t& layer, const RRGraphView& rr_graph,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const vtr::Matrix<size_t>& tile_instance_ids, const FabricTile& fabric_tile,
|
||||
const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup) {
|
||||
const ClockNetwork& clk_ntwk, const RRClockSpatialLookup& rr_clock_lookup,
|
||||
const bool& perimeter_cb) {
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
|
||||
/* Add the global ports which are NOT yet added to the top-level module
|
||||
|
@ -1657,7 +1658,7 @@ static int add_top_module_global_ports_from_tile_modules(
|
|||
status = build_top_module_global_net_from_tile_modules(
|
||||
module_manager, top_module, top_module_port, tile_annotation,
|
||||
tile_global_port, vpr_device_annotation, grids, layer,
|
||||
tile_instance_ids, fabric_tile);
|
||||
tile_instance_ids, fabric_tile, perimeter_cb);
|
||||
}
|
||||
if (status == CMD_EXEC_FATAL_ERROR) {
|
||||
return status;
|
||||
|
@ -1905,7 +1906,7 @@ int build_top_module_tile_child_instances(
|
|||
const FabricTile& fabric_tile, const ConfigProtocol& config_protocol,
|
||||
const CircuitModelId& sram_model, const FabricKey& fabric_key,
|
||||
const bool& group_config_block, const bool& name_module_using_index,
|
||||
const bool& frame_view, const bool& verbose) {
|
||||
const bool& perimeter_cb, const bool& frame_view, const bool& verbose) {
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
vtr::Matrix<size_t> tile_instance_ids;
|
||||
status = add_top_module_tile_instances(module_manager, top_module,
|
||||
|
@ -1942,7 +1943,7 @@ int build_top_module_tile_child_instances(
|
|||
status = add_top_module_global_ports_from_tile_modules(
|
||||
module_manager, top_module, tile_annotation, vpr_device_annotation, grids,
|
||||
layer, rr_graph, device_rr_gsb, tile_instance_ids, fabric_tile, clk_ntwk,
|
||||
rr_clock_lookup);
|
||||
rr_clock_lookup, perimeter_cb);
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ int build_top_module_tile_child_instances(
|
|||
const FabricTile& fabric_tile, const ConfigProtocol& config_protocol,
|
||||
const CircuitModelId& sram_model, const FabricKey& fabric_key,
|
||||
const bool& group_config_block, const bool& name_module_using_index,
|
||||
const bool& frame_view, const bool& verbose);
|
||||
const bool& perimeter_cb, const bool& frame_view, const bool& verbose);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
|
|
|
@ -753,13 +753,24 @@ static void add_top_module_nets_connect_sb_and_cb(
|
|||
* is_cb_exist() FOr RIGHT and BOTTOM side, find the adjacent RRGSB and then
|
||||
* use is_cb_exist()
|
||||
*/
|
||||
if (TOP == side_manager.get_side() || LEFT == side_manager.get_side()) {
|
||||
if (BOTTOM == side_manager.get_side() || LEFT == side_manager.get_side()) {
|
||||
if (false == rr_gsb.is_cb_exist(cb_type)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (RIGHT == side_manager.get_side() || BOTTOM == side_manager.get_side()) {
|
||||
if (RIGHT == side_manager.get_side() || TOP == side_manager.get_side()) {
|
||||
/* Only for the condition where cbs are on perimeter, the neighbour cb
|
||||
* will be invalid Bypass in such case on finding neighbour cbs
|
||||
*/
|
||||
if (TOP == side_manager.get_side() &&
|
||||
instance_sb_coordinate.y() == device_rr_gsb.get_gsb_range().y()) {
|
||||
continue;
|
||||
}
|
||||
if (RIGHT == side_manager.get_side() &&
|
||||
instance_sb_coordinate.x() == device_rr_gsb.get_gsb_range().x()) {
|
||||
continue;
|
||||
}
|
||||
const RRGSB& adjacent_gsb =
|
||||
device_rr_gsb.get_gsb(module_gsb_cb_coordinate);
|
||||
if (false == adjacent_gsb.is_cb_exist(cb_type)) {
|
||||
|
@ -945,7 +956,8 @@ static int build_top_module_global_net_for_given_grid_module(
|
|||
const BasicPort& tile_port_to_connect,
|
||||
const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids,
|
||||
const size_t& layer, const vtr::Point<size_t>& grid_coordinate,
|
||||
const e_side& border_side, const vtr::Matrix<size_t>& grid_instance_ids) {
|
||||
const e_side& border_side, const vtr::Matrix<size_t>& grid_instance_ids,
|
||||
const bool& perimeter_cb) {
|
||||
t_physical_tile_type_ptr physical_tile = grids.get_physical_type(
|
||||
t_physical_tile_loc(grid_coordinate.x(), grid_coordinate.y(), layer));
|
||||
/* Find the module name for this type of grid */
|
||||
|
@ -963,6 +975,7 @@ static int build_top_module_global_net_for_given_grid_module(
|
|||
/* Walk through each instance considering the unique sub tile and capacity
|
||||
* range, each instance may have an independent pin to be driven by a global
|
||||
* net! */
|
||||
int curr_sub_tile_start_pin_index = 0;
|
||||
for (const t_sub_tile& sub_tile : physical_tile->sub_tiles) {
|
||||
VTR_ASSERT(1 == sub_tile.equivalent_sites.size());
|
||||
int grid_pin_start_index = physical_tile->num_pins;
|
||||
|
@ -983,16 +996,15 @@ static int build_top_module_global_net_for_given_grid_module(
|
|||
/* Port size must match!!! */
|
||||
if (false == ref_tile_port.contained(tile_port_to_connect)) {
|
||||
VTR_LOG_ERROR(
|
||||
"Tile annotation '%s' port '%s[%lu:%lu]' is out of the range of "
|
||||
"physical tile port '%s[%lu:%lu]'!",
|
||||
"Tile annotation '%s' port '%s' is out of the range of "
|
||||
"physical tile port '%s'!\n",
|
||||
tile_annotation.global_port_name(tile_global_port).c_str(),
|
||||
tile_port_to_connect.get_name().c_str(),
|
||||
tile_port_to_connect.get_lsb(), tile_port_to_connect.get_msb(),
|
||||
ref_tile_port.get_name().c_str(), ref_tile_port.get_lsb(),
|
||||
ref_tile_port.get_msb());
|
||||
tile_port_to_connect.to_verilog_string().c_str(),
|
||||
ref_tile_port.to_verilog_string().c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
grid_pin_start_index =
|
||||
curr_sub_tile_start_pin_index +
|
||||
(subtile_index - sub_tile.capacity.low) * sub_tile_num_pins +
|
||||
tile_port.absolute_first_pin_index;
|
||||
physical_tile_port = tile_port;
|
||||
|
@ -1022,7 +1034,7 @@ static int build_top_module_global_net_for_given_grid_module(
|
|||
size_t grid_pin_height =
|
||||
physical_tile->pin_height_offset[grid_pin_index];
|
||||
std::vector<e_side> pin_sides = find_physical_tile_pin_side(
|
||||
physical_tile, grid_pin_index, border_side);
|
||||
physical_tile, grid_pin_index, border_side, perimeter_cb);
|
||||
|
||||
BasicPort grid_pin_info =
|
||||
vpr_device_annotation.physical_tile_pin_port_info(physical_tile,
|
||||
|
@ -1067,6 +1079,9 @@ static int build_top_module_global_net_for_given_grid_module(
|
|||
}
|
||||
}
|
||||
}
|
||||
/* Note that the start pin index for a new type of tile should be calculated
|
||||
* by the accumulated number of pins of previous sub tiles */
|
||||
curr_sub_tile_start_pin_index += sub_tile.num_phy_pins;
|
||||
}
|
||||
|
||||
return CMD_EXEC_SUCCESS;
|
||||
|
@ -1080,7 +1095,8 @@ static int build_top_module_global_net_from_grid_modules(
|
|||
const ModulePortId& top_module_port, const TileAnnotation& tile_annotation,
|
||||
const TileGlobalPortId& tile_global_port,
|
||||
const VprDeviceAnnotation& vpr_device_annotation, const DeviceGrid& grids,
|
||||
const size_t& layer, const vtr::Matrix<size_t>& grid_instance_ids) {
|
||||
const size_t& layer, const vtr::Matrix<size_t>& grid_instance_ids,
|
||||
const bool& perimeter_cb) {
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
|
||||
std::map<e_side, std::vector<vtr::Point<size_t>>> io_coordinates =
|
||||
|
@ -1127,7 +1143,7 @@ static int build_top_module_global_net_from_grid_modules(
|
|||
if (true == out_of_range) {
|
||||
VTR_LOG_ERROR(
|
||||
"Coordinate (%lu, %lu) in tile annotation for tile '%s' is out of "
|
||||
"range (%lu:%lu, %lu:%lu)!",
|
||||
"range (%lu:%lu, %lu:%lu)!\n",
|
||||
range.x(), range.y(), tile_name.c_str(), start_coord.x(), end_coord.x(),
|
||||
start_coord.y(), end_coord.y());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
|
@ -1158,7 +1174,8 @@ static int build_top_module_global_net_from_grid_modules(
|
|||
status = build_top_module_global_net_for_given_grid_module(
|
||||
module_manager, top_module, top_module_port, tile_annotation,
|
||||
tile_global_port, tile_port, vpr_device_annotation, grids, layer,
|
||||
vtr::Point<size_t>(ix, iy), NUM_SIDES, grid_instance_ids);
|
||||
vtr::Point<size_t>(ix, iy), NUM_SIDES, grid_instance_ids,
|
||||
perimeter_cb);
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
|
@ -1205,7 +1222,7 @@ static int build_top_module_global_net_from_grid_modules(
|
|||
status = build_top_module_global_net_for_given_grid_module(
|
||||
module_manager, top_module, top_module_port, tile_annotation,
|
||||
tile_global_port, tile_port, vpr_device_annotation, grids, layer,
|
||||
io_coordinate, io_side, grid_instance_ids);
|
||||
io_coordinate, io_side, grid_instance_ids, perimeter_cb);
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
|
@ -1242,11 +1259,11 @@ static int build_top_module_global_net_from_clock_arch_tree(
|
|||
if (clk_ntwk.tree_width(clk_tree) !=
|
||||
module_manager.module_port(top_module, top_module_port).get_width()) {
|
||||
VTR_LOG(
|
||||
"Clock tree '%s' does not have the same width '%lu' as the port '%'s of "
|
||||
"Clock tree '%s' does not have the same width '%lu' as the port '%s' of "
|
||||
"FPGA top module",
|
||||
clk_tree_name.c_str(), clk_ntwk.tree_width(clk_tree),
|
||||
module_manager.module_port(top_module, top_module_port)
|
||||
.get_name()
|
||||
.to_verilog_string()
|
||||
.c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
@ -1306,7 +1323,7 @@ int add_top_module_global_ports_from_grid_modules(
|
|||
const DeviceRRGSB& device_rr_gsb,
|
||||
const std::map<t_rr_type, vtr::Matrix<size_t>>& cb_instance_ids,
|
||||
const vtr::Matrix<size_t>& grid_instance_ids, const ClockNetwork& clk_ntwk,
|
||||
const RRClockSpatialLookup& rr_clock_lookup) {
|
||||
const RRClockSpatialLookup& rr_clock_lookup, const bool& perimeter_cb) {
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
|
||||
/* Add the global ports which are NOT yet added to the top-level module
|
||||
|
@ -1323,12 +1340,21 @@ int add_top_module_global_ports_from_grid_modules(
|
|||
BasicPort global_port_to_add;
|
||||
global_port_to_add.set_name(
|
||||
tile_annotation.global_port_name(tile_global_port));
|
||||
size_t max_port_size = 0;
|
||||
for (const BasicPort& tile_port :
|
||||
tile_annotation.global_port_tile_ports(tile_global_port)) {
|
||||
max_port_size = std::max(tile_port.get_width(), max_port_size);
|
||||
/* Dedicated network has their own sizes of port */
|
||||
if (tile_annotation.global_port_thru_dedicated_network(
|
||||
tile_global_port)) {
|
||||
std::string clk_tree_name =
|
||||
tile_annotation.global_port_clock_arch_tree_name(tile_global_port);
|
||||
ClockTreeId clk_tree = clk_ntwk.find_tree(clk_tree_name);
|
||||
global_port_to_add.set_width(clk_ntwk.tree_width(clk_tree));
|
||||
} else {
|
||||
size_t max_port_size = 0;
|
||||
for (const BasicPort& tile_port :
|
||||
tile_annotation.global_port_tile_ports(tile_global_port)) {
|
||||
max_port_size = std::max(tile_port.get_width(), max_port_size);
|
||||
}
|
||||
global_port_to_add.set_width(max_port_size);
|
||||
}
|
||||
global_port_to_add.set_width(max_port_size);
|
||||
global_ports_to_add.push_back(global_port_to_add);
|
||||
}
|
||||
}
|
||||
|
@ -1352,8 +1378,7 @@ int add_top_module_global_ports_from_grid_modules(
|
|||
* - If the net will be directly wired to tiles, the net will drive an input
|
||||
* of a tile
|
||||
*/
|
||||
if (!tile_annotation.global_port_clock_arch_tree_name(tile_global_port)
|
||||
.empty()) {
|
||||
if (tile_annotation.global_port_thru_dedicated_network(tile_global_port)) {
|
||||
status = build_top_module_global_net_from_clock_arch_tree(
|
||||
module_manager, top_module, top_module_port, rr_graph, device_rr_gsb,
|
||||
cb_instance_ids, clk_ntwk,
|
||||
|
@ -1363,7 +1388,7 @@ int add_top_module_global_ports_from_grid_modules(
|
|||
status = build_top_module_global_net_from_grid_modules(
|
||||
module_manager, top_module, top_module_port, tile_annotation,
|
||||
tile_global_port, vpr_device_annotation, grids, layer,
|
||||
grid_instance_ids);
|
||||
grid_instance_ids, perimeter_cb);
|
||||
}
|
||||
if (status == CMD_EXEC_FATAL_ERROR) {
|
||||
return status;
|
||||
|
|
|
@ -42,7 +42,7 @@ int add_top_module_global_ports_from_grid_modules(
|
|||
const DeviceRRGSB& device_rr_gsb,
|
||||
const std::map<t_rr_type, vtr::Matrix<size_t>>& cb_instance_ids,
|
||||
const vtr::Matrix<size_t>& grid_instance_ids, const ClockNetwork& clk_ntwk,
|
||||
const RRClockSpatialLookup& rr_clock_lookup);
|
||||
const RRClockSpatialLookup& rr_clock_lookup, const bool& perimeter_cb);
|
||||
|
||||
void add_top_module_nets_prog_clock(ModuleManager& module_manager,
|
||||
const ModuleId& top_module,
|
||||
|
|
|
@ -141,7 +141,7 @@ static void organize_top_module_tile_memory_modules(
|
|||
const vtr::Point<size_t>& tile_coord, const e_side& tile_border_side) {
|
||||
vtr::Point<size_t> gsb_coord_range = device_rr_gsb.get_gsb_range();
|
||||
|
||||
vtr::Point<size_t> gsb_coord(tile_coord.x(), tile_coord.y() - 1);
|
||||
vtr::Point<size_t> gsb_coord(tile_coord.x(), tile_coord.y());
|
||||
|
||||
/* We do NOT consider SB and CBs if the gsb is not in the range! */
|
||||
if ((gsb_coord.x() < gsb_coord_range.x()) &&
|
||||
|
@ -370,12 +370,13 @@ void build_top_module_configurable_regions(
|
|||
* Note: the organization of inter-tile aims to reduce the wire length
|
||||
* to connect between tiles. Therefore, it is organized as a snake
|
||||
* where we can avoid long wires between rows and columns
|
||||
* Note: Corner I/Os only occur when perimeter cb is allowed
|
||||
*
|
||||
* +--------------------------------------------------------+
|
||||
* | +------+------+-----+------+ |
|
||||
* | | I/O | I/O | ... | I/O | |
|
||||
* | | TOP | TOP | | TOP | |
|
||||
* | +------+------+-----+------+ |
|
||||
* | +------+ +------+------+-----+------+ +------+ |
|
||||
* | | I/O | | I/O | I/O | ... | I/O | | I/O | |
|
||||
* | | LEFT | | TOP | TOP | | TOP | | TOP | |
|
||||
* | +------+ +------+------+-----+------+ +------+ |
|
||||
* | +---------------------------------->tail |
|
||||
* | +------+ | +------+------+-----+------+ +------+ |
|
||||
* | | | | | | | | | | | |
|
||||
|
@ -397,10 +398,10 @@ void build_top_module_configurable_regions(
|
|||
* | | LEFT | | [0] | [1] | | [i] | | |RIGHT | |
|
||||
* | +------+ +------+------+-----+------+ | +------+ |
|
||||
* +-------------------------------------------+ |
|
||||
* +------+------+-----+------+ |
|
||||
* | I/O | I/O | ... | I/O | |
|
||||
* |BOTTOM|BOTTOM| |BOTTOM| |
|
||||
* +------+------+-----+------+ |
|
||||
* +------+ +------+------+-----+------+ +------+ |
|
||||
* | I/O | | I/O | I/O | ... | I/O | | I/O | |
|
||||
* |BOTTOM| |BOTTOM|BOTTOM| |BOTTOM| |RIGHT | |
|
||||
* +------+ +------+------+-----+------+ +------+ |
|
||||
* head >-----------------------------------------------+
|
||||
*
|
||||
* Inner tile connection:
|
||||
|
@ -458,12 +459,12 @@ void organize_top_module_memory_modules(
|
|||
std::map<e_side, std::vector<vtr::Point<size_t>>> io_coords;
|
||||
|
||||
/* BOTTOM side I/Os */
|
||||
for (size_t ix = 1; ix < grids.width() - 1; ++ix) {
|
||||
for (size_t ix = 0; ix < grids.width() - 1; ++ix) {
|
||||
io_coords[BOTTOM].push_back(vtr::Point<size_t>(ix, 0));
|
||||
}
|
||||
|
||||
/* RIGHT side I/Os */
|
||||
for (size_t iy = 1; iy < grids.height() - 1; ++iy) {
|
||||
for (size_t iy = 0; iy < grids.height() - 1; ++iy) {
|
||||
io_coords[RIGHT].push_back(vtr::Point<size_t>(grids.width() - 1, iy));
|
||||
}
|
||||
|
||||
|
@ -483,13 +484,12 @@ void organize_top_module_memory_modules(
|
|||
* +--------+ +--------+
|
||||
*
|
||||
*/
|
||||
for (size_t ix = grids.width() - 2; ix >= 1; --ix) {
|
||||
for (size_t ix = grids.width() - 1; ix >= 1; --ix) {
|
||||
io_coords[TOP].push_back(vtr::Point<size_t>(ix, grids.height() - 1));
|
||||
}
|
||||
io_coords[TOP].push_back(vtr::Point<size_t>(0, grids.height() - 1));
|
||||
|
||||
/* LEFT side I/Os */
|
||||
for (size_t iy = grids.height() - 2; iy >= 1; --iy) {
|
||||
for (size_t iy = grids.height() - 1; iy >= 1; --iy) {
|
||||
io_coords[LEFT].push_back(vtr::Point<size_t>(0, iy));
|
||||
}
|
||||
|
||||
|
|
|
@ -100,13 +100,13 @@ vtr::Point<size_t> find_top_module_gsb_coordinate_by_sb_side(
|
|||
|
||||
vtr::Point<size_t> gsb_coordinate;
|
||||
|
||||
if ((TOP == sb_side) || (LEFT == sb_side)) {
|
||||
if ((BOTTOM == sb_side) || (LEFT == sb_side)) {
|
||||
gsb_coordinate.set_x(rr_gsb.get_x());
|
||||
gsb_coordinate.set_y(rr_gsb.get_y());
|
||||
return gsb_coordinate;
|
||||
}
|
||||
|
||||
VTR_ASSERT((RIGHT == sb_side) || (BOTTOM == sb_side));
|
||||
VTR_ASSERT((RIGHT == sb_side) || (TOP == sb_side));
|
||||
|
||||
/* RIGHT side: x + 1 */
|
||||
if (RIGHT == sb_side) {
|
||||
|
@ -115,9 +115,9 @@ vtr::Point<size_t> find_top_module_gsb_coordinate_by_sb_side(
|
|||
}
|
||||
|
||||
/* BOTTOM side: y - 1 */
|
||||
if (BOTTOM == sb_side) {
|
||||
if (TOP == sb_side) {
|
||||
gsb_coordinate.set_x(rr_gsb.get_x());
|
||||
gsb_coordinate.set_y(rr_gsb.get_y() - 1);
|
||||
gsb_coordinate.set_y(rr_gsb.get_y() + 1);
|
||||
}
|
||||
|
||||
return gsb_coordinate;
|
||||
|
|
|
@ -21,6 +21,7 @@ FabricVerilogOption::FabricVerilogOption() {
|
|||
default_net_type_ = VERILOG_DEFAULT_NET_TYPE_NONE;
|
||||
time_stamp_ = true;
|
||||
use_relative_path_ = false;
|
||||
constant_undriven_inputs_ = false;
|
||||
verbose_output_ = false;
|
||||
}
|
||||
|
||||
|
@ -53,6 +54,10 @@ e_verilog_default_net_type FabricVerilogOption::default_net_type() const {
|
|||
return default_net_type_;
|
||||
}
|
||||
|
||||
bool FabricVerilogOption::constant_undriven_inputs() const {
|
||||
return constant_undriven_inputs_;
|
||||
}
|
||||
|
||||
bool FabricVerilogOption::verbose_output() const { return verbose_output_; }
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -106,6 +111,10 @@ void FabricVerilogOption::set_default_net_type(
|
|||
}
|
||||
}
|
||||
|
||||
void FabricVerilogOption::set_constant_undriven_inputs(const bool& enabled) {
|
||||
constant_undriven_inputs_ = enabled;
|
||||
}
|
||||
|
||||
void FabricVerilogOption::set_verbose_output(const bool& enabled) {
|
||||
verbose_output_ = enabled;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ class FabricVerilogOption {
|
|||
bool compress_routing() const;
|
||||
e_verilog_default_net_type default_net_type() const;
|
||||
bool print_user_defined_template() const;
|
||||
bool constant_undriven_inputs() const;
|
||||
bool verbose_output() const;
|
||||
|
||||
public: /* Public mutators */
|
||||
|
@ -39,6 +40,7 @@ class FabricVerilogOption {
|
|||
void set_compress_routing(const bool& enabled);
|
||||
void set_print_user_defined_template(const bool& enabled);
|
||||
void set_default_net_type(const std::string& default_net_type);
|
||||
void set_constant_undriven_inputs(const bool& enabled);
|
||||
void set_verbose_output(const bool& enabled);
|
||||
|
||||
private: /* Internal Data */
|
||||
|
@ -50,6 +52,7 @@ class FabricVerilogOption {
|
|||
e_verilog_default_net_type default_net_type_;
|
||||
bool time_stamp_;
|
||||
bool use_relative_path_;
|
||||
bool constant_undriven_inputs_;
|
||||
bool verbose_output_;
|
||||
};
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ static void print_verilog_primitive_block(
|
|||
|
||||
/* Write the verilog module */
|
||||
write_verilog_module_to_file(fp, module_manager, primitive_module, true,
|
||||
options.constant_undriven_inputs(),
|
||||
options.default_net_type());
|
||||
|
||||
/* Close file handler */
|
||||
|
@ -232,9 +233,9 @@ static void rec_print_verilog_logical_tile(
|
|||
std::string(physical_pb_type->name) + " -----"));
|
||||
|
||||
/* Write the verilog module */
|
||||
write_verilog_module_to_file(fp, module_manager, pb_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, pb_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
print_verilog_comment(
|
||||
fp,
|
||||
|
@ -346,9 +347,9 @@ static void print_verilog_physical_tile_netlist(
|
|||
print_verilog_comment(
|
||||
fp, std::string("----- BEGIN Grid Verilog module: " +
|
||||
module_manager.module_name(grid_module) + " -----"));
|
||||
write_verilog_module_to_file(fp, module_manager, grid_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, grid_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
print_verilog_comment(
|
||||
fp, std::string("----- END Grid Verilog module: " +
|
||||
|
|
|
@ -63,7 +63,7 @@ void print_verilog_submodule_luts(const ModuleManager& module_manager,
|
|||
fp, module_manager, lut_module,
|
||||
options.explicit_port_mapping() ||
|
||||
circuit_lib.dump_explicit_port_map(lut_model),
|
||||
options.default_net_type());
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
}
|
||||
|
||||
/* Close the file handler */
|
||||
|
|
|
@ -61,7 +61,7 @@ static void print_verilog_mux_memory_module(
|
|||
fp, module_manager, mem_module,
|
||||
options.explicit_port_mapping() ||
|
||||
circuit_lib.dump_explicit_port_map(mux_model),
|
||||
options.default_net_type());
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
@ -84,7 +84,7 @@ static void print_verilog_mux_memory_module(
|
|||
fp, module_manager, feedthru_mem_module,
|
||||
options.explicit_port_mapping() ||
|
||||
circuit_lib.dump_explicit_port_map(mux_model),
|
||||
options.default_net_type());
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
@ -208,6 +208,7 @@ void print_verilog_submodule_memories(
|
|||
write_verilog_module_to_file(fp, module_manager, mem_module,
|
||||
options.explicit_port_mapping() ||
|
||||
circuit_lib.dump_explicit_port_map(model),
|
||||
options.constant_undriven_inputs(),
|
||||
options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
|
@ -228,6 +229,7 @@ void print_verilog_submodule_memories(
|
|||
write_verilog_module_to_file(fp, module_manager, feedthru_mem_module,
|
||||
options.explicit_port_mapping() ||
|
||||
circuit_lib.dump_explicit_port_map(model),
|
||||
options.constant_undriven_inputs(),
|
||||
options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
|
@ -239,9 +241,9 @@ void print_verilog_submodule_memories(
|
|||
for (ModuleId mem_group_module : module_manager.modules_by_usage(
|
||||
ModuleManager::e_module_usage_type::MODULE_CONFIG_GROUP)) {
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, mem_group_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, mem_group_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
|
|
@ -139,6 +139,66 @@ static BasicPort generate_verilog_port_for_module_net(
|
|||
return port_to_return;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Find all the undriven nets that are going to be local wires
|
||||
* And organize it in a vector of ports
|
||||
* Verilog wire writter function will use the output of this function
|
||||
* to write up local wire declaration in Verilog format
|
||||
*******************************************************************/
|
||||
static void find_verilog_module_local_undriven_wires(
|
||||
std::map<std::string, std::vector<BasicPort>>& local_wires,
|
||||
const ModuleManager& module_manager, const ModuleId& module_id,
|
||||
const std::vector<ModuleManager::e_module_port_type>& port_type_blacklist) {
|
||||
/* Local wires could also happen for undriven ports of child module */
|
||||
for (const ModuleId& child : module_manager.child_modules(module_id)) {
|
||||
for (size_t instance :
|
||||
module_manager.child_module_instances(module_id, child)) {
|
||||
for (const ModulePortId& child_port_id :
|
||||
module_manager.module_ports(child)) {
|
||||
BasicPort child_port = module_manager.module_port(child, child_port_id);
|
||||
ModuleManager::e_module_port_type child_port_type =
|
||||
module_manager.port_type(child, child_port_id);
|
||||
bool filter_out = false;
|
||||
for (ModuleManager::e_module_port_type curr_port_type :
|
||||
port_type_blacklist) {
|
||||
if (child_port_type == curr_port_type) {
|
||||
filter_out = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (filter_out) {
|
||||
continue;
|
||||
}
|
||||
std::vector<size_t> undriven_pins;
|
||||
for (size_t child_pin : child_port.pins()) {
|
||||
/* Find the net linked to the pin */
|
||||
ModuleNetId net = module_manager.module_instance_port_net(
|
||||
module_id, child, instance, child_port_id, child_pin);
|
||||
/* We only care undriven ports */
|
||||
if (ModuleNetId::INVALID() == net) {
|
||||
undriven_pins.push_back(child_pin);
|
||||
}
|
||||
}
|
||||
if (true == undriven_pins.empty()) {
|
||||
continue;
|
||||
}
|
||||
/* Reach here, we need a local wire, we will create a port only for the
|
||||
* undriven pins of the port! */
|
||||
BasicPort instance_port;
|
||||
instance_port.set_name(generate_verilog_undriven_local_wire_name(
|
||||
module_manager, module_id, child, instance, child_port_id));
|
||||
/* We give the same port name as child module, this case happens to
|
||||
* global ports */
|
||||
instance_port.set_width(
|
||||
*std::min_element(undriven_pins.begin(), undriven_pins.end()),
|
||||
*std::max_element(undriven_pins.begin(), undriven_pins.end()));
|
||||
|
||||
local_wires[instance_port.get_name()].push_back(instance_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Find all the nets that are going to be local wires
|
||||
* And organize it in a vector of ports
|
||||
|
@ -206,41 +266,9 @@ find_verilog_module_local_wires(const ModuleManager& module_manager,
|
|||
}
|
||||
}
|
||||
|
||||
/* Local wires could also happen for undriven ports of child module */
|
||||
for (const ModuleId& child : module_manager.child_modules(module_id)) {
|
||||
for (size_t instance :
|
||||
module_manager.child_module_instances(module_id, child)) {
|
||||
for (const ModulePortId& child_port_id :
|
||||
module_manager.module_ports(child)) {
|
||||
BasicPort child_port = module_manager.module_port(child, child_port_id);
|
||||
std::vector<size_t> undriven_pins;
|
||||
for (size_t child_pin : child_port.pins()) {
|
||||
/* Find the net linked to the pin */
|
||||
ModuleNetId net = module_manager.module_instance_port_net(
|
||||
module_id, child, instance, child_port_id, child_pin);
|
||||
/* We only care undriven ports */
|
||||
if (ModuleNetId::INVALID() == net) {
|
||||
undriven_pins.push_back(child_pin);
|
||||
}
|
||||
}
|
||||
if (true == undriven_pins.empty()) {
|
||||
continue;
|
||||
}
|
||||
/* Reach here, we need a local wire, we will create a port only for the
|
||||
* undriven pins of the port! */
|
||||
BasicPort instance_port;
|
||||
instance_port.set_name(generate_verilog_undriven_local_wire_name(
|
||||
module_manager, module_id, child, instance, child_port_id));
|
||||
/* We give the same port name as child module, this case happens to
|
||||
* global ports */
|
||||
instance_port.set_width(
|
||||
*std::min_element(undriven_pins.begin(), undriven_pins.end()),
|
||||
*std::max_element(undriven_pins.begin(), undriven_pins.end()));
|
||||
|
||||
local_wires[instance_port.get_name()].push_back(instance_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
find_verilog_module_local_undriven_wires(
|
||||
local_wires, module_manager, module_id,
|
||||
std::vector<ModuleManager::e_module_port_type>());
|
||||
|
||||
return local_wires;
|
||||
}
|
||||
|
@ -545,6 +573,7 @@ static void write_verilog_instance_to_file(std::fstream& fp,
|
|||
void write_verilog_module_to_file(
|
||||
std::fstream& fp, const ModuleManager& module_manager,
|
||||
const ModuleId& module_id, const bool& use_explicit_port_map,
|
||||
const bool& constant_local_undriven_wires,
|
||||
const e_verilog_default_net_type& default_net_type) {
|
||||
VTR_ASSERT(true == valid_file_stream(fp));
|
||||
|
||||
|
@ -575,6 +604,29 @@ void write_verilog_module_to_file(
|
|||
}
|
||||
}
|
||||
|
||||
/* Use constant to drive undriven local wires */
|
||||
if (constant_local_undriven_wires) {
|
||||
std::vector<ModuleManager::e_module_port_type> blacklist = {
|
||||
ModuleManager::e_module_port_type::MODULE_GLOBAL_PORT,
|
||||
ModuleManager::e_module_port_type::MODULE_GPIN_PORT,
|
||||
ModuleManager::e_module_port_type::MODULE_GPOUT_PORT,
|
||||
ModuleManager::e_module_port_type::MODULE_GPIO_PORT,
|
||||
ModuleManager::e_module_port_type::MODULE_INOUT_PORT,
|
||||
ModuleManager::e_module_port_type::MODULE_OUTPUT_PORT,
|
||||
ModuleManager::e_module_port_type::MODULE_CLOCK_PORT};
|
||||
std::map<std::string, std::vector<BasicPort>> local_undriven_wires;
|
||||
find_verilog_module_local_undriven_wires(
|
||||
local_undriven_wires, module_manager, module_id, blacklist);
|
||||
for (std::pair<std::string, std::vector<BasicPort>> port_group :
|
||||
local_undriven_wires) {
|
||||
for (const BasicPort& local_undriven_wire : port_group.second) {
|
||||
print_verilog_wire_constant_values(
|
||||
fp, local_undriven_wire,
|
||||
std::vector<size_t>(local_undriven_wire.get_width(), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Print an empty line as splitter */
|
||||
fp << std::endl;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace openfpga {
|
|||
void write_verilog_module_to_file(
|
||||
std::fstream& fp, const ModuleManager& module_manager,
|
||||
const ModuleId& module_id, const bool& use_explicit_port_map,
|
||||
const bool& constant_local_undriven_wires,
|
||||
const e_verilog_default_net_type& default_net_type);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
|
|
@ -679,7 +679,7 @@ static void generate_verilog_mux_branch_module(
|
|||
fp, module_manager, mux_module,
|
||||
use_explicit_port_map ||
|
||||
circuit_lib.dump_explicit_port_map(mux_model),
|
||||
default_net_type);
|
||||
false, default_net_type);
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
} else {
|
||||
|
@ -1423,7 +1423,7 @@ static void generate_verilog_mux_module(
|
|||
circuit_lib.dump_explicit_port_map(mux_model) ||
|
||||
circuit_lib.dump_explicit_port_map(
|
||||
circuit_lib.pass_gate_logic_model(mux_model))),
|
||||
default_net_type);
|
||||
false, default_net_type);
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
break;
|
||||
|
|
|
@ -65,6 +65,10 @@ static void print_verilog_preconfig_top_module_ports(
|
|||
/* The block may be renamed as it contains special characters which violate
|
||||
* Verilog syntax */
|
||||
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||
VTR_LOG(
|
||||
"Replace pin name '%s' with '%s' as it is renamed to comply verilog "
|
||||
"syntax\n",
|
||||
block_name.c_str(), netlist_annotation.block_name(atom_blk).c_str());
|
||||
block_name = netlist_annotation.block_name(atom_blk);
|
||||
}
|
||||
/* For output block, remove the prefix which is added by VPR */
|
||||
|
@ -445,8 +449,8 @@ int print_verilog_preconfig_top_module(
|
|||
/* Connect FPGA top module global ports to constant or benchmark global
|
||||
* signals! */
|
||||
status = print_verilog_preconfig_top_module_connect_global_ports(
|
||||
fp, module_manager, core_module, pin_constraints, global_ports,
|
||||
benchmark_clock_port_names,
|
||||
fp, module_manager, core_module, pin_constraints, atom_ctx,
|
||||
netlist_annotation, global_ports, benchmark_clock_port_names,
|
||||
std::string(FORMAL_VERIFICATION_TOP_MODULE_PORT_POSTFIX));
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
|
|
|
@ -57,6 +57,7 @@ void print_verilog_preconfig_top_module_internal_wires(
|
|||
int print_verilog_preconfig_top_module_connect_global_ports(
|
||||
std::fstream &fp, const ModuleManager &module_manager,
|
||||
const ModuleId &top_module, const PinConstraints &pin_constraints,
|
||||
const AtomContext &atom_ctx, const VprNetlistAnnotation &netlist_annotation,
|
||||
const FabricGlobalPortInfo &fabric_global_ports,
|
||||
const std::vector<std::string> &benchmark_clock_port_names,
|
||||
const std::string &port_postfix) {
|
||||
|
@ -121,7 +122,27 @@ int print_verilog_preconfig_top_module_connect_global_ports(
|
|||
}
|
||||
clock_name_to_connect = benchmark_clock_port_names[pin_id];
|
||||
}
|
||||
|
||||
/* The clock name must be a valid primary input. Otherwise, it could be
|
||||
* a signal generated by internal logics, e.g., clb */
|
||||
AtomBlockId atom_blk = atom_ctx.nlist.find_block(clock_name_to_connect);
|
||||
if ((AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))) {
|
||||
VTR_LOG(
|
||||
"Global net '%s' is not a primary input of the netlist (which "
|
||||
"could a signal generated by internal logic). Will not wire it to "
|
||||
"any FPGA primary input pin\n",
|
||||
clock_name_to_connect.c_str());
|
||||
continue;
|
||||
}
|
||||
/* The block may be renamed as it contains special characters which
|
||||
* violate Verilog syntax */
|
||||
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||
VTR_LOG(
|
||||
"Replace pin name '%s' with '%s' as it is renamed to comply "
|
||||
"verilog syntax\n",
|
||||
clock_name_to_connect.c_str(),
|
||||
netlist_annotation.block_name(atom_blk).c_str());
|
||||
clock_name_to_connect = netlist_annotation.block_name(atom_blk);
|
||||
}
|
||||
BasicPort benchmark_clock_pin(clock_name_to_connect, 1);
|
||||
print_verilog_wire_connection(fp, module_clock_pin, benchmark_clock_pin,
|
||||
false);
|
||||
|
@ -151,6 +172,27 @@ int print_verilog_preconfig_top_module_connect_global_ports(
|
|||
*/
|
||||
if ((false == pin_constraints.unconstrained_net(constrained_net_name)) &&
|
||||
(false == pin_constraints.unmapped_net(constrained_net_name))) {
|
||||
/* The clock name must be a valid primary input. Otherwise, it could be
|
||||
* a signal generated by internal logics, e.g., clb */
|
||||
AtomBlockId atom_blk = atom_ctx.nlist.find_block(constrained_net_name);
|
||||
if ((AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))) {
|
||||
VTR_LOG(
|
||||
"Global net '%s' is not a primary input of the netlist (which "
|
||||
"could a signal generated by internal logic). Will not wire it to "
|
||||
"any FPGA primary input pin\n",
|
||||
constrained_net_name.c_str());
|
||||
continue;
|
||||
}
|
||||
/* The block may be renamed as it contains special characters which
|
||||
* violate Verilog syntax */
|
||||
if (true == netlist_annotation.is_block_renamed(atom_blk)) {
|
||||
VTR_LOG(
|
||||
"Replace pin name '%s' with '%s' as it is renamed to comply "
|
||||
"verilog syntax\n",
|
||||
constrained_net_name.c_str(),
|
||||
netlist_annotation.block_name(atom_blk).c_str());
|
||||
constrained_net_name = netlist_annotation.block_name(atom_blk);
|
||||
}
|
||||
BasicPort benchmark_pin(constrained_net_name, 1);
|
||||
print_verilog_wire_connection(fp, module_global_pin, benchmark_pin,
|
||||
false);
|
||||
|
|
|
@ -35,6 +35,7 @@ void print_verilog_preconfig_top_module_internal_wires(
|
|||
int print_verilog_preconfig_top_module_connect_global_ports(
|
||||
std::fstream &fp, const ModuleManager &module_manager,
|
||||
const ModuleId &top_module, const PinConstraints &pin_constraints,
|
||||
const AtomContext &atom_ctx, const VprNetlistAnnotation &netlist_annotation,
|
||||
const FabricGlobalPortInfo &fabric_global_ports,
|
||||
const std::vector<std::string> &benchmark_clock_port_names,
|
||||
const std::string &port_postfix);
|
||||
|
|
|
@ -115,9 +115,9 @@ static void print_verilog_routing_connection_box_unique_module(
|
|||
VTR_ASSERT(true == module_manager.valid_module_id(cb_module));
|
||||
|
||||
/* Write the verilog module */
|
||||
write_verilog_module_to_file(fp, module_manager, cb_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, cb_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
@ -236,9 +236,9 @@ static void print_verilog_routing_switch_box_unique_module(
|
|||
VTR_ASSERT(true == module_manager.valid_module_id(sb_module));
|
||||
|
||||
/* Write the verilog module */
|
||||
write_verilog_module_to_file(fp, module_manager, sb_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, sb_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Close file handler */
|
||||
fp.close();
|
||||
|
|
|
@ -56,9 +56,9 @@ void print_verilog_submodule_shift_register_banks(
|
|||
for (const ModuleId& sr_module : blwl_sr_banks.bl_bank_unique_modules()) {
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(sr_module));
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, sr_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, sr_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
@ -67,9 +67,9 @@ void print_verilog_submodule_shift_register_banks(
|
|||
for (const ModuleId& sr_module : blwl_sr_banks.wl_bank_unique_modules()) {
|
||||
VTR_ASSERT(true == module_manager.valid_module_id(sr_module));
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, sr_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, sr_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
|
|
@ -90,8 +90,9 @@ int print_verilog_testbench_io_connection(
|
|||
/* Connect FPGA top module global ports to constant or benchmark global
|
||||
* signals! */
|
||||
status = print_verilog_preconfig_top_module_connect_global_ports(
|
||||
fp, module_manager, core_module, pin_constraints, global_ports,
|
||||
benchmark_clock_port_names, std::string());
|
||||
fp, module_manager, core_module, pin_constraints, atom_ctx,
|
||||
netlist_annotation, global_ports, benchmark_clock_port_names,
|
||||
std::string());
|
||||
if (CMD_EXEC_FATAL_ERROR == status) {
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -58,9 +58,9 @@ static int print_verilog_tile_module_netlist(
|
|||
options.time_stamp());
|
||||
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, tile_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, tile_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
|
|
@ -61,9 +61,9 @@ void print_verilog_core_module(NetlistManager& netlist_manager,
|
|||
options.time_stamp());
|
||||
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, core_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, core_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
@ -127,9 +127,9 @@ void print_verilog_top_module(NetlistManager& netlist_manager,
|
|||
fp, std::string("Top-level Verilog module for FPGA"), options.time_stamp());
|
||||
|
||||
/* Write the module content in Verilog format */
|
||||
write_verilog_module_to_file(fp, module_manager, top_module,
|
||||
options.explicit_port_mapping(),
|
||||
options.default_net_type());
|
||||
write_verilog_module_to_file(
|
||||
fp, module_manager, top_module, options.explicit_port_mapping(),
|
||||
options.constant_undriven_inputs(), options.default_net_type());
|
||||
|
||||
/* Add an empty line as a splitter */
|
||||
fp << std::endl;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/***************************************************************************************
|
||||
* This file includes most utilized functions that are used to acquire data from
|
||||
* VPR clustered netlist (post-packing netlist)
|
||||
***************************************************************************************/
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "openfpga_clustered_netlist_utils.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/***************************************************************************************
|
||||
* Find the names of all the atom blocks that drive clock nets
|
||||
* This function will find if the block has been renamed due to contain
|
||||
*sensitive characters that violates the Verilog syntax
|
||||
***************************************************************************************/
|
||||
std::vector<ClusterNetId> find_clustered_netlist_global_nets(
|
||||
const ClusteredNetlist& clb_nlist) {
|
||||
std::vector<ClusterNetId> gnets;
|
||||
|
||||
for (ClusterNetId net_id : clb_nlist.nets()) {
|
||||
if (clb_nlist.net_is_ignored(net_id)) {
|
||||
gnets.push_back(net_id);
|
||||
}
|
||||
}
|
||||
|
||||
return gnets;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef OPENFPGA_CLUSTERED_NETLIST_UTILS_H
|
||||
#define OPENFPGA_CLUSTERED_NETLIST_UTILS_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "clustered_netlist.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
std::vector<ClusterNetId> find_clustered_netlist_global_nets(
|
||||
const ClusteredNetlist& clb_nlist);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -27,7 +27,7 @@ namespace openfpga {
|
|||
*******************************************************************/
|
||||
std::vector<e_side> find_physical_tile_pin_side(
|
||||
t_physical_tile_type_ptr physical_tile, const int& physical_pin,
|
||||
const e_side& border_side) {
|
||||
const e_side& border_side, const bool& perimeter_cb) {
|
||||
std::vector<e_side> pin_sides;
|
||||
for (const e_side& side_cand : {TOP, RIGHT, BOTTOM, LEFT}) {
|
||||
int pin_width_offset = physical_tile->pin_width_offset[physical_pin];
|
||||
|
@ -40,17 +40,21 @@ std::vector<e_side> find_physical_tile_pin_side(
|
|||
|
||||
/* For regular grid, we should have pin only one side!
|
||||
* I/O grids: VPR creates the grid with duplicated pins on every side
|
||||
* but the expected side (only used side) will be opposite side of the border
|
||||
* side!
|
||||
* - In regular cases: the expected side (only used side) will be on the
|
||||
* opposite to the border side!
|
||||
* - When perimeter cb is on, the expected sides can be on any sides except
|
||||
* the border side. But we only expect 1 side
|
||||
*/
|
||||
if (NUM_SIDES == border_side) {
|
||||
VTR_ASSERT(1 == pin_sides.size());
|
||||
} else {
|
||||
} else if (!perimeter_cb) {
|
||||
SideManager side_manager(border_side);
|
||||
VTR_ASSERT(pin_sides.end() != std::find(pin_sides.begin(), pin_sides.end(),
|
||||
side_manager.get_opposite()));
|
||||
pin_sides.clear();
|
||||
pin_sides.push_back(side_manager.get_opposite());
|
||||
} else {
|
||||
VTR_ASSERT(1 == pin_sides.size() && pin_sides[0] != border_side);
|
||||
}
|
||||
|
||||
return pin_sides;
|
||||
|
@ -141,9 +145,6 @@ int find_physical_tile_pin_index(t_physical_tile_type_ptr physical_tile,
|
|||
}
|
||||
PortParser tile_parser(pin_tokens[0]);
|
||||
BasicPort tile_info = tile_parser.port();
|
||||
if (tile_info.get_name() != std::string(physical_tile->name)) {
|
||||
return pin_idx;
|
||||
}
|
||||
if (!tile_info.is_valid()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid pin name '%s' whose subtile index is not valid, expect [0, "
|
||||
|
@ -159,13 +160,6 @@ int find_physical_tile_pin_index(t_physical_tile_type_ptr physical_tile,
|
|||
pin_name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
if (tile_info.get_msb() > size_t(physical_tile->capacity) - 1) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid pin name '%s' whose subtile index is out of range, expect [0, "
|
||||
"%lu]\n",
|
||||
pin_name.c_str(), physical_tile->capacity - 1);
|
||||
exit(1);
|
||||
}
|
||||
/* precheck: return unfound pin if the pin index does not match */
|
||||
PortParser pin_parser(pin_tokens[1]);
|
||||
BasicPort pin_info = pin_parser.port();
|
||||
|
@ -180,9 +174,18 @@ int find_physical_tile_pin_index(t_physical_tile_type_ptr physical_tile,
|
|||
|
||||
/* Spot the subtile by using the index */
|
||||
for (const t_sub_tile& sub_tile : physical_tile->sub_tiles) {
|
||||
if (!sub_tile.capacity.is_in_range(tile_info.get_lsb())) {
|
||||
/* Bypass unmatched subtiles*/
|
||||
if (tile_info.get_name() != std::string(sub_tile.name)) {
|
||||
continue;
|
||||
}
|
||||
if (!sub_tile.capacity.is_in_range(tile_info.get_lsb())) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid pin name '%s' whose subtile index is out of range, expect "
|
||||
"[%lu, "
|
||||
"%lu]\n",
|
||||
pin_name.c_str(), sub_tile.capacity.low, sub_tile.capacity.high);
|
||||
exit(1);
|
||||
}
|
||||
for (const t_physical_tile_port& sub_tile_port : sub_tile.ports) {
|
||||
if (std::string(sub_tile_port.name) != pin_info.get_name()) {
|
||||
continue;
|
||||
|
@ -204,7 +207,8 @@ int find_physical_tile_pin_index(t_physical_tile_type_ptr physical_tile,
|
|||
/* Reach here, we get the port we want, return the accumulated index */
|
||||
size_t accumulated_pin_idx =
|
||||
sub_tile_port.absolute_first_pin_index +
|
||||
sub_tile.num_phy_pins * (tile_info.get_lsb() - sub_tile.capacity.low) +
|
||||
(sub_tile.num_phy_pins / sub_tile.capacity.total()) *
|
||||
(tile_info.get_lsb() - sub_tile.capacity.low) +
|
||||
pin_info.get_lsb();
|
||||
return accumulated_pin_idx;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace openfpga {
|
|||
|
||||
std::vector<e_side> find_physical_tile_pin_side(
|
||||
t_physical_tile_type_ptr physical_tile, const int& physical_pin,
|
||||
const e_side& border_side);
|
||||
const e_side& border_side, const bool& perimeter_cb);
|
||||
|
||||
float find_physical_tile_pin_Fc(t_physical_tile_type_ptr type, const int& pin);
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/////////////////////////////////////////
|
||||
// Functionality: A locally generated clock signal which is to test clock network with internal drivers
|
||||
// Author: Xifan Tang
|
||||
////////////////////////////////////////
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module clk_cond(clk_i, clk_cond_i, d_i, q_o);
|
||||
|
||||
input wire clk_cond_i;
|
||||
input wire clk_i;
|
||||
input wire d_i;
|
||||
output reg q_o;
|
||||
|
||||
wire int_clk;
|
||||
assign int_clk = clk_cond_i & clk_i;
|
||||
|
||||
always @(posedge int_clk) begin
|
||||
q_o <= d_i;
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,21 @@
|
|||
/////////////////////////////////////////
|
||||
// Functionality: A register driven by a combinational logic with clk signal
|
||||
// Author: Xifan Tang
|
||||
////////////////////////////////////////
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module clk_on_lut(a, b, q, out, clk);
|
||||
|
||||
input wire clk;
|
||||
input wire a;
|
||||
input wire b;
|
||||
output reg q;
|
||||
output wire out;
|
||||
|
||||
always @(posedge clk) begin
|
||||
q <= a;
|
||||
end
|
||||
|
||||
assign out = b & clk;
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,29 @@
|
|||
/////////////////////////////////////////
|
||||
// Functionality: A register driven by a combinational logic with reset signal
|
||||
// Author: Xifan Tang
|
||||
////////////////////////////////////////
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module rst_and_clk_on_lut(a, b, c, q, out0, out1, clk, rst);
|
||||
|
||||
input wire rst;
|
||||
input wire clk;
|
||||
input wire a;
|
||||
input wire b;
|
||||
input wire c;
|
||||
output reg q;
|
||||
output wire out0;
|
||||
output wire out1;
|
||||
|
||||
always @(posedge rst or posedge clk) begin
|
||||
if (rst) begin
|
||||
q <= 0;
|
||||
end else begin
|
||||
q <= a;
|
||||
end
|
||||
end
|
||||
|
||||
assign out0 = b & ~rst;
|
||||
assign out1 = c & ~clk;
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,26 @@
|
|||
/////////////////////////////////////////
|
||||
// Functionality: A locally generated reset signal which is to test clock network with internal drivers
|
||||
// Author: Xifan Tang
|
||||
////////////////////////////////////////
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
module rst_cond(rst_i, rst_cond_i, clk_i, d_i, q_o);
|
||||
|
||||
input wire rst_cond_i;
|
||||
input wire rst_i;
|
||||
input wire clk_i;
|
||||
input wire d_i;
|
||||
output reg q_o;
|
||||
|
||||
wire int_rst;
|
||||
assign int_rst = rst_cond_i & rst_i;
|
||||
|
||||
always @(posedge int_rst or posedge clk_i) begin
|
||||
if (int_rst) begin
|
||||
q_o <= 0;
|
||||
end else begin
|
||||
q_o <= d_i;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,204 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Architecture annotation for OpenFPGA framework
|
||||
This annotation supports the k6_N10_40nm.xml
|
||||
- General purpose logic block
|
||||
- K = 6, N = 10, I = 40
|
||||
- Single mode
|
||||
- Routing architecture
|
||||
- L = 4, fc_in = 0.15, fc_out = 0.1
|
||||
-->
|
||||
<openfpga_architecture>
|
||||
<technology_library>
|
||||
<device_library>
|
||||
<device_model name="logic" type="transistor">
|
||||
<lib type="industry" corner="TOP_TT" ref="M" path="${OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.pm"/>
|
||||
<design vdd="0.9" pn_ratio="2"/>
|
||||
<pmos name="pch" chan_length="40e-9" min_width="140e-9" variation="logic_transistor_var"/>
|
||||
<nmos name="nch" chan_length="40e-9" min_width="140e-9" variation="logic_transistor_var"/>
|
||||
</device_model>
|
||||
<device_model name="io" type="transistor">
|
||||
<lib type="academia" ref="M" path="${OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.pm"/>
|
||||
<design vdd="2.5" pn_ratio="3"/>
|
||||
<pmos name="pch_25" chan_length="270e-9" min_width="320e-9" variation="io_transistor_var"/>
|
||||
<nmos name="nch_25" chan_length="270e-9" min_width="320e-9" variation="io_transistor_var"/>
|
||||
</device_model>
|
||||
</device_library>
|
||||
<variation_library>
|
||||
<variation name="logic_transistor_var" abs_deviation="0.1" num_sigma="3"/>
|
||||
<variation name="io_transistor_var" abs_deviation="0.1" num_sigma="3"/>
|
||||
</variation_library>
|
||||
</technology_library>
|
||||
<circuit_library>
|
||||
<circuit_model type="inv_buf" name="INVTX1" prefix="INVTX1" is_default="true">
|
||||
<design_technology type="cmos" topology="inverter" size="1"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="inv_buf" name="buf4" prefix="buf4" is_default="false">
|
||||
<design_technology type="cmos" topology="buffer" size="1" num_level="2" f_per_stage="4"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="inv_buf" name="tap_buf4" prefix="tap_buf4" is_default="false">
|
||||
<design_technology type="cmos" topology="buffer" size="1" num_level="3" f_per_stage="4"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="pass_gate" name="TGATE" prefix="TGATE" is_default="true">
|
||||
<design_technology type="cmos" topology="transmission_gate" nmos_size="1" pmos_size="2"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="input" prefix="sel" size="1"/>
|
||||
<port type="input" prefix="selb" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in sel selb" out_port="out">
|
||||
10e-12 5e-12 5e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in sel selb" out_port="out">
|
||||
10e-12 5e-12 5e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="chan_wire" name="chan_segment" prefix="track_seg" is_default="true">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<wire_param model_type="pi" R="101" C="22.5e-15" num_level="1"/>
|
||||
<!-- model_type could be T, res_val and cap_val DON'T CARE -->
|
||||
</circuit_model>
|
||||
<circuit_model type="wire" name="direct_interc" prefix="direct_interc" is_default="true">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<wire_param model_type="pi" R="0" C="0" num_level="1"/>
|
||||
<!-- model_type could be T, res_val cap_val should be defined -->
|
||||
</circuit_model>
|
||||
<circuit_model type="mux" name="mux_tree" prefix="mux_tree" dump_structural_verilog="true">
|
||||
<design_technology type="cmos" structure="tree" add_const_input="true" const_input_val="1"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="1"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="mux" name="mux_tree_tapbuf" prefix="mux_tree_tapbuf" is_default="true" dump_structural_verilog="true">
|
||||
<design_technology type="cmos" structure="tree" add_const_input="true" const_input_val="1"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="tap_buf4"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="1"/>
|
||||
</circuit_model>
|
||||
<!--DFF subckt ports should be defined as <D> <Q> <CLK> <RESET> <SET> -->
|
||||
<circuit_model type="ff" name="DFFSRQ" prefix="DFFSRQ" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="input" prefix="set" lib_name="SET" size="1" is_global="true" default_val="0" is_set="true"/>
|
||||
<port type="input" prefix="reset" lib_name="RST" size="1" is_global="true" default_val="0" is_reset="true"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="clock" prefix="clk" lib_name="CK" size="1" is_global="false" default_val="0"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="lut" name="lut4" prefix="lut4" dump_structural_verilog="true">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<lut_input_inverter exist="true" circuit_model_name="INVTX1"/>
|
||||
<lut_input_buffer exist="true" circuit_model_name="buf4"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="4"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="16"/>
|
||||
</circuit_model>
|
||||
<!--Scan-chain DFF subckt ports should be defined as <D> <Q> <Qb> <CLK> <RESET> <SET> -->
|
||||
<circuit_model type="ccff" name="DFF" prefix="DFF" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="QN" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="CK" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="GPIO" prefix="GPIO" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/gpio.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/gpio.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="inout" prefix="PAD" size="1" is_global="true" is_io="true" is_data_io="true"/>
|
||||
<port type="sram" prefix="DIR" size="1" mode_select="true" circuit_model_name="DFF" default_val="1"/>
|
||||
<port type="input" prefix="outpad" lib_name="A" size="1"/>
|
||||
<port type="output" prefix="inpad" lib_name="Y" size="1"/>
|
||||
</circuit_model>
|
||||
</circuit_library>
|
||||
<configuration_protocol>
|
||||
<organization type="scan_chain" circuit_model_name="DFF"/>
|
||||
</configuration_protocol>
|
||||
<connection_block>
|
||||
<switch name="ipin_cblock" circuit_model_name="mux_tree_tapbuf"/>
|
||||
</connection_block>
|
||||
<switch_block>
|
||||
<switch name="0" circuit_model_name="mux_tree_tapbuf"/>
|
||||
</switch_block>
|
||||
<routing_segment>
|
||||
<segment name="L1" circuit_model_name="chan_segment"/>
|
||||
<segment name="L4" circuit_model_name="chan_segment"/>
|
||||
</routing_segment>
|
||||
<tile_annotations>
|
||||
<global_port name="clk" is_clock="true" default_val="0" clock_arch_tree_name="clk_tree_2lvl">
|
||||
<tile name="clb" port="clk" x="-1" y="-1"/>
|
||||
<tile name="io_top" port="clk" x="-1" y="-1"/>
|
||||
<tile name="io_right" port="clk" x="-1" y="-1"/>
|
||||
<tile name="io_bottom" port="clk" x="-1" y="-1"/>
|
||||
<tile name="io_left" port="clk" x="-1" y="-1"/>
|
||||
</global_port>
|
||||
</tile_annotations>
|
||||
<pb_type_annotations>
|
||||
<!-- physical pb_type binding in complex block IO -->
|
||||
<pb_type name="io" physical_mode_name="physical" idle_mode_name="inpad"/>
|
||||
<pb_type name="io[physical].iopad" circuit_model_name="GPIO" mode_bits="1"/>
|
||||
<pb_type name="io[physical].ff" circuit_model_name="DFFSRQ"/>
|
||||
<pb_type name="io[inpad].inpad" physical_pb_type_name="io[physical].iopad" mode_bits="1"/>
|
||||
<pb_type name="io[inpad_registered].inpad" physical_pb_type_name="io[physical].iopad" mode_bits="1"/>
|
||||
<pb_type name="io[inpad_registered].ff" physical_pb_type_name="io[physical].ff"/>
|
||||
<pb_type name="io[outpad].outpad" physical_pb_type_name="io[physical].iopad" mode_bits="0"/>
|
||||
<!-- End physical pb_type binding in complex block IO -->
|
||||
<!-- physical pb_type binding in complex block CLB -->
|
||||
<!-- physical mode will be the default mode if not specified -->
|
||||
<pb_type name="clb">
|
||||
<!-- Binding interconnect to circuit models as their physical implementation, if not defined, we use the default model -->
|
||||
<interconnect name="crossbar" circuit_model_name="mux_tree"/>
|
||||
</pb_type>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.lut4" circuit_model_name="lut4"/>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.ff" circuit_model_name="DFFSRQ"/>
|
||||
<!-- End physical pb_type binding in complex block IO -->
|
||||
</pb_type_annotations>
|
||||
</openfpga_architecture>
|
|
@ -0,0 +1,204 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Architecture annotation for OpenFPGA framework
|
||||
This annotation supports the k6_N10_40nm.xml
|
||||
- General purpose logic block
|
||||
- K = 6, N = 10, I = 40
|
||||
- Single mode
|
||||
- Routing architecture
|
||||
- L = 4, fc_in = 0.15, fc_out = 0.1
|
||||
-->
|
||||
<openfpga_architecture>
|
||||
<technology_library>
|
||||
<device_library>
|
||||
<device_model name="logic" type="transistor">
|
||||
<lib type="industry" corner="TOP_TT" ref="M" path="${OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.pm"/>
|
||||
<design vdd="0.9" pn_ratio="2"/>
|
||||
<pmos name="pch" chan_length="40e-9" min_width="140e-9" variation="logic_transistor_var"/>
|
||||
<nmos name="nch" chan_length="40e-9" min_width="140e-9" variation="logic_transistor_var"/>
|
||||
</device_model>
|
||||
<device_model name="io" type="transistor">
|
||||
<lib type="academia" ref="M" path="${OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.pm"/>
|
||||
<design vdd="2.5" pn_ratio="3"/>
|
||||
<pmos name="pch_25" chan_length="270e-9" min_width="320e-9" variation="io_transistor_var"/>
|
||||
<nmos name="nch_25" chan_length="270e-9" min_width="320e-9" variation="io_transistor_var"/>
|
||||
</device_model>
|
||||
</device_library>
|
||||
<variation_library>
|
||||
<variation name="logic_transistor_var" abs_deviation="0.1" num_sigma="3"/>
|
||||
<variation name="io_transistor_var" abs_deviation="0.1" num_sigma="3"/>
|
||||
</variation_library>
|
||||
</technology_library>
|
||||
<circuit_library>
|
||||
<circuit_model type="inv_buf" name="INVTX1" prefix="INVTX1" is_default="true">
|
||||
<design_technology type="cmos" topology="inverter" size="1"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="inv_buf" name="buf4" prefix="buf4" is_default="false">
|
||||
<design_technology type="cmos" topology="buffer" size="1" num_level="2" f_per_stage="4"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="inv_buf" name="tap_buf4" prefix="tap_buf4" is_default="false">
|
||||
<design_technology type="cmos" topology="buffer" size="1" num_level="3" f_per_stage="4"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="pass_gate" name="TGATE" prefix="TGATE" is_default="true">
|
||||
<design_technology type="cmos" topology="transmission_gate" nmos_size="1" pmos_size="2"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="input" prefix="sel" size="1"/>
|
||||
<port type="input" prefix="selb" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in sel selb" out_port="out">
|
||||
10e-12 5e-12 5e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in sel selb" out_port="out">
|
||||
10e-12 5e-12 5e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="chan_wire" name="chan_segment" prefix="track_seg" is_default="true">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<wire_param model_type="pi" R="101" C="22.5e-15" num_level="1"/>
|
||||
<!-- model_type could be T, res_val and cap_val DON'T CARE -->
|
||||
</circuit_model>
|
||||
<circuit_model type="wire" name="direct_interc" prefix="direct_interc" is_default="true">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<wire_param model_type="pi" R="0" C="0" num_level="1"/>
|
||||
<!-- model_type could be T, res_val cap_val should be defined -->
|
||||
</circuit_model>
|
||||
<circuit_model type="mux" name="mux_tree" prefix="mux_tree" dump_structural_verilog="true">
|
||||
<design_technology type="cmos" structure="tree" add_const_input="true" const_input_val="1"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="1"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="mux" name="mux_tree_tapbuf" prefix="mux_tree_tapbuf" is_default="true" dump_structural_verilog="true">
|
||||
<design_technology type="cmos" structure="tree" add_const_input="true" const_input_val="1"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="tap_buf4"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="1"/>
|
||||
</circuit_model>
|
||||
<!--DFF subckt ports should be defined as <D> <Q> <CLK> <RESET> <SET> -->
|
||||
<circuit_model type="ff" name="DFFSRQ" prefix="DFFSRQ" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="input" prefix="set" lib_name="SET" size="1" is_global="true" default_val="0" is_set="true"/>
|
||||
<port type="input" prefix="reset" lib_name="RST" size="1" is_global="true" default_val="0" is_reset="true"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="clock" prefix="clk" lib_name="CK" size="1" is_global="false" default_val="0"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="lut" name="lut4" prefix="lut4" dump_structural_verilog="true">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<lut_input_inverter exist="true" circuit_model_name="INVTX1"/>
|
||||
<lut_input_buffer exist="true" circuit_model_name="buf4"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="4"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="16"/>
|
||||
</circuit_model>
|
||||
<!--Scan-chain DFF subckt ports should be defined as <D> <Q> <Qb> <CLK> <RESET> <SET> -->
|
||||
<circuit_model type="ccff" name="DFF" prefix="DFF" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="QN" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="CK" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="GPIO" prefix="GPIO" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/gpio.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/gpio.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="inout" prefix="PAD" size="1" is_global="true" is_io="true" is_data_io="true"/>
|
||||
<port type="sram" prefix="DIR" size="1" mode_select="true" circuit_model_name="DFF" default_val="1"/>
|
||||
<port type="input" prefix="outpad" lib_name="A" size="1"/>
|
||||
<port type="output" prefix="inpad" lib_name="Y" size="1"/>
|
||||
</circuit_model>
|
||||
</circuit_library>
|
||||
<configuration_protocol>
|
||||
<organization type="scan_chain" circuit_model_name="DFF"/>
|
||||
</configuration_protocol>
|
||||
<connection_block>
|
||||
<switch name="ipin_cblock" circuit_model_name="mux_tree_tapbuf"/>
|
||||
</connection_block>
|
||||
<switch_block>
|
||||
<switch name="0" circuit_model_name="mux_tree_tapbuf"/>
|
||||
</switch_block>
|
||||
<routing_segment>
|
||||
<segment name="L1" circuit_model_name="chan_segment"/>
|
||||
<segment name="L4" circuit_model_name="chan_segment"/>
|
||||
</routing_segment>
|
||||
<tile_annotations>
|
||||
<global_port name="clk" is_clock="true" default_val="0">
|
||||
<tile name="clb" port="clk" x="-1" y="-1"/>
|
||||
<tile name="io_top" port="clk" x="-1" y="-1"/>
|
||||
<tile name="io_right" port="clk" x="-1" y="-1"/>
|
||||
<tile name="io_bottom" port="clk" x="-1" y="-1"/>
|
||||
<tile name="io_left" port="clk" x="-1" y="-1"/>
|
||||
</global_port>
|
||||
</tile_annotations>
|
||||
<pb_type_annotations>
|
||||
<!-- physical pb_type binding in complex block IO -->
|
||||
<pb_type name="io" physical_mode_name="physical" idle_mode_name="inpad"/>
|
||||
<pb_type name="io[physical].iopad" circuit_model_name="GPIO" mode_bits="1"/>
|
||||
<pb_type name="io[physical].ff" circuit_model_name="DFFSRQ"/>
|
||||
<pb_type name="io[inpad].inpad" physical_pb_type_name="io[physical].iopad" mode_bits="1"/>
|
||||
<pb_type name="io[inpad_registered].inpad" physical_pb_type_name="io[physical].iopad" mode_bits="1"/>
|
||||
<pb_type name="io[inpad_registered].ff" physical_pb_type_name="io[physical].ff"/>
|
||||
<pb_type name="io[outpad].outpad" physical_pb_type_name="io[physical].iopad" mode_bits="0"/>
|
||||
<!-- End physical pb_type binding in complex block IO -->
|
||||
<!-- physical pb_type binding in complex block CLB -->
|
||||
<!-- physical mode will be the default mode if not specified -->
|
||||
<pb_type name="clb">
|
||||
<!-- Binding interconnect to circuit models as their physical implementation, if not defined, we use the default model -->
|
||||
<interconnect name="crossbar" circuit_model_name="mux_tree"/>
|
||||
</pb_type>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.lut4" circuit_model_name="lut4"/>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.ff" circuit_model_name="DFFSRQ"/>
|
||||
<!-- End physical pb_type binding in complex block IO -->
|
||||
</pb_type_annotations>
|
||||
</openfpga_architecture>
|
|
@ -195,10 +195,10 @@
|
|||
<pb_type name="io[inpad_registered].ff" physical_pb_type_name="io[physical].ff"/>
|
||||
<pb_type name="io[outpad].outpad" physical_pb_type_name="io[physical].iopad" mode_bits="0"/>
|
||||
<pb_type name="io_input" physical_mode_name="physical" idle_mode_name="inpad"/>
|
||||
<pb_type name="io_input[physical].iopad" circuit_model_name="GPIN" mode_bits="1"/>
|
||||
<pb_type name="io_input[physical].iopad" circuit_model_name="GPIN"/>
|
||||
<pb_type name="io_input[physical].ff" circuit_model_name="DFFSRQ"/>
|
||||
<pb_type name="io_input[inpad].inpad" physical_pb_type_name="io_input[physical].iopad" mode_bits="1"/>
|
||||
<pb_type name="io_input[inpad_registered].inpad" physical_pb_type_name="io_input[physical].iopad" mode_bits="1"/>
|
||||
<pb_type name="io_input[inpad].inpad" physical_pb_type_name="io_input[physical].iopad"/>
|
||||
<pb_type name="io_input[inpad_registered].inpad" physical_pb_type_name="io_input[physical].iopad"/>
|
||||
<pb_type name="io_input[inpad_registered].ff" physical_pb_type_name="io_input[physical].ff"/>
|
||||
<!-- End physical pb_type binding in complex block IO -->
|
||||
<!-- physical pb_type binding in complex block CLB -->
|
||||
|
|
|
@ -174,7 +174,7 @@
|
|||
</routing_segment>
|
||||
<tile_annotations>
|
||||
<global_port name="clk" is_clock="true" clock_arch_tree_name="clk_tree_2lvl" default_val="0">
|
||||
<tile name="clb" port="clk[0:1]"/>
|
||||
<tile name="clb" port="clk[0:0]"/>
|
||||
</global_port>
|
||||
</tile_annotations>
|
||||
<pb_type_annotations>
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Architecture annotation for OpenFPGA framework
|
||||
This annotation supports the k6_N10_40nm.xml
|
||||
- General purpose logic block
|
||||
- K = 6, N = 10, I = 40
|
||||
- Single mode
|
||||
- Routing architecture
|
||||
- L = 4, fc_in = 0.15, fc_out = 0.1
|
||||
-->
|
||||
<openfpga_architecture>
|
||||
<technology_library>
|
||||
<device_library>
|
||||
<device_model name="logic" type="transistor">
|
||||
<lib type="industry" corner="TOP_TT" ref="M" path="${OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.pm"/>
|
||||
<design vdd="0.9" pn_ratio="2"/>
|
||||
<pmos name="pch" chan_length="40e-9" min_width="140e-9" variation="logic_transistor_var"/>
|
||||
<nmos name="nch" chan_length="40e-9" min_width="140e-9" variation="logic_transistor_var"/>
|
||||
</device_model>
|
||||
<device_model name="io" type="transistor">
|
||||
<lib type="academia" ref="M" path="${OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.pm"/>
|
||||
<design vdd="2.5" pn_ratio="3"/>
|
||||
<pmos name="pch_25" chan_length="270e-9" min_width="320e-9" variation="io_transistor_var"/>
|
||||
<nmos name="nch_25" chan_length="270e-9" min_width="320e-9" variation="io_transistor_var"/>
|
||||
</device_model>
|
||||
</device_library>
|
||||
<variation_library>
|
||||
<variation name="logic_transistor_var" abs_deviation="0.1" num_sigma="3"/>
|
||||
<variation name="io_transistor_var" abs_deviation="0.1" num_sigma="3"/>
|
||||
</variation_library>
|
||||
</technology_library>
|
||||
<circuit_library>
|
||||
<circuit_model type="inv_buf" name="INVTX1" prefix="INVTX1" is_default="true">
|
||||
<design_technology type="cmos" topology="inverter" size="1"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="inv_buf" name="buf4" prefix="buf4" is_default="false">
|
||||
<design_technology type="cmos" topology="buffer" size="1" num_level="2" f_per_stage="4"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="inv_buf" name="tap_buf4" prefix="tap_buf4" is_default="false">
|
||||
<design_technology type="cmos" topology="buffer" size="1" num_level="3" f_per_stage="4"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in" out_port="out">
|
||||
10e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="gate" name="OR2" prefix="OR2" is_default="true">
|
||||
<design_technology type="cmos" topology="OR"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="a" size="1"/>
|
||||
<port type="input" prefix="b" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<delay_matrix type="rise" in_port="a b" out_port="out">
|
||||
10e-12 5e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="a b" out_port="out">
|
||||
10e-12 5e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="pass_gate" name="TGATE" prefix="TGATE" is_default="true">
|
||||
<design_technology type="cmos" topology="transmission_gate" nmos_size="1" pmos_size="2"/>
|
||||
<device_technology device_model_name="logic"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" lib_name="A" size="1"/>
|
||||
<port type="input" prefix="sel" lib_name="S" size="1"/>
|
||||
<port type="input" prefix="selb" lib_name="SI" size="1"/>
|
||||
<port type="output" prefix="out" lib_name="Y" size="1"/>
|
||||
<delay_matrix type="rise" in_port="in sel selb" out_port="out">
|
||||
10e-12 5e-12 5e-12
|
||||
</delay_matrix>
|
||||
<delay_matrix type="fall" in_port="in sel selb" out_port="out">
|
||||
10e-12 5e-12 5e-12
|
||||
</delay_matrix>
|
||||
</circuit_model>
|
||||
<circuit_model type="chan_wire" name="chan_segment" prefix="track_seg" is_default="true">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<wire_param model_type="pi" R="101" C="22.5e-15" num_level="1"/>
|
||||
<!-- model_type could be T, res_val and cap_val DON'T CARE -->
|
||||
</circuit_model>
|
||||
<circuit_model type="wire" name="direct_interc" prefix="direct_interc" is_default="true">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="false"/>
|
||||
<output_buffer exist="false"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<wire_param model_type="pi" R="0" C="0" num_level="1"/>
|
||||
<!-- model_type could be T, res_val cap_val should be defined -->
|
||||
</circuit_model>
|
||||
<circuit_model type="mux" name="mux_2level" prefix="mux_2level" dump_structural_verilog="true">
|
||||
<design_technology type="cmos" structure="multi_level" num_level="2" add_const_input="true" const_input_val="1"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="1"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="mux" name="mux_2level_tapbuf" prefix="mux_2level_tapbuf" dump_structural_verilog="true">
|
||||
<design_technology type="cmos" structure="multi_level" num_level="2" add_const_input="true" const_input_val="1"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="tap_buf4"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="1"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="mux" name="mux_1level_tapbuf" prefix="mux_1level_tapbuf" is_default="true" dump_structural_verilog="true">
|
||||
<design_technology type="cmos" structure="one_level" add_const_input="true" const_input_val="1"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="tap_buf4"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="1"/>
|
||||
<port type="output" prefix="out" size="1"/>
|
||||
<port type="sram" prefix="sram" size="1"/>
|
||||
</circuit_model>
|
||||
<!--DFF subckt ports should be defined as <D> <Q> <CLK> <RESET> <SET> -->
|
||||
<circuit_model type="ff" name="MULTI_MODE_DFFRQ" prefix="MULTI_MODE_DFFRQ" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="input" prefix="R" lib_name="RST" size="1" default_val="0"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="clock" prefix="C" lib_name="CK" size="1" default_val="0"/>
|
||||
<port type="sram" prefix="mode" size="1" mode_select="true" circuit_model_name="DFFR" default_val="0"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="lut" name="frac_lut4" prefix="frac_lut4" dump_structural_verilog="true">
|
||||
<design_technology type="cmos" fracturable_lut="true"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<lut_input_inverter exist="true" circuit_model_name="INVTX1"/>
|
||||
<lut_input_buffer exist="true" circuit_model_name="buf4"/>
|
||||
<lut_intermediate_buffer exist="true" circuit_model_name="buf4" location_map="-1-"/>
|
||||
<pass_gate_logic circuit_model_name="TGATE"/>
|
||||
<port type="input" prefix="in" size="4" tri_state_map="---1" circuit_model_name="OR2"/>
|
||||
<port type="output" prefix="lut3_out" size="2" lut_frac_level="3" lut_output_mask="0,1"/>
|
||||
<port type="output" prefix="lut4_out" size="1" lut_output_mask="0"/>
|
||||
<port type="sram" prefix="sram" size="16"/>
|
||||
<port type="sram" prefix="mode" size="1" mode_select="true" circuit_model_name="DFFR" default_val="1"/>
|
||||
</circuit_model>
|
||||
<!--Scan-chain DFF subckt ports should be defined as <D> <Q> <Qb> <CLK> <RESET> <SET> -->
|
||||
<circuit_model type="ccff" name="DFFR" prefix="DFFR" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/dff.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/dff.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="input" prefix="pReset" lib_name="RST" size="1" is_global="true" default_val="0" is_reset="true" is_prog="true"/>
|
||||
<port type="input" prefix="D" size="1"/>
|
||||
<port type="output" prefix="Q" size="1"/>
|
||||
<port type="output" prefix="QN" size="1"/>
|
||||
<port type="clock" prefix="prog_clk" lib_name="CK" size="1" is_global="true" default_val="0" is_prog="true"/>
|
||||
</circuit_model>
|
||||
<circuit_model type="iopad" name="GPIO" prefix="GPIO" spice_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/spice/gpio.sp" verilog_netlist="${OPENFPGA_PATH}/openfpga_flow/openfpga_cell_library/verilog/gpio.v">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<output_buffer exist="true" circuit_model_name="INVTX1"/>
|
||||
<port type="inout" prefix="PAD" size="1" is_global="true" is_io="true" is_data_io="true"/>
|
||||
<port type="sram" prefix="DIR" size="1" mode_select="true" circuit_model_name="DFFR" default_val="1"/>
|
||||
<port type="input" prefix="outpad" lib_name="A" size="1"/>
|
||||
<port type="output" prefix="inpad" lib_name="Y" size="1"/>
|
||||
</circuit_model>
|
||||
</circuit_library>
|
||||
<configuration_protocol>
|
||||
<organization type="scan_chain" circuit_model_name="DFFR"/>
|
||||
</configuration_protocol>
|
||||
<connection_block>
|
||||
<switch name="ipin_cblock" circuit_model_name="mux_2level_tapbuf"/>
|
||||
</connection_block>
|
||||
<switch_block>
|
||||
<switch name="0" circuit_model_name="mux_2level_tapbuf"/>
|
||||
</switch_block>
|
||||
<routing_segment>
|
||||
<segment name="L1" circuit_model_name="chan_segment"/>
|
||||
<segment name="L4" circuit_model_name="chan_segment"/>
|
||||
</routing_segment>
|
||||
<tile_annotations>
|
||||
<global_port name="op_clk" clock_arch_tree_name="clk_tree_2lvl" is_clock="true" default_val="0">
|
||||
<tile name="clb" port="clk"/>
|
||||
</global_port>
|
||||
<global_port name="op_reset" clock_arch_tree_name="rst_tree_2lvl" is_reset="true" default_val="0">
|
||||
<tile name="clb" port="reset"/>
|
||||
</global_port>
|
||||
</tile_annotations>
|
||||
<pb_type_annotations>
|
||||
<!-- physical pb_type binding in complex block IO -->
|
||||
<pb_type name="io" physical_mode_name="physical" idle_mode_name="inpad"/>
|
||||
<pb_type name="io[physical].iopad" circuit_model_name="GPIO" mode_bits="1"/>
|
||||
<pb_type name="io[inpad].inpad" physical_pb_type_name="io[physical].iopad" mode_bits="1"/>
|
||||
<pb_type name="io[outpad].outpad" physical_pb_type_name="io[physical].iopad" mode_bits="0"/>
|
||||
<!-- End physical pb_type binding in complex block IO -->
|
||||
<!-- physical pb_type binding in complex block CLB -->
|
||||
<!-- physical mode will be the default mode if not specified -->
|
||||
<pb_type name="clb">
|
||||
<!-- Binding interconnect to circuit models as their physical implementation, if not defined, we use the default model -->
|
||||
<interconnect name="crossbar" circuit_model_name="mux_2level"/>
|
||||
</pb_type>
|
||||
<pb_type name="clb.fle" physical_mode_name="physical"/>
|
||||
<pb_type name="clb.fle[physical].fabric.frac_logic.frac_lut4" circuit_model_name="frac_lut4" mode_bits="0"/>
|
||||
<pb_type name="clb.fle[physical].fabric.ff" circuit_model_name="MULTI_MODE_DFFRQ" mode_bits="0"/>
|
||||
<!-- Binding operating pb_type to physical pb_type -->
|
||||
<pb_type name="clb.fle[n2_lut3].lut3inter.ble3.lut3" physical_pb_type_name="clb.fle[physical].fabric.frac_logic.frac_lut4" mode_bits="1" physical_pb_type_index_factor="0.5">
|
||||
<!-- Binding the lut3 to the first 3 inputs of fracturable lut4 -->
|
||||
<port name="in" physical_mode_port="in[0:2]"/>
|
||||
<port name="out" physical_mode_port="lut3_out[0:0]" physical_mode_pin_rotate_offset="1"/>
|
||||
</pb_type>
|
||||
<pb_type name="clb.fle[n2_lut3].lut3inter.ble3.ff[latch].latch" physical_pb_type_name="clb.fle[physical].fabric.ff" mode_bits="0">
|
||||
<port name="clk" physical_mode_port="C"/>
|
||||
</pb_type>
|
||||
<pb_type name="clb.fle[n2_lut3].lut3inter.ble3.ff[dff].dff" physical_pb_type_name="clb.fle[physical].fabric.ff" mode_bits="0"/>
|
||||
<pb_type name="clb.fle[n2_lut3].lut3inter.ble3.ff[dffr].dffr" physical_pb_type_name="clb.fle[physical].fabric.ff" mode_bits="0"/>
|
||||
<pb_type name="clb.fle[n2_lut3].lut3inter.ble3.ff[dffrn].dffrn" physical_pb_type_name="clb.fle[physical].fabric.ff" mode_bits="1">
|
||||
<port name="RN" physical_mode_port="R"/>
|
||||
</pb_type>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.lut4" physical_pb_type_name="clb.fle[physical].fabric.frac_logic.frac_lut4" mode_bits="0">
|
||||
<!-- Binding the lut4 to the first 4 inputs of fracturable lut4 -->
|
||||
<port name="in" physical_mode_port="in[0:3]"/>
|
||||
<port name="out" physical_mode_port="lut4_out"/>
|
||||
</pb_type>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.ff[latch].latch" physical_pb_type_name="clb.fle[physical].fabric.ff" mode_bits="0" physical_pb_type_index_factor="2" physical_pb_type_index_offset="0">
|
||||
<port name="clk" physical_mode_port="C"/>
|
||||
</pb_type>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.ff[dff].dff" physical_pb_type_name="clb.fle[physical].fabric.ff" mode_bits="0" physical_pb_type_index_factor="2" physical_pb_type_index_offset="0"/>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.ff[dffr].dffr" physical_pb_type_name="clb.fle[physical].fabric.ff" mode_bits="0" physical_pb_type_index_factor="2" physical_pb_type_index_offset="0"/>
|
||||
<pb_type name="clb.fle[n1_lut4].ble4.ff[dffrn].dffrn" physical_pb_type_name="clb.fle[physical].fabric.ff" mode_bits="1" physical_pb_type_index_factor="2" physical_pb_type_index_offset="0">
|
||||
<port name="RN" physical_mode_port="R"/>
|
||||
</pb_type>
|
||||
<!-- End physical pb_type binding in complex block IO -->
|
||||
</pb_type_annotations>
|
||||
</openfpga_architecture>
|
|
@ -197,6 +197,7 @@
|
|||
<switch name="0" circuit_model_name="mux_2level_tapbuf"/>
|
||||
</switch_block>
|
||||
<routing_segment>
|
||||
<segment name="L1" circuit_model_name="chan_segment"/>
|
||||
<segment name="L4" circuit_model_name="chan_segment"/>
|
||||
</routing_segment>
|
||||
<tile_annotations>
|
||||
|
|
|
@ -22,7 +22,7 @@ append_clock_rr_graph
|
|||
link_openfpga_arch --activity_file ${ACTIVITY_FILE} --sort_gsb_chan_node_in_edges
|
||||
|
||||
# Route clock based on clock network definition
|
||||
route_clock_rr_graph
|
||||
route_clock_rr_graph ${OPENFPGA_ROUTE_CLOCK_OPTIONS}
|
||||
|
||||
# Check and correct any naming conflicts in the BLIF netlist
|
||||
check_netlist_naming_conflict --fix --report ./netlist_renaming.xml
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# Run VPR for the 'and' design
|
||||
#--write_rr_graph example_rr_graph.xml
|
||||
vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} \
|
||||
--clock_modeling ideal \
|
||||
--device ${OPENFPGA_VPR_DEVICE_LAYOUT} \
|
||||
--route_chan_width ${OPENFPGA_VPR_ROUTE_CHAN_WIDTH} \
|
||||
--read_vpr_constraints ${OPENFPGA_VPR_CONSTRAINT_FILE}
|
||||
|
||||
# Read OpenFPGA architecture definition
|
||||
read_openfpga_arch -f ${OPENFPGA_ARCH_FILE}
|
||||
|
||||
# Read OpenFPGA simulation settings
|
||||
read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE}
|
||||
|
||||
# Read OpenFPGA clock architecture
|
||||
read_openfpga_clock_arch -f ${OPENFPGA_CLOCK_ARCH_FILE}
|
||||
|
||||
# Append clock network to vpr's routing resource graph
|
||||
append_clock_rr_graph
|
||||
|
||||
# Annotate the OpenFPGA architecture to VPR data base
|
||||
# to debug use --verbose options
|
||||
link_openfpga_arch --sort_gsb_chan_node_in_edges
|
||||
|
||||
# Route clock based on clock network definition
|
||||
route_clock_rr_graph ${OPENFPGA_ROUTE_CLOCK_OPTIONS} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
|
||||
# Check and correct any naming conflicts in the BLIF netlist
|
||||
check_netlist_naming_conflict --fix --report ./netlist_renaming.xml
|
||||
|
||||
# Apply fix-up to Look-Up Table truth tables based on packing results
|
||||
lut_truth_table_fixup
|
||||
|
||||
# Build the module graph
|
||||
# - Enabled compression on routing architecture modules
|
||||
# - Enable pin duplication on grid modules
|
||||
build_fabric --compress_routing #--verbose
|
||||
|
||||
# Write the fabric hierarchy of module graph to a file
|
||||
# This is used by hierarchical PnR flows
|
||||
write_fabric_hierarchy --file ./fabric_hierarchy.txt
|
||||
|
||||
# Repack the netlist to physical pbs
|
||||
# This must be done before bitstream generator and testbench generation
|
||||
# Strongly recommend it is done after all the fix-up have been applied
|
||||
repack --design_constraints ${OPENFPGA_REPACK_CONSTRAINTS_FILE} #--verbose
|
||||
|
||||
# Build the bitstream
|
||||
# - Output the fabric-independent bitstream to a file
|
||||
build_architecture_bitstream --verbose --write_file fabric_independent_bitstream.xml
|
||||
|
||||
# Build fabric-dependent bitstream
|
||||
build_fabric_bitstream --verbose
|
||||
|
||||
# Write fabric-dependent bitstream
|
||||
write_fabric_bitstream --file fabric_bitstream.bit --format plain_text
|
||||
|
||||
# Write the Verilog netlist for FPGA fabric
|
||||
# - Enable the use of explicit port mapping in Verilog netlist
|
||||
write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --print_user_defined_template --verbose
|
||||
|
||||
# Write the Verilog testbench for FPGA fabric
|
||||
# - We suggest the use of same output directory as fabric Verilog netlists
|
||||
# - Must specify the reference benchmark file if you want to output any testbenches
|
||||
# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA
|
||||
# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase
|
||||
# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts
|
||||
write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} ${OPENFPGA_VERILOG_TESTBENCH_PORT_MAPPING} --include_signal_init --bitstream fabric_bitstream.bit --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
write_preconfigured_fabric_wrapper --embed_bitstream iverilog --file ./SRC ${OPENFPGA_VERILOG_TESTBENCH_PORT_MAPPING} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} ${OPENFPGA_VERILOG_TESTBENCH_PORT_MAPPING} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
|
||||
# Finish and exit OpenFPGA
|
||||
exit
|
||||
|
||||
# Note :
|
||||
# To run verification at the end of the flow maintain source in ./SRC directory
|
|
@ -0,0 +1,75 @@
|
|||
# Run VPR for the 'and' design
|
||||
#--write_rr_graph example_rr_graph.xml
|
||||
vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} \
|
||||
--clock_modeling ideal \
|
||||
--device ${OPENFPGA_VPR_DEVICE_LAYOUT} \
|
||||
--route_chan_width ${OPENFPGA_VPR_ROUTE_CHAN_WIDTH}
|
||||
|
||||
# Read OpenFPGA architecture definition
|
||||
read_openfpga_arch -f ${OPENFPGA_ARCH_FILE}
|
||||
|
||||
# Read OpenFPGA simulation settings
|
||||
read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE}
|
||||
|
||||
# Read OpenFPGA clock architecture
|
||||
read_openfpga_clock_arch -f ${OPENFPGA_CLOCK_ARCH_FILE}
|
||||
|
||||
# Append clock network to vpr's routing resource graph
|
||||
append_clock_rr_graph
|
||||
|
||||
# Annotate the OpenFPGA architecture to VPR data base
|
||||
# to debug use --verbose options
|
||||
link_openfpga_arch --sort_gsb_chan_node_in_edges
|
||||
|
||||
# Route clock based on clock network definition
|
||||
route_clock_rr_graph ${OPENFPGA_ROUTE_CLOCK_OPTIONS} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
|
||||
# Check and correct any naming conflicts in the BLIF netlist
|
||||
check_netlist_naming_conflict --fix --report ./netlist_renaming.xml
|
||||
|
||||
# Apply fix-up to Look-Up Table truth tables based on packing results
|
||||
lut_truth_table_fixup
|
||||
|
||||
# Build the module graph
|
||||
# - Enabled compression on routing architecture modules
|
||||
# - Enable pin duplication on grid modules
|
||||
build_fabric --compress_routing #--verbose
|
||||
|
||||
# Write the fabric hierarchy of module graph to a file
|
||||
# This is used by hierarchical PnR flows
|
||||
write_fabric_hierarchy --file ./fabric_hierarchy.txt
|
||||
|
||||
# Repack the netlist to physical pbs
|
||||
# This must be done before bitstream generator and testbench generation
|
||||
# Strongly recommend it is done after all the fix-up have been applied
|
||||
repack --design_constraints ${OPENFPGA_REPACK_CONSTRAINTS_FILE} #--verbose
|
||||
|
||||
# Build the bitstream
|
||||
# - Output the fabric-independent bitstream to a file
|
||||
build_architecture_bitstream --verbose --write_file fabric_independent_bitstream.xml
|
||||
|
||||
# Build fabric-dependent bitstream
|
||||
build_fabric_bitstream --verbose
|
||||
|
||||
# Write fabric-dependent bitstream
|
||||
write_fabric_bitstream --file fabric_bitstream.bit --format plain_text
|
||||
|
||||
# Write the Verilog netlist for FPGA fabric
|
||||
# - Enable the use of explicit port mapping in Verilog netlist
|
||||
write_fabric_verilog --file ./SRC --explicit_port_mapping --include_timing --print_user_defined_template --verbose
|
||||
|
||||
# Write the Verilog testbench for FPGA fabric
|
||||
# - We suggest the use of same output directory as fabric Verilog netlists
|
||||
# - Must specify the reference benchmark file if you want to output any testbenches
|
||||
# - Enable top-level testbench which is a full verification including programming circuit and core logic of FPGA
|
||||
# - Enable pre-configured top-level testbench which is a fast verification skipping programming phase
|
||||
# - Simulation ini file is optional and is needed only when you need to interface different HDL simulators using openfpga flow-run scripts
|
||||
write_full_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} ${OPENFPGA_VERILOG_TESTBENCH_PORT_MAPPING} --include_signal_init --bitstream fabric_bitstream.bit --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
write_preconfigured_fabric_wrapper --embed_bitstream iverilog --file ./SRC ${OPENFPGA_VERILOG_TESTBENCH_PORT_MAPPING} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
write_preconfigured_testbench --file ./SRC --reference_benchmark_file_path ${REFERENCE_VERILOG_TESTBENCH} ${OPENFPGA_VERILOG_TESTBENCH_PORT_MAPPING} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
|
||||
# Finish and exit OpenFPGA
|
||||
exit
|
||||
|
||||
# Note :
|
||||
# To run verification at the end of the flow maintain source in ./SRC directory
|
|
@ -22,7 +22,7 @@ append_clock_rr_graph
|
|||
link_openfpga_arch --activity_file ${ACTIVITY_FILE} --sort_gsb_chan_node_in_edges
|
||||
|
||||
# Route clock based on clock network definition
|
||||
route_clock_rr_graph --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
route_clock_rr_graph ${OPENFPGA_ROUTE_CLOCK_OPTIONS} --pin_constraints_file ${OPENFPGA_PIN_CONSTRAINTS_FILE}
|
||||
|
||||
# Check and correct any naming conflicts in the BLIF netlist
|
||||
check_netlist_naming_conflict --fix --report ./netlist_renaming.xml
|
||||
|
|
|
@ -190,10 +190,15 @@ run-task basic_tests/tile_organization/tileable_io $@
|
|||
echo -e "Testing tiles with I/O consisting of subtiles";
|
||||
run-task basic_tests/tile_organization/io_subtile $@
|
||||
run-task basic_tests/tile_organization/io_subtile_strong $@
|
||||
echo -e "Testing tiles with routing tracks around I/O";
|
||||
run-task basic_tests/tile_organization/perimeter_cb $@
|
||||
echo -e "Testing tile grouping on a homogeneous FPGA fabric (Full testbench)";
|
||||
run-task basic_tests/tile_organization/homo_fabric_tile $@
|
||||
run-task basic_tests/tile_organization/homo_fabric_tile_bl $@
|
||||
echo -e "Testing tile grouping on a homogeneous FPGA fabric (Preconfigured testbench)";
|
||||
run-task basic_tests/tile_organization/fabric_tile_global_tile_clock_io_subtile $@
|
||||
run-task basic_tests/tile_organization/fabric_tile_perimeter_cb_global_tile_clock $@
|
||||
run-task basic_tests/tile_organization/fabric_tile_clkntwk_io_subtile $@
|
||||
run-task basic_tests/tile_organization/homo_fabric_tile_preconfig $@
|
||||
run-task basic_tests/tile_organization/homo_fabric_tile_2x2_preconfig $@
|
||||
run-task basic_tests/tile_organization/homo_fabric_tile_4x4_preconfig $@
|
||||
|
@ -232,6 +237,13 @@ echo -e "Testing programmable clock architecture";
|
|||
run-task basic_tests/clock_network/homo_1clock_2layer $@
|
||||
run-task basic_tests/clock_network/homo_1clock_2layer_full_tb $@
|
||||
run-task basic_tests/clock_network/homo_2clock_2layer $@
|
||||
run-task basic_tests/clock_network/homo_2clock_2layer_disable_unused $@
|
||||
run-task basic_tests/clock_network/homo_2clock_2layer_disable_unused_tree $@
|
||||
run-task basic_tests/clock_network/homo_1clock_1reset_2layer $@
|
||||
run-task basic_tests/clock_network/homo_1clock_1reset_2layer_on_lut $@
|
||||
run-task basic_tests/clock_network/homo_1clock_1reset_2layer_syntax $@
|
||||
run-task basic_tests/clock_network/homo_1clock_1reset_2layer_disable_unused_spines $@
|
||||
run-task basic_tests/clock_network/homo_1clock_1reset_2layer_internal_driver $@
|
||||
|
||||
echo -e "Testing configuration chain of a K4N4 FPGA using .blif generated by yosys+verific";
|
||||
run-task basic_tests/verific_test $@
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<clock_networks default_segment="L1" default_tap_switch="ipin_cblock" default_driver_switch="0">
|
||||
<clock_network name="clk_tree_2lvl" global_port="op_clk[0:0]">
|
||||
<spine name="clk_spine_lvl0" start_x="1" start_y="1" end_x="2" end_y="1">
|
||||
<switch_point tap="clk_rib_lvl1_sw0_upper" x="1" y="1"/>
|
||||
<switch_point tap="clk_rib_lvl1_sw0_lower" x="1" y="1"/>
|
||||
<switch_point tap="clk_rib_lvl1_sw1_upper" x="2" y="1"/>
|
||||
<switch_point tap="clk_rib_lvl1_sw1_lower" x="2" y="1"/>
|
||||
</spine>
|
||||
<spine name="clk_rib_lvl1_sw0_upper" start_x="1" start_y="2" end_x="1" end_y="2" type="CHANY" direction="INC_DIRECTION"/>
|
||||
<spine name="clk_rib_lvl1_sw0_lower" start_x="1" start_y="1" end_x="1" end_y="1" type="CHANY" direction="DEC_DIRECTION"/>
|
||||
<spine name="clk_rib_lvl1_sw1_upper" start_x="2" start_y="2" end_x="2" end_y="2" type="CHANY" direction="INC_DIRECTION"/>
|
||||
<spine name="clk_rib_lvl1_sw1_lower" start_x="2" start_y="1" end_x="2" end_y="1" type="CHANY" direction="DEC_DIRECTION"/>
|
||||
<taps>
|
||||
<all from_pin="op_clk[0:0]" to_pin="clb[0:0].clk[0:0]"/>
|
||||
</taps>
|
||||
</clock_network>
|
||||
<clock_network name="rst_tree_2lvl" global_port="op_reset[0:0]">
|
||||
<spine name="rst_spine_lvl0" start_x="1" start_y="1" end_x="2" end_y="1">
|
||||
<switch_point tap="rst_rib_lvl1_sw0_upper" x="1" y="1"/>
|
||||
<switch_point tap="rst_rib_lvl1_sw0_lower" x="1" y="1"/>
|
||||
<switch_point tap="rst_rib_lvl1_sw1_upper" x="2" y="1"/>
|
||||
<switch_point tap="rst_rib_lvl1_sw1_lower" x="2" y="1"/>
|
||||
</spine>
|
||||
<spine name="rst_rib_lvl1_sw0_upper" start_x="1" start_y="2" end_x="1" end_y="2" type="CHANY" direction="INC_DIRECTION"/>
|
||||
<spine name="rst_rib_lvl1_sw0_lower" start_x="1" start_y="1" end_x="1" end_y="1" type="CHANY" direction="DEC_DIRECTION"/>
|
||||
<spine name="rst_rib_lvl1_sw1_upper" start_x="2" start_y="2" end_x="2" end_y="2" type="CHANY" direction="INC_DIRECTION"/>
|
||||
<spine name="rst_rib_lvl1_sw1_lower" start_x="2" start_y="1" end_x="2" end_y="1" type="CHANY" direction="DEC_DIRECTION"/>
|
||||
<taps>
|
||||
<all from_pin="op_reset[0:0]" to_pin="clb[0:0].reset[0:0]"/>
|
||||
</taps>
|
||||
</clock_network>
|
||||
</clock_networks>
|
|
@ -0,0 +1,8 @@
|
|||
<pin_constraints>
|
||||
<!-- For a given .blif file, we want to assign
|
||||
- the reset signal to the op_reset[0] port of the FPGA fabric
|
||||
-->
|
||||
<set_io pin="op_reset[0]" net="reset"/>
|
||||
<set_io pin="op_clk[0]" net="clk"/>
|
||||
</pin_constraints>
|
||||
|