Merge branch 'master' into ganesh_dev
|
@ -127,15 +127,10 @@ jobs:
|
|||
|
||||
- uses: hendrikmuhs/ccache-action@v1
|
||||
|
||||
- name: checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
make checkout
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
make compile BUILD_TYPE=$BUILD_TYPE
|
||||
make all BUILD_TYPE=$BUILD_TYPE
|
||||
|
||||
# Check the cache size and see if it is over the limit
|
||||
- name: Check ccache size
|
||||
|
@ -216,15 +211,10 @@ jobs:
|
|||
|
||||
- uses: hendrikmuhs/ccache-action@v1
|
||||
|
||||
- name: checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
make checkout
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
make compile BUILD_TYPE=$BUILD_TYPE CMAKE_FLAGS="${{ matrix.config.cmake_flags }}"
|
||||
make all BUILD_TYPE=$BUILD_TYPE CMAKE_FLAGS="${{ matrix.config.cmake_flags }}"
|
||||
|
||||
ubuntu_support:
|
||||
needs: change_detect
|
||||
|
@ -262,15 +252,10 @@ jobs:
|
|||
|
||||
- uses: hendrikmuhs/ccache-action@v1
|
||||
|
||||
- name: checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
make checkout
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
make compile BUILD_TYPE=$BUILD_TYPE
|
||||
make all BUILD_TYPE=$BUILD_TYPE
|
||||
|
||||
debug_build:
|
||||
needs: change_detect
|
||||
|
@ -315,15 +300,10 @@ jobs:
|
|||
|
||||
- uses: hendrikmuhs/ccache-action@v1
|
||||
|
||||
- name: checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
make checkout
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
make compile BUILD_TYPE=${{ matrix.config.build_type }} -j ${{ matrix.config.cores }}
|
||||
make all BUILD_TYPE=${{ matrix.config.build_type }} -j ${{ matrix.config.cores }}
|
||||
|
||||
- name: Quick Test
|
||||
shell: bash
|
||||
|
@ -371,15 +351,10 @@ jobs:
|
|||
|
||||
- uses: hendrikmuhs/ccache-action@v1
|
||||
|
||||
- name: checkout submodules
|
||||
shell: bash
|
||||
run: |
|
||||
make checkout
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: |
|
||||
make compile BUILD_TYPE=${{ matrix.config.build_type }} -j ${{ matrix.config.cores }} CMAKE_FLAGS="-DOPENFPGA_ENABLE_STRICT_COMPILE=ON"
|
||||
make all BUILD_TYPE=${{ matrix.config.build_type }} -j ${{ matrix.config.cores }} CMAKE_FLAGS="-DOPENFPGA_ENABLE_STRICT_COMPILE=ON"
|
||||
|
||||
- name: Quick Test
|
||||
shell: bash
|
||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
# Test the RTL compilation compatibility
|
||||
verilog:
|
||||
name: RTL compilation and tests
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Cancel previous
|
||||
uses: styfle/cancel-workflow-action@0.9.1
|
||||
|
@ -25,7 +25,7 @@ jobs:
|
|||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo bash .github/workflows/install_dependencies_run.sh
|
||||
sudo bash .github/workflows/install_dependencies_run_ubuntu22p04.sh
|
||||
|
||||
- name: Dump tool versions
|
||||
run: |
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# Update as required by some packages
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y \
|
||||
libdatetime-perl libc6 libffi-dev libgcc1 libreadline8 libstdc++6 \
|
||||
libtcl8.6 tcl python3.8 python3-pip zlib1g libbz2-1.0 \
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# Update as required by some packages
|
||||
apt-get update
|
||||
apt-get install --no-install-recommends -y \
|
||||
libdatetime-perl libc6 libffi-dev libgcc1 libreadline8 libstdc++6 \
|
||||
libtcl8.6 tcl python3-pip zlib1g libbz2-1.0 \
|
||||
|
|
|
@ -7,7 +7,9 @@ version: 2
|
|||
|
||||
# Specify docker image for building the doc
|
||||
build:
|
||||
image: latest
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.7"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
|
@ -19,7 +21,5 @@ formats: all
|
|||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
version: 3.7
|
||||
system_packages: true
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
|
|
@ -109,9 +109,9 @@ endif()
|
|||
include(CheckCXXCompilerFlag)
|
||||
|
||||
#
|
||||
# We require c++14 support
|
||||
# We require c++17 support
|
||||
#
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF) #No compiler specific extensions
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ FROM ghcr.io/lnis-uofu/openfpga-master:8d555772
|
|||
|
||||
# Install node js
|
||||
USER root
|
||||
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
|
||||
RUN curl -sL https://deb.nodesource.com/setup_18.x | bash -
|
||||
RUN curl -fsSL https://code-server.dev/install.sh | sh
|
||||
RUN apt-get install -y nodejs
|
||||
RUN apt-get install tree
|
||||
|
@ -49,7 +49,7 @@ RUN python3 -m pip install --upgrade pip
|
|||
RUN python3 -m pip install --user --no-cache-dir notebook
|
||||
RUN python3 -m pip install --user --no-cache-dir jupyterlab
|
||||
RUN python3 -m pip install --user --no-cache-dir jupyterhub
|
||||
RUN python3 -m pip install --user --no-cache-dir "jupyter-server<2.0.0"
|
||||
RUN python3 -m pip install --user --no-cache-dir jupyter-server
|
||||
RUN python3 -m pip install --user --no-cache-dir jupyter-server-proxy
|
||||
RUN python3 -m pip install --user --no-cache-dir jupyter-vscode-proxy
|
||||
|
||||
|
|
9
Makefile
|
@ -24,7 +24,7 @@ CMAKE_BUILD_TYPE := $(shell echo ${BUILD_TYPE} | sed 's/_\?pgo//' | sed 's/_\?st
|
|||
# e.g. make CMAKE_FLAGS="-DCMAKE_CXX_COMPILER=g++-9'
|
||||
override CMAKE_FLAGS := -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -G 'Unix Makefiles' ${CMAKE_FLAGS}
|
||||
|
||||
# -s : Suppresss makefile output (e.g. entering/leaving directories)
|
||||
# -s : Suppress makefile output (e.g. entering/leaving directories)
|
||||
# --output-sync target : For parallel compilation ensure output for each target is synchronized (make version >= 4.0)
|
||||
MAKEFLAGS := -s
|
||||
|
||||
|
@ -66,7 +66,7 @@ prebuild:
|
|||
echo "cd ${BUILD_DIR} && ${CMAKE_COMMAND} ${CMAKE_FLAGS} ${SOURCE_DIR}" && \
|
||||
cd ${BUILD_DIR} && ${CMAKE_COMMAND} ${CMAKE_FLAGS} ${SOURCE_DIR}
|
||||
|
||||
compile: prebuild
|
||||
compile: | prebuild
|
||||
# Compile the code base. By default, all the targets will be compiled
|
||||
# Following options are available
|
||||
# .. option:: CMAKE_GOALS
|
||||
|
@ -75,12 +75,13 @@ compile: prebuild
|
|||
echo "Building target(s): ${CMAKE_GOALS}"
|
||||
@+${MAKE} -C ${BUILD_DIR} ${CMAKE_GOALS}
|
||||
|
||||
list_cmake_targets: prebuild
|
||||
list_cmake_targets: | prebuild
|
||||
# Show the targets available to be built, which can be specified through ``CMAKE_GOALS`` when compile
|
||||
cd ${BUILD_DIR} && make help && cd -
|
||||
|
||||
all: checkout compile
|
||||
all: checkout
|
||||
# A shortcut command to run checkout and compile in serial
|
||||
@+${MAKE} compile
|
||||
|
||||
format-cpp:
|
||||
# Format all the C/C++ files under this project, excluding submodules
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.2.971
|
||||
1.2.1505
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
#recommonmark
|
||||
|
||||
#Handle references in bibtex format
|
||||
sphinxcontrib-bibtex<2.0.0
|
||||
sphinxcontrib-bibtex
|
||||
|
||||
# Package required to embed youtube video
|
||||
sphinxcontrib-yt
|
||||
sphinxcontrib-youtube
|
||||
|
||||
# Package required to convert SVG for latex building
|
||||
sphinxcontrib-svg2pdfconverter
|
||||
|
|
|
@ -26,7 +26,7 @@ import sphinx_rtd_theme
|
|||
# For bibtex support
|
||||
import sphinxcontrib.bibtex
|
||||
# For embedded youtube
|
||||
import sphinxcontrib.yt
|
||||
import sphinxcontrib.youtube
|
||||
# For converting SVG to PNG using rsvg
|
||||
import sphinxcontrib.rsvgconverter
|
||||
|
||||
|
@ -57,7 +57,7 @@ extensions = [
|
|||
'sphinx.ext.graphviz',
|
||||
'sphinxcontrib.bibtex',
|
||||
'sphinx.ext.autosectionlabel',
|
||||
'sphinxcontrib.yt',
|
||||
'sphinxcontrib.youtube',
|
||||
'sphinxcontrib.rsvgconverter',
|
||||
]
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ in which case the docker image compiled for the latest master branch is used for
|
|||
];
|
||||
|
||||
RunRegression [
|
||||
label = "Run functional regeression test"
|
||||
label = "Run functional regression test"
|
||||
shape = box
|
||||
];
|
||||
|
||||
|
@ -69,7 +69,7 @@ in which case the docker image compiled for the latest master branch is used for
|
|||
|
||||
.. option:: Build regression test
|
||||
|
||||
The OpenFPGA soure is compiled with the following set of compilers.
|
||||
The OpenFPGA source is compiled with the following set of compilers.
|
||||
|
||||
#. gcc-7
|
||||
#. gcc-8
|
||||
|
@ -81,9 +81,9 @@ in which case the docker image compiled for the latest master branch is used for
|
|||
#. clang-8
|
||||
#. clang-10
|
||||
|
||||
The docker images for these build enviroment are available on `github packages <https://github.com/orgs/lnis-uofu/packages>`_.
|
||||
The docker images for these build environment are available on `github packages <https://github.com/orgs/lnis-uofu/packages>`_.
|
||||
|
||||
.. option:: Functional regeression test
|
||||
.. option:: Functional regression test
|
||||
|
||||
OpenFPGA maintains a set of functional tests to validate the different functionality.
|
||||
The test are broadly catagories into ``basic_reg_test``, ``fpga_verilog_reg_test``,
|
||||
|
@ -93,7 +93,7 @@ in which case the docker image compiled for the latest master branch is used for
|
|||
|
||||
How to debug failed regression test
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
In case the ``funtional regression test`` fails,
|
||||
In case the ``functional regression test`` fails,
|
||||
the actions script will collect all ``.log`` files from
|
||||
the task directory and upload as a artifacts on github storage.
|
||||
These artifacts can be downloaded from the github website actions tab, for more reference follow `this <https://docs.github.com/en/actions/managing-workflow-runs/downloading-workflow-artifacts>`_ article.
|
||||
|
@ -113,11 +113,11 @@ Release Docker Images
|
|||
|
||||
CI after cloning repository
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
If you clone the repository the CI setup will still function, except the based images are still pullled from "lnis-uofu" repsitory and the master branch
|
||||
If you clone the repository the CI setup will still function, except the based images are still pulled from "lnis-uofu" repository and the master branch
|
||||
of cloned repo will not push final docker image to any repository .
|
||||
|
||||
**In case you want to host your own copies of OpenFPGA base images** and final release create a githib secret variable with name ``DOCKER_REPO`` and set it to ``true``. This will make ci script to download base images from your own repo pakcages, and upload final realse to the same.
|
||||
**In case you want to host your own copies of OpenFPGA base images** and final release create a github secret variable with name ``DOCKER_REPO`` and set it to ``true``. This will make ci script to download base images from your own repo packages, and upload final release to the same.
|
||||
|
||||
**If you don not want to use docker images based regression test** and like to compile all the bianries for each CI run. You can set ``IGNORE_DOCKER_TEST`` secrete variable to ``true``.
|
||||
**If you don not want to use docker images based regression test** and like to compile all the binaries for each CI run. You can set ``IGNORE_DOCKER_TEST`` secrete variable to ``true``.
|
||||
|
||||
.. note:: Once you add ``DOCKER_REPO`` variable, you need to genrerate base images. To do this trigger mannual workflow ``Build docker CI images``
|
||||
.. note:: Once you add ``DOCKER_REPO`` variable, you need to generate base images. To do this trigger manual workflow ``Build docker CI images``
|
||||
|
|
|
@ -42,6 +42,6 @@ Version updates are made in the following scenario
|
|||
|
||||
- significant improvements on Quality-of-Results (QoR).
|
||||
- significant changes on user interface.
|
||||
- a techical feature is developed and validated by the community, which can impact the complete design flow.
|
||||
- a technical feature is developed and validated by the community, which can impact the complete design flow.
|
||||
|
||||
|
||||
|
|
|
@ -59,12 +59,12 @@ Layout
|
|||
.. option:: through_channel="<bool>"
|
||||
|
||||
Allow routing channels to pass through multi-width and multi-height programable blocks. This is mainly used in heterogeneous FPGAs to increase routability, as illustrated in :numref:`fig_thru_channel`.
|
||||
By default, it is ``off``.
|
||||
By default, it is ``false``.
|
||||
|
||||
.. _fig_thru_channel:
|
||||
|
||||
.. figure:: ./figures/thru_channel.png
|
||||
:scale: 80%
|
||||
:width: 100%
|
||||
:alt: Impact of through channel
|
||||
|
||||
Impact on routing architecture when through channel in multi-width and multi-height programmable blocks: (a) disabled; (b) enabled.
|
||||
|
@ -73,12 +73,26 @@ Layout
|
|||
|
||||
.. warning:: You cannot use ``spread`` pin location for the ``height > 1`` or ``width >1`` tiles when using the tileable routing resource graph!!! Otherwise, it will cause undriven pins in your device!!!
|
||||
|
||||
.. option:: shrink_boundary="<bool>"
|
||||
|
||||
Remove all the routing wires in empty regions. This is mainly used in non-rectangle FPGAs to avoid redundant routing wires in blank area, as illustrated in :numref:`fig_shrink_boundary`.
|
||||
By default, it is ``false``.
|
||||
|
||||
A quick example to show tileable routing is enabled and through channels are disabled:
|
||||
.. _fig_shrink_boundary:
|
||||
|
||||
.. figure:: ./figures/shrink_boundary.png
|
||||
:width: 100%
|
||||
:alt: Impact of shrink boundary
|
||||
|
||||
Impact on routing architecture when shrink-boundary: (a) disabled; (b) enabled.
|
||||
|
||||
.. warning:: Do NOT enable ``shrink_boundary`` if you are not using the tileable routing resource graph generator!
|
||||
|
||||
A quick example to show tileable routing is enabled, other options, e.g., through channels are disabled:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<layout tileable="true" through_channel="false">
|
||||
<layout tileable="true" through_channel="false" shrink_boundary="false">
|
||||
</layout>
|
||||
|
||||
Switch Block
|
||||
|
|
After Width: | Height: | Size: 556 KiB |
|
@ -11,7 +11,7 @@ Plain text (.bit)
|
|||
This file format is designed to be directly loaded to an FPGA fabric.
|
||||
It does not include any comments but only bitstream.
|
||||
|
||||
The information depends on the type of configuration procotol.
|
||||
The information depends on the type of configuration protocol.
|
||||
|
||||
.. option:: vanilla
|
||||
|
||||
|
@ -232,7 +232,7 @@ A quick example:
|
|||
<bit id="0" value="1" path="fpga_top.grid_clb_1__2_.logical_tile_clb_mode_clb__0.mem_fle_9_in_5.mem_out[0]"/>
|
||||
</bit>
|
||||
|
||||
Other information may depend on the type of configuration procotol.
|
||||
Other information may depend on the type of configuration protocol.
|
||||
|
||||
.. option:: memory_bank
|
||||
|
||||
|
|
|
@ -5,6 +5,19 @@ Fabric Key (.xml)
|
|||
|
||||
A fabric key follows an XML format. As shown in the following XML code, the key file includes the organization of configurable blocks in the top-level FPGA fabric.
|
||||
|
||||
Configurable Module
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Fabric key can be applied to various modules. Each module can be a top-level FPGA fabric, or a submodule of the FPGA fabric.
|
||||
|
||||
.. option:: <module name="<string>"/>
|
||||
|
||||
Under each module, a set of keys can be defined. Note that for the top-level FPGA fabric, not only keys but also regions and shift-register banks can be defined. For non-top-level module, only keys are allowed.
|
||||
|
||||
- ``name`` indicates the unique name of a valid module in FPGA fabric. Note that ``fpga_top`` is the considered as the module name of the top-level FPGA fabric.
|
||||
|
||||
.. note:: ``fpga_core`` is not applicable to fabric key.
|
||||
|
||||
Configurable Region
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -23,60 +36,62 @@ The following example shows how to define multiple configuration regions in the
|
|||
.. code-block:: xml
|
||||
|
||||
<fabric_key>
|
||||
<region id="0">
|
||||
<bl_shift_register_banks>
|
||||
<bank id="0" range="bl[0:24]"/>
|
||||
<bank id="1" range="bl[25:40]"/>
|
||||
</bl_shift_register_banks>
|
||||
<wl_shift_register_banks>
|
||||
<bank id="0" range="wl[0:19],wl[40:59]"/>
|
||||
<bank id="1" range="wl[21:39],wl[60:69]"/>
|
||||
</wl_shift_register_banks>
|
||||
<key id="0" name="grid_io_bottom" value="0" alias="grid_io_bottom_1__0_"/>
|
||||
<key id="1" name="grid_io_right" value="0" alias="grid_io_right_2__1_"/>
|
||||
<key id="2" name="sb_1__1_" value="0" alias="sb_1__1_"/>
|
||||
</region>
|
||||
<region id="1">
|
||||
<bl_shift_register_banks>
|
||||
<bank id="0" range="bl[0:24]"/>
|
||||
<bank id="1" range="bl[25:40]"/>
|
||||
</bl_shift_register_banks>
|
||||
<wl_shift_register_banks>
|
||||
<bank id="0" range="wl[0:19]"/>
|
||||
</wl_shift_register_banks>
|
||||
<key id="3" name="cbx_1__1_" value="0" alias="cbx_1__1_"/>
|
||||
<key id="4" name="grid_io_top" value="0" alias="grid_io_top_1__2_"/>
|
||||
<key id="5" name="sb_0__1_" value="0" alias="sb_0__1_"/>
|
||||
</region>
|
||||
<region id="2">
|
||||
<bl_shift_register_banks>
|
||||
<bank id="0" range="bl[0:24]"/>
|
||||
<bank id="1" range="bl[25:40]"/>
|
||||
<bank id="2" range="bl[41:59]"/>
|
||||
</bl_shift_register_banks>
|
||||
<wl_shift_register_banks>
|
||||
<bank id="0" range="wl[0:19]"/>
|
||||
<bank id="1" range="wl[21:39]"/>
|
||||
</wl_shift_register_banks>
|
||||
<key id="6" name="sb_0__0_" value="0" alias="sb_0__0_"/>
|
||||
<key id="7" name="cby_0__1_" value="0" alias="cby_0__1_"/>
|
||||
<key id="8" name="grid_io_left" value="0" alias="grid_io_left_0__1_"/>
|
||||
</region>
|
||||
<region id="3">
|
||||
<bl_shift_register_banks>
|
||||
<bank id="0" range="bl[0:24]"/>
|
||||
<bank id="1" range="bl[25:40]"/>
|
||||
</bl_shift_register_banks>
|
||||
<wl_shift_register_banks>
|
||||
<bank id="0" range="wl[0:19]"/>
|
||||
<bank id="1" range="wl[21:39]"/>
|
||||
<bank id="2" range="wl[40:49]"/>
|
||||
</wl_shift_register_banks>
|
||||
<key id="9" name="sb_1__0_" value="0" alias="sb_1__0_"/>
|
||||
<key id="10" name="cbx_1__0_" value="0" alias="cbx_1__0_"/>
|
||||
<key id="11" name="cby_1__1_" value="0" alias="cby_1__1_"/>
|
||||
<key id="12" name="grid_clb" value="0" alias="grid_clb_1__1_"/>
|
||||
</region>
|
||||
<module name="fpga_top">
|
||||
<region id="0">
|
||||
<bl_shift_register_banks>
|
||||
<bank id="0" range="bl[0:24]"/>
|
||||
<bank id="1" range="bl[25:40]"/>
|
||||
</bl_shift_register_banks>
|
||||
<wl_shift_register_banks>
|
||||
<bank id="0" range="wl[0:19],wl[40:59]"/>
|
||||
<bank id="1" range="wl[21:39],wl[60:69]"/>
|
||||
</wl_shift_register_banks>
|
||||
<key id="0" name="grid_io_bottom" value="0" alias="grid_io_bottom_1__0_"/>
|
||||
<key id="1" name="grid_io_right" value="0" alias="grid_io_right_2__1_"/>
|
||||
<key id="2" name="sb_1__1_" value="0" alias="sb_1__1_"/>
|
||||
</region>
|
||||
<region id="1">
|
||||
<bl_shift_register_banks>
|
||||
<bank id="0" range="bl[0:24]"/>
|
||||
<bank id="1" range="bl[25:40]"/>
|
||||
</bl_shift_register_banks>
|
||||
<wl_shift_register_banks>
|
||||
<bank id="0" range="wl[0:19]"/>
|
||||
</wl_shift_register_banks>
|
||||
<key id="3" name="cbx_1__1_" value="0" alias="cbx_1__1_"/>
|
||||
<key id="4" name="grid_io_top" value="0" alias="grid_io_top_1__2_"/>
|
||||
<key id="5" name="sb_0__1_" value="0" alias="sb_0__1_"/>
|
||||
</region>
|
||||
<region id="2">
|
||||
<bl_shift_register_banks>
|
||||
<bank id="0" range="bl[0:24]"/>
|
||||
<bank id="1" range="bl[25:40]"/>
|
||||
<bank id="2" range="bl[41:59]"/>
|
||||
</bl_shift_register_banks>
|
||||
<wl_shift_register_banks>
|
||||
<bank id="0" range="wl[0:19]"/>
|
||||
<bank id="1" range="wl[21:39]"/>
|
||||
</wl_shift_register_banks>
|
||||
<key id="6" name="sb_0__0_" value="0" alias="sb_0__0_"/>
|
||||
<key id="7" name="cby_0__1_" value="0" alias="cby_0__1_"/>
|
||||
<key id="8" name="grid_io_left" value="0" alias="grid_io_left_0__1_"/>
|
||||
</region>
|
||||
<region id="3">
|
||||
<bl_shift_register_banks>
|
||||
<bank id="0" range="bl[0:24]"/>
|
||||
<bank id="1" range="bl[25:40]"/>
|
||||
</bl_shift_register_banks>
|
||||
<wl_shift_register_banks>
|
||||
<bank id="0" range="wl[0:19]"/>
|
||||
<bank id="1" range="wl[21:39]"/>
|
||||
<bank id="2" range="wl[40:49]"/>
|
||||
</wl_shift_register_banks>
|
||||
<key id="9" name="sb_1__0_" value="0" alias="sb_1__0_"/>
|
||||
<key id="10" name="cbx_1__0_" value="0" alias="cbx_1__0_"/>
|
||||
<key id="11" name="cby_1__1_" value="0" alias="cby_1__1_"/>
|
||||
<key id="12" name="grid_clb" value="0" alias="grid_clb_1__1_"/>
|
||||
</region>
|
||||
</module>
|
||||
</fabric_key>
|
||||
|
||||
|
||||
|
@ -111,41 +126,43 @@ This key contains only ``alias`` which is easy to craft.
|
|||
.. code-block:: xml
|
||||
|
||||
<fabric_key>
|
||||
<region id="0">
|
||||
<key id="0" alias="sb_2__2_"/>
|
||||
<key id="1" alias="grid_clb_2_2"/>
|
||||
<key id="2" alias="sb_0__1_"/>
|
||||
<key id="3" alias="cby_0__1_"/>
|
||||
<key id="4" alias="grid_clb_2_1"/>
|
||||
<key id="5" alias="grid_io_left_0_1"/>
|
||||
<key id="6" alias="sb_1__0_"/>
|
||||
<key id="7" alias="sb_1__1_"/>
|
||||
<key id="8" alias="cbx_2__1_"/>
|
||||
<key id="9" alias="cby_1__2_"/>
|
||||
<key id="10" alias="grid_io_right_3_2"/>
|
||||
<key id="11" alias="cbx_2__0_"/>
|
||||
<key id="12" alias="cby_1__1_"/>
|
||||
<key id="13" alias="grid_io_right_3_1"/>
|
||||
<key id="14" alias="grid_io_bottom_1_0"/>
|
||||
<key id="15" alias="cby_2__1_"/>
|
||||
<key id="16" alias="sb_2__1_"/>
|
||||
<key id="17" alias="cbx_1__0_"/>
|
||||
<key id="18" alias="grid_clb_1_2"/>
|
||||
<key id="19" alias="cbx_1__2_"/>
|
||||
<key id="20" alias="cbx_2__2_"/>
|
||||
<key id="21" alias="sb_2__0_"/>
|
||||
<key id="22" alias="sb_1__2_"/>
|
||||
<key id="23" alias="cby_0__2_"/>
|
||||
<key id="24" alias="sb_0__0_"/>
|
||||
<key id="25" alias="grid_clb_1_1"/>
|
||||
<key id="26" alias="cby_2__2_"/>
|
||||
<key id="27" alias="grid_io_top_2_3"/>
|
||||
<key id="28" alias="sb_0__2_"/>
|
||||
<key id="29" alias="grid_io_bottom_2_0"/>
|
||||
<key id="30" alias="cbx_1__1_"/>
|
||||
<key id="31" alias="grid_io_top_1_3"/>
|
||||
<key id="32" alias="grid_io_left_0_2"/>
|
||||
</region>
|
||||
<module name="fpga_top">
|
||||
<region id="0">
|
||||
<key id="0" alias="sb_2__2_"/>
|
||||
<key id="1" alias="grid_clb_2_2"/>
|
||||
<key id="2" alias="sb_0__1_"/>
|
||||
<key id="3" alias="cby_0__1_"/>
|
||||
<key id="4" alias="grid_clb_2_1"/>
|
||||
<key id="5" alias="grid_io_left_0_1"/>
|
||||
<key id="6" alias="sb_1__0_"/>
|
||||
<key id="7" alias="sb_1__1_"/>
|
||||
<key id="8" alias="cbx_2__1_"/>
|
||||
<key id="9" alias="cby_1__2_"/>
|
||||
<key id="10" alias="grid_io_right_3_2"/>
|
||||
<key id="11" alias="cbx_2__0_"/>
|
||||
<key id="12" alias="cby_1__1_"/>
|
||||
<key id="13" alias="grid_io_right_3_1"/>
|
||||
<key id="14" alias="grid_io_bottom_1_0"/>
|
||||
<key id="15" alias="cby_2__1_"/>
|
||||
<key id="16" alias="sb_2__1_"/>
|
||||
<key id="17" alias="cbx_1__0_"/>
|
||||
<key id="18" alias="grid_clb_1_2"/>
|
||||
<key id="19" alias="cbx_1__2_"/>
|
||||
<key id="20" alias="cbx_2__2_"/>
|
||||
<key id="21" alias="sb_2__0_"/>
|
||||
<key id="22" alias="sb_1__2_"/>
|
||||
<key id="23" alias="cby_0__2_"/>
|
||||
<key id="24" alias="sb_0__0_"/>
|
||||
<key id="25" alias="grid_clb_1_1"/>
|
||||
<key id="26" alias="cby_2__2_"/>
|
||||
<key id="27" alias="grid_io_top_2_3"/>
|
||||
<key id="28" alias="sb_0__2_"/>
|
||||
<key id="29" alias="grid_io_bottom_2_0"/>
|
||||
<key id="30" alias="cbx_1__1_"/>
|
||||
<key id="31" alias="grid_io_top_1_3"/>
|
||||
<key id="32" alias="grid_io_left_0_2"/>
|
||||
</region>
|
||||
</module>
|
||||
</fabric_key>
|
||||
|
||||
The following shows another example of a fabric key generate by OpenFPGA for a 2 :math:`\times` 2 FPGA.
|
||||
|
@ -154,41 +171,43 @@ This key contains only ``name`` and ``value`` which is fast to parse.
|
|||
.. code-block:: xml
|
||||
|
||||
<fabric_key>
|
||||
<region id="0">
|
||||
<key id="0" name="sb_2__2_" value="0"/>
|
||||
<key id="1" name="grid_clb" value="3"/>
|
||||
<key id="2" name="sb_0__1_" value="0"/>
|
||||
<key id="3" name="cby_0__1_" value="0"/>
|
||||
<key id="4" name="grid_clb" value="2"/>
|
||||
<key id="5" name="grid_io_left" value="0"/>
|
||||
<key id="6" name="sb_1__0_" value="0"/>
|
||||
<key id="7" name="sb_1__1_" value="0"/>
|
||||
<key id="8" name="cbx_1__1_" value="1"/>
|
||||
<key id="9" name="cby_1__1_" value="1"/>
|
||||
<key id="10" name="grid_io_right" value="1"/>
|
||||
<key id="11" name="cbx_1__0_" value="1"/>
|
||||
<key id="12" name="cby_1__1_" value="0"/>
|
||||
<key id="13" name="grid_io_right" value="0"/>
|
||||
<key id="14" name="grid_io_bottom" value="0"/>
|
||||
<key id="15" name="cby_2__1_" value="0"/>
|
||||
<key id="16" name="sb_2__1_" value="0"/>
|
||||
<key id="17" name="cbx_1__0_" value="0"/>
|
||||
<key id="18" name="grid_clb" value="1"/>
|
||||
<key id="19" name="cbx_1__2_" value="0"/>
|
||||
<key id="20" name="cbx_1__2_" value="1"/>
|
||||
<key id="21" name="sb_2__0_" value="0"/>
|
||||
<key id="22" name="sb_1__2_" value="0"/>
|
||||
<key id="23" name="cby_0__1_" value="1"/>
|
||||
<key id="24" name="sb_0__0_" value="0"/>
|
||||
<key id="25" name="grid_clb" value="0"/>
|
||||
<key id="26" name="cby_2__1_" value="1"/>
|
||||
<key id="27" name="grid_io_top" value="1"/>
|
||||
<key id="28" name="sb_0__2_" value="0"/>
|
||||
<key id="29" name="grid_io_bottom" value="1"/>
|
||||
<key id="30" name="cbx_1__1_" value="0"/>
|
||||
<key id="31" name="grid_io_top" value="0"/>
|
||||
<key id="32" name="grid_io_left" value="1"/>
|
||||
</region>
|
||||
<module name="fpga_top">
|
||||
<region id="0">
|
||||
<key id="0" name="sb_2__2_" value="0"/>
|
||||
<key id="1" name="grid_clb" value="3"/>
|
||||
<key id="2" name="sb_0__1_" value="0"/>
|
||||
<key id="3" name="cby_0__1_" value="0"/>
|
||||
<key id="4" name="grid_clb" value="2"/>
|
||||
<key id="5" name="grid_io_left" value="0"/>
|
||||
<key id="6" name="sb_1__0_" value="0"/>
|
||||
<key id="7" name="sb_1__1_" value="0"/>
|
||||
<key id="8" name="cbx_1__1_" value="1"/>
|
||||
<key id="9" name="cby_1__1_" value="1"/>
|
||||
<key id="10" name="grid_io_right" value="1"/>
|
||||
<key id="11" name="cbx_1__0_" value="1"/>
|
||||
<key id="12" name="cby_1__1_" value="0"/>
|
||||
<key id="13" name="grid_io_right" value="0"/>
|
||||
<key id="14" name="grid_io_bottom" value="0"/>
|
||||
<key id="15" name="cby_2__1_" value="0"/>
|
||||
<key id="16" name="sb_2__1_" value="0"/>
|
||||
<key id="17" name="cbx_1__0_" value="0"/>
|
||||
<key id="18" name="grid_clb" value="1"/>
|
||||
<key id="19" name="cbx_1__2_" value="0"/>
|
||||
<key id="20" name="cbx_1__2_" value="1"/>
|
||||
<key id="21" name="sb_2__0_" value="0"/>
|
||||
<key id="22" name="sb_1__2_" value="0"/>
|
||||
<key id="23" name="cby_0__1_" value="1"/>
|
||||
<key id="24" name="sb_0__0_" value="0"/>
|
||||
<key id="25" name="grid_clb" value="0"/>
|
||||
<key id="26" name="cby_2__1_" value="1"/>
|
||||
<key id="27" name="grid_io_top" value="1"/>
|
||||
<key id="28" name="sb_0__2_" value="0"/>
|
||||
<key id="29" name="grid_io_bottom" value="1"/>
|
||||
<key id="30" name="cbx_1__1_" value="0"/>
|
||||
<key id="31" name="grid_io_top" value="0"/>
|
||||
<key id="32" name="grid_io_left" value="1"/>
|
||||
</region>
|
||||
</module>
|
||||
</fabric_key>
|
||||
|
||||
The following shows another example of a fabric key generate by OpenFPGA for a 2 :math:`\times` 2 FPGA using memory bank.
|
||||
|
@ -197,41 +216,43 @@ This key contains only ``name``, ``value``, ``row`` and ``column``.
|
|||
.. code-block:: xml
|
||||
|
||||
<fabric_key>
|
||||
<region id="0">
|
||||
<key id="0" name="sb_2__2_" value="0" alias="sb_2__2_" column="5" row="5"/>
|
||||
<key id="1" name="grid_clb" value="3" alias="grid_clb_2__2_" column="4" row="4"/>
|
||||
<key id="2" name="sb_0__1_" value="0" alias="sb_0__1_" column="1" row="3"/>
|
||||
<key id="3" name="cby_0__1_" value="0" alias="cby_0__1_" column="1" row="2"/>
|
||||
<key id="4" name="grid_clb" value="2" alias="grid_clb_2__1_" column="4" row="2"/>
|
||||
<key id="5" name="grid_io_left" value="0" alias="grid_io_left_0__1_" column="0" row="2"/>
|
||||
<key id="6" name="sb_1__0_" value="0" alias="sb_1__0_" column="3" row="1"/>
|
||||
<key id="7" name="sb_1__1_" value="0" alias="sb_1__1_" column="3" row="3"/>
|
||||
<key id="8" name="cbx_1__1_" value="1" alias="cbx_2__1_" column="4" row="3"/>
|
||||
<key id="9" name="cby_1__1_" value="1" alias="cby_1__2_" column="3" row="4"/>
|
||||
<key id="10" name="grid_io_right" value="0" alias="grid_io_right_3__2_" column="6" row="4"/>
|
||||
<key id="11" name="cbx_1__0_" value="1" alias="cbx_2__0_" column="4" row="1"/>
|
||||
<key id="12" name="cby_1__1_" value="0" alias="cby_1__1_" column="3" row="2"/>
|
||||
<key id="13" name="grid_io_right" value="1" alias="grid_io_right_3__1_" column="6" row="2"/>
|
||||
<key id="14" name="grid_io_bottom" value="1" alias="grid_io_bottom_1__0_" column="2" row="0"/>
|
||||
<key id="15" name="cby_2__1_" value="0" alias="cby_2__1_" column="5" row="2"/>
|
||||
<key id="16" name="sb_2__1_" value="0" alias="sb_2__1_" column="5" row="3"/>
|
||||
<key id="17" name="cbx_1__0_" value="0" alias="cbx_1__0_" column="2" row="1"/>
|
||||
<key id="18" name="grid_clb" value="1" alias="grid_clb_1__2_" column="2" row="4"/>
|
||||
<key id="19" name="cbx_1__2_" value="0" alias="cbx_1__2_" column="2" row="5"/>
|
||||
<key id="20" name="cbx_1__2_" value="1" alias="cbx_2__2_" column="4" row="5"/>
|
||||
<key id="21" name="sb_2__0_" value="0" alias="sb_2__0_" column="5" row="1"/>
|
||||
<key id="22" name="sb_1__2_" value="0" alias="sb_1__2_" column="3" row="5"/>
|
||||
<key id="23" name="cby_0__1_" value="1" alias="cby_0__2_" column="1" row="4"/>
|
||||
<key id="24" name="sb_0__0_" value="0" alias="sb_0__0_" column="1" row="1"/>
|
||||
<key id="25" name="grid_clb" value="0" alias="grid_clb_1__1_" column="2" row="2"/>
|
||||
<key id="26" name="cby_2__1_" value="1" alias="cby_2__2_" column="5" row="4"/>
|
||||
<key id="27" name="grid_io_top" value="1" alias="grid_io_top_2__3_" column="4" row="6"/>
|
||||
<key id="28" name="sb_0__2_" value="0" alias="sb_0__2_" column="1" row="5"/>
|
||||
<key id="29" name="grid_io_bottom" value="0" alias="grid_io_bottom_2__0_" column="4" row="0"/>
|
||||
<key id="30" name="cbx_1__1_" value="0" alias="cbx_1__1_" column="2" row="3"/>
|
||||
<key id="31" name="grid_io_top" value="0" alias="grid_io_top_1__3_" column="2" row="6"/>
|
||||
<key id="32" name="grid_io_left" value="1" alias="grid_io_left_0__2_" column="0" row="4"/>
|
||||
</region>
|
||||
<module name="fpga_top">
|
||||
<region id="0">
|
||||
<key id="0" name="sb_2__2_" value="0" alias="sb_2__2_" column="5" row="5"/>
|
||||
<key id="1" name="grid_clb" value="3" alias="grid_clb_2__2_" column="4" row="4"/>
|
||||
<key id="2" name="sb_0__1_" value="0" alias="sb_0__1_" column="1" row="3"/>
|
||||
<key id="3" name="cby_0__1_" value="0" alias="cby_0__1_" column="1" row="2"/>
|
||||
<key id="4" name="grid_clb" value="2" alias="grid_clb_2__1_" column="4" row="2"/>
|
||||
<key id="5" name="grid_io_left" value="0" alias="grid_io_left_0__1_" column="0" row="2"/>
|
||||
<key id="6" name="sb_1__0_" value="0" alias="sb_1__0_" column="3" row="1"/>
|
||||
<key id="7" name="sb_1__1_" value="0" alias="sb_1__1_" column="3" row="3"/>
|
||||
<key id="8" name="cbx_1__1_" value="1" alias="cbx_2__1_" column="4" row="3"/>
|
||||
<key id="9" name="cby_1__1_" value="1" alias="cby_1__2_" column="3" row="4"/>
|
||||
<key id="10" name="grid_io_right" value="0" alias="grid_io_right_3__2_" column="6" row="4"/>
|
||||
<key id="11" name="cbx_1__0_" value="1" alias="cbx_2__0_" column="4" row="1"/>
|
||||
<key id="12" name="cby_1__1_" value="0" alias="cby_1__1_" column="3" row="2"/>
|
||||
<key id="13" name="grid_io_right" value="1" alias="grid_io_right_3__1_" column="6" row="2"/>
|
||||
<key id="14" name="grid_io_bottom" value="1" alias="grid_io_bottom_1__0_" column="2" row="0"/>
|
||||
<key id="15" name="cby_2__1_" value="0" alias="cby_2__1_" column="5" row="2"/>
|
||||
<key id="16" name="sb_2__1_" value="0" alias="sb_2__1_" column="5" row="3"/>
|
||||
<key id="17" name="cbx_1__0_" value="0" alias="cbx_1__0_" column="2" row="1"/>
|
||||
<key id="18" name="grid_clb" value="1" alias="grid_clb_1__2_" column="2" row="4"/>
|
||||
<key id="19" name="cbx_1__2_" value="0" alias="cbx_1__2_" column="2" row="5"/>
|
||||
<key id="20" name="cbx_1__2_" value="1" alias="cbx_2__2_" column="4" row="5"/>
|
||||
<key id="21" name="sb_2__0_" value="0" alias="sb_2__0_" column="5" row="1"/>
|
||||
<key id="22" name="sb_1__2_" value="0" alias="sb_1__2_" column="3" row="5"/>
|
||||
<key id="23" name="cby_0__1_" value="1" alias="cby_0__2_" column="1" row="4"/>
|
||||
<key id="24" name="sb_0__0_" value="0" alias="sb_0__0_" column="1" row="1"/>
|
||||
<key id="25" name="grid_clb" value="0" alias="grid_clb_1__1_" column="2" row="2"/>
|
||||
<key id="26" name="cby_2__1_" value="1" alias="cby_2__2_" column="5" row="4"/>
|
||||
<key id="27" name="grid_io_top" value="1" alias="grid_io_top_2__3_" column="4" row="6"/>
|
||||
<key id="28" name="sb_0__2_" value="0" alias="sb_0__2_" column="1" row="5"/>
|
||||
<key id="29" name="grid_io_bottom" value="0" alias="grid_io_bottom_2__0_" column="4" row="0"/>
|
||||
<key id="30" name="cbx_1__1_" value="0" alias="cbx_1__1_" column="2" row="3"/>
|
||||
<key id="31" name="grid_io_top" value="0" alias="grid_io_top_1__3_" column="2" row="6"/>
|
||||
<key id="32" name="grid_io_left" value="1" alias="grid_io_left_0__2_" column="0" row="4"/>
|
||||
</region>
|
||||
</module>
|
||||
</fabric_key>
|
||||
|
||||
BL Shift Register Banks
|
||||
|
|
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 99 KiB |
|
@ -1,7 +1,7 @@
|
|||
File Formats
|
||||
------------
|
||||
|
||||
OpenFPGA widely uses XML format for interchangable files
|
||||
OpenFPGA widely uses XML format for interchangeable files
|
||||
|
||||
|
||||
.. _file_formats:
|
||||
|
@ -35,3 +35,7 @@ OpenFPGA widely uses XML format for interchangable files
|
|||
pin_table_file
|
||||
|
||||
clock_network
|
||||
|
||||
io_naming_file
|
||||
|
||||
tile_config_file
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
.. _file_formats_io_naming_file:
|
||||
|
||||
Fabric I/O Naming (.xml)
|
||||
------------------------
|
||||
|
||||
The XML-based description language is used to describe
|
||||
|
||||
- I/O names for an FPGA fabric when creating a top-level wrapper
|
||||
- I/O connections between the fabric and top-level wrappers
|
||||
|
||||
Using the description language, users can customize the I/O names for each pin/port of an FPGA fabric, including dummy pins (not from an FPGA fabric but required for system integration).
|
||||
|
||||
Under the root node ``<ports>``, naming rules can be defined line-by-line through syntax ``<port>``.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<ports>
|
||||
<port top_name="<string>" core_name="<string>" is_dummy="<bool>" direction="<string>"/>
|
||||
</ports>
|
||||
|
||||
.. note:: If you do not need to rename a port of an FPGA fabric, there is no need to define it explicitly in the naming rules. OpenFPGA can infer it.
|
||||
|
||||
Please be aware of the following restrictions:
|
||||
|
||||
.. note:: Please note that when naming rules should be applied to a port at its full size. For example, given a port of ``in[0:31]``, naming rules should cover all the 32 bits.
|
||||
|
||||
.. note:: Please note that we currently only supports port splitting at the top-level wrapper. For example, there is a port ``a[0:9]`` from the FPGA fabric, it can be split to ``a0[0:4]`` and ``a1[0:4]`` at the top-level wrapper.
|
||||
|
||||
.. warning:: Port grouping is **NOT** supported yet. For example, there are ports ``b[0:7]`` and ``c[0:7]`` from the FPGA fabric, it can **NOT** be grouped to a port ``bnc[0:15]`` at the top-level wrapper.
|
||||
|
||||
Syntax
|
||||
``````
|
||||
|
||||
Detailed syntax are presented as follows.
|
||||
|
||||
.. option:: top_name="<string>"
|
||||
|
||||
Define the port name and width which will appear in the top-level wrapper. For example,
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
top_name="a[0:2]"
|
||||
|
||||
.. option:: core_name="<string>"
|
||||
|
||||
Define the port name and width which exists in the current FPGA fabric. For example,
|
||||
|
||||
.. note:: You can find the available ports in the current top-level module of FPGA netlists. See details in :ref:`fabric_netlists`.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
core_name="gfpga_pad_GPIO_PAD[0:2]"
|
||||
|
||||
.. option:: is_dummy="<bool>"
|
||||
|
||||
Define if the port is a dummy one in the top-level wrapper, which does not connect to any pin/port of the current FPGA fabric. For example,
|
||||
|
||||
.. note:: When a dummy port is defined. ``core_name`` is not required.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
is_dummy="true"
|
||||
|
||||
.. option:: direction="<string>"
|
||||
|
||||
Direction can be ``input`` | ``output`` | ``inout``. Only applicable to dummy ports. For example,
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
direction="input"
|
||||
|
||||
Example
|
||||
```````
|
||||
|
||||
:numref:`fig_fpga_core_wrapper` shows an example of a top-level wrapper with naming rules, which is built on top of an existing FPGA core fabric.
|
||||
There is a dummy input port at the top-level wrapper.
|
||||
|
||||
.. _fig_fpga_core_wrapper:
|
||||
|
||||
.. figure:: figures/fpga_core_wrapper.png
|
||||
:width: 100%
|
||||
:alt: Illustration of a top-level wrapper on an existing FPGA core fabric
|
||||
|
||||
Example of a top-level wrapper: how it interfaces between SoC and an existing FPGA core fabric
|
||||
|
||||
The I/O naming in the :numref:`fig_fpga_core_wrapper` can be described in the following XML:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<ports>
|
||||
<port top_name="pclk0[0:3]" core_name="prog_clk[0:3]"/>
|
||||
<port top_name="pclk1[0:3]" core_name="prog_clk[4:7]"/>
|
||||
<port top_name="right_io[0:23]" core_name="pad[0:23]"/>
|
||||
<port top_name="bottom_io[0:7]" core_name="pad[24:31]"/>
|
||||
<port top_name="pvt_sense[0:0]" is_dummy="true" direction="input"/>
|
||||
</ports>
|
||||
|
||||
Note that since port ``reset[0:0]`` require no name changes, it is not required to be defined in the XML.
|
|
@ -0,0 +1,39 @@
|
|||
.. _file_formats_tile_config_file:
|
||||
|
||||
Tile Organization (.xml)
|
||||
------------------------
|
||||
|
||||
The XML-based description language is used to describe how each tile is composed.
|
||||
For example, what programmable blocks, connection blocks and switch blocks should be included.
|
||||
|
||||
Using the description language, users can customize the tiles of an FPGA fabric, as detailed as each component in each tile.
|
||||
|
||||
Under the root node ``<tiles>``, the detailes of tile organization can be described.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<tiles style="<string>"/>
|
||||
</tiles>
|
||||
|
||||
Syntax
|
||||
``````
|
||||
|
||||
Detailed syntax are presented as follows.
|
||||
|
||||
.. option:: style="<string>"
|
||||
|
||||
Specify the style of tile organization. Can be [``top_left`` | ``top_right`` | ``bottom_left`` | ``bottom_right`` | ``custom``]
|
||||
|
||||
.. warning:: Currently, only ``top_left`` is 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.
|
||||
|
||||
.. _fig_tile_style_top_left:
|
||||
|
||||
.. figure:: ./figures/tile_style_top_left.png
|
||||
:width: 100%
|
||||
:alt: An example of top-left style of tile
|
||||
|
||||
An example of top-left style of a tile in FPGA fabric
|
||||
|
||||
|
|
@ -23,16 +23,16 @@ FPGA-SPICE Supported Options::
|
|||
--fpga_spice_testbench_load_extraction_off
|
||||
--fpga_spice_sim_mt_num <int>
|
||||
|
||||
.. note:: FPGA-SPICE requires the input of activity estimation results (\*.act file) from ACE2.
|
||||
Remember to use the option --activity_file <act_file> to read the activity file.
|
||||
.. note:: FPGA-SPICE requires the input of activity estimation results (\*.act file) from ACE2.
|
||||
Remember to use the option --activity_file <act_file> to read the activity file.
|
||||
|
||||
.. note:: To dump full-chip-level testbenches, the option –-fpga_spice_print_top_testbench should be enabled.
|
||||
|
||||
.. note:: To dump grid-level testbenches, the options -- fpga_spice_print_grid_testbench, -- fpga_spice_print_cb_testbench and -- fpga_spice_print_sb_testbench should be enabled.
|
||||
|
||||
.. note:: To dump component-level testbenches, the options –fpga_spice_print_lut_testbench, --fpga_spice_print_hardlogic_testbench, --fpga_spice_print_pb_mux_testbench, --fpga_spice_print_cb_mux_testbench and --fpga_spice_print_sb_mux_testbench should be enabled.
|
||||
.. note:: To dump full-chip-level testbenches, the option –-fpga_spice_print_top_testbench should be enabled.
|
||||
|
||||
.. csv-table:: Commmand-line Options of FPGA-SPICE
|
||||
.. note:: To dump grid-level testbenches, the options -- fpga_spice_print_grid_testbench, -- fpga_spice_print_cb_testbench and -- fpga_spice_print_sb_testbench should be enabled.
|
||||
|
||||
.. note:: To dump component-level testbenches, the options –fpga_spice_print_lut_testbench, --fpga_spice_print_hardlogic_testbench, --fpga_spice_print_pb_mux_testbench, --fpga_spice_print_cb_mux_testbench and --fpga_spice_print_sb_mux_testbench should be enabled.
|
||||
|
||||
.. csv-table:: Command-line Options of FPGA-SPICE
|
||||
:header: "Command Options", "Description"
|
||||
:widths: 15, 20
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ Fabric Netlists
|
|||
|
||||
In this part, we will introduce the hierarchy, dependency and functionality of each Verilog netlist, which are generated to model the FPGA fabric.
|
||||
|
||||
.. note:: These netlists are automatically generated by the OpenFPGA command ``write_fabric_verilog``. See :ref:`openfpga_verilog_commands` for its detailed usage.
|
||||
.. note:: These netlists are automatically generated by the OpenFPGA command :ref:`cmd_write_fabric_verilog`. See :ref:`openfpga_verilog_commands` for its detailed usage.
|
||||
|
||||
All the generated Verilog netlists are located in the directory as you specify in the OpenFPGA command ``write_fabric_verilog``.
|
||||
All the generated Verilog netlists are located in the directory as you specify in the OpenFPGA command :ref:`cmd_write_fabric_verilog`.
|
||||
Inside the directory, the Verilog netlists are organized as illustrated in :numref:`fig_fabric_netlist_hierarchy`.
|
||||
|
||||
.. _fig_fabric_netlist_hierarchy:
|
||||
|
@ -24,6 +24,8 @@ Inside the directory, the Verilog netlists are organized as illustrated in :numr
|
|||
|
||||
An illustrative FPGA fabric modelled by the Verilog netlists
|
||||
|
||||
.. _fabric_netlists_top_level_netlists:
|
||||
|
||||
Top-level Netlists
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -48,6 +50,21 @@ Top-level Netlists
|
|||
|
||||
.. note:: We strongly recommend users to turn on this flag as it can help simulators to converge quickly.
|
||||
|
||||
.. _fabric_netlists_tiles:
|
||||
|
||||
Tiles
|
||||
~~~~~
|
||||
|
||||
This sub-directory contains all the tile-level modules. Only seen when the ``--group_tile`` option is enabled when calling command :ref:`cmd_build_fabric`.
|
||||
Each tile groups a number of programmable blocks (:ref:`fabric_netlists_logic_blocks`) and routing blocks (:ref:`fabric_netlists_routing_blocks`), as depicted in :numref:`fig_generic_fabric`.
|
||||
Tiles are instanciated under the top-level module (:ref:`fabric_netlists_top_level_netlists`).
|
||||
|
||||
.. option:: tile_<x>__<y>_.v
|
||||
|
||||
For each unique tile, a Verilog netlist will be generated. The ``<x>`` and ``<y>`` denote the coordinate of the tile in the FPGA fabric.
|
||||
|
||||
.. _fabric_netlists_logic_blocks:
|
||||
|
||||
Logic Blocks
|
||||
~~~~~~~~~~~~
|
||||
This sub-directory contains all the Verilog modules modeling configurable logic blocks, heterogeneous blocks as well as I/O blocks.
|
||||
|
@ -63,6 +80,8 @@ Take the example in :numref:`fig_generic_fabric`, the modules are CLBs, DSP bloc
|
|||
|
||||
For each root ``pb_type`` defined in the ``<complexblock>`` of VPR architecture description, a Verilog netlist will be generated to model its internal structure.
|
||||
|
||||
.. _fabric_netlists_routing_blocks:
|
||||
|
||||
Routing Blocks
|
||||
~~~~~~~~~~~~~~
|
||||
This sub-directory contains all the Verilog modules modeling Switch Blocks (SBs) and Connection Blocks (CBs).
|
||||
|
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 74 KiB |
|
@ -10,3 +10,5 @@ FPGA-Verilog
|
|||
fabric_netlist
|
||||
|
||||
testbench
|
||||
|
||||
mock_fpga_wrapper
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
.. _fpga_verilog_mock_fpga_wrapper:
|
||||
|
||||
Mock FPGA Wrapper
|
||||
-----------------
|
||||
|
||||
OpenFPGA can generates HDL netlists that model a complete eFPGA fabric (see details in :ref:`fabric_netlists`).
|
||||
Through bitstream forcing, users can verify the eFPGAs that are mapped by various applications in the context of SoC (see details in :numref:`fig_preconfig_module`).
|
||||
However, the complete eFPGA fabric is very costly in design verification runtime.
|
||||
To reduce runtime, a mock eFPGA wrapper is required to bridge the application HDL and other components in the SoC.
|
||||
As illustrated in :numref:`fig_mock_fpga_wrapper`, a 3-bit counter application is mapped to an FPGA, while a mock wrapper is interfacing the signals between the counter module and the SoC.
|
||||
The mock wrapper consists of the same ports as the FPGA fabric, which is generated by the OpenFPGA command ``write_fabric_verilog``. See :ref:`openfpga_verilog_commands` for its detailed usage.
|
||||
The only difference lies in that the mock wrapper contains an instance of the application HDL design which is implemented on the FPGA, while the FPGA fabric contains a complete structure of programmable resources.
|
||||
|
||||
.. note:: The mock wrapper is useful for connectivity checks on FPGA datapaths. It does not cover any configuration protocols (see details in :ref:`config_protocol`)
|
||||
|
||||
|
||||
.. _fig_mock_fpga_wrapper:
|
||||
|
||||
.. figure:: figures/mock_fpga_wrapper.png
|
||||
:width: 100%
|
||||
:alt: Illustraion of a mock FPGA wrapper
|
||||
|
||||
Principles of a mock FPGA wrapper: ease SoC-level design verification
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ To enable self-testing, the FPGA and user's RTL design (simulate using an HDL si
|
|||
.. _fig_verilog_testbench_organization:
|
||||
|
||||
.. figure:: figures/full_testbench_block_diagram.svg
|
||||
:scale: 50%
|
||||
:width: 100%
|
||||
:alt: Verilog testbench principles
|
||||
|
||||
Principles of Verilog testbenches: (1) using common input stimuli; (2) applying bitstream; (3) checking output vectors.
|
||||
|
@ -30,14 +30,14 @@ To enable self-testing, the FPGA and user's RTL design (simulate using an HDL si
|
|||
.. _fig_verilog_full_testbench_waveform:
|
||||
|
||||
.. figure:: figures/full_testbench_waveform.svg
|
||||
:scale: 50%
|
||||
:width: 100%
|
||||
:alt: Full testbench waveform
|
||||
|
||||
Illustration on the waveforms in full testbench
|
||||
|
||||
Full Testbench
|
||||
~~~~~~~~~~~~~~
|
||||
Full testbench aims at simulating an entire FPGA operating period, consisting of two phases:
|
||||
Full testbench aims at simulating an entire FPGA operating period, consisting of two phases:
|
||||
|
||||
- the **Configuration Phase**, where the synthesized design bitstream is loaded to the programmable fabric, as highlighted by the green rectangle of :numref:`fig_verilog_full_testbench_waveform`;
|
||||
|
||||
|
@ -48,7 +48,7 @@ Formal-oriented Testbench
|
|||
The formal-oriented testbench aims to test a programmed FPGA is instantiated with the user's bitstream.
|
||||
The module of the programmed FPGA is encapsulated with the same port mapping as the user's RTL design and thus can be fed to a formal tool for a 100% coverage formal verification. Compared to the full testbench, this skips the time-consuming configuration phase, reducing the simulation time, potentially also significantly accelerating the functional verification, especially for large FPGAs.
|
||||
|
||||
.. warning:: Formal-oriented testbenches do not validate the configuration protocol of FPGAs. It is used to validate FPGA with a wide range of benchmarks.
|
||||
.. warning:: Formal-oriented testbenches do not validate the configuration protocol of FPGAs. It is used to validate FPGA with a wide range of benchmarks.
|
||||
|
||||
General Usage
|
||||
~~~~~~~~~~~~~
|
||||
|
@ -59,11 +59,11 @@ Inside the directory, the Verilog testbenches are organized as illustrated in :n
|
|||
.. _fig_verilog_testbench_hierarchy:
|
||||
|
||||
.. figure:: ./figures/verilog_testbench_hierarchy.svg
|
||||
:scale: 100%
|
||||
:width: 100%
|
||||
|
||||
Hierarchy of Verilog testbenches for a FPGA fabric implemented with an application
|
||||
|
||||
.. note:: ``<bench_name>`` is the module name of users' RTL design.
|
||||
.. note:: ``<bench_name>`` is the module name of users' RTL design.
|
||||
|
||||
.. option:: <bench_name>_include_netlist.v
|
||||
|
||||
|
@ -84,14 +84,13 @@ Inside the directory, the Verilog testbenches are organized as illustrated in :n
|
|||
.. option:: <bench_name>_top_formal_verification.v
|
||||
|
||||
This netlist includes a Verilog module of a pre-configured FPGA fabric, which is a wrapper on top of the ``fpga_top.v`` netlist.
|
||||
The wrapper module has the same port map as the top-level module of user's RTL design, which be directly def to formal verification tools to validate FPGA's functional equivalence.
|
||||
The wrapper module has the same port map as the top-level module of user's RTL design, which be directly def to formal verification tools to validate FPGA's functional equivalence.
|
||||
:numref:`fig_preconfig_module` illustrates the organization of a pre-configured module, which consists of a FPGA fabric (see :ref:`fabric_netlists`) and a hard-coded bitstream.
|
||||
Only used I/Os of FPGA fabric will appear in the port list of the pre-configured module.
|
||||
Only used I/Os of FPGA fabric will appear in the port list of the pre-configured module.
|
||||
|
||||
.. _fig_preconfig_module:
|
||||
|
||||
.. figure:: ./figures/preconfig_module.png
|
||||
:scale: 25%
|
||||
:width: 100%
|
||||
|
||||
Internal structure of a pre-configured FPGA module
|
||||
|
||||
|
|
|
@ -18,3 +18,5 @@
|
|||
|
||||
file_formats/index
|
||||
|
||||
utilities/index
|
||||
|
||||
|
|
After Width: | Height: | Size: 222 KiB |
After Width: | Height: | Size: 75 KiB |
|
@ -3,6 +3,8 @@
|
|||
FPGA-Verilog
|
||||
------------
|
||||
|
||||
.. _cmd_write_fabric_verilog:
|
||||
|
||||
write_fabric_verilog
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -49,6 +51,10 @@ write_full_testbench
|
|||
|
||||
The output directory for all the testbench netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench``
|
||||
|
||||
.. option:: --dut_module <string>
|
||||
|
||||
Specify the name of *Design Under Test* (DUT) module to be considered in the testbench. Can be either ``fpga_top`` or ``fpga_core. By default, it is ``fpga_top``.
|
||||
|
||||
.. option:: --bitstream <string>
|
||||
|
||||
The bitstream file to be loaded to the full testbench, which should be in the same file format that OpenFPGA can outputs (See detailes in :ref:`file_formats_fabric_bitstream_plain_text`). For example, ``--bitstream and2.bit``
|
||||
|
@ -120,6 +126,10 @@ write_preconfigured_fabric_wrapper
|
|||
|
||||
Specify the fabric Verilog file if they are not in the same directory as the testbenches to be generated. If not specified, OpenFPGA will assume that the fabric netlists are the in the same directory as testbenches and assign default names. For example, ``--file /temp/fabric/fabric_netlists.v``
|
||||
|
||||
.. option:: --dut_module <string>
|
||||
|
||||
Specify the name of *Design Under Test* (DUT) module to be considered in the testbench. Can be either ``fpga_top`` or ``fpga_core. By default, it is ``fpga_top``.
|
||||
|
||||
.. option:: --pin_constraints_file <string> or -pcf <string>
|
||||
|
||||
Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml``
|
||||
|
@ -166,6 +176,49 @@ __ iverilog_website_
|
|||
|
||||
Show verbose log
|
||||
|
||||
write_mock_fpga_wrapper
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Write the Verilog wrapper which mockes a mapped FPGA fabric. See details in :ref:`fpga_verilog_mock_fpga_wrapper`.
|
||||
|
||||
.. option:: --file <string> or -f <string>
|
||||
|
||||
The output directory for the netlists. We suggest the use of same output directory as fabric Verilog netlists. For example, ``--file /temp/testbench``
|
||||
|
||||
.. option:: --top_module <string>
|
||||
|
||||
Specify the name of top-level module to be considered in the wrapper. Can be either ``fpga_top`` or ``fpga_core. By default, it is ``fpga_top``.
|
||||
|
||||
.. option:: --pin_constraints_file <string> or -pcf <string>
|
||||
|
||||
Specify the *Pin Constraints File* (PCF) if you want to custom stimulus in testbenches. For example, ``-pin_constraints_file pin_constraints.xml``
|
||||
Strongly recommend for multi-clock simulations. See detailed file format about :ref:`file_format_pin_constraints_file`.
|
||||
|
||||
.. option:: --bus_group_file <string> or -bgf <string>
|
||||
|
||||
Specify the *Bus Group File* (BGF) if you want to group pins to buses. For example, ``-bgf bus_group.xml``
|
||||
Strongly recommend when input HDL contains bus ports. See detailed file format about :ref:`file_format_bus_group_file`.
|
||||
|
||||
.. option:: --explicit_port_mapping
|
||||
|
||||
Use explicit port mapping when writing the Verilog netlists
|
||||
|
||||
.. option:: --use_relative_path
|
||||
|
||||
Force to use relative path in netlists when including other netlists. By default, this is off, which means that netlists use absolute paths when including other netlists
|
||||
|
||||
.. option:: --default_net_type <string>
|
||||
|
||||
Specify the default net type for the Verilog netlists. Currently, supported types are ``none`` and ``wire``. Default value: ``none``.
|
||||
|
||||
.. option:: --no_time_stamp
|
||||
|
||||
Do not print time stamp in Verilog netlists
|
||||
|
||||
.. option:: --verbose
|
||||
|
||||
Show verbose log
|
||||
|
||||
write_preconfigured_testbench
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -252,7 +252,34 @@ build_fabric
|
|||
.. option:: --compress_routing
|
||||
|
||||
Enable compression on routing architecture modules. Strongly recommend this as it will minimize the number of routing modules to be outputted. It can reduce the netlist size significantly.
|
||||
|
||||
.. option:: --group_tile <string>
|
||||
|
||||
Group fine-grained programmable blocks, connection blocks and switch blocks into tiles. Once enabled, tiles will be added to the top-level module. Otherwise, the top-level module consists of programmable blocks, connection blocks and switch blocks. The tile style can be customized through a file. See details in :ref:`file_formats_tile_config_file`. When enabled, the Verilog netlists will contain additional netlists that model tiles (see details in :ref:`fabric_netlists_tiles`).
|
||||
|
||||
.. warning:: This option does not support ``--duplicate_grid_pin``!
|
||||
|
||||
.. warning:: This option requires ``--compress_routing`` to be enabled!
|
||||
|
||||
.. option:: --group_config_block
|
||||
|
||||
Group configuration memory blocks under each CLB/SB/CB etc. into a centralized configuration memory blocks, as depicted in :numref:`fig_group_config_block_overview`. When disabled, the configuration memory blocks are placed in a distributed way under CLB/SB/CB etc. For example, each programming resource, e.g., LUT, has a dedicated configuration memory block, being placed in the same module. When enabled, as illustrated in :numref:`fig_group_config_block_hierarchy`, the physical memory block locates under a CLB, driving a number of logical memory blocks which are close to the programmable resources. The logical memory blocks contain only pass-through wires which can be optimized out during physical design phase.
|
||||
|
||||
.. _fig_group_config_block_overview:
|
||||
|
||||
.. figure:: ./figures/group_config_block_overview.png
|
||||
:width: 100%
|
||||
|
||||
Impact on grouping configuable blocks: before and after
|
||||
|
||||
.. _fig_group_config_block_hierarchy:
|
||||
|
||||
.. figure:: ./figures/group_config_block_hierarchy.png
|
||||
:width: 100%
|
||||
|
||||
Netlist hierarchy on grouped configuable blocks
|
||||
|
||||
|
||||
.. option:: --duplicate_grid_pin
|
||||
|
||||
Enable pin duplication on grid modules. This is optional unless ultra-dense layout generation is needed
|
||||
|
@ -269,6 +296,8 @@ build_fabric
|
|||
|
||||
Output current fabric key to an XML file. For example, ``--write_fabric_key fpga_2x2.xml`` See details in :ref:`file_formats_fabric_key`.
|
||||
|
||||
.. warning:: This option will be deprecated. Use :ref:`cmd_write_fabric_key` as a replacement.
|
||||
|
||||
.. option:: --frame_view
|
||||
|
||||
Create only frame views of the module graph. When enabled, top-level module will not include any nets. This option is made for save runtime and memory.
|
||||
|
@ -281,6 +310,53 @@ build_fabric
|
|||
|
||||
.. note:: This is a must-run command before launching FPGA-Verilog, FPGA-Bitstream, FPGA-SDC and FPGA-SPICE
|
||||
|
||||
.. _cmd_write_fabric_key:
|
||||
|
||||
write_fabric_key
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Output current fabric key to an XML file. For example, ``write_fabric_key --file fpga_2x2.xml`` See details in :ref:`file_formats_fabric_key`.
|
||||
|
||||
.. note:: This command can output module-level keys while the ``--write_fabric_key`` option in command ``build_fabric`` does NOT support! Strongly recommend to use this command to obtain fabric key.
|
||||
|
||||
.. option:: --file <string> or -f <string>
|
||||
|
||||
Specify the file name. For example, ``--file fabric_key_echo.xml``.
|
||||
|
||||
.. option:: --include_module_keys
|
||||
|
||||
Output module-level keys to the file.
|
||||
|
||||
.. option:: --verbose
|
||||
|
||||
Show verbose log
|
||||
|
||||
.. _cmd_add_fpga_core_to_fabric:
|
||||
|
||||
add_fpga_core_to_fabric
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Add a wrapper module ``fpga_core`` as an intermediate layer to FPGA fabric. After this command, the existing module ``fpga_top`` will remain the top-level module while there is a new module ``fpga_core`` under it. Under fpga_core, there will be the detailed building blocks.
|
||||
|
||||
.. option:: --io_naming <string>
|
||||
|
||||
This is optional. Specify the I/O naming rules when connecting I/Os of ``fpga_core`` module to the top-level module ``fpga_top``. If not defined, the ``fpga_top`` will be the same as ``fpga_core`` w.r.t. ports. See details about the file format of I/O naming rules in :ref:`file_formats_io_naming_file`.
|
||||
|
||||
.. option:: --instance_name <string>
|
||||
|
||||
This is optional. Specify the instance name to be used when instanciate the ``fpga_core`` module under the top-level module ``fpga_top``. If not defined, by default it is ``fpga_core_inst``.
|
||||
|
||||
.. option:: --frame_view
|
||||
|
||||
Create only frame views of the module graph. When enabled, top-level module will not include any nets. This option is made for save runtime and memory.
|
||||
|
||||
.. warning:: Recommend to turn the option on when bitstream generation is the only purpose of the flow. Do not use it when you need generate netlists!
|
||||
|
||||
.. option:: --verbose
|
||||
|
||||
Show verbose log
|
||||
|
||||
|
||||
write_fabric_hierarchy
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
.. _utility_fabric_key_assistant:
|
||||
|
||||
Fabric Key Assistant
|
||||
--------------------
|
||||
|
||||
Fabric Key Assistant is a tool to help users to craft fabric key files (see details in :ref:`file_formats_fabric_key`).
|
||||
Note that crafting a fabric key is not an easy task for engineers, as its complexity grows exponentially with FPGA sizes.
|
||||
This tool is developed to assist engineers when finalizing fabric key files.
|
||||
It can apply sanity checks on hand-crafted fabric key files, helping engineers to correct and debug.
|
||||
|
||||
The tool can be found at ``/build/libs/libfabrickey/test/fabric_key_assistant``
|
||||
|
||||
The tool includes the following options:
|
||||
|
||||
.. option:: --reference <string>
|
||||
|
||||
Specifiy a reference fabric key file, which has been already validated by OpenFPGA. For example, the reference fabric key can be a file which is written by OpenFPGA as a default key. The reference fabric key file is treated as the baseline, on which the input fabric key file will be compared to.
|
||||
|
||||
.. note:: The reference fabric key should contain all the syntax, e.g., ``name``, ``value`` and ``alias``.
|
||||
|
||||
.. option:: --input <string>
|
||||
|
||||
Specify the input fabric key file, which is typically hand-crafted by users. Sanity checks will be applied to the input fabric key file by comparing the reference.
|
||||
|
||||
.. note:: The input fabric key should contain only the syntax ``alias``.
|
||||
|
||||
.. option:: --output <string>
|
||||
|
||||
Specify the output fabric key file, which is an updated version of the input fabric key file. Difference from the input file, the output file contains ``name`` and ``value``, which is added by linking the ``alias`` from input file to reference file. For example, the reference fabric key includes a key:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<key id="1" name="tile_0__0_" value="5" alias="tile_4__2_"/>
|
||||
|
||||
while the input fabric key includes a key:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<key id="23" alias="tile_4__2_"/>
|
||||
|
||||
the resulting output fabric key file includes a key:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<key id="23" name="tile_0__0_" value="5" alias="tile_4__2_"/>
|
||||
|
||||
.. option:: --verbose
|
||||
|
||||
To enable verbose output
|
||||
|
||||
.. option:: --help
|
||||
|
||||
Show help desk
|
|
@ -0,0 +1,13 @@
|
|||
Utilities
|
||||
---------
|
||||
|
||||
OpenFPGA contains a number of utility tools to help users to craft files.
|
||||
|
||||
|
||||
.. _utilities:
|
||||
Utility Tools
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
fabric_key_assistant
|
|
@ -30,7 +30,7 @@ A summary of the architectural features is as follows:
|
|||
- K4N4 Configurable Logic Block (CLB), which consists of four Basic Logic Elements (BLEs) and a fully-connected crossbar. Each BLE contains a 4-input Look-Up Table (LUT), a Flip-Flop (FF) and a 2:1 routing multiplexer
|
||||
- Length-1 routing wires interconnected by Wilton-Style Switch Block (SB)
|
||||
|
||||
The VPR architecture description is designed for EDA needs mainly, which lacks the details physical modelingrequired by OpenFPGA.
|
||||
The VPR architecture description is designed for EDA needs mainly, which lacks the details physical modeling required by OpenFPGA.
|
||||
Here, we show a step-by-step adaption on the architecture template.
|
||||
|
||||
Physical I/O Modeling
|
||||
|
|
|
@ -46,7 +46,7 @@ Once the ``openfpga.sh`` script is sourced, you can run any following commands d
|
|||
|
||||
.. option:: clear-task-run <task_name>
|
||||
|
||||
Clears all run diretories of the given task
|
||||
Clears all run directories of the given task
|
||||
|
||||
.. option:: run-modelsim <task_name>
|
||||
|
||||
|
|
|
@ -8,3 +8,5 @@ add_subdirectory(libfabrickey)
|
|||
add_subdirectory(libfpgabitstream)
|
||||
add_subdirectory(libpcf)
|
||||
add_subdirectory(libbusgroup)
|
||||
add_subdirectory(libionamemap)
|
||||
add_subdirectory(libtileconfig)
|
||||
|
|
|
@ -129,10 +129,13 @@ constexpr std::array<const char*, NUM_CIRCUIT_MODEL_DELAY_TYPES>
|
|||
|
||||
/********************************************************************
|
||||
* Types of configuration protocol
|
||||
* 1. configurable memories are organized and accessed as standalone elements
|
||||
* 2. configurable memories are organized and accessed by a scan-chain
|
||||
* 3. configurable memories are organized and accessed by memory bank
|
||||
* 4. configurable memories are organized and accessed by frames
|
||||
* - configurable memories are organized and accessed as standalone elements
|
||||
* - configurable memories are organized and accessed by a scan-chain
|
||||
* - configurable memories are organized and accessed by quicklogic memory bank
|
||||
* - configurable memories are organized and accessed by memory bank
|
||||
* - configurable memories are organized and accessed by frames
|
||||
* - configurable memories are organized and accessed by feedthrough. Currently,
|
||||
* this is only for internal use only
|
||||
*/
|
||||
enum e_config_protocol_type {
|
||||
CONFIG_MEM_STANDALONE,
|
||||
|
@ -140,11 +143,13 @@ enum e_config_protocol_type {
|
|||
CONFIG_MEM_MEMORY_BANK,
|
||||
CONFIG_MEM_QL_MEMORY_BANK,
|
||||
CONFIG_MEM_FRAME_BASED,
|
||||
CONFIG_MEM_FEEDTHROUGH,
|
||||
NUM_CONFIG_PROTOCOL_TYPES
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, NUM_CONFIG_PROTOCOL_TYPES>
|
||||
CONFIG_PROTOCOL_TYPE_STRING = {{"standalone", "scan_chain", "memory_bank",
|
||||
"ql_memory_bank", "frame_based"}};
|
||||
"ql_memory_bank", "frame_based",
|
||||
"feedthrough"}};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.9)
|
|||
project("libfabrickey")
|
||||
|
||||
file(GLOB_RECURSE EXEC_SOURCES test/*.cpp)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*/*.h)
|
||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||
|
||||
#Remove test executable from library
|
||||
|
@ -20,6 +20,7 @@ set_target_properties(libfabrickey PROPERTIES PREFIX "") #Avoid extra 'lib' pref
|
|||
#Specify link-time dependancies
|
||||
target_link_libraries(libfabrickey
|
||||
libopenfpgautil
|
||||
libopenfpgashell
|
||||
libarchopenfpga
|
||||
libvtrutil
|
||||
libpugiutil)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<fabric_key>
|
||||
<region id="0">
|
||||
<key id="0" name="sb" value="0"/>
|
||||
<key id="2" name="cb" value="10"/>
|
||||
<key id="1" name="sb" value="7"/>
|
||||
<key id="3" name="clb" value="53"/>
|
||||
<key id="4" name="bram" value="32"/>
|
||||
</region>
|
||||
<module name="fpga_top">
|
||||
<region id="0">
|
||||
<key id="0" name="sb" value="0"/>
|
||||
<key id="2" name="cb" value="10"/>
|
||||
<key id="1" name="sb" value="7"/>
|
||||
<key id="3" name="clb" value="53"/>
|
||||
<key id="4" name="bram" value="32"/>
|
||||
</region>
|
||||
</module>
|
||||
</fabric_key>
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/************************************************************************
|
||||
* Member functions for class FabricKey
|
||||
***********************************************************************/
|
||||
|
@ -39,9 +41,49 @@ FabricKey::fabric_word_line_bank_range FabricKey::wl_banks(
|
|||
wl_bank_ids_[region_id].end());
|
||||
}
|
||||
|
||||
FabricKey::fabric_key_module_range FabricKey::modules() const {
|
||||
return vtr::make_range(sub_key_module_ids_.begin(),
|
||||
sub_key_module_ids_.end());
|
||||
}
|
||||
|
||||
std::vector<FabricSubKeyId> FabricKey::sub_keys(
|
||||
const FabricKeyModuleId& module_id) const {
|
||||
VTR_ASSERT(valid_module_id(module_id));
|
||||
return module_sub_keys_[module_id];
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Accessors : Basic data query
|
||||
***********************************************************************/
|
||||
size_t FabricKey::num_regions() const { return region_ids_.size(); }
|
||||
|
||||
size_t FabricKey::num_keys() const { return key_ids_.size(); }
|
||||
|
||||
std::vector<FabricKeyId> FabricKey::find_key_by_alias(
|
||||
const std::string& alias) const {
|
||||
/* Throw warning on empty alias which may cause unexpected results: whole key
|
||||
* is dumped! */
|
||||
if (alias.empty()) {
|
||||
VTR_LOG_WARN(
|
||||
"Empty alias is given! This may cause unexpected results, i.e., a whole "
|
||||
"data base is dumped!\n");
|
||||
}
|
||||
size_t num_found_keys = 0;
|
||||
for (FabricKeyId key_id : key_ids_) {
|
||||
if (key_alias(key_id) == alias) {
|
||||
num_found_keys++;
|
||||
}
|
||||
}
|
||||
std::vector<FabricKeyId> found_keys;
|
||||
found_keys.reserve(num_found_keys);
|
||||
for (FabricKeyId key_id : key_ids_) {
|
||||
if (key_alias(key_id) == alias) {
|
||||
found_keys.push_back(key_id);
|
||||
}
|
||||
}
|
||||
return found_keys;
|
||||
}
|
||||
|
||||
std::vector<FabricKeyId> FabricKey::region_keys(
|
||||
const FabricRegionId& region_id) const {
|
||||
/* validate the region_id */
|
||||
|
@ -87,6 +129,29 @@ std::vector<openfpga::BasicPort> FabricKey::wl_bank_data_ports(
|
|||
return wl_bank_data_ports_[region_id][bank_id];
|
||||
}
|
||||
|
||||
std::string FabricKey::module_name(const FabricKeyModuleId& module_id) const {
|
||||
VTR_ASSERT(valid_module_id(module_id));
|
||||
return sub_key_module_names_[module_id];
|
||||
}
|
||||
|
||||
std::string FabricKey::sub_key_name(const FabricSubKeyId& key_id) const {
|
||||
/* validate the key_id */
|
||||
VTR_ASSERT(valid_sub_key_id(key_id));
|
||||
return sub_key_names_[key_id];
|
||||
}
|
||||
|
||||
size_t FabricKey::sub_key_value(const FabricSubKeyId& key_id) const {
|
||||
/* validate the key_id */
|
||||
VTR_ASSERT(valid_sub_key_id(key_id));
|
||||
return sub_key_values_[key_id];
|
||||
}
|
||||
|
||||
std::string FabricKey::sub_key_alias(const FabricSubKeyId& key_id) const {
|
||||
/* validate the key_id */
|
||||
VTR_ASSERT(valid_sub_key_id(key_id));
|
||||
return sub_key_alias_[key_id];
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public Mutators
|
||||
***********************************************************************/
|
||||
|
@ -263,6 +328,70 @@ void FabricKey::add_data_port_to_wl_shift_register_bank(
|
|||
wl_bank_data_ports_[region_id][bank_id].push_back(data_port);
|
||||
}
|
||||
|
||||
void FabricKey::reserve_modules(const size_t& num_modules) {
|
||||
sub_key_module_ids_.reserve(num_modules);
|
||||
sub_key_module_names_.reserve(num_modules);
|
||||
module_sub_keys_.reserve(num_modules);
|
||||
}
|
||||
|
||||
void FabricKey::reserve_module_keys(const FabricKeyModuleId& module_id,
|
||||
const size_t& num_keys) {
|
||||
VTR_ASSERT(valid_module_id(module_id));
|
||||
module_sub_keys_[module_id].reserve(num_keys);
|
||||
sub_key_ids_.reserve(sub_key_ids_.size() + num_keys);
|
||||
sub_key_names_.reserve(sub_key_names_.size() + num_keys);
|
||||
sub_key_values_.reserve(sub_key_values_.size() + num_keys);
|
||||
sub_key_alias_.reserve(sub_key_alias_.size() + num_keys);
|
||||
}
|
||||
|
||||
FabricKeyModuleId FabricKey::create_module(const std::string& name) {
|
||||
/* Ensure name is not duplicated */
|
||||
auto result = module2subkey_lookup_.find(name);
|
||||
if (result != module2subkey_lookup_.end()) {
|
||||
return FabricKeyModuleId::INVALID(); /* Return an invalid id */
|
||||
}
|
||||
/* Create a new id */
|
||||
FabricKeyModuleId module_id = FabricKeyModuleId(sub_key_module_ids_.size());
|
||||
sub_key_module_ids_.push_back(module_id);
|
||||
sub_key_module_names_.push_back(name);
|
||||
module_sub_keys_.emplace_back();
|
||||
/* Register in lookup */
|
||||
module2subkey_lookup_[name] = module_id;
|
||||
return module_id;
|
||||
}
|
||||
|
||||
FabricSubKeyId FabricKey::create_module_key(
|
||||
const FabricKeyModuleId& module_id) {
|
||||
VTR_ASSERT(valid_module_id(module_id));
|
||||
/* Create a new id */
|
||||
FabricSubKeyId key_id = FabricSubKeyId(sub_key_ids_.size());
|
||||
sub_key_ids_.push_back(key_id);
|
||||
sub_key_names_.emplace_back();
|
||||
sub_key_values_.emplace_back();
|
||||
sub_key_alias_.emplace_back();
|
||||
/* Add the new id to module */
|
||||
module_sub_keys_[module_id].emplace_back(key_id);
|
||||
return key_id;
|
||||
}
|
||||
|
||||
void FabricKey::set_sub_key_name(const FabricSubKeyId& key_id,
|
||||
const std::string& name) {
|
||||
VTR_ASSERT(valid_sub_key_id(key_id));
|
||||
sub_key_names_[key_id] = name;
|
||||
}
|
||||
|
||||
void FabricKey::set_sub_key_value(const FabricSubKeyId& key_id,
|
||||
const size_t& value) {
|
||||
VTR_ASSERT(valid_sub_key_id(key_id));
|
||||
sub_key_values_[key_id] = value;
|
||||
}
|
||||
|
||||
void FabricKey::set_sub_key_alias(const FabricSubKeyId& key_id,
|
||||
const std::string& alias) {
|
||||
VTR_ASSERT(valid_sub_key_id(key_id));
|
||||
sub_key_alias_[key_id] = alias;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Internal invalidators/validators
|
||||
***********************************************************************/
|
||||
|
@ -297,3 +426,15 @@ bool FabricKey::valid_wl_bank_id(const FabricRegionId& region_id,
|
|||
return (size_t(bank_id) < wl_bank_ids_[region_id].size()) &&
|
||||
(bank_id == wl_bank_ids_[region_id][bank_id]);
|
||||
}
|
||||
|
||||
bool FabricKey::valid_module_id(const FabricKeyModuleId& module_id) const {
|
||||
return (size_t(module_id) < sub_key_module_ids_.size()) &&
|
||||
(module_id == sub_key_module_ids_[module_id]);
|
||||
}
|
||||
|
||||
bool FabricKey::valid_sub_key_id(const FabricSubKeyId& sub_key_id) const {
|
||||
return (size_t(sub_key_id) < sub_key_ids_.size()) &&
|
||||
(sub_key_id == sub_key_ids_[sub_key_id]);
|
||||
}
|
||||
|
||||
} // End of namespace openfpga
|
|
@ -16,6 +16,8 @@
|
|||
#include "fabric_key_fwd.h"
|
||||
#include "openfpga_port.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/********************************************************************
|
||||
* A data structure to describe a secure key for fabric organization
|
||||
* A fabric may consist of multiple regions
|
||||
|
@ -47,12 +49,18 @@ class FabricKey {
|
|||
typedef vtr::vector<FabricWordLineBankId,
|
||||
FabricWordLineBankId>::const_iterator
|
||||
fabric_word_line_bank_iterator;
|
||||
typedef vtr::vector<FabricSubKeyId, FabricSubKeyId>::const_iterator
|
||||
fabric_sub_key_iterator;
|
||||
typedef vtr::vector<FabricKeyModuleId, FabricKeyModuleId>::const_iterator
|
||||
fabric_key_module_iterator;
|
||||
/* Create range */
|
||||
typedef vtr::Range<fabric_region_iterator> fabric_region_range;
|
||||
typedef vtr::Range<fabric_key_iterator> fabric_key_range;
|
||||
typedef vtr::Range<fabric_bit_line_bank_iterator> fabric_bit_line_bank_range;
|
||||
typedef vtr::Range<fabric_word_line_bank_iterator>
|
||||
fabric_word_line_bank_range;
|
||||
typedef vtr::Range<fabric_sub_key_iterator> fabric_sub_key_range;
|
||||
typedef vtr::Range<fabric_key_module_iterator> fabric_key_module_range;
|
||||
|
||||
public: /* Constructors */
|
||||
FabricKey();
|
||||
|
@ -62,36 +70,48 @@ class FabricKey {
|
|||
fabric_region_range regions() const;
|
||||
fabric_bit_line_bank_range bl_banks(const FabricRegionId& region_id) const;
|
||||
fabric_word_line_bank_range wl_banks(const FabricRegionId& region_id) const;
|
||||
fabric_key_module_range modules() const;
|
||||
std::vector<FabricSubKeyId> sub_keys(
|
||||
const FabricKeyModuleId& module_id) const;
|
||||
|
||||
public: /* Public Accessors: Basic data query */
|
||||
size_t num_regions() const;
|
||||
size_t num_keys() const;
|
||||
/* Access all the keys of a region */
|
||||
std::vector<FabricKeyId> region_keys(const FabricRegionId& region_id) const;
|
||||
|
||||
/* Access the name of a key */
|
||||
std::string key_name(const FabricKeyId& key_id) const;
|
||||
|
||||
/* Access the value of a key */
|
||||
size_t key_value(const FabricKeyId& key_id) const;
|
||||
|
||||
/* Access the alias of a key */
|
||||
std::string key_alias(const FabricKeyId& key_id) const;
|
||||
|
||||
/* Access the coordinate of a key */
|
||||
vtr::Point<int> key_coordinate(const FabricKeyId& key_id) const;
|
||||
|
||||
/** @brief Find valid key ids for a given alias. Note that you should NOT send
|
||||
* an empty alias which may cause a complete list of key ids to be returned
|
||||
* (extremely inefficent and NOT useful). Suggest to check if the existing
|
||||
* fabric key contains valid alias for each key before calling this API!!! */
|
||||
std::vector<FabricKeyId> find_key_by_alias(const std::string& alias) const;
|
||||
|
||||
/* Check if there are any keys */
|
||||
bool empty() const;
|
||||
|
||||
/* Return a list of data ports which will be driven by a BL shift register
|
||||
* bank */
|
||||
std::vector<openfpga::BasicPort> bl_bank_data_ports(
|
||||
std::vector<BasicPort> bl_bank_data_ports(
|
||||
const FabricRegionId& region_id, const FabricBitLineBankId& bank_id) const;
|
||||
|
||||
/* Return a list of data ports which will be driven by a WL shift register
|
||||
* bank */
|
||||
std::vector<openfpga::BasicPort> wl_bank_data_ports(
|
||||
std::vector<BasicPort> wl_bank_data_ports(
|
||||
const FabricRegionId& region_id, const FabricWordLineBankId& bank_id) const;
|
||||
|
||||
std::string module_name(const FabricKeyModuleId& module_id) const;
|
||||
std::string sub_key_name(const FabricSubKeyId& key_id) const;
|
||||
size_t sub_key_value(const FabricSubKeyId& key_id) const;
|
||||
std::string sub_key_alias(const FabricSubKeyId& key_id) const;
|
||||
|
||||
public: /* Public Mutators: model-related */
|
||||
/* Reserve a number of regions to be memory efficent */
|
||||
void reserve_regions(const size_t& num_regions);
|
||||
|
@ -115,11 +135,8 @@ class FabricKey {
|
|||
|
||||
/* Configure attributes of a key */
|
||||
void set_key_name(const FabricKeyId& key_id, const std::string& name);
|
||||
|
||||
void set_key_value(const FabricKeyId& key_id, const size_t& value);
|
||||
|
||||
void set_key_alias(const FabricKeyId& key_id, const std::string& alias);
|
||||
|
||||
void set_key_coordinate(const FabricKeyId& key_id,
|
||||
const vtr::Point<int>& coord);
|
||||
|
||||
|
@ -136,7 +153,7 @@ class FabricKey {
|
|||
/* Add a data port to a given BL shift register bank */
|
||||
void add_data_port_to_bl_shift_register_bank(
|
||||
const FabricRegionId& region_id, const FabricBitLineBankId& bank_id,
|
||||
const openfpga::BasicPort& data_port);
|
||||
const BasicPort& data_port);
|
||||
|
||||
/* Create a new shift register bank for WLs and return an id */
|
||||
FabricWordLineBankId create_wl_shift_register_bank(
|
||||
|
@ -145,7 +162,20 @@ class FabricKey {
|
|||
/* Add a data port to a given WL shift register bank */
|
||||
void add_data_port_to_wl_shift_register_bank(
|
||||
const FabricRegionId& region_id, const FabricWordLineBankId& bank_id,
|
||||
const openfpga::BasicPort& data_port);
|
||||
const BasicPort& data_port);
|
||||
|
||||
/* Reserve a number of keys to be memory efficent */
|
||||
void reserve_modules(const size_t& num_modules);
|
||||
void reserve_module_keys(const FabricKeyModuleId& module_id,
|
||||
const size_t& num_keys);
|
||||
/* Create a new key and add it to the library, return an id */
|
||||
FabricKeyModuleId create_module(const std::string& name);
|
||||
FabricSubKeyId create_module_key(const FabricKeyModuleId& module_id);
|
||||
/* Configure attributes of a sub key */
|
||||
void set_sub_key_name(const FabricSubKeyId& key_id, const std::string& name);
|
||||
void set_sub_key_value(const FabricSubKeyId& key_id, const size_t& value);
|
||||
void set_sub_key_alias(const FabricSubKeyId& key_id,
|
||||
const std::string& alias);
|
||||
|
||||
public: /* Public invalidators/validators */
|
||||
bool valid_region_id(const FabricRegionId& region_id) const;
|
||||
|
@ -156,29 +186,26 @@ class FabricKey {
|
|||
const FabricBitLineBankId& bank_id) const;
|
||||
bool valid_wl_bank_id(const FabricRegionId& region_id,
|
||||
const FabricWordLineBankId& bank_id) const;
|
||||
bool valid_module_id(const FabricKeyModuleId& module_id) const;
|
||||
bool valid_sub_key_id(const FabricSubKeyId& sub_key_id) const;
|
||||
|
||||
private: /* Internal data */
|
||||
/* ---- Top-level keys and regions ---- */
|
||||
/* Unique ids for each region */
|
||||
vtr::vector<FabricRegionId, FabricRegionId> region_ids_;
|
||||
|
||||
/* Key ids for each region */
|
||||
vtr::vector<FabricRegionId, std::vector<FabricKeyId>> region_key_ids_;
|
||||
|
||||
/* Unique ids for each key */
|
||||
vtr::vector<FabricKeyId, FabricKeyId> key_ids_;
|
||||
|
||||
/* Names for each key */
|
||||
vtr::vector<FabricKeyId, std::string> key_names_;
|
||||
|
||||
/* Values for each key */
|
||||
vtr::vector<FabricKeyId, size_t> key_values_;
|
||||
|
||||
/* Values for each key */
|
||||
vtr::vector<FabricKeyId, vtr::Point<int>> key_coordinates_;
|
||||
|
||||
/* Region for each key */
|
||||
vtr::vector<FabricKeyId, FabricRegionId> key_regions_;
|
||||
|
||||
/* Optional alias for each key, with which a key can also be represented */
|
||||
vtr::vector<FabricKeyId, std::string> key_alias_;
|
||||
|
||||
|
@ -187,8 +214,8 @@ class FabricKey {
|
|||
vtr::vector<FabricBitLineBankId, FabricBitLineBankId>>
|
||||
bl_bank_ids_;
|
||||
/* Data ports to be connected to each BL shift register bank */
|
||||
vtr::vector<FabricRegionId, vtr::vector<FabricBitLineBankId,
|
||||
std::vector<openfpga::BasicPort>>>
|
||||
vtr::vector<FabricRegionId,
|
||||
vtr::vector<FabricBitLineBankId, std::vector<BasicPort>>>
|
||||
bl_bank_data_ports_;
|
||||
|
||||
/* Unique ids for each WL shift register bank */
|
||||
|
@ -196,9 +223,23 @@ class FabricKey {
|
|||
vtr::vector<FabricWordLineBankId, FabricWordLineBankId>>
|
||||
wl_bank_ids_;
|
||||
/* Data ports to be connected to each WL shift register bank */
|
||||
vtr::vector<FabricRegionId, vtr::vector<FabricWordLineBankId,
|
||||
std::vector<openfpga::BasicPort>>>
|
||||
vtr::vector<FabricRegionId,
|
||||
vtr::vector<FabricWordLineBankId, std::vector<BasicPort>>>
|
||||
wl_bank_data_ports_;
|
||||
|
||||
/* ---- List of sub modules ---- */
|
||||
vtr::vector<FabricKeyModuleId, FabricKeyModuleId> sub_key_module_ids_;
|
||||
vtr::vector<FabricKeyModuleId, std::string> sub_key_module_names_;
|
||||
vtr::vector<FabricKeyModuleId, std::vector<FabricSubKeyId>> module_sub_keys_;
|
||||
std::map<std::string, FabricKeyModuleId> module2subkey_lookup_;
|
||||
|
||||
/* ---- Sub keys ---- */
|
||||
vtr::vector<FabricSubKeyId, FabricSubKeyId> sub_key_ids_;
|
||||
vtr::vector<FabricSubKeyId, std::string> sub_key_names_;
|
||||
vtr::vector<FabricSubKeyId, size_t> sub_key_values_;
|
||||
vtr::vector<FabricSubKeyId, std::string> sub_key_alias_;
|
||||
};
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -12,17 +12,25 @@
|
|||
|
||||
#include "vtr_strong_id.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
struct fabric_region_id_tag;
|
||||
struct fabric_key_id_tag;
|
||||
struct fabric_bit_line_bank_id_tag;
|
||||
struct fabric_word_line_bank_id_tag;
|
||||
struct fabric_sub_key_id_tag;
|
||||
struct fabric_key_module_id_tag;
|
||||
|
||||
typedef vtr::StrongId<fabric_region_id_tag> FabricRegionId;
|
||||
typedef vtr::StrongId<fabric_key_id_tag> FabricKeyId;
|
||||
typedef vtr::StrongId<fabric_bit_line_bank_id_tag> FabricBitLineBankId;
|
||||
typedef vtr::StrongId<fabric_word_line_bank_id_tag> FabricWordLineBankId;
|
||||
typedef vtr::StrongId<fabric_sub_key_id_tag> FabricSubKeyId;
|
||||
typedef vtr::StrongId<fabric_key_module_id_tag> FabricKeyModuleId;
|
||||
|
||||
/* Short declaration of class */
|
||||
class FabricKey;
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef FABRIC_KEY_XML_CONSTANTS_H
|
||||
#define FABRIC_KEY_XML_CONSTANTS_H
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/* Constants required by XML parser */
|
||||
constexpr const char* XML_FABRIC_KEY_ROOT_NAME = "fabric_key";
|
||||
constexpr const char* XML_FABRIC_KEY_MODULE_NODE_NAME = "module";
|
||||
constexpr const char* XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME = "name";
|
||||
constexpr const char* XML_FABRIC_KEY_REGION_NODE_NAME = "region";
|
||||
constexpr const char* XML_FABRIC_KEY_REGION_ATTRIBUTE_ID_NAME = "id";
|
||||
constexpr const char* XML_FABRIC_KEY_KEY_NODE_NAME = "key";
|
||||
constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME = "id";
|
||||
constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME = "alias";
|
||||
constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME = "name";
|
||||
constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME = "value";
|
||||
constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_COLUMN_NAME = "column";
|
||||
constexpr const char* XML_FABRIC_KEY_KEY_ATTRIBUTE_ROW_NAME = "row";
|
||||
constexpr const char* XML_FABRIC_KEY_BL_SHIFT_REGISTER_BANKS_NODE_NAME =
|
||||
"bl_shift_register_banks";
|
||||
constexpr const char* XML_FABRIC_KEY_WL_SHIFT_REGISTER_BANKS_NODE_NAME =
|
||||
"wl_shift_register_banks";
|
||||
constexpr const char* XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME =
|
||||
"bank";
|
||||
constexpr const char*
|
||||
XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_ID_NAME = "id";
|
||||
constexpr const char*
|
||||
XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME = "range";
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -0,0 +1,461 @@
|
|||
/********************************************************************
|
||||
* This file includes the top-level function of this library
|
||||
* which reads an XML of a fabric key to the associated
|
||||
* data structures
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
|
||||
/* Headers from pugi XML library */
|
||||
#include "pugixml.hpp"
|
||||
#include "pugixml_util.hpp"
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpga util library */
|
||||
#include "openfpga_port_parser.h"
|
||||
#include "openfpga_reserved_words.h"
|
||||
#include "openfpga_tokenizer.h"
|
||||
|
||||
/* Headers from libarchfpga */
|
||||
#include "arch_error.h"
|
||||
#include "fabric_key_xml_constants.h"
|
||||
#include "read_xml_fabric_key.h"
|
||||
#include "read_xml_util.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <key> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_module_key(pugi::xml_node& xml_component_key,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key,
|
||||
const FabricKeyModuleId& module_id) {
|
||||
/* Find the id of component key */
|
||||
size_t id = get_attribute(xml_component_key,
|
||||
XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME, loc_data)
|
||||
.as_int();
|
||||
|
||||
FabricSubKeyId sub_key_id = fabric_key.sub_keys(module_id)[id];
|
||||
|
||||
if (false == fabric_key.valid_sub_key_id(sub_key_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_component_key),
|
||||
"Invalid 'id' attribute '%d' (in total %lu keys)!\n", id,
|
||||
fabric_key.sub_keys(module_id).size());
|
||||
}
|
||||
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_sub_key_id(sub_key_id));
|
||||
|
||||
/* If we have an alias, set the value as well */
|
||||
const std::string& alias =
|
||||
get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME,
|
||||
loc_data, pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_string();
|
||||
if (!alias.empty()) {
|
||||
fabric_key.set_sub_key_alias(sub_key_id, alias);
|
||||
}
|
||||
|
||||
/* If we have the alias set, name and valus are optional then
|
||||
* Otherwise, they are mandatory attributes
|
||||
*/
|
||||
pugiutil::ReqOpt required_name_value = pugiutil::ReqOpt::OPTIONAL;
|
||||
if (true == alias.empty()) {
|
||||
required_name_value = pugiutil::ReqOpt::REQUIRED;
|
||||
}
|
||||
|
||||
const std::string& name =
|
||||
get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME,
|
||||
loc_data, required_name_value)
|
||||
.as_string();
|
||||
const size_t& value =
|
||||
get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME,
|
||||
loc_data, required_name_value)
|
||||
.as_int();
|
||||
|
||||
fabric_key.set_sub_key_name(sub_key_id, name);
|
||||
fabric_key.set_sub_key_value(sub_key_id, value);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <key> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_key(pugi::xml_node& xml_component_key,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key,
|
||||
const FabricRegionId& fabric_region) {
|
||||
/* Find the id of component key */
|
||||
const size_t& id =
|
||||
get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME,
|
||||
loc_data)
|
||||
.as_int();
|
||||
|
||||
if (false == fabric_key.valid_key_id(FabricKeyId(id))) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_component_key),
|
||||
"Invalid 'id' attribute '%d' (in total %lu keys)!\n", id,
|
||||
fabric_key.keys().size());
|
||||
}
|
||||
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_key_id(FabricKeyId(id)));
|
||||
|
||||
/* If we have an alias, set the value as well */
|
||||
const std::string& alias =
|
||||
get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME,
|
||||
loc_data, pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_string();
|
||||
if (!alias.empty()) {
|
||||
fabric_key.set_key_alias(FabricKeyId(id), alias);
|
||||
}
|
||||
|
||||
/* If we have the alias set, name and valus are optional then
|
||||
* Otherwise, they are mandatory attributes
|
||||
*/
|
||||
pugiutil::ReqOpt required_name_value = pugiutil::ReqOpt::OPTIONAL;
|
||||
if (true == alias.empty()) {
|
||||
required_name_value = pugiutil::ReqOpt::REQUIRED;
|
||||
}
|
||||
|
||||
const std::string& name =
|
||||
get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME,
|
||||
loc_data, required_name_value)
|
||||
.as_string();
|
||||
const size_t& value =
|
||||
get_attribute(xml_component_key, XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME,
|
||||
loc_data, required_name_value)
|
||||
.as_int();
|
||||
|
||||
fabric_key.set_key_name(FabricKeyId(id), name);
|
||||
fabric_key.set_key_value(FabricKeyId(id), value);
|
||||
fabric_key.add_key_to_region(fabric_region, FabricKeyId(id));
|
||||
|
||||
/* Parse coordinates */
|
||||
vtr::Point<int> coord;
|
||||
coord.set_x(get_attribute(xml_component_key,
|
||||
XML_FABRIC_KEY_KEY_ATTRIBUTE_COLUMN_NAME, loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_int(-1));
|
||||
coord.set_y(get_attribute(xml_component_key,
|
||||
XML_FABRIC_KEY_KEY_ATTRIBUTE_ROW_NAME, loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_int(-1));
|
||||
if (fabric_key.valid_key_coordinate(coord)) {
|
||||
fabric_key.set_key_coordinate(FabricKeyId(id), coord);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <bank> under <bl_shift_register_banks> to an object of
|
||||
*FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_bl_shift_register_bank(
|
||||
pugi::xml_node& xml_bank, const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key, const FabricRegionId& fabric_region) {
|
||||
/* Find the id of the bank */
|
||||
FabricBitLineBankId bank_id = FabricBitLineBankId(
|
||||
get_attribute(xml_bank,
|
||||
XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_ID_NAME,
|
||||
loc_data)
|
||||
.as_int());
|
||||
|
||||
if (!fabric_key.valid_bl_bank_id(fabric_region, bank_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_bank),
|
||||
"Invalid 'id' attribute '%lu' (in total %lu BL banks)!\n",
|
||||
size_t(bank_id), fabric_key.bl_banks(fabric_region).size());
|
||||
}
|
||||
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_bl_bank_id(fabric_region, bank_id));
|
||||
|
||||
/* Parse the ports */
|
||||
std::string data_ports =
|
||||
get_attribute(xml_bank,
|
||||
XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME,
|
||||
loc_data)
|
||||
.as_string();
|
||||
/* Split with ',' if we have multiple ports */
|
||||
openfpga::StringToken tokenizer(data_ports);
|
||||
for (const std::string& data_port : tokenizer.split(',')) {
|
||||
openfpga::PortParser data_port_parser(data_port);
|
||||
fabric_key.add_data_port_to_bl_shift_register_bank(fabric_region, bank_id,
|
||||
data_port_parser.port());
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <bl_shift_register_banks> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_bl_shift_register_banks(
|
||||
pugi::xml_node& xml_bl_bank, const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key, const FabricRegionId& fabric_region) {
|
||||
size_t num_banks = count_children(
|
||||
xml_bl_bank, XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME, loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL);
|
||||
fabric_key.reserve_bl_shift_register_banks(fabric_region, num_banks);
|
||||
|
||||
for (size_t ibank = 0; ibank < num_banks; ++ibank) {
|
||||
fabric_key.create_bl_shift_register_bank(fabric_region);
|
||||
}
|
||||
|
||||
for (pugi::xml_node xml_bank : xml_bl_bank.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_bank.name() !=
|
||||
std::string(XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME)) {
|
||||
bad_tag(xml_bank, loc_data, xml_bl_bank,
|
||||
{XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME});
|
||||
}
|
||||
read_xml_region_bl_shift_register_bank(xml_bank, loc_data, fabric_key,
|
||||
fabric_region);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <bank> under <wl_shift_register_banks> to an object of
|
||||
*FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_wl_shift_register_bank(
|
||||
pugi::xml_node& xml_bank, const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key, const FabricRegionId& fabric_region) {
|
||||
/* Find the id of the bank */
|
||||
FabricWordLineBankId bank_id = FabricWordLineBankId(
|
||||
get_attribute(xml_bank,
|
||||
XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_ID_NAME,
|
||||
loc_data)
|
||||
.as_int());
|
||||
|
||||
if (!fabric_key.valid_wl_bank_id(fabric_region, bank_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_bank),
|
||||
"Invalid 'id' attribute '%lu' (in total %lu WL banks)!\n",
|
||||
size_t(bank_id), fabric_key.wl_banks(fabric_region).size());
|
||||
}
|
||||
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_wl_bank_id(fabric_region, bank_id));
|
||||
|
||||
/* Parse the ports */
|
||||
std::string data_ports =
|
||||
get_attribute(xml_bank,
|
||||
XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME,
|
||||
loc_data)
|
||||
.as_string();
|
||||
/* Split with ',' if we have multiple ports */
|
||||
openfpga::StringToken tokenizer(data_ports);
|
||||
for (const std::string& data_port : tokenizer.split(',')) {
|
||||
openfpga::PortParser data_port_parser(data_port);
|
||||
fabric_key.add_data_port_to_wl_shift_register_bank(fabric_region, bank_id,
|
||||
data_port_parser.port());
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <bl_shift_register_banks> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_wl_shift_register_banks(
|
||||
pugi::xml_node& xml_wl_bank, const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key, const FabricRegionId& fabric_region) {
|
||||
size_t num_banks = count_children(
|
||||
xml_wl_bank, XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME, loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL);
|
||||
fabric_key.reserve_wl_shift_register_banks(fabric_region, num_banks);
|
||||
|
||||
for (size_t ibank = 0; ibank < num_banks; ++ibank) {
|
||||
fabric_key.create_wl_shift_register_bank(fabric_region);
|
||||
}
|
||||
|
||||
for (pugi::xml_node xml_bank : xml_wl_bank.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_bank.name() !=
|
||||
std::string(XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME)) {
|
||||
bad_tag(xml_bank, loc_data, xml_wl_bank,
|
||||
{XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME});
|
||||
}
|
||||
read_xml_region_wl_shift_register_bank(xml_bank, loc_data, fabric_key,
|
||||
fabric_region);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <key> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_fabric_region(pugi::xml_node& xml_region,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key) {
|
||||
/* Find the unique id for the region */
|
||||
const FabricRegionId& region_id = FabricRegionId(
|
||||
get_attribute(xml_region, XML_FABRIC_KEY_REGION_ATTRIBUTE_ID_NAME, loc_data)
|
||||
.as_int());
|
||||
if (false == fabric_key.valid_region_id(region_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_region),
|
||||
"Invalid region id '%lu' (in total %lu regions)!\n",
|
||||
size_t(region_id), fabric_key.regions().size());
|
||||
}
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_region_id(region_id));
|
||||
|
||||
/* Reserve memory space for the keys in the region */
|
||||
size_t num_keys = count_children(xml_region, XML_FABRIC_KEY_KEY_NODE_NAME,
|
||||
loc_data, pugiutil::ReqOpt::OPTIONAL);
|
||||
fabric_key.reserve_region_keys(region_id, num_keys);
|
||||
|
||||
/* Parse the key for this region */
|
||||
if (0 < num_keys) {
|
||||
pugi::xml_node xml_key =
|
||||
get_first_child(xml_region, XML_FABRIC_KEY_KEY_NODE_NAME, loc_data);
|
||||
while (xml_key) {
|
||||
read_xml_region_key(xml_key, loc_data, fabric_key, region_id);
|
||||
xml_key = xml_key.next_sibling(xml_key.name());
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the BL shift register bank for this region */
|
||||
pugi::xml_node xml_bl_bank = get_single_child(
|
||||
xml_region, XML_FABRIC_KEY_BL_SHIFT_REGISTER_BANKS_NODE_NAME, loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL);
|
||||
read_xml_region_bl_shift_register_banks(xml_bl_bank, loc_data, fabric_key,
|
||||
region_id);
|
||||
|
||||
/* Parse the WL shift register bank for this region */
|
||||
pugi::xml_node xml_wl_bank = get_single_child(
|
||||
xml_region, XML_FABRIC_KEY_WL_SHIFT_REGISTER_BANKS_NODE_NAME, loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL);
|
||||
read_xml_region_wl_shift_register_banks(xml_wl_bank, loc_data, fabric_key,
|
||||
region_id);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <module> which is a top-level module
|
||||
*******************************************************************/
|
||||
static void read_xml_fabric_key_top_module(pugi::xml_node& xml_module,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key) {
|
||||
size_t num_regions =
|
||||
std::distance(xml_module.children().begin(), xml_module.children().end());
|
||||
/* Reserve memory space for the region */
|
||||
fabric_key.reserve_regions(num_regions);
|
||||
for (size_t iregion = 0; iregion < num_regions; ++iregion) {
|
||||
fabric_key.create_region();
|
||||
}
|
||||
|
||||
/* Reserve memory space for the keys */
|
||||
size_t num_keys = 0;
|
||||
|
||||
for (pugi::xml_node xml_region : xml_module.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) {
|
||||
bad_tag(xml_region, loc_data, xml_module,
|
||||
{XML_FABRIC_KEY_REGION_NODE_NAME});
|
||||
}
|
||||
num_keys +=
|
||||
std::distance(xml_region.children().begin(), xml_region.children().end());
|
||||
}
|
||||
|
||||
fabric_key.reserve_keys(num_keys);
|
||||
for (size_t ikey = 0; ikey < num_keys; ++ikey) {
|
||||
fabric_key.create_key();
|
||||
}
|
||||
|
||||
/* Iterate over the children under this node,
|
||||
* each child should be named after circuit_model
|
||||
*/
|
||||
for (pugi::xml_node xml_region : xml_module.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_region.name() != std::string(XML_FABRIC_KEY_REGION_NODE_NAME)) {
|
||||
bad_tag(xml_region, loc_data, xml_module,
|
||||
{XML_FABRIC_KEY_REGION_NODE_NAME});
|
||||
}
|
||||
read_xml_fabric_region(xml_region, loc_data, fabric_key);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <module> which is a regular module
|
||||
*******************************************************************/
|
||||
static void read_xml_fabric_key_module(pugi::xml_node& xml_module,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key) {
|
||||
std::string name =
|
||||
get_attribute(xml_module, XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME,
|
||||
loc_data)
|
||||
.as_string();
|
||||
FabricKeyModuleId module_id = fabric_key.create_module(name);
|
||||
|
||||
/* Parse all the sub keys */
|
||||
size_t num_keys =
|
||||
std::distance(xml_module.children().begin(), xml_module.children().end());
|
||||
|
||||
/* Reserve for better memory efficiency */
|
||||
fabric_key.reserve_module_keys(module_id, num_keys);
|
||||
for (size_t ikey = 0; ikey < num_keys; ++ikey) {
|
||||
fabric_key.create_module_key(module_id);
|
||||
}
|
||||
|
||||
for (pugi::xml_node xml_key : xml_module.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_key.name() != std::string(XML_FABRIC_KEY_KEY_NODE_NAME)) {
|
||||
bad_tag(xml_key, loc_data, xml_module, {XML_FABRIC_KEY_KEY_NODE_NAME});
|
||||
}
|
||||
read_xml_module_key(xml_key, loc_data, fabric_key, module_id);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <module> to an object of FabricKey
|
||||
* - For top-level module, we expect a fixed name. If so, we use a special
|
||||
*parser
|
||||
* - For regular module, we follow regular parser
|
||||
*******************************************************************/
|
||||
static void read_xml_fabric_keys_by_modules(pugi::xml_node& xml_module,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key) {
|
||||
std::string name =
|
||||
get_attribute(xml_module, XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME,
|
||||
loc_data)
|
||||
.as_string();
|
||||
if (name == std::string(openfpga::FPGA_TOP_MODULE_NAME)) {
|
||||
read_xml_fabric_key_top_module(xml_module, loc_data, fabric_key);
|
||||
} else {
|
||||
read_xml_fabric_key_module(xml_module, loc_data, fabric_key);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes about <fabric> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
FabricKey read_xml_fabric_key(const char* key_fname) {
|
||||
vtr::ScopedStartFinishTimer timer("Read Fabric Key");
|
||||
|
||||
FabricKey fabric_key;
|
||||
|
||||
/* Parse the file */
|
||||
pugi::xml_document doc;
|
||||
pugiutil::loc_data loc_data;
|
||||
|
||||
try {
|
||||
loc_data = pugiutil::load_xml(doc, key_fname);
|
||||
|
||||
pugi::xml_node xml_root =
|
||||
get_single_child(doc, XML_FABRIC_KEY_ROOT_NAME, loc_data);
|
||||
|
||||
/* Under the root node, we expect only modules */
|
||||
size_t num_modules =
|
||||
std::distance(xml_root.children().begin(), xml_root.children().end());
|
||||
|
||||
/* Reserve for memory efficiency */
|
||||
fabric_key.reserve_modules(num_modules);
|
||||
|
||||
/* Walk through the modules, fill keys one by one */
|
||||
for (pugi::xml_node xml_module : xml_root.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_module.name() != std::string(XML_FABRIC_KEY_MODULE_NODE_NAME)) {
|
||||
bad_tag(xml_module, loc_data, xml_root,
|
||||
{XML_FABRIC_KEY_MODULE_NODE_NAME});
|
||||
}
|
||||
/* Parse fabric keys by module */
|
||||
read_xml_fabric_keys_by_modules(xml_module, loc_data, fabric_key);
|
||||
}
|
||||
|
||||
} catch (pugiutil::XmlError& e) {
|
||||
archfpga_throw(key_fname, e.line(), "%s", e.what());
|
||||
}
|
||||
|
||||
return fabric_key;
|
||||
}
|
||||
|
||||
} // End of namespace openfpga
|
|
@ -9,6 +9,11 @@
|
|||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
FabricKey read_xml_fabric_key(const char* key_fname);
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -0,0 +1,351 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that outputs a configuration protocol to XML
|
||||
*format
|
||||
*******************************************************************/
|
||||
/* Headers from system goes first */
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpga util library */
|
||||
#include "openfpga_digest.h"
|
||||
#include "openfpga_reserved_words.h"
|
||||
|
||||
/* Headers from arch openfpga library */
|
||||
#include "write_xml_utils.h"
|
||||
|
||||
/* Headers from fabrickey library */
|
||||
#include "fabric_key_xml_constants.h"
|
||||
#include "write_xml_fabric_key.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a component sub key to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
static int write_xml_fabric_component_sub_key(
|
||||
std::fstream& fp, const FabricKey& fabric_key,
|
||||
const FabricSubKeyId& component_key, const size_t& key_idx,
|
||||
const size_t& level) {
|
||||
/* Validate the file stream */
|
||||
if (false == openfpga::valid_file_stream(fp)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "<" << XML_FABRIC_KEY_KEY_NODE_NAME;
|
||||
|
||||
if (false == fabric_key.valid_sub_key_id(component_key)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME, key_idx);
|
||||
if (!fabric_key.sub_key_name(component_key).empty()) {
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME,
|
||||
fabric_key.sub_key_name(component_key).c_str());
|
||||
}
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME,
|
||||
fabric_key.sub_key_value(component_key));
|
||||
|
||||
if (!fabric_key.sub_key_alias(component_key).empty()) {
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME,
|
||||
fabric_key.sub_key_alias(component_key).c_str());
|
||||
}
|
||||
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a component key to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
static int write_xml_fabric_component_key(std::fstream& fp,
|
||||
const FabricKey& fabric_key,
|
||||
const FabricKeyId& component_key,
|
||||
const size_t& level) {
|
||||
/* Validate the file stream */
|
||||
if (false == openfpga::valid_file_stream(fp)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "<" << XML_FABRIC_KEY_KEY_NODE_NAME;
|
||||
|
||||
if (false == fabric_key.valid_key_id(component_key)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_ID_NAME,
|
||||
size_t(component_key));
|
||||
if (!fabric_key.key_name(component_key).empty()) {
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_NAME_NAME,
|
||||
fabric_key.key_name(component_key).c_str());
|
||||
}
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_VALUE_NAME,
|
||||
fabric_key.key_value(component_key));
|
||||
|
||||
if (!fabric_key.key_alias(component_key).empty()) {
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_ALIAS_NAME,
|
||||
fabric_key.key_alias(component_key).c_str());
|
||||
}
|
||||
|
||||
vtr::Point<int> coord = fabric_key.key_coordinate(component_key);
|
||||
if (fabric_key.valid_key_coordinate(coord)) {
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_COLUMN_NAME,
|
||||
coord.x());
|
||||
write_xml_attribute(fp, XML_FABRIC_KEY_KEY_ATTRIBUTE_ROW_NAME, coord.y());
|
||||
}
|
||||
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a BL shift register bank description to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
static int write_xml_fabric_bl_shift_register_banks(
|
||||
std::fstream& fp, const FabricKey& fabric_key, const FabricRegionId& region,
|
||||
const size_t& level) {
|
||||
/* Validate the file stream */
|
||||
if (false == openfpga::valid_file_stream(fp)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* If we have an empty bank, we just skip it */
|
||||
if (0 == fabric_key.bl_banks(region).size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write the root node */
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "<" << XML_FABRIC_KEY_BL_SHIFT_REGISTER_BANKS_NODE_NAME << ">"
|
||||
<< "\n";
|
||||
|
||||
for (const auto& bank : fabric_key.bl_banks(region)) {
|
||||
openfpga::write_tab_to_file(fp, level + 1);
|
||||
fp << "<" << XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME;
|
||||
|
||||
write_xml_attribute(
|
||||
fp, XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_ID_NAME,
|
||||
size_t(bank));
|
||||
|
||||
std::string port_str;
|
||||
for (const auto& port : fabric_key.bl_bank_data_ports(region, bank)) {
|
||||
port_str += generate_xml_port_name(port) + ",";
|
||||
}
|
||||
/* Chop the last comma */
|
||||
if (!port_str.empty()) {
|
||||
port_str.pop_back();
|
||||
}
|
||||
write_xml_attribute(
|
||||
fp, XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME,
|
||||
port_str.c_str());
|
||||
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "</" << XML_FABRIC_KEY_BL_SHIFT_REGISTER_BANKS_NODE_NAME << ">"
|
||||
<< "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a WL shift register bank description to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
static int write_xml_fabric_wl_shift_register_banks(
|
||||
std::fstream& fp, const FabricKey& fabric_key, const FabricRegionId& region,
|
||||
const size_t& level) {
|
||||
/* Validate the file stream */
|
||||
if (false == openfpga::valid_file_stream(fp)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* If we have an empty bank, we just skip it */
|
||||
if (0 == fabric_key.wl_banks(region).size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write the root node */
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "<" << XML_FABRIC_KEY_WL_SHIFT_REGISTER_BANKS_NODE_NAME << ">"
|
||||
<< "\n";
|
||||
|
||||
for (const auto& bank : fabric_key.wl_banks(region)) {
|
||||
openfpga::write_tab_to_file(fp, level + 1);
|
||||
fp << "<" << XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_NODE_NAME;
|
||||
|
||||
write_xml_attribute(
|
||||
fp, XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_ID_NAME,
|
||||
size_t(bank));
|
||||
|
||||
std::string port_str;
|
||||
for (const auto& port : fabric_key.wl_bank_data_ports(region, bank)) {
|
||||
port_str += generate_xml_port_name(port) + ",";
|
||||
}
|
||||
/* Chop the last comma */
|
||||
if (!port_str.empty()) {
|
||||
port_str.pop_back();
|
||||
}
|
||||
write_xml_attribute(
|
||||
fp, XML_FABRIC_KEY_BLWL_SHIFT_REGISTER_BANK_ATTRIBUTE_RANGE_NAME,
|
||||
port_str.c_str());
|
||||
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "</" << XML_FABRIC_KEY_WL_SHIFT_REGISTER_BANKS_NODE_NAME << ">"
|
||||
<< "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write keys under the top-level module to a file */
|
||||
static int write_xml_top_module_keys(std::fstream& fp,
|
||||
const FabricKey& fabric_key,
|
||||
const size_t& level) {
|
||||
int err_code = 0;
|
||||
/* Write the module declaration */
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "<" << XML_FABRIC_KEY_MODULE_NODE_NAME << " "
|
||||
<< XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME << "=\""
|
||||
<< FPGA_TOP_MODULE_NAME << "\""
|
||||
<< ">\n";
|
||||
|
||||
/* Write region by region */
|
||||
for (const FabricRegionId& region : fabric_key.regions()) {
|
||||
openfpga::write_tab_to_file(fp, level + 1);
|
||||
fp << "<" << XML_FABRIC_KEY_REGION_NODE_NAME << " "
|
||||
<< XML_FABRIC_KEY_REGION_ATTRIBUTE_ID_NAME << "=\"" << size_t(region)
|
||||
<< "\""
|
||||
<< ">\n";
|
||||
|
||||
/* Write shift register banks */
|
||||
write_xml_fabric_bl_shift_register_banks(fp, fabric_key, region, level + 2);
|
||||
write_xml_fabric_wl_shift_register_banks(fp, fabric_key, region, level + 2);
|
||||
|
||||
/* Write component by component */
|
||||
for (const FabricKeyId& key : fabric_key.region_keys(region)) {
|
||||
err_code = write_xml_fabric_component_key(fp, fabric_key, key, level + 2);
|
||||
if (0 != err_code) {
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, level + 1);
|
||||
fp << "</" << XML_FABRIC_KEY_REGION_NODE_NAME << ">"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "</" << XML_FABRIC_KEY_MODULE_NODE_NAME << ">\n";
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/* Write keys under the a given module to a file */
|
||||
static int write_xml_module_keys(std::fstream& fp, const FabricKey& fabric_key,
|
||||
const FabricKeyModuleId& module_id,
|
||||
const size_t& level) {
|
||||
int err_code = 0;
|
||||
/* Write the module declaration */
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "<" << XML_FABRIC_KEY_MODULE_NODE_NAME << " "
|
||||
<< XML_FABRIC_KEY_MODULE_ATTRIBUTE_NAME_NAME << "=\""
|
||||
<< fabric_key.module_name(module_id) << "\""
|
||||
<< ">\n";
|
||||
|
||||
/* Write component by component */
|
||||
size_t key_idx = 0;
|
||||
for (const FabricSubKeyId& key : fabric_key.sub_keys(module_id)) {
|
||||
err_code = write_xml_fabric_component_sub_key(fp, fabric_key, key, key_idx,
|
||||
level + 1);
|
||||
if (0 != err_code) {
|
||||
return err_code;
|
||||
}
|
||||
key_idx++;
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, level);
|
||||
fp << "</" << XML_FABRIC_KEY_MODULE_NODE_NAME << ">\n";
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a fabric key to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key) {
|
||||
vtr::ScopedStartFinishTimer timer("Write Fabric Key");
|
||||
|
||||
/* Create a file handler */
|
||||
std::fstream fp;
|
||||
/* Open the file stream */
|
||||
fp.open(std::string(fname), std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate the file stream */
|
||||
openfpga::check_file_stream(fname, fp);
|
||||
|
||||
/* Write the root node */
|
||||
fp << "<" << XML_FABRIC_KEY_ROOT_NAME << ">"
|
||||
<< "\n";
|
||||
|
||||
int err_code = 0;
|
||||
|
||||
/* Write the top-level module */
|
||||
err_code = write_xml_top_module_keys(fp, fabric_key, 1);
|
||||
if (0 != err_code) {
|
||||
return err_code;
|
||||
}
|
||||
|
||||
/* Write regular modules */
|
||||
for (FabricKeyModuleId module_id : fabric_key.modules()) {
|
||||
err_code = write_xml_module_keys(fp, fabric_key, module_id, 1);
|
||||
if (0 != err_code) {
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish writing the root node */
|
||||
fp << "</" << XML_FABRIC_KEY_ROOT_NAME << ">"
|
||||
<< "\n";
|
||||
|
||||
/* Close the file stream */
|
||||
fp.close();
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
} // End of namespace openfpga
|
|
@ -11,6 +11,11 @@
|
|||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key);
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -1,295 +0,0 @@
|
|||
/********************************************************************
|
||||
* This file includes the top-level function of this library
|
||||
* which reads an XML of a fabric key to the associated
|
||||
* data structures
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
|
||||
/* Headers from pugi XML library */
|
||||
#include "pugixml.hpp"
|
||||
#include "pugixml_util.hpp"
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpga util library */
|
||||
#include "openfpga_port_parser.h"
|
||||
#include "openfpga_tokenizer.h"
|
||||
|
||||
/* Headers from libarchfpga */
|
||||
#include "arch_error.h"
|
||||
#include "read_xml_fabric_key.h"
|
||||
#include "read_xml_util.h"
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <key> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_key(pugi::xml_node& xml_component_key,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key,
|
||||
const FabricRegionId& fabric_region) {
|
||||
/* Find the id of component key */
|
||||
const size_t& id = get_attribute(xml_component_key, "id", loc_data).as_int();
|
||||
|
||||
if (false == fabric_key.valid_key_id(FabricKeyId(id))) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_component_key),
|
||||
"Invalid 'id' attribute '%d' (in total %lu keys)!\n", id,
|
||||
fabric_key.keys().size());
|
||||
}
|
||||
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_key_id(FabricKeyId(id)));
|
||||
|
||||
/* If we have an alias, set the value as well */
|
||||
const std::string& alias = get_attribute(xml_component_key, "alias", loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_string();
|
||||
if (!alias.empty()) {
|
||||
fabric_key.set_key_alias(FabricKeyId(id), alias);
|
||||
}
|
||||
|
||||
/* If we have the alias set, name and valus are optional then
|
||||
* Otherwise, they are mandatory attributes
|
||||
*/
|
||||
pugiutil::ReqOpt required_name_value = pugiutil::ReqOpt::OPTIONAL;
|
||||
if (true == alias.empty()) {
|
||||
required_name_value = pugiutil::ReqOpt::REQUIRED;
|
||||
}
|
||||
|
||||
const std::string& name =
|
||||
get_attribute(xml_component_key, "name", loc_data, required_name_value)
|
||||
.as_string();
|
||||
const size_t& value =
|
||||
get_attribute(xml_component_key, "value", loc_data, required_name_value)
|
||||
.as_int();
|
||||
|
||||
fabric_key.set_key_name(FabricKeyId(id), name);
|
||||
fabric_key.set_key_value(FabricKeyId(id), value);
|
||||
fabric_key.add_key_to_region(fabric_region, FabricKeyId(id));
|
||||
|
||||
/* Parse coordinates */
|
||||
vtr::Point<int> coord;
|
||||
coord.set_x(get_attribute(xml_component_key, "column", loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_int(-1));
|
||||
coord.set_y(get_attribute(xml_component_key, "row", loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_int(-1));
|
||||
if (fabric_key.valid_key_coordinate(coord)) {
|
||||
fabric_key.set_key_coordinate(FabricKeyId(id), coord);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <bank> under <bl_shift_register_banks> to an object of
|
||||
*FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_bl_shift_register_bank(
|
||||
pugi::xml_node& xml_bank, const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key, const FabricRegionId& fabric_region) {
|
||||
/* Find the id of the bank */
|
||||
FabricBitLineBankId bank_id =
|
||||
FabricBitLineBankId(get_attribute(xml_bank, "id", loc_data).as_int());
|
||||
|
||||
if (!fabric_key.valid_bl_bank_id(fabric_region, bank_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_bank),
|
||||
"Invalid 'id' attribute '%lu' (in total %lu BL banks)!\n",
|
||||
size_t(bank_id), fabric_key.bl_banks(fabric_region).size());
|
||||
}
|
||||
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_bl_bank_id(fabric_region, bank_id));
|
||||
|
||||
/* Parse the ports */
|
||||
std::string data_ports =
|
||||
get_attribute(xml_bank, "range", loc_data).as_string();
|
||||
/* Split with ',' if we have multiple ports */
|
||||
openfpga::StringToken tokenizer(data_ports);
|
||||
for (const std::string& data_port : tokenizer.split(',')) {
|
||||
openfpga::PortParser data_port_parser(data_port);
|
||||
fabric_key.add_data_port_to_bl_shift_register_bank(fabric_region, bank_id,
|
||||
data_port_parser.port());
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <bl_shift_register_banks> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_bl_shift_register_banks(
|
||||
pugi::xml_node& xml_bl_bank, const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key, const FabricRegionId& fabric_region) {
|
||||
size_t num_banks =
|
||||
count_children(xml_bl_bank, "bank", loc_data, pugiutil::ReqOpt::OPTIONAL);
|
||||
fabric_key.reserve_bl_shift_register_banks(fabric_region, num_banks);
|
||||
|
||||
for (size_t ibank = 0; ibank < num_banks; ++ibank) {
|
||||
fabric_key.create_bl_shift_register_bank(fabric_region);
|
||||
}
|
||||
|
||||
for (pugi::xml_node xml_bank : xml_bl_bank.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_bank.name() != std::string("bank")) {
|
||||
bad_tag(xml_bank, loc_data, xml_bl_bank, {"bank"});
|
||||
}
|
||||
read_xml_region_bl_shift_register_bank(xml_bank, loc_data, fabric_key,
|
||||
fabric_region);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <bank> under <wl_shift_register_banks> to an object of
|
||||
*FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_wl_shift_register_bank(
|
||||
pugi::xml_node& xml_bank, const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key, const FabricRegionId& fabric_region) {
|
||||
/* Find the id of the bank */
|
||||
FabricWordLineBankId bank_id =
|
||||
FabricWordLineBankId(get_attribute(xml_bank, "id", loc_data).as_int());
|
||||
|
||||
if (!fabric_key.valid_wl_bank_id(fabric_region, bank_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_bank),
|
||||
"Invalid 'id' attribute '%lu' (in total %lu WL banks)!\n",
|
||||
size_t(bank_id), fabric_key.wl_banks(fabric_region).size());
|
||||
}
|
||||
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_wl_bank_id(fabric_region, bank_id));
|
||||
|
||||
/* Parse the ports */
|
||||
std::string data_ports =
|
||||
get_attribute(xml_bank, "range", loc_data).as_string();
|
||||
/* Split with ',' if we have multiple ports */
|
||||
openfpga::StringToken tokenizer(data_ports);
|
||||
for (const std::string& data_port : tokenizer.split(',')) {
|
||||
openfpga::PortParser data_port_parser(data_port);
|
||||
fabric_key.add_data_port_to_wl_shift_register_bank(fabric_region, bank_id,
|
||||
data_port_parser.port());
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <bl_shift_register_banks> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_region_wl_shift_register_banks(
|
||||
pugi::xml_node& xml_wl_bank, const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key, const FabricRegionId& fabric_region) {
|
||||
size_t num_banks =
|
||||
count_children(xml_wl_bank, "bank", loc_data, pugiutil::ReqOpt::OPTIONAL);
|
||||
fabric_key.reserve_wl_shift_register_banks(fabric_region, num_banks);
|
||||
|
||||
for (size_t ibank = 0; ibank < num_banks; ++ibank) {
|
||||
fabric_key.create_wl_shift_register_bank(fabric_region);
|
||||
}
|
||||
|
||||
for (pugi::xml_node xml_bank : xml_wl_bank.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_bank.name() != std::string("bank")) {
|
||||
bad_tag(xml_bank, loc_data, xml_wl_bank, {"bank"});
|
||||
}
|
||||
read_xml_region_wl_shift_register_bank(xml_bank, loc_data, fabric_key,
|
||||
fabric_region);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <key> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
static void read_xml_fabric_region(pugi::xml_node& xml_region,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
FabricKey& fabric_key) {
|
||||
/* Find the unique id for the region */
|
||||
const FabricRegionId& region_id =
|
||||
FabricRegionId(get_attribute(xml_region, "id", loc_data).as_int());
|
||||
if (false == fabric_key.valid_region_id(region_id)) {
|
||||
archfpga_throw(loc_data.filename_c_str(), loc_data.line(xml_region),
|
||||
"Invalid region id '%lu' (in total %lu regions)!\n",
|
||||
size_t(region_id), fabric_key.regions().size());
|
||||
}
|
||||
VTR_ASSERT_SAFE(true == fabric_key.valid_region_id(region_id));
|
||||
|
||||
/* Reserve memory space for the keys in the region */
|
||||
size_t num_keys =
|
||||
count_children(xml_region, "key", loc_data, pugiutil::ReqOpt::OPTIONAL);
|
||||
fabric_key.reserve_region_keys(region_id, num_keys);
|
||||
|
||||
/* Parse the key for this region */
|
||||
if (0 < num_keys) {
|
||||
pugi::xml_node xml_key = get_first_child(xml_region, "key", loc_data);
|
||||
while (xml_key) {
|
||||
read_xml_region_key(xml_key, loc_data, fabric_key, region_id);
|
||||
xml_key = xml_key.next_sibling(xml_key.name());
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the BL shift register bank for this region */
|
||||
pugi::xml_node xml_bl_bank =
|
||||
get_single_child(xml_region, "bl_shift_register_banks", loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL);
|
||||
read_xml_region_bl_shift_register_banks(xml_bl_bank, loc_data, fabric_key,
|
||||
region_id);
|
||||
|
||||
/* Parse the WL shift register bank for this region */
|
||||
pugi::xml_node xml_wl_bank =
|
||||
get_single_child(xml_region, "wl_shift_register_banks", loc_data,
|
||||
pugiutil::ReqOpt::OPTIONAL);
|
||||
read_xml_region_wl_shift_register_banks(xml_wl_bank, loc_data, fabric_key,
|
||||
region_id);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes about <fabric> to an object of FabricKey
|
||||
*******************************************************************/
|
||||
FabricKey read_xml_fabric_key(const char* key_fname) {
|
||||
vtr::ScopedStartFinishTimer timer("Read Fabric Key");
|
||||
|
||||
FabricKey fabric_key;
|
||||
|
||||
/* Parse the file */
|
||||
pugi::xml_document doc;
|
||||
pugiutil::loc_data loc_data;
|
||||
|
||||
try {
|
||||
loc_data = pugiutil::load_xml(doc, key_fname);
|
||||
|
||||
pugi::xml_node xml_root = get_single_child(doc, "fabric_key", loc_data);
|
||||
|
||||
size_t num_regions =
|
||||
std::distance(xml_root.children().begin(), xml_root.children().end());
|
||||
/* Reserve memory space for the region */
|
||||
fabric_key.reserve_regions(num_regions);
|
||||
for (size_t iregion = 0; iregion < num_regions; ++iregion) {
|
||||
fabric_key.create_region();
|
||||
}
|
||||
|
||||
/* Reserve memory space for the keys */
|
||||
size_t num_keys = 0;
|
||||
|
||||
for (pugi::xml_node xml_region : xml_root.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_region.name() != std::string("region")) {
|
||||
bad_tag(xml_region, loc_data, xml_root, {"region"});
|
||||
}
|
||||
num_keys += std::distance(xml_region.children().begin(),
|
||||
xml_region.children().end());
|
||||
}
|
||||
|
||||
fabric_key.reserve_keys(num_keys);
|
||||
for (size_t ikey = 0; ikey < num_keys; ++ikey) {
|
||||
fabric_key.create_key();
|
||||
}
|
||||
|
||||
/* Iterate over the children under this node,
|
||||
* each child should be named after circuit_model
|
||||
*/
|
||||
for (pugi::xml_node xml_region : xml_root.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_region.name() != std::string("region")) {
|
||||
bad_tag(xml_region, loc_data, xml_root, {"region"});
|
||||
}
|
||||
read_xml_fabric_region(xml_region, loc_data, fabric_key);
|
||||
}
|
||||
} catch (pugiutil::XmlError& e) {
|
||||
archfpga_throw(key_fname, e.line(), "%s", e.what());
|
||||
}
|
||||
|
||||
return fabric_key;
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/************************************************************************
|
||||
* Check functions for the content of fabric key to avoid conflicts with
|
||||
* other data structures
|
||||
* These functions are not universal methods for the FabricKey class
|
||||
* They are made to ease the development in some specific purposes
|
||||
* Please classify such functions in this file
|
||||
***********************************************************************/
|
||||
#include "check_fabric_key.h"
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/** @brief Sanity checks for fabric key alias attribute:
|
||||
* - Each alias should NOT be empty
|
||||
* - Each alias should be defined only once!
|
||||
*/
|
||||
int check_fabric_key_alias(const FabricKey& input_key, const bool& verbose) {
|
||||
/* Check each key now */
|
||||
size_t num_errors = 0;
|
||||
float progress = 0.;
|
||||
size_t num_keys_checked = 0;
|
||||
|
||||
std::map<std::string, size_t> alias_count;
|
||||
for (FabricKeyId key_id : input_key.keys()) {
|
||||
/* Note that this is slow. May consider to build a map first */
|
||||
std::string curr_alias = input_key.key_alias(key_id);
|
||||
progress = static_cast<float>(num_keys_checked) /
|
||||
static_cast<float>(input_key.num_keys()) * 100.0;
|
||||
VTR_LOGV(verbose, "[%lu%] Checking key alias '%s'\r", size_t(progress),
|
||||
curr_alias.c_str());
|
||||
if (curr_alias.empty()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Empty key alias (id='%lu') found in keys which is invalid!\n",
|
||||
size_t(key_id));
|
||||
num_errors++;
|
||||
}
|
||||
auto result = alias_count.find(curr_alias);
|
||||
if (result == alias_count.end()) {
|
||||
alias_count[curr_alias] = 0;
|
||||
} else {
|
||||
alias_count[curr_alias] += 1;
|
||||
}
|
||||
num_keys_checked++;
|
||||
}
|
||||
for (const auto& kv : alias_count) {
|
||||
if (kv.second > 1) {
|
||||
std::string key_id_str;
|
||||
std::vector<FabricKeyId> found_keys =
|
||||
input_key.find_key_by_alias(kv.first);
|
||||
for (FabricKeyId found_key_id : found_keys) {
|
||||
key_id_str += std::to_string(size_t(found_key_id)) + ",";
|
||||
}
|
||||
key_id_str.pop_back(); /* Remove last comma */
|
||||
VTR_LOG_ERROR(
|
||||
"Duplicated key alias '%s' found %lu times in keys (ids: %s), which is "
|
||||
"invalid!\n",
|
||||
kv.first.c_str(), kv.second, key_id_str.c_str());
|
||||
num_errors++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_errors;
|
||||
}
|
||||
|
||||
/** @brief Sanity checks for fabric key name and value attribute:
|
||||
* - Each name should not be empty
|
||||
* - Each value should be larger than zero !
|
||||
*/
|
||||
int check_fabric_key_names_and_values(const FabricKey& input_key,
|
||||
const bool& verbose) {
|
||||
/* Check each key now */
|
||||
size_t num_errors = 0;
|
||||
float progress = 0.;
|
||||
size_t num_keys_checked = 0;
|
||||
|
||||
std::map<std::string, std::map<size_t, size_t>> key_value_count;
|
||||
for (FabricKeyId key_id : input_key.keys()) {
|
||||
/* Note that this is slow. May consider to build a map first */
|
||||
std::string curr_name = input_key.key_name(key_id);
|
||||
size_t curr_value = input_key.key_value(key_id);
|
||||
progress = static_cast<float>(num_keys_checked) /
|
||||
static_cast<float>(input_key.num_keys()) * 100.0;
|
||||
VTR_LOGV(verbose, "[%lu%] Checking key names and values '(%s, %lu)'\r",
|
||||
size_t(progress), curr_name.c_str(), curr_value);
|
||||
if (curr_name.empty()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Empty key name (id='%lu') found in keys which is invalid!\n",
|
||||
size_t(key_id));
|
||||
num_errors++;
|
||||
}
|
||||
auto result = key_value_count[curr_name].find(curr_value);
|
||||
if (result == key_value_count[curr_name].end()) {
|
||||
key_value_count[curr_name][curr_value] = 0;
|
||||
} else {
|
||||
key_value_count[curr_name][curr_value] += 1;
|
||||
}
|
||||
num_keys_checked++;
|
||||
}
|
||||
for (const auto& key_name_kv : key_value_count) {
|
||||
for (const auto& key_value_kv : key_name_kv.second) {
|
||||
if (key_value_kv.second > 1) {
|
||||
VTR_LOG_ERROR(
|
||||
"Duplicated key name and value pair (%s, %lu) found %lu times in "
|
||||
"keys, which is invalid!\n",
|
||||
key_name_kv.first.c_str(), key_value_kv.first, key_value_kv.second);
|
||||
num_errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num_errors;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef CHECK_FABRIC_KEY_H
|
||||
#define CHECK_FABRIC_KEY_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include "fabric_key.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
int check_fabric_key_alias(const FabricKey& input_key, const bool& verbose);
|
||||
|
||||
int check_fabric_key_names_and_values(const FabricKey& input_key,
|
||||
const bool& verbose);
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -1,225 +0,0 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that outputs a configuration protocol to XML
|
||||
*format
|
||||
*******************************************************************/
|
||||
/* Headers from system goes first */
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpga util library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
/* Headers from arch openfpga library */
|
||||
#include "write_xml_utils.h"
|
||||
|
||||
/* Headers from fabrickey library */
|
||||
#include "write_xml_fabric_key.h"
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a component key to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
static int write_xml_fabric_component_key(std::fstream& fp,
|
||||
const FabricKey& fabric_key,
|
||||
const FabricKeyId& component_key) {
|
||||
/* Validate the file stream */
|
||||
if (false == openfpga::valid_file_stream(fp)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, 2);
|
||||
fp << "<key";
|
||||
|
||||
if (false == fabric_key.valid_key_id(component_key)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
write_xml_attribute(fp, "id", size_t(component_key));
|
||||
if (!fabric_key.key_name(component_key).empty()) {
|
||||
write_xml_attribute(fp, "name", fabric_key.key_name(component_key).c_str());
|
||||
}
|
||||
write_xml_attribute(fp, "value", fabric_key.key_value(component_key));
|
||||
|
||||
if (!fabric_key.key_alias(component_key).empty()) {
|
||||
write_xml_attribute(fp, "alias",
|
||||
fabric_key.key_alias(component_key).c_str());
|
||||
}
|
||||
|
||||
vtr::Point<int> coord = fabric_key.key_coordinate(component_key);
|
||||
if (fabric_key.valid_key_coordinate(coord)) {
|
||||
write_xml_attribute(fp, "column", coord.x());
|
||||
write_xml_attribute(fp, "row", coord.y());
|
||||
}
|
||||
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a BL shift register bank description to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
static int write_xml_fabric_bl_shift_register_banks(
|
||||
std::fstream& fp, const FabricKey& fabric_key, const FabricRegionId& region) {
|
||||
/* Validate the file stream */
|
||||
if (false == openfpga::valid_file_stream(fp)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* If we have an empty bank, we just skip it */
|
||||
if (0 == fabric_key.bl_banks(region).size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write the root node */
|
||||
openfpga::write_tab_to_file(fp, 2);
|
||||
fp << "<bl_shift_register_banks>"
|
||||
<< "\n";
|
||||
|
||||
for (const auto& bank : fabric_key.bl_banks(region)) {
|
||||
openfpga::write_tab_to_file(fp, 3);
|
||||
fp << "<bank";
|
||||
|
||||
write_xml_attribute(fp, "id", size_t(bank));
|
||||
|
||||
std::string port_str;
|
||||
for (const auto& port : fabric_key.bl_bank_data_ports(region, bank)) {
|
||||
port_str += generate_xml_port_name(port) + ",";
|
||||
}
|
||||
/* Chop the last comma */
|
||||
if (!port_str.empty()) {
|
||||
port_str.pop_back();
|
||||
}
|
||||
write_xml_attribute(fp, "range", port_str.c_str());
|
||||
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, 2);
|
||||
fp << "</bl_shift_register_banks>"
|
||||
<< "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a WL shift register bank description to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
static int write_xml_fabric_wl_shift_register_banks(
|
||||
std::fstream& fp, const FabricKey& fabric_key, const FabricRegionId& region) {
|
||||
/* Validate the file stream */
|
||||
if (false == openfpga::valid_file_stream(fp)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* If we have an empty bank, we just skip it */
|
||||
if (0 == fabric_key.wl_banks(region).size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write the root node */
|
||||
openfpga::write_tab_to_file(fp, 2);
|
||||
fp << "<wl_shift_register_banks>"
|
||||
<< "\n";
|
||||
|
||||
for (const auto& bank : fabric_key.wl_banks(region)) {
|
||||
openfpga::write_tab_to_file(fp, 3);
|
||||
fp << "<bank";
|
||||
|
||||
write_xml_attribute(fp, "id", size_t(bank));
|
||||
|
||||
std::string port_str;
|
||||
for (const auto& port : fabric_key.wl_bank_data_ports(region, bank)) {
|
||||
port_str += generate_xml_port_name(port) + ",";
|
||||
}
|
||||
/* Chop the last comma */
|
||||
if (!port_str.empty()) {
|
||||
port_str.pop_back();
|
||||
}
|
||||
write_xml_attribute(fp, "range", port_str.c_str());
|
||||
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, 2);
|
||||
fp << "</wl_shift_register_banks>"
|
||||
<< "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a fabric key to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
int write_xml_fabric_key(const char* fname, const FabricKey& fabric_key) {
|
||||
vtr::ScopedStartFinishTimer timer("Write Fabric Key");
|
||||
|
||||
/* Create a file handler */
|
||||
std::fstream fp;
|
||||
/* Open the file stream */
|
||||
fp.open(std::string(fname), std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate the file stream */
|
||||
openfpga::check_file_stream(fname, fp);
|
||||
|
||||
/* Write the root node */
|
||||
fp << "<fabric_key>"
|
||||
<< "\n";
|
||||
|
||||
int err_code = 0;
|
||||
|
||||
/* Write region by region */
|
||||
for (const FabricRegionId& region : fabric_key.regions()) {
|
||||
openfpga::write_tab_to_file(fp, 1);
|
||||
fp << "<region id=\"" << size_t(region) << "\""
|
||||
<< ">\n";
|
||||
|
||||
/* Write shift register banks */
|
||||
write_xml_fabric_bl_shift_register_banks(fp, fabric_key, region);
|
||||
write_xml_fabric_wl_shift_register_banks(fp, fabric_key, region);
|
||||
|
||||
/* Write component by component */
|
||||
for (const FabricKeyId& key : fabric_key.region_keys(region)) {
|
||||
err_code = write_xml_fabric_component_key(fp, fabric_key, key);
|
||||
if (0 != err_code) {
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, 1);
|
||||
fp << "</region>"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
/* Finish writing the root node */
|
||||
fp << "</fabric_key>"
|
||||
<< "\n";
|
||||
|
||||
/* Close the file stream */
|
||||
fp.close();
|
||||
|
||||
return err_code;
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from fabric key */
|
||||
#include "check_fabric_key.h"
|
||||
#include "command_echo.h"
|
||||
#include "command_exit_codes.h"
|
||||
#include "command_parser.h"
|
||||
#include "read_xml_fabric_key.h"
|
||||
#include "write_xml_fabric_key.h"
|
||||
|
||||
/** @brief Initialize the options from command-line inputs and organize in the
|
||||
* format that is ready for parsing */
|
||||
static std::vector<std::string> format_argv(const std::string& cmd_name,
|
||||
int argc, const char** argv) {
|
||||
std::vector<std::string> cmd_opts;
|
||||
cmd_opts.push_back(cmd_name);
|
||||
for (int iarg = 1; iarg < argc; ++iarg) {
|
||||
cmd_opts.push_back(std::string(argv[iarg]));
|
||||
}
|
||||
return cmd_opts;
|
||||
}
|
||||
|
||||
/** @brief Checks to be done:
|
||||
* - Each alias of reference key can be found in the input key
|
||||
*/
|
||||
static int check_input_and_ref_key_alias_match(
|
||||
const openfpga::FabricKey& input_key, const openfpga::FabricKey& ref_key,
|
||||
const bool& verbose) {
|
||||
size_t num_errors = 0;
|
||||
size_t num_keys_checked = 0;
|
||||
float progress = 0.;
|
||||
VTR_LOG(
|
||||
"Checking key alias matching between reference key and input keys...\n");
|
||||
for (openfpga::FabricKeyId key_id : ref_key.keys()) {
|
||||
/* Note that this is slow. May consider to build a map first */
|
||||
std::string curr_alias = ref_key.key_alias(key_id);
|
||||
std::vector<openfpga::FabricKeyId> input_found_keys =
|
||||
input_key.find_key_by_alias(curr_alias);
|
||||
progress = static_cast<float>(num_keys_checked) /
|
||||
static_cast<float>(ref_key.num_keys()) * 100.0;
|
||||
VTR_LOGV(verbose, "[%lu%] Checking key alias '%s'\r", size_t(progress),
|
||||
curr_alias.c_str());
|
||||
if (input_found_keys.empty()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid alias '%s' in the reference key (id='%lu'), which does not "
|
||||
"exist in the input key!\n",
|
||||
curr_alias.c_str(), size_t(key_id));
|
||||
num_errors++;
|
||||
}
|
||||
if (input_found_keys.size() > 1) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid alias '%s' in the input key (id='%lu'), which have been "
|
||||
"found %lu times!\n",
|
||||
curr_alias.c_str(), size_t(key_id), input_found_keys.size());
|
||||
num_errors++;
|
||||
}
|
||||
num_keys_checked++;
|
||||
}
|
||||
VTR_LOG(
|
||||
"Checking key alias matching between reference key and input keys... %s\n",
|
||||
num_errors ? "[Fail]" : "[Pass]");
|
||||
return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR
|
||||
: openfpga::CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/** @brief Checks to be done:
|
||||
* - Number of configuration regions match
|
||||
* - Number of keys match
|
||||
*/
|
||||
static int check_input_key(const openfpga::FabricKey& input_key,
|
||||
const openfpga::FabricKey& ref_key,
|
||||
const bool& verbose) {
|
||||
if (ref_key.num_regions() != input_key.num_regions()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Different number of configuration regions between reference key "
|
||||
"(='%lu') and input key ('=%lu')!\n",
|
||||
ref_key.num_regions(), input_key.num_regions());
|
||||
return openfpga::CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (ref_key.num_keys() != input_key.num_keys()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Different number of keys between reference key (='%lu') and input key "
|
||||
"('=%lu')!\n",
|
||||
ref_key.num_keys(), input_key.num_keys());
|
||||
return openfpga::CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
size_t num_errors = 0;
|
||||
size_t curr_num_err = 0;
|
||||
VTR_LOG("Checking key alias in reference key...\n");
|
||||
curr_num_err = openfpga::check_fabric_key_alias(ref_key, verbose);
|
||||
VTR_LOG("Checking key alias in reference key... %s\n",
|
||||
curr_num_err ? "[Fail]" : "[Pass]");
|
||||
VTR_LOG("Checking key names and values in reference key...\n");
|
||||
curr_num_err = openfpga::check_fabric_key_names_and_values(ref_key, verbose);
|
||||
num_errors += curr_num_err;
|
||||
VTR_LOG("Checking key names and valus in reference key... %s\n",
|
||||
curr_num_err ? "[Fail]" : "[Pass]");
|
||||
VTR_LOG("Checking key alias in input key...\n");
|
||||
curr_num_err = openfpga::check_fabric_key_alias(input_key, verbose);
|
||||
num_errors += curr_num_err;
|
||||
VTR_LOG("Checking key alias in input key... %s\n",
|
||||
curr_num_err ? "[Fail]" : "[Pass]");
|
||||
num_errors +=
|
||||
check_input_and_ref_key_alias_match(input_key, ref_key, verbose);
|
||||
return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR
|
||||
: openfpga::CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/** @brief Checks to be done:
|
||||
* - Each alias of input key can be found in the reference key
|
||||
* - Update input key with pair of name and value which matches the alias from
|
||||
* the reference key
|
||||
*/
|
||||
static int update_input_key(openfpga::FabricKey& input_key,
|
||||
const openfpga::FabricKey& ref_key,
|
||||
const bool& verbose) {
|
||||
size_t num_errors = 0;
|
||||
size_t num_keys_checked = 0;
|
||||
float progress = 0.;
|
||||
VTR_LOG("Pairing key alias between reference key and input keys...\n");
|
||||
for (openfpga::FabricKeyId key_id : input_key.keys()) {
|
||||
/* Note that this is slow. May consider to build a map first */
|
||||
std::string curr_alias = input_key.key_alias(key_id);
|
||||
std::vector<openfpga::FabricKeyId> ref_found_keys =
|
||||
ref_key.find_key_by_alias(curr_alias);
|
||||
progress = static_cast<float>(num_keys_checked) /
|
||||
static_cast<float>(input_key.num_keys()) * 100.0;
|
||||
VTR_LOGV(verbose, "[%lu%] Pairing key alias '%s'\r", size_t(progress),
|
||||
curr_alias.c_str());
|
||||
if (ref_found_keys.empty()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid alias '%s' in the input key (id='%lu'), which does not "
|
||||
"exist in the reference key!\n",
|
||||
curr_alias.c_str(), size_t(key_id));
|
||||
num_errors++;
|
||||
}
|
||||
if (ref_found_keys.size() > 1) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid alias '%s' in the reference key (id='%lu'), which have been "
|
||||
"found %lu times!\n",
|
||||
curr_alias.c_str(), size_t(key_id));
|
||||
num_errors++;
|
||||
}
|
||||
/* Now we have a key, get the name and value, and update input key */
|
||||
input_key.set_key_name(key_id, ref_key.key_name(ref_found_keys[0]));
|
||||
input_key.set_key_value(key_id, ref_key.key_value(ref_found_keys[0]));
|
||||
VTR_LOGV(verbose, "[%lu%] Pairing key alias '%s' -> ('%s', %lu)\r",
|
||||
size_t(progress), curr_alias.c_str(),
|
||||
input_key.key_name(key_id).c_str(), input_key.key_value(key_id));
|
||||
num_keys_checked++;
|
||||
}
|
||||
return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR
|
||||
: openfpga::CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
/** @brief Checks to be done:
|
||||
* - Number of configuration regions match
|
||||
* - Number of keys match
|
||||
* - Each alias can be found in the reference key
|
||||
*/
|
||||
static int check_and_update_input_key(openfpga::FabricKey& input_key,
|
||||
const openfpga::FabricKey& ref_key,
|
||||
const bool& verbose) {
|
||||
int status = openfpga::CMD_EXEC_SUCCESS;
|
||||
status = check_input_key(input_key, ref_key, verbose);
|
||||
if (status != openfpga::CMD_EXEC_SUCCESS) {
|
||||
return openfpga::CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
return update_input_key(input_key, ref_key, verbose);
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
/* Create a new command and Initialize the options available in the user
|
||||
* interface */
|
||||
openfpga::Command cmd("fabric_key_assistant");
|
||||
openfpga::CommandOptionId opt_ref =
|
||||
cmd.add_option("reference", true, "Specify the reference fabric key file");
|
||||
cmd.set_option_require_value(opt_ref, openfpga::OPT_STRING);
|
||||
openfpga::CommandOptionId opt_input =
|
||||
cmd.add_option("input", true, "Specify the hand-crafted fabric key file");
|
||||
cmd.set_option_require_value(opt_input, openfpga::OPT_STRING);
|
||||
openfpga::CommandOptionId opt_output = cmd.add_option(
|
||||
"output", true, "Specify the final fabric key file to be outputted");
|
||||
cmd.set_option_require_value(opt_output, openfpga::OPT_STRING);
|
||||
openfpga::CommandOptionId opt_verbose =
|
||||
cmd.add_option("verbose", false, "Show verbose outputs");
|
||||
openfpga::CommandOptionId opt_help =
|
||||
cmd.add_option("help", false, "Show help desk");
|
||||
|
||||
/* Parse the option, to avoid issues, we use the command name to replace the
|
||||
* argv[0] */
|
||||
std::vector<std::string> cmd_opts = format_argv(cmd.name(), argc, argv);
|
||||
|
||||
openfpga::CommandContext cmd_ctx(cmd);
|
||||
if (false == parse_command(cmd_opts, cmd, cmd_ctx) ||
|
||||
cmd_ctx.option_enable(cmd, opt_help)) {
|
||||
/* Echo the command */
|
||||
print_command_options(cmd);
|
||||
return openfpga::CMD_EXEC_FATAL_ERROR;
|
||||
} else {
|
||||
/* Let user to confirm selected options */
|
||||
print_command_context(cmd, cmd_ctx);
|
||||
}
|
||||
|
||||
/* Parse the fabric key from an XML file */
|
||||
VTR_LOG("Read the reference fabric key from an XML file: %s.\n",
|
||||
cmd_ctx.option_value(cmd, opt_ref).c_str());
|
||||
openfpga::FabricKey ref_key =
|
||||
openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_ref).c_str());
|
||||
|
||||
VTR_LOG("Read the hand-crafted fabric key from an XML file: %s.\n",
|
||||
cmd_ctx.option_value(cmd, opt_input).c_str());
|
||||
openfpga::FabricKey input_key =
|
||||
openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_input).c_str());
|
||||
|
||||
/* Check the input key */
|
||||
if (check_and_update_input_key(input_key, ref_key,
|
||||
cmd_ctx.option_enable(cmd, opt_verbose))) {
|
||||
return openfpga::CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
|
||||
VTR_LOG("Write the final fabric key to an XML file: %s.\n",
|
||||
cmd_ctx.option_value(cmd, opt_output).c_str());
|
||||
return openfpga::write_xml_fabric_key(
|
||||
cmd_ctx.option_value(cmd, opt_output).c_str(), input_key);
|
||||
}
|
|
@ -16,14 +16,14 @@ int main(int argc, const char** argv) {
|
|||
VTR_ASSERT((2 == argc) || (3 == argc));
|
||||
|
||||
/* Parse the fabric key from an XML file */
|
||||
FabricKey test_key = read_xml_fabric_key(argv[1]);
|
||||
openfpga::FabricKey test_key = openfpga::read_xml_fabric_key(argv[1]);
|
||||
VTR_LOG("Read the fabric key from an XML file: %s.\n", argv[1]);
|
||||
|
||||
/* Output the circuit library to an XML file
|
||||
* This is optional only used when there is a second argument
|
||||
*/
|
||||
if (3 <= argc) {
|
||||
write_xml_fabric_key(argv[2], test_key);
|
||||
openfpga::write_xml_fabric_key(argv[2], test_key);
|
||||
VTR_LOG("Echo the fabric key to an XML file: %s.\n", argv[2]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
@ -200,10 +201,20 @@ ConfigBlockId BitstreamManager::create_block() {
|
|||
}
|
||||
|
||||
ConfigBlockId BitstreamManager::add_block(const std::string& block_name) {
|
||||
ConfigBlockId block = create_block();
|
||||
set_block_name(block, block_name);
|
||||
ConfigBlockId new_block = create_block();
|
||||
set_block_name(new_block, block_name);
|
||||
return new_block;
|
||||
}
|
||||
|
||||
return block;
|
||||
ConfigBlockId BitstreamManager::find_or_create_child_block(
|
||||
const ConfigBlockId& block_id, const std::string& child_block_name) {
|
||||
ConfigBlockId curr_block = find_child_block(block_id, child_block_name);
|
||||
if (valid_block_id(curr_block)) {
|
||||
return curr_block;
|
||||
}
|
||||
curr_block = add_block(child_block_name);
|
||||
add_child_block(block_id, curr_block);
|
||||
return curr_block;
|
||||
}
|
||||
|
||||
void BitstreamManager::set_block_name(const ConfigBlockId& block_id,
|
||||
|
|
|
@ -181,6 +181,11 @@ class BitstreamManager {
|
|||
/* Add a new block of configuration bits to the bitstream manager */
|
||||
ConfigBlockId add_block(const std::string& block_name);
|
||||
|
||||
/* Try to find the child block in a bitstream manager with a given name. If
|
||||
* not found, create a new child block */
|
||||
ConfigBlockId find_or_create_child_block(const ConfigBlockId& block_id,
|
||||
const std::string& child_block_name);
|
||||
|
||||
/* Set a name for a block */
|
||||
void set_block_name(const ConfigBlockId& block_id,
|
||||
const std::string& block_name);
|
||||
|
@ -234,6 +239,9 @@ class BitstreamManager {
|
|||
vtr::vector<ConfigBlockId, ConfigBlockId> parent_block_ids_;
|
||||
vtr::vector<ConfigBlockId, std::vector<ConfigBlockId>> child_block_ids_;
|
||||
|
||||
/* Fast look-up by block name to ids */
|
||||
std::map<std::string, ConfigBlockId> block_name2ids_;
|
||||
|
||||
/* The ids of the inputs of routing multiplexer blocks which is propagated to
|
||||
* outputs By default, it will be -2 (which is invalid) A valid id starts from
|
||||
* -1 -1 indicates an unused routing multiplexer. It will be converted to a
|
||||
|
|
|
@ -19,15 +19,23 @@ namespace openfpga {
|
|||
* Return a vector of the block ids, where the top-level block
|
||||
* locates in the head, while the leaf block locates in the tail
|
||||
* top, next, ... , block
|
||||
* Optionally, the top block name in the path can be specified. Useful to trim
|
||||
*the hierarchy with a given range
|
||||
*******************************************************************/
|
||||
std::vector<ConfigBlockId> find_bitstream_manager_block_hierarchy(
|
||||
const BitstreamManager& bitstream_manager, const ConfigBlockId& block) {
|
||||
const BitstreamManager& bitstream_manager, const ConfigBlockId& block,
|
||||
const std::string& top_block_name) {
|
||||
std::vector<ConfigBlockId> block_hierarchy;
|
||||
ConfigBlockId temp_block = block;
|
||||
|
||||
/* Generate a tree of parent block */
|
||||
while (true == bitstream_manager.valid_block_id(temp_block)) {
|
||||
block_hierarchy.push_back(temp_block);
|
||||
/* Check if we have reached the designated top block */
|
||||
if (!top_block_name.empty() &&
|
||||
bitstream_manager.block_name(temp_block) == top_block_name) {
|
||||
break;
|
||||
}
|
||||
/* Go to upper level */
|
||||
temp_block = bitstream_manager.block_parent(temp_block);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
namespace openfpga {
|
||||
|
||||
std::vector<ConfigBlockId> find_bitstream_manager_block_hierarchy(
|
||||
const BitstreamManager& bitstream_manager, const ConfigBlockId& block);
|
||||
const BitstreamManager& bitstream_manager, const ConfigBlockId& block,
|
||||
const std::string& top_block_name = "");
|
||||
|
||||
std::vector<ConfigBlockId> find_bitstream_manager_top_blocks(
|
||||
const BitstreamManager& bitstream_manager);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
project("libionamemap")
|
||||
|
||||
file(GLOB_RECURSE EXEC_SOURCES test/*.cpp)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*/*.h)
|
||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||
|
||||
#Remove test executable from library
|
||||
list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES})
|
||||
|
||||
#Create the library
|
||||
add_library(libionamemap STATIC
|
||||
${LIB_HEADERS}
|
||||
${LIB_SOURCES})
|
||||
target_include_directories(libionamemap PUBLIC ${LIB_INCLUDE_DIRS})
|
||||
set_target_properties(libionamemap PROPERTIES PREFIX "") #Avoid extra 'lib' prefix
|
||||
|
||||
#Specify link-time dependancies
|
||||
target_link_libraries(libionamemap
|
||||
libarchopenfpga
|
||||
libopenfpgautil
|
||||
libopenfpgashell
|
||||
libvtrutil
|
||||
libpugiutil)
|
||||
|
||||
#Create the test executable
|
||||
foreach(testsourcefile ${EXEC_SOURCES})
|
||||
# Use a simple string replace, to cut off .cpp.
|
||||
get_filename_component(testname ${testsourcefile} NAME_WE)
|
||||
add_executable(${testname} ${testsourcefile})
|
||||
# Make sure the library is linked to each test executable
|
||||
target_link_libraries(${testname} libionamemap)
|
||||
endforeach(testsourcefile ${EXEC_SOURCES})
|
||||
|
||||
install(TARGETS libionamemap DESTINATION bin)
|
|
@ -0,0 +1,8 @@
|
|||
<ports>
|
||||
<port core_name="prog_clock[0]" top_name="prog_clk0"/>
|
||||
<port core_name="prog_clock[1]" top_name="prog_clk1"/>
|
||||
<port core_name="gfpga_io_pad[0:31]" top_name="top_io[0:31]"/>
|
||||
<port core_name="gfpga_io_pad[32:47]" top_name="right_io[32:47]"/>
|
||||
<port core_name="gfpga_io_pad[48:55]" top_name="bottom_io[48:55]"/>
|
||||
<port top_name="pvt_sense" is_dummy="true" direction="input"/>
|
||||
</ports>
|
|
@ -0,0 +1,348 @@
|
|||
/******************************************************************************
|
||||
* Memember functions for data structure IoLocationMap
|
||||
******************************************************************************/
|
||||
/* Headers from vtrutil library */
|
||||
#include "io_name_map.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "command_exit_codes.h"
|
||||
#include "openfpga_port_parser.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
IoNameMap::IoNameMap() {
|
||||
DUMMY_PORT_DIR_STRING_ = {"input", "output", "inout"};
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Public Accessors
|
||||
*************************************************/
|
||||
std::vector<BasicPort> IoNameMap::fpga_top_ports() const {
|
||||
std::vector<BasicPort> ports;
|
||||
|
||||
for (auto it = top2core_io_name_map_.begin();
|
||||
it != top2core_io_name_map_.end(); ++it) {
|
||||
ports.push_back(str2port(it->first));
|
||||
}
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
BasicPort IoNameMap::fpga_core_port(const BasicPort& fpga_top_port) const {
|
||||
BasicPort core_port;
|
||||
/* First, find the pin name matching */
|
||||
auto result_key = top2core_io_name_keys_.find(fpga_top_port.get_name());
|
||||
if (result_key == top2core_io_name_keys_.end()) {
|
||||
return core_port; /* Not found, return invalid port */
|
||||
}
|
||||
/* Second, find the exact key */
|
||||
std::string top_port_key;
|
||||
for (std::string cand : result_key->second) {
|
||||
BasicPort cand_port = str2port(cand);
|
||||
/* if the top port is part of the cand port, e.g., clk[1] vs. clk[0:2], the
|
||||
* candidate is the key that we want! */
|
||||
if (cand_port.contained(fpga_top_port)) {
|
||||
top_port_key = cand;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (top_port_key.empty()) {
|
||||
return core_port; /* Not found, return invalid port */
|
||||
}
|
||||
auto result = top2core_io_name_map_.find(top_port_key);
|
||||
if (result != top2core_io_name_map_.end() && result->second.is_valid()) {
|
||||
BasicPort top_port_pool = str2port(top_port_key);
|
||||
BasicPort fpga_top_port_lsb(fpga_top_port.get_name(),
|
||||
fpga_top_port.get_lsb(),
|
||||
fpga_top_port.get_lsb());
|
||||
BasicPort fpga_top_port_msb(fpga_top_port.get_name(),
|
||||
fpga_top_port.get_msb(),
|
||||
fpga_top_port.get_msb());
|
||||
size_t ipin_anchor_lsb = top_port_pool.find_ipin(fpga_top_port_lsb);
|
||||
size_t ipin_anchor_msb = top_port_pool.find_ipin(fpga_top_port_msb);
|
||||
/* Now find the exact pin and spot the core port with pin index */
|
||||
if (ipin_anchor_lsb < top_port_pool.get_width() &&
|
||||
ipin_anchor_msb < top_port_pool.get_width()) {
|
||||
core_port.set_name(result->second.get_name());
|
||||
core_port.set_lsb(result->second.pins()[ipin_anchor_lsb]);
|
||||
core_port.set_msb(result->second.pins()[ipin_anchor_msb]);
|
||||
}
|
||||
}
|
||||
return core_port;
|
||||
}
|
||||
|
||||
BasicPort IoNameMap::fpga_top_port(const BasicPort& fpga_core_port) const {
|
||||
BasicPort top_port;
|
||||
/* First, find the pin name matching */
|
||||
auto result_key = core2top_io_name_keys_.find(fpga_core_port.get_name());
|
||||
if (result_key == core2top_io_name_keys_.end()) {
|
||||
return top_port; /* Not found, return invalid port */
|
||||
}
|
||||
/* Second, find the exact key */
|
||||
std::string core_port_key;
|
||||
for (std::string cand : result_key->second) {
|
||||
BasicPort cand_port = str2port(cand);
|
||||
/* if the top port is part of the cand port, e.g., clk[1] vs. clk[0:2], the
|
||||
* candidate is the key that we want! */
|
||||
if (cand_port.contained(fpga_core_port)) {
|
||||
core_port_key = cand;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (core_port_key.empty()) {
|
||||
return top_port; /* Not found, return invalid port */
|
||||
}
|
||||
auto result = core2top_io_name_map_.find(core_port_key);
|
||||
if (result != core2top_io_name_map_.end() && result->second.is_valid()) {
|
||||
BasicPort core_port_pool = str2port(core_port_key);
|
||||
BasicPort fpga_core_port_lsb(fpga_core_port.get_name(),
|
||||
fpga_core_port.get_lsb(),
|
||||
fpga_core_port.get_lsb());
|
||||
BasicPort fpga_core_port_msb(fpga_core_port.get_name(),
|
||||
fpga_core_port.get_msb(),
|
||||
fpga_core_port.get_msb());
|
||||
size_t ipin_anchor_lsb = core_port_pool.find_ipin(fpga_core_port);
|
||||
size_t ipin_anchor_msb = core_port_pool.find_ipin(fpga_core_port);
|
||||
/* Now find the exact pin and spot the core port with pin index */
|
||||
if (ipin_anchor_lsb < core_port_pool.get_width() &&
|
||||
ipin_anchor_msb < core_port_pool.get_width()) {
|
||||
top_port.set_name(result->second.get_name());
|
||||
top_port.set_lsb(result->second.pins()[ipin_anchor_lsb]);
|
||||
top_port.set_msb(result->second.pins()[ipin_anchor_msb]);
|
||||
}
|
||||
}
|
||||
return top_port;
|
||||
}
|
||||
|
||||
IoNameMap::e_port_mapping_status IoNameMap::fpga_core_port_mapping_status(
|
||||
const BasicPort& fpga_core_port, const bool& verbose) const {
|
||||
/* First, find the pin name matching */
|
||||
auto result_key = core2top_io_name_keys_.find(fpga_core_port.get_name());
|
||||
if (result_key == core2top_io_name_keys_.end()) {
|
||||
return IoNameMap::e_port_mapping_status::NONE;
|
||||
}
|
||||
/* Second, find the exact port. Create a scoreboard and check every pin.
|
||||
* Expect only one hit per pin. Error on any pin which has been hit twice
|
||||
* (indicate overlapped ports). Error on any pin which has no hit (indicate
|
||||
* partially unmapped) */
|
||||
std::vector<int8_t> scoreboard(fpga_core_port.get_width(), 0);
|
||||
for (std::string cand : result_key->second) {
|
||||
BasicPort cand_port = str2port(cand);
|
||||
for (auto pin : cand_port.pins()) {
|
||||
scoreboard[pin - fpga_core_port.get_lsb()]++;
|
||||
}
|
||||
}
|
||||
for (int8_t bit : scoreboard) {
|
||||
if (bit == 0) {
|
||||
VTR_LOGV_ERROR(verbose,
|
||||
"Unmapped pin '%lu' of fpga_core port '%s'! Partially "
|
||||
"mapping is not allowed\n",
|
||||
fpga_core_port.pins()[bit + fpga_core_port.get_lsb()],
|
||||
fpga_core_port.to_verilog_string().c_str());
|
||||
return IoNameMap::e_port_mapping_status::PARTIAL;
|
||||
}
|
||||
if (bit > 1) {
|
||||
VTR_LOGV_ERROR(verbose,
|
||||
"Overlapped %d times on pin '%lu' of fpga_core port '%s' "
|
||||
"when mapping!\n",
|
||||
bit, fpga_core_port.pins()[bit + fpga_core_port.get_lsb()],
|
||||
fpga_core_port.to_verilog_string().c_str());
|
||||
return IoNameMap::e_port_mapping_status::OVERLAPPED;
|
||||
}
|
||||
}
|
||||
return IoNameMap::e_port_mapping_status::FULL;
|
||||
}
|
||||
|
||||
bool IoNameMap::fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const {
|
||||
return !fpga_core_port(fpga_top_port).is_valid();
|
||||
}
|
||||
|
||||
IoNameMap::e_dummy_port_direction IoNameMap::fpga_top_dummy_port_direction(
|
||||
const BasicPort& fpga_top_port) const {
|
||||
for (auto& kv : dummy_port_direction_) {
|
||||
BasicPort cand = str2port(kv.first);
|
||||
if (cand.contained(fpga_top_port)) {
|
||||
return kv.second;
|
||||
}
|
||||
}
|
||||
/* Return an invalid port type */
|
||||
return IoNameMap::e_dummy_port_direction::NUM_TYPES;
|
||||
}
|
||||
|
||||
bool IoNameMap::empty() const {
|
||||
return top2core_io_name_keys_.empty() && top2core_io_name_map_.empty() &&
|
||||
core2top_io_name_keys_.empty() && core2top_io_name_map_.empty() &&
|
||||
dummy_port_direction_.empty();
|
||||
}
|
||||
|
||||
int IoNameMap::set_io_pair(const BasicPort& fpga_top_port,
|
||||
const BasicPort& fpga_core_port) {
|
||||
/* Ensure the two ports are matching in size */
|
||||
if (fpga_top_port.get_width() != fpga_core_port.get_width()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Unable to pair two ports 'fpga_top.%s[%lu:%lu]' and "
|
||||
"'fpga_core.%s[%lu:%lu]' which are in the same size!\n",
|
||||
fpga_top_port.get_name().c_str(), fpga_top_port.get_lsb(),
|
||||
fpga_top_port.get_msb(), fpga_core_port.get_name().c_str(),
|
||||
fpga_core_port.get_lsb(), fpga_core_port.get_msb());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
VTR_ASSERT_SAFE(fpga_top_port.get_width() != fpga_core_port.get_width());
|
||||
/* Register in the key first, and then add to the exact name mapping */
|
||||
{
|
||||
std::string top_port_str = port2str(fpga_top_port);
|
||||
auto result_key = top2core_io_name_keys_.find(fpga_top_port.get_name());
|
||||
if (result_key == top2core_io_name_keys_.end()) {
|
||||
/* Add to the key registery */
|
||||
top2core_io_name_keys_[fpga_top_port.get_name()].push_back(top_port_str);
|
||||
top2core_io_name_map_[top_port_str] = fpga_core_port;
|
||||
} else {
|
||||
/* Ensure that the key is not duplicated */
|
||||
if (std::find(result_key->second.begin(), result_key->second.end(),
|
||||
top_port_str) == result_key->second.end()) {
|
||||
top2core_io_name_keys_[fpga_top_port.get_name()].push_back(
|
||||
top_port_str);
|
||||
top2core_io_name_map_[top_port_str] = fpga_core_port;
|
||||
} else {
|
||||
/* Throw a warning since we have to overwrite */
|
||||
VTR_LOG_WARN(
|
||||
"Overwrite the top-to-core pin mapping: top pin '%s' to core pin "
|
||||
"'%s' (previously was '%s')!\n",
|
||||
top_port_str.c_str(), port2str(fpga_core_port).c_str(),
|
||||
port2str(top2core_io_name_map_[top_port_str]).c_str());
|
||||
top2core_io_name_map_[top_port_str] = fpga_core_port;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now, do similar to the core port */
|
||||
{
|
||||
std::string core_port_str = port2str(fpga_core_port);
|
||||
auto result_key = core2top_io_name_keys_.find(fpga_core_port.get_name());
|
||||
if (result_key == core2top_io_name_keys_.end()) {
|
||||
/* Add to the key registery */
|
||||
core2top_io_name_keys_[fpga_core_port.get_name()].push_back(
|
||||
core_port_str);
|
||||
core2top_io_name_map_[core_port_str] = fpga_top_port;
|
||||
} else {
|
||||
/* Ensure that the key is not duplicated */
|
||||
if (std::find(result_key->second.begin(), result_key->second.end(),
|
||||
core_port_str) == result_key->second.end()) {
|
||||
core2top_io_name_keys_[fpga_core_port.get_name()].push_back(
|
||||
core_port_str);
|
||||
core2top_io_name_map_[core_port_str] = fpga_top_port;
|
||||
} else {
|
||||
/* Throw a warning since we have to overwrite */
|
||||
VTR_LOG_WARN(
|
||||
"Overwrite the core-to-top pin mapping: core pin '%s' to top pin "
|
||||
"'%s' (previously was '%s')!\n",
|
||||
core_port_str.c_str(), port2str(fpga_top_port).c_str(),
|
||||
port2str(core2top_io_name_map_[core_port_str]).c_str());
|
||||
core2top_io_name_map_[core_port_str] = fpga_top_port;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
int IoNameMap::set_dummy_io(const BasicPort& fpga_top_port,
|
||||
const e_dummy_port_direction& direction) {
|
||||
/* Must be a true dummy port, none of its pins have been paired! */
|
||||
std::string top_port_str = port2str(fpga_top_port);
|
||||
/* First, find the pin name matching */
|
||||
auto result_key = top2core_io_name_keys_.find(fpga_top_port.get_name());
|
||||
if (result_key == top2core_io_name_keys_.end()) {
|
||||
/* Add to the key registery */
|
||||
top2core_io_name_keys_[fpga_top_port.get_name()].push_back(top_port_str);
|
||||
top2core_io_name_map_[top_port_str] = BasicPort();
|
||||
} else {
|
||||
/* Ensure that the key is not duplicated */
|
||||
if (std::find(result_key->second.begin(), result_key->second.end(),
|
||||
top_port_str) == result_key->second.end()) {
|
||||
top2core_io_name_keys_[fpga_top_port.get_name()].push_back(top_port_str);
|
||||
top2core_io_name_map_[top_port_str] = BasicPort();
|
||||
} else {
|
||||
/* Throw a error because the dummy pin should NOT be mapped before! */
|
||||
VTR_LOG_ERROR(
|
||||
"Dummy port '%s' of fpga_top is already mapped "
|
||||
"to a valid pin '%s' of fpga_core!\n",
|
||||
port2str(fpga_top_port).c_str(),
|
||||
port2str(top2core_io_name_map_[top_port_str]).c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
/* Add the direction list */
|
||||
bool dir_defined = false;
|
||||
for (auto& kv : dummy_port_direction_) {
|
||||
BasicPort cand = str2port(kv.first);
|
||||
if (cand.contained(fpga_top_port)) {
|
||||
if (kv.second != direction) {
|
||||
/* Throw a error because the dummy pin should NOT be mapped before! */
|
||||
VTR_LOG_ERROR(
|
||||
"Dummy port '%s' of fpga_top is already assigned to a different "
|
||||
"direction through another dummy port definition '%s'!\n",
|
||||
port2str(fpga_top_port).c_str(), port2str(cand).c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
dir_defined = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dir_defined) {
|
||||
dummy_port_direction_[top_port_str] = direction;
|
||||
}
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
std::string IoNameMap::port2str(const BasicPort& port) const {
|
||||
return port.to_verilog_string();
|
||||
}
|
||||
|
||||
BasicPort IoNameMap::str2port(const std::string& port_str) const {
|
||||
return PortParser(port_str).port();
|
||||
}
|
||||
|
||||
std::string IoNameMap::dummy_port_dir_all2str() const {
|
||||
std::string full_types = "[";
|
||||
for (int itype = size_t(IoNameMap::e_dummy_port_direction::INPUT);
|
||||
itype != size_t(IoNameMap::e_dummy_port_direction::NUM_TYPES); ++itype) {
|
||||
full_types += std::string(DUMMY_PORT_DIR_STRING_[itype]) + std::string("|");
|
||||
}
|
||||
full_types.pop_back();
|
||||
full_types += "]";
|
||||
return full_types;
|
||||
}
|
||||
|
||||
IoNameMap::e_dummy_port_direction IoNameMap::str2dummy_port_dir(
|
||||
const std::string& dir_str, const bool& verbose) const {
|
||||
for (int itype = size_t(IoNameMap::e_dummy_port_direction::INPUT);
|
||||
itype != size_t(IoNameMap::e_dummy_port_direction::NUM_TYPES); ++itype) {
|
||||
if (dir_str == std::string(DUMMY_PORT_DIR_STRING_[itype])) {
|
||||
return static_cast<IoNameMap::e_dummy_port_direction>(itype);
|
||||
}
|
||||
}
|
||||
VTR_LOGV_ERROR(verbose, "Invalid direction for dummy port! Expect %s\n",
|
||||
dummy_port_dir_all2str().c_str());
|
||||
return IoNameMap::e_dummy_port_direction::NUM_TYPES;
|
||||
}
|
||||
|
||||
std::string IoNameMap::dummy_port_dir2str(const e_dummy_port_direction& dir,
|
||||
const bool& verbose) const {
|
||||
if (!valid_dummy_port_direction(dir)) {
|
||||
VTR_LOGV_ERROR(verbose, "Invalid direction for dummy port! Expect %s\n",
|
||||
dummy_port_dir_all2str().c_str());
|
||||
return std::string();
|
||||
}
|
||||
return std::string(DUMMY_PORT_DIR_STRING_[size_t(dir)]);
|
||||
}
|
||||
|
||||
bool IoNameMap::valid_dummy_port_direction(
|
||||
const IoNameMap::e_dummy_port_direction& direction) const {
|
||||
return direction != IoNameMap::e_dummy_port_direction::NUM_TYPES;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,111 @@
|
|||
#ifndef IO_NAME_MAP_H
|
||||
#define IO_NAME_MAP_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files required by the data structure definition
|
||||
*******************************************************************/
|
||||
#include <map>
|
||||
|
||||
#include "openfpga_port.h"
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/**
|
||||
* @brief I/O name map is a data structure to show mapping between the ports
|
||||
* of fpga_top and fpga_core, which are the two possible top-level modules that
|
||||
* modeling a complete FPGA fabric Using the data structure, developers can find
|
||||
* - the corresponding port of fpga_core, with a given port of fpga_top
|
||||
* - the corresponding port of fpga_top, with a given port of fpga_core
|
||||
*/
|
||||
class IoNameMap {
|
||||
public: /* Types */
|
||||
enum class e_dummy_port_direction { INPUT = 0, OUTPUT, INOUT, NUM_TYPES };
|
||||
enum class e_port_mapping_status {
|
||||
NONE = 0,
|
||||
PARTIAL,
|
||||
FULL,
|
||||
OVERLAPPED,
|
||||
NUM_TYPES
|
||||
};
|
||||
|
||||
public: /* Constructors */
|
||||
IoNameMap();
|
||||
|
||||
public: /* Public accessors */
|
||||
/** @brief Get all the fpga top ports */
|
||||
std::vector<BasicPort> fpga_top_ports() const;
|
||||
/** @brief With a given port at fpga_top, find the corresponding I/O at
|
||||
* fpga_core. Return an invalid port if not found */
|
||||
BasicPort fpga_core_port(const BasicPort& fpga_top_port) const;
|
||||
/** @brief With a given port at fpga_core, find the corresponding I/O at
|
||||
* fpga_top. Return an invalid port if not found */
|
||||
BasicPort fpga_top_port(const BasicPort& fpga_core_port) const;
|
||||
/** @brief Identify if the fpga_top port is dummy or not */
|
||||
bool fpga_top_port_is_dummy(const BasicPort& fpga_top_port) const;
|
||||
/** @brief Get the direction of a dummy port */
|
||||
e_dummy_port_direction fpga_top_dummy_port_direction(
|
||||
const BasicPort& fpga_top_port) const;
|
||||
/** @brief Check if a core port, by considering its port name only, has been
|
||||
* fully/partially/no mapped to fpga top. For example, there is a core port
|
||||
* 'a[0:3]', while only 'a[0]' is mapped to the fpga top. We can use the port
|
||||
* name find it quickly
|
||||
*/
|
||||
e_port_mapping_status fpga_core_port_mapping_status(
|
||||
const BasicPort& fpga_core_port, const bool& verbose = false) const;
|
||||
/** @brief Identify if there are any naming rules inside */
|
||||
bool empty() const;
|
||||
|
||||
public: /* Public mutators */
|
||||
/** @brief Create the one-on-one mapping between an port of fpga_top and
|
||||
* fpga_core. Return 0 for success, return 1 for fail */
|
||||
int set_io_pair(const BasicPort& fpga_top_port,
|
||||
const BasicPort& fpga_core_port);
|
||||
/** @brief Add a dummy port at the fpga top, which is not mapped any port at
|
||||
* fpga_core */
|
||||
int set_dummy_io(const BasicPort& fpga_top_port,
|
||||
const e_dummy_port_direction& direction);
|
||||
|
||||
public: /* Public utility */
|
||||
/** @brief Parse the dummy port direction from string to valid type. Parser
|
||||
* error can be turned on */
|
||||
e_dummy_port_direction str2dummy_port_dir(const std::string& dir_str,
|
||||
const bool& verbose = false) const;
|
||||
/** @brief Output the string representing dummy port direction */
|
||||
std::string dummy_port_dir2str(const e_dummy_port_direction& dir,
|
||||
const bool& verbose = false) const;
|
||||
/** @brief Validate the dummy port direction */
|
||||
bool valid_dummy_port_direction(
|
||||
const e_dummy_port_direction& direction) const;
|
||||
|
||||
private: /* Internal utility */
|
||||
/* Convert a port info to string, which can be used to store keys */
|
||||
std::string port2str(const BasicPort& port) const;
|
||||
/* Convert a string to port, which can be used to echo internal info */
|
||||
BasicPort str2port(const std::string& port_str) const;
|
||||
/* Generate a string include all the valid directions of the dummy port.
|
||||
* Useful for printing debugging messages */
|
||||
std::string dummy_port_dir_all2str() const;
|
||||
|
||||
private: /* Internal Data */
|
||||
/* fpga_top -> fpga_core io_name_keys. Use the port name to find all the port
|
||||
* details. For instance: prog_clk -> ["prog_clk[0:1]", "prog_clk[2:3]"] The
|
||||
* keys are then used to spot the fpga core ports in the io_name_map_. For
|
||||
* instance: "prog_clk[0:1]" -> pclk[0:1]
|
||||
*/
|
||||
std::map<std::string, std::vector<std::string>> top2core_io_name_keys_;
|
||||
std::map<std::string, BasicPort> top2core_io_name_map_;
|
||||
|
||||
std::map<std::string, std::vector<std::string>> core2top_io_name_keys_;
|
||||
std::map<std::string, BasicPort> core2top_io_name_map_;
|
||||
|
||||
std::map<std::string, e_dummy_port_direction> dummy_port_direction_;
|
||||
|
||||
/* Constants */
|
||||
std::array<const char*, size_t(e_dummy_port_direction::NUM_TYPES)>
|
||||
DUMMY_PORT_DIR_STRING_;
|
||||
};
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef IO_NAME_MAP_XML_CONSTANTS_H
|
||||
#define IO_NAME_MAP_XML_CONSTANTS_H
|
||||
|
||||
/* Constants required by XML parser */
|
||||
|
||||
constexpr const char* XML_IO_NAME_MAP_ROOT_NAME = "ports";
|
||||
constexpr const char* XML_IO_NAME_MAP_NODE_NAME = "port";
|
||||
constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_TOP_NAME = "top_name";
|
||||
constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME = "core_name";
|
||||
constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY = "is_dummy";
|
||||
constexpr const char* XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION = "direction";
|
||||
|
||||
constexpr std::array<const char*, 3>
|
||||
XML_IO_NAME_MAP_DUMMY_PORT_DIRECTION_STRING = {
|
||||
{"input", "output", "inout"}}; // String versions of side orientations
|
||||
|
||||
#endif
|
|
@ -0,0 +1,104 @@
|
|||
/********************************************************************
|
||||
* This file includes the top-level function of this library
|
||||
* which reads an XML of clock network file to the associated
|
||||
* data structures
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
|
||||
/* Headers from pugi XML library */
|
||||
#include "pugixml.hpp"
|
||||
#include "pugixml_util.hpp"
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from libopenfpga util library */
|
||||
#include "openfpga_port_parser.h"
|
||||
|
||||
/* Headers from libarchfpga */
|
||||
#include "arch_error.h"
|
||||
#include "command_exit_codes.h"
|
||||
#include "io_name_map_xml_constants.h"
|
||||
#include "read_xml_io_name_map.h"
|
||||
#include "read_xml_util.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes of a <port> to an object of I/O naming
|
||||
*******************************************************************/
|
||||
static int read_xml_io_map_port(pugi::xml_node& xml_port,
|
||||
const pugiutil::loc_data& loc_data,
|
||||
IoNameMap& io_name_map) {
|
||||
/* Parse fpga top port information */
|
||||
std::string top_name =
|
||||
get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_TOP_NAME, loc_data)
|
||||
.as_string();
|
||||
BasicPort top_port = openfpga::PortParser(top_name).port();
|
||||
|
||||
/* For dummy port, create the dummy io */
|
||||
bool is_dummy = get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY,
|
||||
loc_data, pugiutil::ReqOpt::OPTIONAL)
|
||||
.as_bool(false);
|
||||
if (is_dummy) {
|
||||
std::string dir_str =
|
||||
get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION, loc_data)
|
||||
.as_string();
|
||||
IoNameMap::e_dummy_port_direction dummy_port_dir =
|
||||
io_name_map.str2dummy_port_dir(dir_str, true);
|
||||
if (!io_name_map.valid_dummy_port_direction(dummy_port_dir)) {
|
||||
VTR_LOG_ERROR("Invalid direction for dummy port '%s'!\n",
|
||||
top_port.to_verilog_string().c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
return io_name_map.set_dummy_io(top_port,
|
||||
dummy_port_dir); /* Early return */
|
||||
}
|
||||
|
||||
/* This is not a dummy io, create the io mapping */
|
||||
std::string core_name =
|
||||
get_attribute(xml_port, XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME, loc_data)
|
||||
.as_string();
|
||||
BasicPort core_port = openfpga::PortParser(core_name).port();
|
||||
|
||||
return io_name_map.set_io_pair(top_port, core_port);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes about <ports> to an object of ClockNetwork
|
||||
*******************************************************************/
|
||||
int read_xml_io_name_map(const char* fname, IoNameMap& io_name_map) {
|
||||
vtr::ScopedStartFinishTimer timer("Read I/O naming rules");
|
||||
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
|
||||
/* Parse the file */
|
||||
pugi::xml_document doc;
|
||||
pugiutil::loc_data loc_data;
|
||||
|
||||
try {
|
||||
loc_data = pugiutil::load_xml(doc, fname);
|
||||
|
||||
pugi::xml_node xml_root =
|
||||
get_single_child(doc, XML_IO_NAME_MAP_ROOT_NAME, loc_data);
|
||||
|
||||
for (pugi::xml_node xml_port : xml_root.children()) {
|
||||
/* Error out if the XML child has an invalid name! */
|
||||
if (xml_port.name() != std::string(XML_IO_NAME_MAP_NODE_NAME)) {
|
||||
bad_tag(xml_port, loc_data, xml_root, {XML_IO_NAME_MAP_NODE_NAME});
|
||||
}
|
||||
status = read_xml_io_map_port(xml_port, loc_data, io_name_map);
|
||||
if (status != CMD_EXEC_SUCCESS) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
} catch (pugiutil::XmlError& e) {
|
||||
archfpga_throw(fname, e.line(), "%s", e.what());
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
} // End of namespace openfpga
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef READ_XML_IO_NAME_MAP_H
|
||||
#define READ_XML_IO_NAME_MAP_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include "io_name_map.h"
|
||||
#include "pugixml.hpp"
|
||||
#include "pugixml_util.hpp"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
int read_xml_io_name_map(const char* fname, IoNameMap& io_name_map);
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -0,0 +1,106 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that outputs a clock network object to XML
|
||||
*format
|
||||
*******************************************************************/
|
||||
/* Headers from system goes first */
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from openfpga util library */
|
||||
#include "openfpga_digest.h"
|
||||
|
||||
/* Headers from arch openfpga library */
|
||||
#include "write_xml_utils.h"
|
||||
|
||||
/* Headers from pin constraint library */
|
||||
#include "io_name_map_xml_constants.h"
|
||||
#include "write_xml_io_name_map.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output a I/O name mapping to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
static int write_xml_io_map_port(std::fstream& fp, const IoNameMap& io_name_map,
|
||||
const BasicPort& fpga_top_port) {
|
||||
/* Validate the file stream */
|
||||
if (false == openfpga::valid_file_stream(fp)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
openfpga::write_tab_to_file(fp, 1);
|
||||
fp << "<" << XML_IO_NAME_MAP_NODE_NAME << "";
|
||||
write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_TOP_NAME,
|
||||
generate_xml_port_name(fpga_top_port).c_str());
|
||||
|
||||
if (io_name_map.fpga_top_port_is_dummy(fpga_top_port)) {
|
||||
write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_IS_DUMMY, "true");
|
||||
IoNameMap::e_dummy_port_direction dir =
|
||||
io_name_map.fpga_top_dummy_port_direction(fpga_top_port);
|
||||
write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_DIRECTION,
|
||||
io_name_map.dummy_port_dir2str(dir, true).c_str());
|
||||
} else {
|
||||
BasicPort fpga_core_port = io_name_map.fpga_core_port(fpga_top_port);
|
||||
write_xml_attribute(fp, XML_IO_NAME_MAP_ATTRIBUTE_CORE_NAME,
|
||||
generate_xml_port_name(fpga_core_port).c_str());
|
||||
}
|
||||
fp << ">"
|
||||
<< "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output an object to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
int write_xml_io_name_map(const char* fname, const IoNameMap& io_name_map) {
|
||||
vtr::ScopedStartFinishTimer timer("Write I/O naming rules");
|
||||
|
||||
/* Create a file handler */
|
||||
std::fstream fp;
|
||||
/* Open the file stream */
|
||||
fp.open(std::string(fname), std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate the file stream */
|
||||
openfpga::check_file_stream(fname, fp);
|
||||
|
||||
/* Write the root node */
|
||||
fp << "<" << XML_IO_NAME_MAP_ROOT_NAME;
|
||||
fp << ">"
|
||||
<< "\n";
|
||||
|
||||
int err_code = 0;
|
||||
|
||||
/* Write each port */
|
||||
for (BasicPort fpga_top_port : io_name_map.fpga_top_ports()) {
|
||||
/* Write bus */
|
||||
err_code = write_xml_io_map_port(fp, io_name_map, fpga_top_port);
|
||||
if (0 != err_code) {
|
||||
return err_code;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish writing the root node */
|
||||
fp << "</" << XML_IO_NAME_MAP_ROOT_NAME << ">"
|
||||
<< "\n";
|
||||
|
||||
/* Close the file stream */
|
||||
fp.close();
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
} // End of namespace openfpga
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef WRITE_XML_IO_NAME_MAP_H
|
||||
#define WRITE_XML_IO_NAME_MAP_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
|
||||
#include "io_name_map.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
int write_xml_io_name_map(const char* fname, const IoNameMap& io_name_map);
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -0,0 +1,38 @@
|
|||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from readarchopenfpga */
|
||||
#include "read_xml_io_name_map.h"
|
||||
#include "write_xml_io_name_map.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
/* Ensure we have only one or two argument */
|
||||
VTR_ASSERT((2 == argc) || (3 == argc));
|
||||
|
||||
int status = 0;
|
||||
|
||||
/* Parse the circuit library from an XML file */
|
||||
openfpga::IoNameMap io_name_map;
|
||||
status = openfpga::read_xml_io_name_map(argv[1], io_name_map);
|
||||
if (status != 0) {
|
||||
return status;
|
||||
}
|
||||
VTR_LOG("Parsed %lu fpga top ports from XML.\n",
|
||||
io_name_map.fpga_top_ports().size());
|
||||
|
||||
/* Output the bus group to an XML file
|
||||
* This is optional only used when there is a second argument
|
||||
*/
|
||||
if (3 <= argc) {
|
||||
status = openfpga::write_xml_io_name_map(argv[2], io_name_map);
|
||||
VTR_LOG("Write the I/O name mapping to an XML file: %s.\n", argv[2]);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
|
@ -2,10 +2,10 @@
|
|||
* This file includes functions that are used to decode integer to binary
|
||||
*vectors or the reverse operation
|
||||
***************************************************************************************/
|
||||
#include <cmath>
|
||||
|
||||
/* Headers from vtrutil library */
|
||||
#include "openfpga_decode.h"
|
||||
|
||||
#include "vtr_assert.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
|
@ -109,12 +109,28 @@ std::string combine_two_1hot_str(const std::string& code1,
|
|||
* Output:
|
||||
* index | 0 | 1 | 2
|
||||
* ret | 0 | 0 | 1
|
||||
*
|
||||
* ToDo: Need to revisit and change all the feature that call to this function
|
||||
* Had studied the code, should be safe to make the change
|
||||
* Apparently we only want to store 0 or 1 (binary vector)
|
||||
* Yet we store it in a vector on size_t (8 Bytes)
|
||||
* We are using 8x memory that we supposed to
|
||||
* uint8_t is good enough
|
||||
* Not a bug, but is a serious unoptimized issue
|
||||
********************************************************************/
|
||||
std::vector<size_t> itobin_vec(const size_t& in_int, const size_t& bin_len) {
|
||||
/* bin_len must be in valid range*/
|
||||
VTR_ASSERT(bin_len > 0 && bin_len <= 64);
|
||||
std::vector<size_t> ret(bin_len, 0);
|
||||
|
||||
/* Make sure we do not have any overflow! */
|
||||
VTR_ASSERT((in_int < pow(2., bin_len)));
|
||||
/* If the length is 64 bits, in_int can be any number since this is the max
|
||||
bit length, and BTW pow(2, 64) itself should be zero (from 64bits size_t
|
||||
perspective). Once we fix the bintoi_charvec() bug, without this change, it
|
||||
will cause assertion */
|
||||
if (bin_len < 64) {
|
||||
VTR_ASSERT(in_int < (size_t(1) << bin_len));
|
||||
}
|
||||
|
||||
size_t temp = in_int;
|
||||
for (size_t i = 0; i < bin_len; i++) {
|
||||
|
@ -140,10 +156,18 @@ std::vector<size_t> itobin_vec(const size_t& in_int, const size_t& bin_len) {
|
|||
* which has a smaller memory footprint than size_t
|
||||
********************************************************************/
|
||||
std::vector<char> itobin_charvec(const size_t& in_int, const size_t& bin_len) {
|
||||
/* bin_len must be in valid range*/
|
||||
VTR_ASSERT(bin_len > 0 && bin_len <= 64);
|
||||
std::vector<char> ret(bin_len, '0');
|
||||
|
||||
/* Make sure we do not have any overflow! */
|
||||
VTR_ASSERT((in_int < pow(2., bin_len)));
|
||||
/* If the length is 64 bits, in_int can be any number since this is the max
|
||||
bit length, and BTW pow(2, 64) itself should be zero (from 64bits size_t
|
||||
perspective). Once we fix the bintoi_charvec() bug, without this change, it
|
||||
will cause assertion */
|
||||
if (bin_len < 64) {
|
||||
VTR_ASSERT(in_int < (size_t(1) << bin_len));
|
||||
}
|
||||
|
||||
size_t temp = in_int;
|
||||
for (size_t i = 0; i < bin_len; i++) {
|
||||
|
@ -170,11 +194,14 @@ std::vector<char> itobin_charvec(const size_t& in_int, const size_t& bin_len) {
|
|||
* which has a smaller memory footprint than size_t
|
||||
********************************************************************/
|
||||
size_t bintoi_charvec(const std::vector<char>& bin) {
|
||||
/* bin.size() must be in valid range*/
|
||||
VTR_ASSERT(bin.size() > 0 && bin.size() <= 64);
|
||||
|
||||
size_t ret = 0;
|
||||
|
||||
for (size_t i = 0; i < bin.size(); ++i) {
|
||||
if ('1' == bin[i]) {
|
||||
ret += pow(2., i);
|
||||
ret |= ((size_t)(1) << i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,6 +111,25 @@ std::string BasicPort::to_verilog_string() const {
|
|||
std::to_string(get_msb()) + "]";
|
||||
}
|
||||
|
||||
size_t BasicPort::find_ipin(const BasicPort& ref_port) const {
|
||||
/* Port name should match first */
|
||||
if (!this->mergeable(ref_port)) {
|
||||
return get_width(); /* Name does not match, no need to find the pin index,
|
||||
return an invalid range */
|
||||
}
|
||||
/* it should be only a pin (width = 1) */
|
||||
if (!ref_port.is_valid() || ref_port.get_width() != 1) {
|
||||
return get_width(); /* Return an invalid range */
|
||||
}
|
||||
/* Must cache the pin list otherwise the begin() and end() are not constant */
|
||||
auto pin_list = pins();
|
||||
auto it = std::find(pin_list.begin(), pin_list.end(), ref_port.get_lsb());
|
||||
if (it == pin_list.end()) {
|
||||
return get_width(); /* Out of range, return an invalid range */
|
||||
}
|
||||
return it - pin_list.begin();
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Overloaded operators
|
||||
***********************************************************************/
|
||||
|
|
|
@ -37,6 +37,10 @@ class BasicPort {
|
|||
size_t get_origin_port_width() const;
|
||||
std::string to_verilog_string()
|
||||
const; /* Generate verilog-style string, e.g., a[0:1] */
|
||||
/** @brief Find the index of the pin in the reference port w.r.t. to this
|
||||
* port. For example, given a reference pin 'a[1]', this port is 'a[0:2]', the
|
||||
* pin is the second pin in the port. As a result, the index will be 1. */
|
||||
size_t find_ipin(const BasicPort& ref_port) const;
|
||||
|
||||
public: /* Mutators */
|
||||
void set(const BasicPort& basic_port); /* copy */
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace openfpga {
|
|||
|
||||
/* Top-level module name */
|
||||
constexpr const char* FPGA_TOP_MODULE_NAME = "fpga_top";
|
||||
constexpr const char* FPGA_CORE_MODULE_NAME = "fpga_core";
|
||||
constexpr const char* FPGA_CORE_INSTANCE_NAME = "fpga_instance";
|
||||
|
||||
/* Configuration chain naming constant strings */
|
||||
constexpr const char* CONFIGURABLE_MEMORY_CHAIN_IN_NAME = "ccff_head";
|
||||
|
@ -39,6 +41,11 @@ constexpr const char* GRID_MEM_INSTANCE_PREFIX = "mem_";
|
|||
constexpr const char* SWITCH_BLOCK_MEM_INSTANCE_PREFIX = "mem_";
|
||||
constexpr const char* CONNECTION_BLOCK_MEM_INSTANCE_PREFIX = "mem_";
|
||||
constexpr const char* MEMORY_MODULE_POSTFIX = "_mem";
|
||||
constexpr const char* MEMORY_FEEDTHROUGH_MODULE_POSTFIX = "_feedthrough_mem";
|
||||
constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_PORT_NAME =
|
||||
"feedthrough_mem_in";
|
||||
constexpr const char* MEMORY_FEEDTHROUGH_DATA_IN_INV_PORT_NAME =
|
||||
"feedthrough_mem_inb";
|
||||
constexpr const char* MEMORY_BL_PORT_NAME = "bl";
|
||||
constexpr const char* MEMORY_WL_PORT_NAME = "wl";
|
||||
constexpr const char* MEMORY_WLR_PORT_NAME = "wlr";
|
||||
|
@ -66,6 +73,7 @@ constexpr const char* INV_PORT_POSTFIX = "_inv";
|
|||
/* Bitstream file strings */
|
||||
constexpr const char* BITSTREAM_XML_FILE_NAME_POSTFIX = "_bitstream.xml";
|
||||
|
||||
constexpr const char* DEFAULT_TILE_DIR_NAME = "tile/";
|
||||
constexpr const char* DEFAULT_LB_DIR_NAME = "lb/";
|
||||
constexpr const char* DEFAULT_RR_DIR_NAME = "routing/";
|
||||
constexpr const char* DEFAULT_SUBMODULE_DIR_NAME = "sub_module/";
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
project("libtileconfig")
|
||||
|
||||
file(GLOB_RECURSE EXEC_SOURCES test/*.cpp)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*/*.h)
|
||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||
|
||||
#Remove test executable from library
|
||||
list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES})
|
||||
|
||||
#Create the library
|
||||
add_library(libtileconfig STATIC
|
||||
${LIB_HEADERS}
|
||||
${LIB_SOURCES})
|
||||
target_include_directories(libtileconfig PUBLIC ${LIB_INCLUDE_DIRS})
|
||||
set_target_properties(libtileconfig PROPERTIES PREFIX "") #Avoid extra 'lib' prefix
|
||||
|
||||
#Specify link-time dependancies
|
||||
target_link_libraries(libtileconfig
|
||||
libarchopenfpga
|
||||
libopenfpgautil
|
||||
libopenfpgashell
|
||||
libvtrutil
|
||||
libpugiutil)
|
||||
|
||||
#Create the test executable
|
||||
foreach(testsourcefile ${EXEC_SOURCES})
|
||||
# Use a simple string replace, to cut off .cpp.
|
||||
get_filename_component(testname ${testsourcefile} NAME_WE)
|
||||
add_executable(${testname} ${testsourcefile})
|
||||
# Make sure the library is linked to each test executable
|
||||
target_link_libraries(${testname} libtileconfig)
|
||||
endforeach(testsourcefile ${EXEC_SOURCES})
|
||||
|
||||
install(TARGETS libtileconfig DESTINATION bin)
|
|
@ -0,0 +1 @@
|
|||
<tiles style="top_left"/>
|
|
@ -0,0 +1,76 @@
|
|||
/******************************************************************************
|
||||
* Memember functions for data structure TileConfig
|
||||
******************************************************************************/
|
||||
#include "tile_config.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "command_exit_codes.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
TileConfig::TileConfig() {
|
||||
style_ = TileConfig::e_style::NUM_TYPES; /* Deposit an invalid value */
|
||||
STYLE_STRING_ = {"top_left", "top_right", "bottom_left", "bottom_right",
|
||||
"custom"};
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Public Accessors
|
||||
*************************************************/
|
||||
TileConfig::e_style TileConfig::style() const { return style_; }
|
||||
|
||||
std::string TileConfig::style_to_string() const { return style2str(style_); }
|
||||
|
||||
bool TileConfig::is_valid() const {
|
||||
return style_ != TileConfig::e_style::NUM_TYPES;
|
||||
}
|
||||
|
||||
int TileConfig::set_style(const std::string& value) {
|
||||
style_ = str2style(value);
|
||||
return valid_style(style_);
|
||||
}
|
||||
|
||||
std::string TileConfig::style_all2str() const {
|
||||
std::string full_types = "[";
|
||||
for (int itype = size_t(TileConfig::e_style::TOP_LEFT);
|
||||
itype != size_t(TileConfig::e_style::NUM_TYPES); ++itype) {
|
||||
full_types += std::string(STYLE_STRING_[itype]) + std::string("|");
|
||||
}
|
||||
full_types.pop_back();
|
||||
full_types += "]";
|
||||
return full_types;
|
||||
}
|
||||
|
||||
TileConfig::e_style TileConfig::str2style(const std::string& style_str,
|
||||
const bool& verbose) const {
|
||||
for (int itype = size_t(TileConfig::e_style::TOP_LEFT);
|
||||
itype != size_t(TileConfig::e_style::NUM_TYPES); ++itype) {
|
||||
if (style_str == std::string(STYLE_STRING_[itype])) {
|
||||
return static_cast<TileConfig::e_style>(itype);
|
||||
}
|
||||
}
|
||||
VTR_LOGV_ERROR(verbose, "Invalid style for tile configuration! Expect %s\n",
|
||||
style_all2str().c_str());
|
||||
return TileConfig::e_style::NUM_TYPES;
|
||||
}
|
||||
|
||||
std::string TileConfig::style2str(const TileConfig::e_style& style,
|
||||
const bool& verbose) const {
|
||||
if (!valid_style(style)) {
|
||||
VTR_LOGV_ERROR(verbose, "Invalid style for tile configuration! Expect %s\n",
|
||||
style_all2str().c_str());
|
||||
return std::string();
|
||||
}
|
||||
return std::string(STYLE_STRING_[size_t(style)]);
|
||||
}
|
||||
|
||||
bool TileConfig::valid_style(const TileConfig::e_style& style) const {
|
||||
return style != TileConfig::e_style::NUM_TYPES;
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef TILE_CONFIG_H
|
||||
#define TILE_CONFIG_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files required by the data structure definition
|
||||
*******************************************************************/
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
/* Begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/**
|
||||
* @brief tile configuration is a data structure to represent how programmable
|
||||
* blocks and routing blocks are grouped into tiles
|
||||
*/
|
||||
class TileConfig {
|
||||
public: /* Types */
|
||||
enum class e_style {
|
||||
TOP_LEFT = 0,
|
||||
TOP_RIGHT,
|
||||
BOTTOM_LEFT,
|
||||
BOTTOM_RIGHT,
|
||||
CUSTOM,
|
||||
NUM_TYPES
|
||||
};
|
||||
|
||||
public: /* Constructors */
|
||||
TileConfig();
|
||||
|
||||
public: /* Public accessors */
|
||||
/** @brief Get all the fpga style */
|
||||
e_style style() const;
|
||||
std::string style_to_string() const;
|
||||
|
||||
public: /* Public mutators */
|
||||
/** @brief Create the one-on-one mapping between an port of fpga_top and
|
||||
* fpga_core. Return 0 for success, return 1 for fail */
|
||||
int set_style(const std::string& value);
|
||||
|
||||
public: /* Public utility */
|
||||
/** @brief identify if the tile config is valid or not */
|
||||
bool is_valid() const;
|
||||
/** @brief Parse the style from string to valid type. Parser
|
||||
* error can be turned on */
|
||||
e_style str2style(const std::string& style_str,
|
||||
const bool& verbose = false) const;
|
||||
/** @brief Output the string representing style */
|
||||
std::string style2str(const e_style& style,
|
||||
const bool& verbose = false) const;
|
||||
/** @brief Validate the style */
|
||||
bool valid_style(const e_style& style) const;
|
||||
|
||||
private: /* Internal utility */
|
||||
/* Generate a string include all the valid style
|
||||
* Useful for printing debugging messages */
|
||||
std::string style_all2str() const;
|
||||
|
||||
private: /* Internal Data */
|
||||
e_style style_;
|
||||
/* Constants */
|
||||
std::array<const char*, size_t(e_style::NUM_TYPES)> STYLE_STRING_;
|
||||
};
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
/********************************************************************
|
||||
* This file includes the top-level function of this library
|
||||
* which reads an XML of clock network file to the associated
|
||||
* data structures
|
||||
*******************************************************************/
|
||||
#include <string>
|
||||
|
||||
/* Headers from pugi XML library */
|
||||
#include "pugixml.hpp"
|
||||
#include "pugixml_util.hpp"
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from libarchfpga */
|
||||
#include "arch_error.h"
|
||||
#include "command_exit_codes.h"
|
||||
#include "read_xml_tile_config.h"
|
||||
#include "read_xml_util.h"
|
||||
#include "tile_config_xml_constants.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/********************************************************************
|
||||
* Parse XML codes about <ports> to an object of ClockNetwork
|
||||
*******************************************************************/
|
||||
int read_xml_tile_config(const char* fname, TileConfig& tile_config) {
|
||||
vtr::ScopedStartFinishTimer timer("Read tile configuration rules");
|
||||
|
||||
int status = CMD_EXEC_SUCCESS;
|
||||
|
||||
/* Parse the file */
|
||||
pugi::xml_document doc;
|
||||
pugiutil::loc_data loc_data;
|
||||
|
||||
try {
|
||||
loc_data = pugiutil::load_xml(doc, fname);
|
||||
|
||||
pugi::xml_node xml_root =
|
||||
get_single_child(doc, XML_TILE_CONFIG_ROOT_NAME, loc_data);
|
||||
|
||||
std::string style =
|
||||
get_attribute(xml_root, XML_TILE_CONFIG_ATTRIBUTE_STYLE_NAME, loc_data)
|
||||
.as_string();
|
||||
tile_config.set_style(style);
|
||||
} catch (pugiutil::XmlError& e) {
|
||||
archfpga_throw(fname, e.line(), "%s", e.what());
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
} // End of namespace openfpga
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef READ_XML_TILE_CONFIG_H
|
||||
#define READ_XML_TILE_CONFIG_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include "pugixml.hpp"
|
||||
#include "pugixml_util.hpp"
|
||||
#include "tile_config.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
int read_xml_tile_config(const char* fname, TileConfig& tile_config);
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef TILE_CONFIG_XML_CONSTANTS_H
|
||||
#define TILE_CONFIG_XML_CONSTANTS_H
|
||||
|
||||
/* Constants required by XML parser */
|
||||
constexpr const char* XML_TILE_CONFIG_ROOT_NAME = "tiles";
|
||||
constexpr const char* XML_TILE_CONFIG_ATTRIBUTE_STYLE_NAME = "style";
|
||||
|
||||
#endif
|
|
@ -0,0 +1,59 @@
|
|||
/********************************************************************
|
||||
* This file includes functions that outputs a clock network object to XML
|
||||
*format
|
||||
*******************************************************************/
|
||||
/* Headers from system goes first */
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
/* Headers from vtr util library */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
/* Headers from arch openfpga library */
|
||||
#include "openfpga_digest.h"
|
||||
#include "write_xml_utils.h"
|
||||
|
||||
/* Headers from pin constraint library */
|
||||
#include "tile_config_xml_constants.h"
|
||||
#include "write_xml_tile_config.h"
|
||||
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
/********************************************************************
|
||||
* A writer to output an object to XML format
|
||||
*
|
||||
* Return 0 if successful
|
||||
* Return 1 if there are more serious bugs in the architecture
|
||||
* Return 2 if fail when creating files
|
||||
*******************************************************************/
|
||||
int write_xml_tile_config(const char* fname, const TileConfig& tile_config) {
|
||||
vtr::ScopedStartFinishTimer timer("Write tile configuration rules");
|
||||
|
||||
/* Create a file handler */
|
||||
std::fstream fp;
|
||||
/* Open the file stream */
|
||||
fp.open(std::string(fname), std::fstream::out | std::fstream::trunc);
|
||||
|
||||
/* Validate the file stream */
|
||||
openfpga::check_file_stream(fname, fp);
|
||||
|
||||
int err_code = 0;
|
||||
|
||||
/* Write the root node */
|
||||
fp << "<" << XML_TILE_CONFIG_ROOT_NAME;
|
||||
|
||||
write_xml_attribute(fp, XML_TILE_CONFIG_ATTRIBUTE_STYLE_NAME,
|
||||
tile_config.style_to_string().c_str());
|
||||
/* Finish writing the root node */
|
||||
fp << "/>"
|
||||
<< "\n";
|
||||
|
||||
/* Close the file stream */
|
||||
fp.close();
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
} // End of namespace openfpga
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef WRITE_XML_TILE_CONFIG_H
|
||||
#define WRITE_XML_TILE_CONFIG_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files that are required by function declaration
|
||||
*******************************************************************/
|
||||
#include <fstream>
|
||||
|
||||
#include "tile_config.h"
|
||||
|
||||
/********************************************************************
|
||||
* Function declaration
|
||||
*******************************************************************/
|
||||
namespace openfpga { // Begin namespace openfpga
|
||||
|
||||
int write_xml_tile_config(const char* fname, const TileConfig& tile_config);
|
||||
|
||||
} // End of namespace openfpga
|
||||
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
/********************************************************************
|
||||
* Unit test functions to validate the correctness of
|
||||
* 1. parser of data structures
|
||||
* 2. writer of data structures
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutils */
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* Headers from readarchopenfpga */
|
||||
#include "read_xml_tile_config.h"
|
||||
#include "write_xml_tile_config.h"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
/* Ensure we have only one or two argument */
|
||||
VTR_ASSERT((2 == argc) || (3 == argc));
|
||||
|
||||
int status = 0;
|
||||
|
||||
/* Parse the circuit library from an XML file */
|
||||
openfpga::TileConfig tile_config;
|
||||
status = openfpga::read_xml_tile_config(argv[1], tile_config);
|
||||
if (status != 0) {
|
||||
return status;
|
||||
}
|
||||
VTR_LOG("Parsed tile configuration from XML file: %s\n", argv[1]);
|
||||
|
||||
/* Output the bus group to an XML file
|
||||
* This is optional only used when there is a second argument
|
||||
*/
|
||||
if (3 <= argc) {
|
||||
status = openfpga::write_xml_tile_config(argv[2], tile_config);
|
||||
VTR_LOG("Write the tile configuration to an XML file: %s.\n", argv[2]);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
|
@ -133,7 +133,7 @@ goto-task () {
|
|||
done
|
||||
}
|
||||
|
||||
# Clears enviroment variables and fucntions
|
||||
# Clears environment variables and functions
|
||||
unset-openfpga (){
|
||||
unset -v OPENFPGA_PATH
|
||||
unset -f list-tasks run-task run-flow goto-task goto-root >/dev/null 2>&1
|
||||
|
|
|
@ -41,6 +41,8 @@ target_link_libraries(libopenfpga
|
|||
libpcf
|
||||
libvtrutil
|
||||
libbusgroup
|
||||
libionamemap
|
||||
libtileconfig
|
||||
libpugixml
|
||||
libvpr)
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace openfpga {
|
|||
|
||||
/* Build a RRChan Object with the given channel type and coorindators */
|
||||
static RRChan build_one_rr_chan(const DeviceContext& vpr_device_ctx,
|
||||
const t_rr_type& chan_type,
|
||||
const t_rr_type& chan_type, const size_t& layer,
|
||||
vtr::Point<size_t>& chan_coord) {
|
||||
std::vector<RRNodeId> chan_rr_nodes;
|
||||
|
||||
|
@ -32,7 +32,7 @@ static RRChan build_one_rr_chan(const DeviceContext& vpr_device_ctx,
|
|||
|
||||
/* Collect rr_nodes for this channel */
|
||||
chan_rr_nodes = find_rr_graph_chan_nodes(
|
||||
vpr_device_ctx.rr_graph, chan_coord.x(), chan_coord.y(), chan_type);
|
||||
vpr_device_ctx.rr_graph, layer, chan_coord.x(), chan_coord.y(), chan_type);
|
||||
/* Fill the rr_chan */
|
||||
for (const RRNodeId& chan_rr_node : chan_rr_nodes) {
|
||||
rr_chan.add_node(vpr_device_ctx.rr_graph, chan_rr_node,
|
||||
|
@ -96,6 +96,7 @@ static RRChan build_one_rr_chan(const DeviceContext& vpr_device_ctx,
|
|||
*/
|
||||
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) {
|
||||
/* Create an object to return */
|
||||
|
@ -133,7 +134,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
/* Routing channels*/
|
||||
/* Side: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
||||
/* Create a rr_chan object and check if it is unique in the graph */
|
||||
rr_chan = build_one_rr_chan(vpr_device_ctx, CHANY, coordinate);
|
||||
rr_chan = build_one_rr_chan(vpr_device_ctx, CHANY, layer, coordinate);
|
||||
chan_dir_to_port_dir_mapping[0] =
|
||||
OUT_PORT; /* INC_DIRECTION => OUT_PORT */
|
||||
chan_dir_to_port_dir_mapping[1] =
|
||||
|
@ -147,12 +148,12 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
opin_grid_side[1] = LEFT;
|
||||
/* Include Grid[x][y+1] RIGHT side outputs pins */
|
||||
temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x(),
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(),
|
||||
gsb_coord.y() + 1, OPIN, opin_grid_side[0]);
|
||||
/* Include Grid[x+1][y+1] Left side output pins */
|
||||
temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x() + 1,
|
||||
gsb_coord.y() + 1, OPIN, opin_grid_side[1]);
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer,
|
||||
gsb_coord.x() + 1, gsb_coord.y() + 1, OPIN, opin_grid_side[1]);
|
||||
|
||||
break;
|
||||
case RIGHT: /* RIGHT = 1 */
|
||||
|
@ -165,7 +166,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
/* Side: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
||||
/* Collect rr_nodes for Tracks for top: chany[x][y+1] */
|
||||
/* Create a rr_chan object and check if it is unique in the graph */
|
||||
rr_chan = build_one_rr_chan(vpr_device_ctx, CHANX, coordinate);
|
||||
rr_chan = build_one_rr_chan(vpr_device_ctx, CHANX, layer, coordinate);
|
||||
chan_dir_to_port_dir_mapping[0] =
|
||||
OUT_PORT; /* INC_DIRECTION => OUT_PORT */
|
||||
chan_dir_to_port_dir_mapping[1] =
|
||||
|
@ -180,12 +181,12 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
|
||||
/* include Grid[x+1][y+1] Bottom side output pins */
|
||||
temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x() + 1,
|
||||
gsb_coord.y() + 1, OPIN, opin_grid_side[0]);
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer,
|
||||
gsb_coord.x() + 1, gsb_coord.y() + 1, OPIN, opin_grid_side[0]);
|
||||
/* include Grid[x+1][y] Top side output pins */
|
||||
temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x() + 1,
|
||||
gsb_coord.y(), OPIN, opin_grid_side[1]);
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer,
|
||||
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 */
|
||||
|
@ -197,7 +198,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
/* Side: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
||||
/* Collect rr_nodes for Tracks for bottom: chany[x][y] */
|
||||
/* Create a rr_chan object and check if it is unique in the graph */
|
||||
rr_chan = build_one_rr_chan(vpr_device_ctx, CHANY, coordinate);
|
||||
rr_chan = build_one_rr_chan(vpr_device_ctx, CHANY, layer, coordinate);
|
||||
chan_dir_to_port_dir_mapping[0] =
|
||||
IN_PORT; /* INC_DIRECTION => IN_PORT */
|
||||
chan_dir_to_port_dir_mapping[1] =
|
||||
|
@ -211,11 +212,11 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
opin_grid_side[1] = RIGHT;
|
||||
/* include Grid[x+1][y] Left side output pins */
|
||||
temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x() + 1,
|
||||
gsb_coord.y(), OPIN, opin_grid_side[0]);
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer,
|
||||
gsb_coord.x() + 1, gsb_coord.y(), OPIN, opin_grid_side[0]);
|
||||
/* include Grid[x][y] Right side output pins */
|
||||
temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x(),
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(),
|
||||
gsb_coord.y(), OPIN, opin_grid_side[1]);
|
||||
break;
|
||||
case LEFT: /* LEFT = 3 */
|
||||
|
@ -228,7 +229,7 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
/* Side: TOP => 0, RIGHT => 1, BOTTOM => 2, LEFT => 3 */
|
||||
/* Collect rr_nodes for Tracks for left: chanx[x][y] */
|
||||
/* Create a rr_chan object and check if it is unique in the graph */
|
||||
rr_chan = build_one_rr_chan(vpr_device_ctx, CHANX, coordinate);
|
||||
rr_chan = build_one_rr_chan(vpr_device_ctx, CHANX, layer, coordinate);
|
||||
chan_dir_to_port_dir_mapping[0] =
|
||||
IN_PORT; /* INC_DIRECTION => IN_PORT */
|
||||
chan_dir_to_port_dir_mapping[1] =
|
||||
|
@ -241,11 +242,11 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
opin_grid_side[1] = TOP;
|
||||
/* include Grid[x][y+1] Bottom side outputs pins */
|
||||
temp_opin_rr_nodes[0] = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x(),
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(),
|
||||
gsb_coord.y() + 1, OPIN, opin_grid_side[0]);
|
||||
/* include Grid[x][y] Top side output pins */
|
||||
temp_opin_rr_nodes[1] = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, gsb_coord.x(),
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, gsb_coord.x(),
|
||||
gsb_coord.y(), OPIN, opin_grid_side[1]);
|
||||
break;
|
||||
default:
|
||||
|
@ -369,9 +370,9 @@ static RRGSB build_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
continue;
|
||||
}
|
||||
/* Collect IPIN rr_nodes*/
|
||||
temp_ipin_rr_nodes =
|
||||
find_rr_graph_grid_nodes(vpr_device_ctx.rr_graph, vpr_device_ctx.grid, ix,
|
||||
iy, IPIN, ipin_rr_node_grid_side, include_clock);
|
||||
temp_ipin_rr_nodes = find_rr_graph_grid_nodes(
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, layer, ix, iy, IPIN,
|
||||
ipin_rr_node_grid_side, include_clock);
|
||||
/* Fill the ipin nodes of RRGSB */
|
||||
for (const RRNodeId& inode : temp_ipin_rr_nodes) {
|
||||
/* Skip those has no configurable outgoing, they should NOT appear in the
|
||||
|
@ -422,6 +423,7 @@ void annotate_device_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
gsb_range.x(), gsb_range.y());
|
||||
|
||||
size_t gsb_cnt = 0;
|
||||
size_t layer = 0;
|
||||
/* For each switch block, determine the size of array */
|
||||
for (size_t ix = 0; ix < gsb_range.x(); ++ix) {
|
||||
for (size_t iy = 0; iy < gsb_range.y(); ++iy) {
|
||||
|
@ -433,8 +435,7 @@ void annotate_device_rr_gsb(const DeviceContext& vpr_device_ctx,
|
|||
build_rr_gsb(vpr_device_ctx,
|
||||
vtr::Point<size_t>(vpr_device_ctx.grid.width() - 2,
|
||||
vpr_device_ctx.grid.height() - 2),
|
||||
vtr::Point<size_t>(ix, iy), include_clock);
|
||||
|
||||
layer, vtr::Point<size_t>(ix, iy), 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);
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
/* Headers from vpr library */
|
||||
#include "AnalysisDelayCalculator.h"
|
||||
#include "annotate_simulation_setting.h"
|
||||
#include "concrete_timing_info.h"
|
||||
#include "net_delay.h"
|
||||
#include "timing_info.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
@ -212,7 +212,7 @@ int annotate_simulation_setting(
|
|||
make_net_pins_matrix<float>((const Netlist<>&)cluster_ctx.clb_nlist);
|
||||
/* Load the net delays */
|
||||
load_net_delay_from_routing((const Netlist<>&)cluster_ctx.clb_nlist,
|
||||
net_delay, false);
|
||||
net_delay);
|
||||
|
||||
/* Do final timing analysis */
|
||||
auto analysis_delay_calc = std::make_shared<AnalysisDelayCalculator>(
|
||||
|
|
|
@ -44,6 +44,7 @@ static size_t estimate_clock_rr_graph_num_chan_nodes(
|
|||
* Note that switch blocks do not require any new nodes but new edges
|
||||
*******************************************************************/
|
||||
static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids,
|
||||
const size_t& layer,
|
||||
const bool& through_channel,
|
||||
const ClockNetwork& clk_ntwk) {
|
||||
size_t num_nodes = 0;
|
||||
|
@ -54,7 +55,7 @@ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids,
|
|||
/* Bypass if the routing channel does not exist when through channels are
|
||||
* not allowed */
|
||||
if ((false == through_channel) &&
|
||||
(false == is_chanx_exist(grids, chanx_coord))) {
|
||||
(false == is_chanx_exist(grids, layer, chanx_coord))) {
|
||||
continue;
|
||||
}
|
||||
/* Estimate the routing tracks required by clock routing only */
|
||||
|
@ -68,7 +69,7 @@ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids,
|
|||
/* Bypass if the routing channel does not exist when through channel are
|
||||
* not allowed */
|
||||
if ((false == through_channel) &&
|
||||
(false == is_chany_exist(grids, chany_coord))) {
|
||||
(false == is_chany_exist(grids, layer, chany_coord))) {
|
||||
continue;
|
||||
}
|
||||
/* Estimate the routing tracks required by clock routing only */
|
||||
|
@ -87,11 +88,12 @@ static size_t estimate_clock_rr_graph_num_nodes(const DeviceGrid& grids,
|
|||
static void add_rr_graph_block_clock_nodes(
|
||||
RRGraphBuilder& rr_graph_builder, RRClockSpatialLookup& clk_rr_lookup,
|
||||
const RRGraphView& rr_graph_view, const ClockNetwork& clk_ntwk,
|
||||
const vtr::Point<size_t> chan_coord, const t_rr_type& chan_type,
|
||||
const int& cost_index_offset, const bool& verbose) {
|
||||
const size_t& layer, const vtr::Point<size_t> chan_coord,
|
||||
const t_rr_type& chan_type, const int& cost_index_offset,
|
||||
const bool& verbose) {
|
||||
size_t orig_chan_width =
|
||||
rr_graph_view.node_lookup()
|
||||
.find_channel_nodes(chan_coord.x(), chan_coord.y(), chan_type)
|
||||
.find_channel_nodes(layer, chan_coord.x(), chan_coord.y(), chan_type)
|
||||
.size();
|
||||
size_t curr_node_ptc = orig_chan_width;
|
||||
|
||||
|
@ -115,9 +117,10 @@ static void add_rr_graph_block_clock_nodes(
|
|||
for (size_t ipin = 0; ipin < num_pins / 2; ++ipin) {
|
||||
for (auto node_dir : {Direction::INC, Direction::DEC}) {
|
||||
RRNodeId clk_node = rr_graph_builder.create_node(
|
||||
chan_coord.x(), chan_coord.y(), chan_type, curr_node_ptc);
|
||||
layer, chan_coord.x(), chan_coord.y(), chan_type, curr_node_ptc);
|
||||
rr_graph_builder.set_node_direction(clk_node, node_dir);
|
||||
rr_graph_builder.set_node_capacity(clk_node, 1);
|
||||
rr_graph_builder.set_node_layer(clk_node, layer);
|
||||
/* set cost_index using segment id */
|
||||
rr_graph_builder.set_node_cost_index(
|
||||
clk_node, RRIndexedDataId(cost_index_offset +
|
||||
|
@ -145,13 +148,11 @@ static void add_rr_graph_block_clock_nodes(
|
|||
* Add clock nodes one by one to the routing resource graph.
|
||||
* Assign node-level attributes properly and register in dedicated lookup
|
||||
*******************************************************************/
|
||||
static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder,
|
||||
RRClockSpatialLookup& clk_rr_lookup,
|
||||
const RRGraphView& rr_graph_view,
|
||||
const DeviceGrid& grids,
|
||||
const bool& through_channel,
|
||||
const ClockNetwork& clk_ntwk,
|
||||
const bool& verbose) {
|
||||
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 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(),
|
||||
|
@ -164,12 +165,12 @@ static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder,
|
|||
/* Bypass if the routing channel does not exist when through channels are
|
||||
* not allowed */
|
||||
if ((false == through_channel) &&
|
||||
(false == is_chanx_exist(grids, chanx_coord))) {
|
||||
(false == is_chanx_exist(grids, layer, chanx_coord))) {
|
||||
continue;
|
||||
}
|
||||
add_rr_graph_block_clock_nodes(rr_graph_builder, clk_rr_lookup,
|
||||
rr_graph_view, clk_ntwk, chanx_coord,
|
||||
CHANX, CHANX_COST_INDEX_START, verbose);
|
||||
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)));
|
||||
|
@ -186,13 +187,13 @@ static void add_rr_graph_clock_nodes(RRGraphBuilder& rr_graph_builder,
|
|||
/* Bypass if the routing channel does not exist when through channel are
|
||||
* not allowed */
|
||||
if ((false == through_channel) &&
|
||||
(false == is_chany_exist(grids, chany_coord))) {
|
||||
(false == is_chany_exist(grids, layer, chany_coord))) {
|
||||
continue;
|
||||
}
|
||||
add_rr_graph_block_clock_nodes(
|
||||
rr_graph_builder, clk_rr_lookup, rr_graph_view, clk_ntwk, chany_coord,
|
||||
CHANY, CHANX_COST_INDEX_START + rr_graph_view.num_rr_segments(),
|
||||
verbose);
|
||||
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)));
|
||||
|
@ -392,11 +393,12 @@ static std::vector<RRNodeId> find_clock_track2track_node(
|
|||
*******************************************************************/
|
||||
static void try_find_and_add_clock_track2ipin_node(
|
||||
std::vector<RRNodeId>& des_nodes, const DeviceGrid& grids,
|
||||
const RRGraphView& rr_graph_view, const vtr::Point<size_t>& grid_coord,
|
||||
const e_side& pin_side, const ClockNetwork& clk_ntwk,
|
||||
const ClockTreeId& clk_tree, const ClockTreePinId& clk_pin) {
|
||||
t_physical_tile_type_ptr grid_type =
|
||||
grids[grid_coord.x()][grid_coord.y()].type;
|
||||
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) {
|
||||
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)) {
|
||||
/* tap pin name could be 'io[5:5].a2f[0]' */
|
||||
|
@ -405,7 +407,7 @@ static void try_find_and_add_clock_track2ipin_node(
|
|||
continue;
|
||||
}
|
||||
RRNodeId des_node = rr_graph_view.node_lookup().find_node(
|
||||
grid_coord.x(), grid_coord.y(), IPIN, grid_pin_idx, pin_side);
|
||||
layer, grid_coord.x(), grid_coord.y(), IPIN, grid_pin_idx, pin_side);
|
||||
if (rr_graph_view.valid_node(des_node)) {
|
||||
des_nodes.push_back(des_node);
|
||||
}
|
||||
|
@ -440,36 +442,36 @@ static void try_find_and_add_clock_track2ipin_node(
|
|||
*******************************************************************/
|
||||
static std::vector<RRNodeId> find_clock_track2ipin_node(
|
||||
const DeviceGrid& grids, const RRGraphView& rr_graph_view,
|
||||
const t_rr_type& chan_type, const vtr::Point<size_t>& chan_coord,
|
||||
const ClockNetwork& clk_ntwk, const ClockTreeId& clk_tree,
|
||||
const ClockTreePinId& clk_pin) {
|
||||
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) {
|
||||
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,
|
||||
bot_grid_coord, BOTTOM, clk_ntwk,
|
||||
clk_tree, clk_pin);
|
||||
layer, bot_grid_coord, BOTTOM,
|
||||
clk_ntwk, clk_tree, clk_pin);
|
||||
|
||||
/* 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,
|
||||
top_grid_coord, TOP, clk_ntwk,
|
||||
layer, top_grid_coord, TOP, clk_ntwk,
|
||||
clk_tree, clk_pin);
|
||||
} 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,
|
||||
left_grid_coord, LEFT, clk_ntwk,
|
||||
clk_tree, clk_pin);
|
||||
layer, left_grid_coord, LEFT,
|
||||
clk_ntwk, clk_tree, clk_pin);
|
||||
|
||||
/* 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,
|
||||
right_grid_coord, RIGHT, clk_ntwk,
|
||||
clk_tree, clk_pin);
|
||||
layer, right_grid_coord, RIGHT,
|
||||
clk_ntwk, clk_tree, clk_pin);
|
||||
}
|
||||
|
||||
return des_nodes;
|
||||
|
@ -481,7 +483,7 @@ static std::vector<RRNodeId> find_clock_track2ipin_node(
|
|||
static void add_rr_graph_block_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 ClockNetwork& clk_ntwk,
|
||||
const DeviceGrid& grids, const size_t& layer, const ClockNetwork& clk_ntwk,
|
||||
const vtr::Point<size_t>& chan_coord, const t_rr_type& chan_type,
|
||||
const bool& verbose) {
|
||||
size_t edge_count = 0;
|
||||
|
@ -524,7 +526,7 @@ static void add_rr_graph_block_clock_edges(
|
|||
/* Create edges */
|
||||
VTR_ASSERT(rr_graph_view.valid_node(des_node));
|
||||
rr_graph_builder.create_edge(src_node, des_node,
|
||||
clk_ntwk.default_switch());
|
||||
clk_ntwk.default_switch(), false);
|
||||
edge_count++;
|
||||
}
|
||||
VTR_LOGV(verbose, "\tWill add %lu edges to other clock nodes\n",
|
||||
|
@ -535,12 +537,12 @@ static void add_rr_graph_block_clock_edges(
|
|||
if (clk_ntwk.is_last_level(itree, ilvl)) {
|
||||
size_t curr_edge_count = edge_count;
|
||||
for (RRNodeId des_node : find_clock_track2ipin_node(
|
||||
grids, rr_graph_view, chan_type, chan_coord, clk_ntwk, itree,
|
||||
ClockTreePinId(ipin))) {
|
||||
grids, rr_graph_view, chan_type, layer, chan_coord, clk_ntwk,
|
||||
itree, ClockTreePinId(ipin))) {
|
||||
/* Create edges */
|
||||
VTR_ASSERT(rr_graph_view.valid_node(des_node));
|
||||
rr_graph_builder.create_edge(src_node, des_node,
|
||||
clk_ntwk.default_switch());
|
||||
clk_ntwk.default_switch(), false);
|
||||
edge_count++;
|
||||
}
|
||||
VTR_LOGV(verbose, "\tWill add %lu edges to other IPIN\n",
|
||||
|
@ -579,7 +581,7 @@ 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 bool& through_channel,
|
||||
const DeviceGrid& grids, const size_t& layer, const bool& through_channel,
|
||||
const ClockNetwork& clk_ntwk, const bool& verbose) {
|
||||
/* Add edges which is driven by X-direction clock routing tracks */
|
||||
for (size_t iy = 0; iy < grids.height() - 1; ++iy) {
|
||||
|
@ -588,11 +590,11 @@ static void add_rr_graph_clock_edges(
|
|||
/* Bypass if the routing channel does not exist when through channels are
|
||||
* not allowed */
|
||||
if ((false == through_channel) &&
|
||||
(false == is_chanx_exist(grids, chanx_coord))) {
|
||||
(false == is_chanx_exist(grids, layer, chanx_coord))) {
|
||||
continue;
|
||||
}
|
||||
add_rr_graph_block_clock_edges(rr_graph_builder, num_edges_to_create,
|
||||
clk_rr_lookup, rr_graph_view, grids,
|
||||
clk_rr_lookup, rr_graph_view, grids, layer,
|
||||
clk_ntwk, chanx_coord, CHANX, verbose);
|
||||
}
|
||||
}
|
||||
|
@ -604,11 +606,11 @@ static void add_rr_graph_clock_edges(
|
|||
/* Bypass if the routing channel does not exist when through channel are
|
||||
* not allowed */
|
||||
if ((false == through_channel) &&
|
||||
(false == is_chany_exist(grids, chany_coord))) {
|
||||
(false == is_chany_exist(grids, layer, chany_coord))) {
|
||||
continue;
|
||||
}
|
||||
add_rr_graph_block_clock_edges(rr_graph_builder, num_edges_to_create,
|
||||
clk_rr_lookup, rr_graph_view, grids,
|
||||
clk_rr_lookup, rr_graph_view, grids, layer,
|
||||
clk_ntwk, chany_coord, CHANY, verbose);
|
||||
}
|
||||
}
|
||||
|
@ -647,7 +649,7 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx,
|
|||
/* 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, vpr_device_ctx.arch->through_channel, clk_ntwk);
|
||||
vpr_device_ctx.grid, 0, 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);
|
||||
|
@ -658,7 +660,7 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx,
|
|||
|
||||
/* 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,
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, 0,
|
||||
vpr_device_ctx.arch->through_channel, clk_ntwk,
|
||||
verbose);
|
||||
VTR_LOGV(verbose,
|
||||
|
@ -673,7 +675,7 @@ int append_clock_rr_graph(DeviceContext& vpr_device_ctx,
|
|||
add_rr_graph_clock_edges(
|
||||
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,
|
||||
vpr_device_ctx.rr_graph, vpr_device_ctx.grid, 0,
|
||||
vpr_device_ctx.arch->through_channel, clk_ntwk, verbose);
|
||||
VTR_LOGV(verbose,
|
||||
"Added %lu clock edges to routing "
|
||||
|
|
|
@ -78,7 +78,8 @@ size_t DeviceRRGSB::get_num_cb_unique_module(const t_rr_type& cb_type) const {
|
|||
}
|
||||
|
||||
/* Identify if a GSB actually exists at a location */
|
||||
bool DeviceRRGSB::is_gsb_exist(const vtr::Point<size_t> coord) const {
|
||||
bool DeviceRRGSB::is_gsb_exist(const RRGraphView& rr_graph,
|
||||
const vtr::Point<size_t> coord) const {
|
||||
/* Out of range, does not exist */
|
||||
if (false == validate_coordinate(coord)) {
|
||||
return false;
|
||||
|
@ -93,7 +94,7 @@ bool DeviceRRGSB::is_gsb_exist(const vtr::Point<size_t> coord) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (true == get_gsb(coord).is_sb_exist()) {
|
||||
if (true == get_gsb(coord).is_sb_exist(rr_graph)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -145,34 +146,14 @@ const RRGSB& DeviceRRGSB::get_cb_unique_module(const t_rr_type& cb_type,
|
|||
/* Give a coordinate of a rr switch block, and return its unique mirror */
|
||||
const RRGSB& DeviceRRGSB::get_cb_unique_module(
|
||||
const t_rr_type& cb_type, const vtr::Point<size_t>& coordinate) const {
|
||||
VTR_ASSERT(validate_cb_type(cb_type));
|
||||
VTR_ASSERT(validate_coordinate(coordinate));
|
||||
size_t cb_unique_module_id;
|
||||
|
||||
switch (cb_type) {
|
||||
case CHANX:
|
||||
cb_unique_module_id =
|
||||
cbx_unique_module_id_[coordinate.x()][coordinate.y()];
|
||||
break;
|
||||
case CHANY:
|
||||
cb_unique_module_id =
|
||||
cby_unique_module_id_[coordinate.x()][coordinate.y()];
|
||||
break;
|
||||
default:
|
||||
VTR_LOG_ERROR("Invalid type of connection block!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return get_cb_unique_module(cb_type, cb_unique_module_id);
|
||||
return get_cb_unique_module(cb_type,
|
||||
get_cb_unique_module_index(cb_type, coordinate));
|
||||
}
|
||||
|
||||
/* Give a coordinate of a rr switch block, and return its unique mirror */
|
||||
const RRGSB& DeviceRRGSB::get_sb_unique_module(
|
||||
const vtr::Point<size_t>& coordinate) const {
|
||||
VTR_ASSERT(validate_coordinate(coordinate));
|
||||
size_t sb_unique_module_id =
|
||||
sb_unique_module_id_[coordinate.x()][coordinate.y()];
|
||||
return get_sb_unique_module(sb_unique_module_id);
|
||||
return get_sb_unique_module(get_sb_unique_module_index(coordinate));
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
|
@ -550,4 +531,35 @@ bool DeviceRRGSB::validate_cb_type(const t_rr_type& cb_type) const {
|
|||
return ((CHANX == cb_type) || (CHANY == cb_type));
|
||||
}
|
||||
|
||||
size_t DeviceRRGSB::get_sb_unique_module_index(
|
||||
const vtr::Point<size_t>& coordinate) const {
|
||||
VTR_ASSERT(validate_coordinate(coordinate));
|
||||
size_t sb_unique_module_id =
|
||||
sb_unique_module_id_[coordinate.x()][coordinate.y()];
|
||||
return sb_unique_module_id;
|
||||
}
|
||||
|
||||
size_t DeviceRRGSB::get_cb_unique_module_index(
|
||||
const t_rr_type& cb_type, const vtr::Point<size_t>& coordinate) const {
|
||||
VTR_ASSERT(validate_cb_type(cb_type));
|
||||
VTR_ASSERT(validate_coordinate(coordinate));
|
||||
size_t cb_unique_module_id;
|
||||
|
||||
switch (cb_type) {
|
||||
case CHANX:
|
||||
cb_unique_module_id =
|
||||
cbx_unique_module_id_[coordinate.x()][coordinate.y()];
|
||||
break;
|
||||
case CHANY:
|
||||
cb_unique_module_id =
|
||||
cby_unique_module_id_[coordinate.x()][coordinate.y()];
|
||||
break;
|
||||
default:
|
||||
VTR_LOG_ERROR("Invalid type of connection block!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return cb_unique_module_id;
|
||||
}
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
|
|
@ -58,7 +58,15 @@ class DeviceRRGSB {
|
|||
const vtr::Point<size_t>& coordinate) const;
|
||||
size_t get_num_cb_unique_module(const t_rr_type& cb_type)
|
||||
const; /* get the number of unique mirrors of CBs */
|
||||
bool is_gsb_exist(const vtr::Point<size_t> coord) const;
|
||||
bool is_gsb_exist(const RRGraphView& rr_graph,
|
||||
const vtr::Point<size_t> coord) const;
|
||||
/* Get the index of the unique Switch block module with a given GSB
|
||||
* coordinate. Note: Do NOT use sb coordinate!!! */
|
||||
size_t get_sb_unique_module_index(const vtr::Point<size_t>& coordinate) const;
|
||||
/* Get the index of the unique Connection block module with a given GSB
|
||||
* coordinate. Note: Do NOT use sb coordinate!!! */
|
||||
size_t get_cb_unique_module_index(const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& coordinate) const;
|
||||
|
||||
public: /* Mutators */
|
||||
void reserve(
|
||||
|
|
|
@ -0,0 +1,708 @@
|
|||
/************************************************************************
|
||||
* Member functions for class FabricTile
|
||||
***********************************************************************/
|
||||
#include "fabric_tile.h"
|
||||
|
||||
#include "build_top_module_utils.h"
|
||||
#include "command_exit_codes.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
/* namespace openfpga begins */
|
||||
namespace openfpga {
|
||||
|
||||
vtr::Point<size_t> FabricTile::tile_coordinate(
|
||||
const FabricTileId& tile_id) const {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
return coords_[tile_id];
|
||||
}
|
||||
|
||||
vtr::Point<size_t> FabricTile::unique_tile_coordinate(
|
||||
const FabricTileId& tile_id) const {
|
||||
vtr::Point<size_t> tile_coord = tile_coordinate(tile_id);
|
||||
FabricTileId unique_fabric_tile_id = unique_tile(tile_coord);
|
||||
return tile_coordinate(unique_fabric_tile_id);
|
||||
}
|
||||
|
||||
FabricTileId FabricTile::find_unique_tile(const FabricTileId& tile_id) const {
|
||||
vtr::Point<size_t> tile_coord = tile_coordinate(tile_id);
|
||||
return unique_tile(tile_coord);
|
||||
}
|
||||
|
||||
std::vector<vtr::Point<size_t>> FabricTile::pb_coordinates(
|
||||
const FabricTileId& tile_id) const {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
std::vector<vtr::Point<size_t>> pb_root_coords;
|
||||
pb_root_coords.reserve(pb_coords_[tile_id].size());
|
||||
for (auto curr_rect : pb_coords_[tile_id]) {
|
||||
pb_root_coords.push_back(curr_rect.bottom_left());
|
||||
}
|
||||
return pb_root_coords;
|
||||
}
|
||||
|
||||
std::vector<vtr::Point<size_t>> FabricTile::cb_coordinates(
|
||||
const FabricTileId& tile_id, const t_rr_type& cb_type) const {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
switch (cb_type) {
|
||||
case CHANX:
|
||||
return cbx_coords_[tile_id];
|
||||
case CHANY:
|
||||
return cby_coords_[tile_id];
|
||||
default:
|
||||
VTR_LOG("Invalid type of connection block!\n");
|
||||
exit(1);
|
||||
}
|
||||
return std::vector<vtr::Point<size_t>>();
|
||||
}
|
||||
|
||||
std::vector<vtr::Point<size_t>> FabricTile::sb_coordinates(
|
||||
const FabricTileId& tile_id) const {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
return sb_coords_[tile_id];
|
||||
}
|
||||
|
||||
FabricTileId FabricTile::unique_tile(const vtr::Point<size_t>& coord) const {
|
||||
/* Return invalid Id when out of range! */
|
||||
if (coord.x() < tile_coord2unique_tile_ids_.size()) {
|
||||
if (coord.y() < tile_coord2unique_tile_ids_[coord.x()].size()) {
|
||||
return tile_coord2unique_tile_ids_[coord.x()][coord.y()];
|
||||
}
|
||||
}
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
|
||||
FabricTileId FabricTile::find_tile(const vtr::Point<size_t>& coord) const {
|
||||
if (coord.x() >= tile_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Tile coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), tile_coord2id_lookup_.size(),
|
||||
tile_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.y() >= tile_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Tile coordinate [%lu][%lu] exceeds the maximum range [%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), tile_coord2id_lookup_.size(),
|
||||
tile_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
return tile_coord2id_lookup_[coord.x()][coord.y()];
|
||||
}
|
||||
|
||||
FabricTileId FabricTile::find_tile_by_pb_coordinate(
|
||||
const vtr::Point<size_t>& coord) const {
|
||||
if (pb_coord2id_lookup_.empty()) {
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.x() >= pb_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Programmable block coordinate [%lu][%lu] exceeds the maximum range "
|
||||
"[%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), pb_coord2id_lookup_.size(),
|
||||
pb_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.y() >= pb_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Programmable block coordinate [%lu][%lu] exceeds the maximum range "
|
||||
"[%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), pb_coord2id_lookup_.size(),
|
||||
pb_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
return pb_coord2id_lookup_[coord.x()][coord.y()];
|
||||
}
|
||||
|
||||
FabricTileId FabricTile::find_tile_by_cb_coordinate(
|
||||
const t_rr_type& cb_type, const vtr::Point<size_t>& coord) const {
|
||||
switch (cb_type) {
|
||||
case CHANX: {
|
||||
if (cbx_coord2id_lookup_.empty()) {
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.x() >= cbx_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"X-direction connection block coordinate [%lu][%lu] exceeds the "
|
||||
"maximum range [%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), cbx_coord2id_lookup_.size(),
|
||||
cbx_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.y() >= cbx_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"X-direction connection block coordinate [%lu][%lu] exceeds the "
|
||||
"maximum range [%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), cbx_coord2id_lookup_.size(),
|
||||
cbx_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
return cbx_coord2id_lookup_[coord.x()][coord.y()];
|
||||
}
|
||||
case CHANY: {
|
||||
if (cby_coord2id_lookup_.empty()) {
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.x() >= cby_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Y-direction connection block coordinate [%lu][%lu] exceeds the "
|
||||
"maximum range [%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), cby_coord2id_lookup_.size(),
|
||||
cby_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.y() >= cby_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Y-direction connection block coordinate [%lu][%lu] exceeds the "
|
||||
"maximum range [%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), cby_coord2id_lookup_.size(),
|
||||
cby_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
return cby_coord2id_lookup_[coord.x()][coord.y()];
|
||||
}
|
||||
default:
|
||||
VTR_LOG("Invalid type of connection block!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
FabricTileId FabricTile::find_tile_by_sb_coordinate(
|
||||
const vtr::Point<size_t>& coord) const {
|
||||
if (sb_coord2id_lookup_.empty()) {
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.x() >= sb_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Switch block coordinate [%lu][%lu] exceeds the maximum range "
|
||||
"[%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), sb_coord2id_lookup_.size(),
|
||||
sb_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
if (coord.y() >= sb_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Switch block coordinate [%lu][%lu] exceeds the maximum range "
|
||||
"[%lu][%lu]!\n",
|
||||
coord.x(), coord.y(), sb_coord2id_lookup_.size(),
|
||||
sb_coord2id_lookup_[0].size());
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
return sb_coord2id_lookup_[coord.x()][coord.y()];
|
||||
}
|
||||
|
||||
bool FabricTile::pb_in_tile(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord,
|
||||
const bool& use_gsb_coord) const {
|
||||
if (use_gsb_coord) {
|
||||
return !pb_gsb_coords_[tile_id].empty() &&
|
||||
find_pb_index_in_tile(tile_id, coord, use_gsb_coord) !=
|
||||
pb_gsb_coords_[tile_id].size();
|
||||
}
|
||||
return !pb_coords_[tile_id].empty() &&
|
||||
find_pb_index_in_tile(tile_id, coord) != pb_coords_[tile_id].size();
|
||||
}
|
||||
|
||||
size_t FabricTile::find_pb_index_in_tile(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord,
|
||||
const bool& use_gsb_coord) const {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
if (use_gsb_coord) {
|
||||
for (size_t idx = 0; idx < pb_gsb_coords_[tile_id].size(); ++idx) {
|
||||
vtr::Point<size_t> curr_coord = pb_gsb_coords_[tile_id][idx];
|
||||
if (curr_coord == coord) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
/* Not found, return an invalid index */
|
||||
return pb_gsb_coords_[tile_id].size();
|
||||
} else {
|
||||
for (size_t idx = 0; idx < pb_coords_[tile_id].size(); ++idx) {
|
||||
if (pb_coords_[tile_id][idx].coincident(coord)) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
/* Not found, return an invalid index */
|
||||
return pb_coords_[tile_id].size();
|
||||
}
|
||||
}
|
||||
|
||||
bool FabricTile::sb_in_tile(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) const {
|
||||
return !sb_coords_[tile_id].empty() &&
|
||||
find_sb_index_in_tile(tile_id, coord) != sb_coords_[tile_id].size();
|
||||
}
|
||||
|
||||
size_t FabricTile::find_sb_index_in_tile(
|
||||
const FabricTileId& tile_id, const vtr::Point<size_t>& coord) const {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
for (size_t idx = 0; idx < sb_coords_[tile_id].size(); ++idx) {
|
||||
vtr::Point<size_t> curr_coord = sb_coords_[tile_id][idx];
|
||||
if (curr_coord == coord) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
/* Not found, return an invalid index */
|
||||
return sb_coords_[tile_id].size();
|
||||
}
|
||||
|
||||
bool FabricTile::cb_in_tile(const FabricTileId& tile_id,
|
||||
const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& coord) const {
|
||||
switch (cb_type) {
|
||||
case CHANX:
|
||||
return !cbx_coords_[tile_id].empty() &&
|
||||
find_cb_index_in_tile(tile_id, cb_type, coord) !=
|
||||
cbx_coords_[tile_id].size();
|
||||
case CHANY:
|
||||
return !cby_coords_[tile_id].empty() &&
|
||||
find_cb_index_in_tile(tile_id, cb_type, coord) !=
|
||||
cby_coords_[tile_id].size();
|
||||
default:
|
||||
VTR_LOG("Invalid type of connection block!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t FabricTile::find_cb_index_in_tile(
|
||||
const FabricTileId& tile_id, const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& coord) const {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
switch (cb_type) {
|
||||
case CHANX:
|
||||
for (size_t idx = 0; idx < cbx_coords_[tile_id].size(); ++idx) {
|
||||
vtr::Point<size_t> curr_coord = cbx_coords_[tile_id][idx];
|
||||
if (curr_coord == coord) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return cbx_coords_[tile_id].size();
|
||||
case CHANY:
|
||||
for (size_t idx = 0; idx < cby_coords_[tile_id].size(); ++idx) {
|
||||
vtr::Point<size_t> curr_coord = cby_coords_[tile_id][idx];
|
||||
if (curr_coord == coord) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return cby_coords_[tile_id].size();
|
||||
default:
|
||||
VTR_LOG("Invalid type of connection block!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
vtr::Point<size_t> FabricTile::find_cb_coordinate_in_unique_tile(
|
||||
const FabricTileId& tile_id, const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& cb_coord) const {
|
||||
size_t cb_idx_in_curr_tile =
|
||||
find_cb_index_in_tile(tile_id, cb_type, cb_coord);
|
||||
FabricTileId unique_tile = find_unique_tile(tile_id);
|
||||
return cb_coordinates(unique_tile, cb_type)[cb_idx_in_curr_tile];
|
||||
}
|
||||
|
||||
vtr::Point<size_t> FabricTile::find_pb_coordinate_in_unique_tile(
|
||||
const FabricTileId& tile_id, const vtr::Point<size_t>& pb_coord) const {
|
||||
size_t pb_idx_in_curr_tile = find_pb_index_in_tile(tile_id, pb_coord);
|
||||
FabricTileId unique_tile = find_unique_tile(tile_id);
|
||||
return pb_coordinates(unique_tile)[pb_idx_in_curr_tile];
|
||||
}
|
||||
|
||||
vtr::Point<size_t> FabricTile::find_sb_coordinate_in_unique_tile(
|
||||
const FabricTileId& tile_id, const vtr::Point<size_t>& sb_coord) const {
|
||||
size_t sb_idx_in_curr_tile = find_sb_index_in_tile(tile_id, sb_coord);
|
||||
FabricTileId unique_tile = find_unique_tile(tile_id);
|
||||
return sb_coordinates(unique_tile)[sb_idx_in_curr_tile];
|
||||
}
|
||||
|
||||
std::vector<FabricTileId> FabricTile::unique_tiles() const {
|
||||
return unique_tile_ids_;
|
||||
}
|
||||
|
||||
bool FabricTile::empty() const { return ids_.empty(); }
|
||||
|
||||
FabricTileId FabricTile::create_tile(const vtr::Point<size_t>& coord) {
|
||||
FabricTileId tile_id = FabricTileId(ids_.size());
|
||||
ids_.push_back(tile_id);
|
||||
coords_.push_back(coord);
|
||||
pb_coords_.emplace_back();
|
||||
pb_gsb_coords_.emplace_back();
|
||||
cbx_coords_.emplace_back();
|
||||
cby_coords_.emplace_back();
|
||||
sb_coords_.emplace_back();
|
||||
|
||||
/* Register in fast look-up */
|
||||
if (register_tile_in_lookup(tile_id, coord)) {
|
||||
return tile_id;
|
||||
}
|
||||
return FabricTileId::INVALID();
|
||||
}
|
||||
|
||||
void FabricTile::init(const vtr::Point<size_t>& max_coord) {
|
||||
tile_coord2id_lookup_.resize(max_coord.x());
|
||||
pb_coord2id_lookup_.resize(max_coord.x());
|
||||
cbx_coord2id_lookup_.resize(max_coord.x());
|
||||
cby_coord2id_lookup_.resize(max_coord.x());
|
||||
sb_coord2id_lookup_.resize(max_coord.x());
|
||||
for (size_t ix = 0; ix < max_coord.x(); ++ix) {
|
||||
tile_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID());
|
||||
pb_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID());
|
||||
cbx_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID());
|
||||
cby_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID());
|
||||
sb_coord2id_lookup_[ix].resize(max_coord.y(), FabricTileId::INVALID());
|
||||
}
|
||||
tile_coord2unique_tile_ids_.resize(max_coord.x());
|
||||
for (size_t ix = 0; ix < max_coord.x(); ++ix) {
|
||||
tile_coord2unique_tile_ids_[ix].resize(max_coord.y(),
|
||||
FabricTileId::INVALID());
|
||||
}
|
||||
}
|
||||
|
||||
bool FabricTile::register_tile_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) {
|
||||
if (coord.x() >= tile_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given x='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.x(), tile_coord2id_lookup_.size());
|
||||
return false;
|
||||
}
|
||||
if (coord.y() >= tile_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given y='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.y(), tile_coord2id_lookup_[coord.x()].size());
|
||||
return false;
|
||||
}
|
||||
/* Throw error if this coord is already registered! */
|
||||
if (tile_coord2id_lookup_[coord.x()][coord.y()]) {
|
||||
VTR_LOG_ERROR("Tile at [%lu][%lu] has already been registered!\n",
|
||||
coord.x(), coord.y());
|
||||
return false;
|
||||
}
|
||||
tile_coord2id_lookup_[coord.x()][coord.y()] = tile_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FabricTile::register_pb_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) {
|
||||
if (coord.x() >= pb_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given x='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.x(), pb_coord2id_lookup_.size());
|
||||
return false;
|
||||
}
|
||||
if (coord.y() >= pb_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given y='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.y(), pb_coord2id_lookup_[coord.x()].size());
|
||||
return false;
|
||||
}
|
||||
/* Throw error if this coord is already registered! */
|
||||
if (pb_coord2id_lookup_[coord.x()][coord.y()] &&
|
||||
pb_coord2id_lookup_[coord.x()][coord.y()] != tile_id) {
|
||||
VTR_LOG_ERROR(
|
||||
"Programmable block at [%lu][%lu] has already been registered!\n",
|
||||
coord.x(), coord.y());
|
||||
return false;
|
||||
}
|
||||
pb_coord2id_lookup_[coord.x()][coord.y()] = tile_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FabricTile::register_cbx_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) {
|
||||
if (coord.x() >= cbx_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given x='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.x(), cbx_coord2id_lookup_.size());
|
||||
return false;
|
||||
}
|
||||
if (coord.y() >= cbx_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given y='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.y(), cbx_coord2id_lookup_[coord.x()].size());
|
||||
return false;
|
||||
}
|
||||
/* Throw error if this coord is already registered! */
|
||||
if (cbx_coord2id_lookup_[coord.x()][coord.y()]) {
|
||||
VTR_LOG_ERROR(
|
||||
"X-direction connection block at [%lu][%lu] has already been "
|
||||
"registered!\n",
|
||||
coord.x(), coord.y());
|
||||
return false;
|
||||
}
|
||||
cbx_coord2id_lookup_[coord.x()][coord.y()] = tile_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FabricTile::register_cby_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) {
|
||||
if (coord.x() >= cby_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given x='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.x(), cby_coord2id_lookup_.size());
|
||||
return false;
|
||||
}
|
||||
if (coord.y() >= cby_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given y='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.y(), cby_coord2id_lookup_[coord.x()].size());
|
||||
return false;
|
||||
}
|
||||
/* Throw error if this coord is already registered! */
|
||||
if (cby_coord2id_lookup_[coord.x()][coord.y()]) {
|
||||
VTR_LOG_ERROR(
|
||||
"Y-direction connection block at [%lu][%lu] has already been "
|
||||
"registered!\n",
|
||||
coord.x(), coord.y());
|
||||
return false;
|
||||
}
|
||||
cby_coord2id_lookup_[coord.x()][coord.y()] = tile_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FabricTile::register_sb_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) {
|
||||
if (coord.x() >= sb_coord2id_lookup_.size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given x='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.x(), sb_coord2id_lookup_.size());
|
||||
return false;
|
||||
}
|
||||
if (coord.y() >= sb_coord2id_lookup_[coord.x()].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Fast look-up has not been re-allocated properly! Given y='%lu' exceeds "
|
||||
"the upper-bound '%lu'!\n",
|
||||
coord.y(), sb_coord2id_lookup_[coord.x()].size());
|
||||
return false;
|
||||
}
|
||||
/* Throw error if this coord is already registered! */
|
||||
if (sb_coord2id_lookup_[coord.x()][coord.y()]) {
|
||||
VTR_LOG_ERROR("Switch block at [%lu][%lu] has already been registered!\n",
|
||||
coord.x(), coord.y());
|
||||
return false;
|
||||
}
|
||||
sb_coord2id_lookup_[coord.x()][coord.y()] = tile_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FabricTile::invalidate_tile_in_lookup(const vtr::Point<size_t>& coord) {
|
||||
tile_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID();
|
||||
}
|
||||
|
||||
void FabricTile::invalidate_pb_in_lookup(const vtr::Point<size_t>& coord) {
|
||||
pb_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID();
|
||||
}
|
||||
|
||||
void FabricTile::invalidate_cbx_in_lookup(const vtr::Point<size_t>& coord) {
|
||||
cbx_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID();
|
||||
}
|
||||
|
||||
void FabricTile::invalidate_cby_in_lookup(const vtr::Point<size_t>& coord) {
|
||||
cby_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID();
|
||||
}
|
||||
|
||||
void FabricTile::invalidate_sb_in_lookup(const vtr::Point<size_t>& coord) {
|
||||
sb_coord2id_lookup_[coord.x()][coord.y()] = FabricTileId::INVALID();
|
||||
}
|
||||
|
||||
bool FabricTile::set_tile_coordinate(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
/* Invalidate previous coordinate in look-up */
|
||||
invalidate_tile_in_lookup(coords_[tile_id]);
|
||||
/* update coordinate */
|
||||
coords_[tile_id] = coord;
|
||||
/* Register in fast look-up */
|
||||
return register_tile_in_lookup(tile_id, coord);
|
||||
}
|
||||
|
||||
int FabricTile::add_pb_coordinate(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord,
|
||||
const vtr::Point<size_t>& gsb_coord) {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
pb_coords_[tile_id].push_back(vtr::Rect<size_t>(coord, coord));
|
||||
pb_gsb_coords_[tile_id].push_back(gsb_coord);
|
||||
/* Register in fast look-up */
|
||||
return register_pb_in_lookup(tile_id, coord);
|
||||
}
|
||||
|
||||
int FabricTile::set_pb_max_coordinate(const FabricTileId& tile_id,
|
||||
const size_t& pb_index,
|
||||
const vtr::Point<size_t>& max_coord) {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
if (pb_index >= pb_coords_[tile_id].size()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid pb_index '%lu' is out of range of programmable block list "
|
||||
"(size='%lu') of tile[%lu][%lu]!\n",
|
||||
pb_index, pb_coords_[tile_id].size(), tile_coordinate(tile_id).x(),
|
||||
tile_coordinate(tile_id).y());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (max_coord.x() < pb_coords_[tile_id][pb_index].xmin() ||
|
||||
max_coord.y() < pb_coords_[tile_id][pb_index].ymin()) {
|
||||
VTR_LOG_ERROR(
|
||||
"Invalid max. coordinate (%lu, %lu) is out of range of programmable "
|
||||
"block list (%lu, %lu) <-> (%lu, %lu) of tile[%lu][%lu]!\n",
|
||||
max_coord.x(), max_coord.y(), pb_coords_[tile_id][pb_index].xmin(),
|
||||
pb_coords_[tile_id][pb_index].ymin(),
|
||||
pb_coords_[tile_id][pb_index].xmax(),
|
||||
pb_coords_[tile_id][pb_index].ymax(), tile_coordinate(tile_id).x(),
|
||||
tile_coordinate(tile_id).y());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
pb_coords_[tile_id][pb_index].set_xmax(max_coord.x());
|
||||
pb_coords_[tile_id][pb_index].set_ymax(max_coord.y());
|
||||
/* Update fast lookup */
|
||||
for (size_t ix = pb_coords_[tile_id][pb_index].xmin();
|
||||
ix <= pb_coords_[tile_id][pb_index].xmax(); ++ix) {
|
||||
for (size_t iy = pb_coords_[tile_id][pb_index].ymin();
|
||||
iy <= pb_coords_[tile_id][pb_index].ymax(); ++iy) {
|
||||
register_pb_in_lookup(tile_id, vtr::Point<size_t>(ix, iy));
|
||||
}
|
||||
}
|
||||
return CMD_EXEC_SUCCESS;
|
||||
}
|
||||
|
||||
int FabricTile::add_cb_coordinate(const FabricTileId& tile_id,
|
||||
const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& coord) {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
switch (cb_type) {
|
||||
case CHANX:
|
||||
cbx_coords_[tile_id].push_back(coord);
|
||||
/* Register in fast look-up */
|
||||
return register_cbx_in_lookup(tile_id, coord);
|
||||
case CHANY:
|
||||
cby_coords_[tile_id].push_back(coord);
|
||||
/* Register in fast look-up */
|
||||
return register_cby_in_lookup(tile_id, coord);
|
||||
default:
|
||||
VTR_LOG("Invalid type of connection block!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int FabricTile::add_sb_coordinate(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) {
|
||||
VTR_ASSERT(valid_tile_id(tile_id));
|
||||
sb_coords_[tile_id].push_back(coord);
|
||||
/* Register in fast look-up */
|
||||
return register_sb_in_lookup(tile_id, coord);
|
||||
}
|
||||
|
||||
void FabricTile::clear() {
|
||||
ids_.clear();
|
||||
coords_.clear();
|
||||
pb_coords_.clear();
|
||||
pb_gsb_coords_.clear();
|
||||
cbx_coords_.clear();
|
||||
cby_coords_.clear();
|
||||
sb_coords_.clear();
|
||||
tile_coord2id_lookup_.clear();
|
||||
pb_coord2id_lookup_.clear();
|
||||
cbx_coord2id_lookup_.clear();
|
||||
cby_coord2id_lookup_.clear();
|
||||
sb_coord2id_lookup_.clear();
|
||||
tile_coord2unique_tile_ids_.clear();
|
||||
unique_tile_ids_.clear();
|
||||
}
|
||||
|
||||
bool FabricTile::valid_tile_id(const FabricTileId& tile_id) const {
|
||||
return (size_t(tile_id) < ids_.size()) && (tile_id == ids_[tile_id]);
|
||||
}
|
||||
|
||||
bool FabricTile::equivalent_tile(const FabricTileId& tile_a,
|
||||
const FabricTileId& tile_b,
|
||||
const DeviceGrid& grids,
|
||||
const DeviceRRGSB& device_rr_gsb) const {
|
||||
/* The number of cbx, cby and sb blocks should be the same */
|
||||
if (pb_coords_[tile_a].size() != pb_coords_[tile_b].size() ||
|
||||
pb_gsb_coords_[tile_a].size() != pb_gsb_coords_[tile_b].size() ||
|
||||
cbx_coords_[tile_a].size() != cbx_coords_[tile_b].size() ||
|
||||
cby_coords_[tile_a].size() != cby_coords_[tile_b].size() ||
|
||||
sb_coords_[tile_a].size() != sb_coords_[tile_b].size()) {
|
||||
return false;
|
||||
}
|
||||
/* The pb of two tiles should be the same, otherwise not equivalent */
|
||||
for (size_t iblk = 0; iblk < pb_coords_[tile_a].size(); ++iblk) {
|
||||
vtr::Point<size_t> tile_a_pb_coord = pb_coords_[tile_a][iblk].bottom_left();
|
||||
vtr::Point<size_t> tile_b_pb_coord = pb_coords_[tile_b][iblk].bottom_left();
|
||||
if (generate_grid_block_module_name_in_top_module(std::string(), grids,
|
||||
tile_a_pb_coord) !=
|
||||
generate_grid_block_module_name_in_top_module(std::string(), grids,
|
||||
tile_b_pb_coord)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* Each CBx should have the same unique modules in the device rr_gsb */
|
||||
for (size_t iblk = 0; iblk < cbx_coords_[tile_a].size(); ++iblk) {
|
||||
if (device_rr_gsb.get_cb_unique_module_index(CHANX,
|
||||
cbx_coords_[tile_a][iblk]) !=
|
||||
device_rr_gsb.get_cb_unique_module_index(CHANX,
|
||||
cbx_coords_[tile_b][iblk])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (size_t iblk = 0; iblk < cby_coords_[tile_a].size(); ++iblk) {
|
||||
if (device_rr_gsb.get_cb_unique_module_index(CHANY,
|
||||
cby_coords_[tile_a][iblk]) !=
|
||||
device_rr_gsb.get_cb_unique_module_index(CHANY,
|
||||
cby_coords_[tile_b][iblk])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (size_t iblk = 0; iblk < sb_coords_[tile_a].size(); ++iblk) {
|
||||
if (device_rr_gsb.get_sb_unique_module_index(sb_coords_[tile_a][iblk]) !=
|
||||
device_rr_gsb.get_sb_unique_module_index(sb_coords_[tile_b][iblk])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int FabricTile::build_unique_tiles(const DeviceGrid& grids,
|
||||
const DeviceRRGSB& device_rr_gsb,
|
||||
const bool& verbose) {
|
||||
for (size_t ix = 0; ix < grids.width(); ++ix) {
|
||||
for (size_t iy = 0; iy < grids.height(); ++iy) {
|
||||
if (!valid_tile_id(tile_coord2id_lookup_[ix][iy])) {
|
||||
continue; /* Skip invalid tile (which does not exist) */
|
||||
}
|
||||
bool is_unique_tile = true;
|
||||
for (FabricTileId unique_tile_id : unique_tile_ids_) {
|
||||
if (equivalent_tile(tile_coord2id_lookup_[ix][iy], unique_tile_id,
|
||||
grids, device_rr_gsb)) {
|
||||
VTR_LOGV(verbose,
|
||||
"Tile[%lu][%lu] is a mirror to the unique tile[%lu][%lu]\n",
|
||||
ix, iy, tile_coordinate(unique_tile_id).x(),
|
||||
tile_coordinate(unique_tile_id).y());
|
||||
is_unique_tile = false;
|
||||
tile_coord2unique_tile_ids_[ix][iy] = unique_tile_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Update list if this is a unique tile */
|
||||
if (is_unique_tile) {
|
||||
VTR_LOGV(verbose, "Tile[%lu][%lu] is added as a new unique tile\n", ix,
|
||||
iy);
|
||||
unique_tile_ids_.push_back(tile_coord2id_lookup_[ix][iy]);
|
||||
tile_coord2unique_tile_ids_[ix][iy] = tile_coord2id_lookup_[ix][iy];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* End namespace openfpga*/
|
|
@ -0,0 +1,198 @@
|
|||
#ifndef FABRIC_TILE_H
|
||||
#define FABRIC_TILE_H
|
||||
|
||||
/********************************************************************
|
||||
* Include header files required by the data structure definition
|
||||
*******************************************************************/
|
||||
#include <vector>
|
||||
|
||||
#include "device_grid.h"
|
||||
#include "device_rr_gsb.h"
|
||||
#include "fabric_tile_fwd.h"
|
||||
#include "vtr_geometry.h"
|
||||
#include "vtr_vector.h"
|
||||
|
||||
/* namespace openfpga begins */
|
||||
namespace openfpga {
|
||||
|
||||
/********************************************************************
|
||||
* Object models the tiles in an FPGA fabric
|
||||
* This includes:
|
||||
* 1. a collection of tiles, each which contains a programmable block and
|
||||
*surrounding routing blocks
|
||||
* 2. a collection of unique tiles
|
||||
*******************************************************************/
|
||||
class FabricTile {
|
||||
public: /* Accessors */
|
||||
vtr::Point<size_t> tile_coordinate(const FabricTileId& tile_id) const;
|
||||
/* Return all the root (bottom-left point) coordinates of programmable blocks
|
||||
* under a given tile. */
|
||||
std::vector<vtr::Point<size_t>> pb_coordinates(
|
||||
const FabricTileId& tile_id) const;
|
||||
std::vector<vtr::Point<size_t>> cb_coordinates(
|
||||
const FabricTileId& tile_id, const t_rr_type& cb_type) const;
|
||||
std::vector<vtr::Point<size_t>> sb_coordinates(
|
||||
const FabricTileId& tile_id) const;
|
||||
/** @brief With a given coordinate, find the id of the unique tile (which is
|
||||
* the same as the tile in structure) */
|
||||
FabricTileId unique_tile(const vtr::Point<size_t>& coord) const;
|
||||
/** @brief With a given tile id, find the id of its unique tile (which is
|
||||
* the same as the tile in structure) */
|
||||
FabricTileId find_unique_tile(const FabricTileId& tile_id) const;
|
||||
/** @brief Find the tile info with a given coordinate */
|
||||
FabricTileId find_tile(const vtr::Point<size_t>& coord) const;
|
||||
/** @brief Find the id of a tile, with a given coordinate of the programmable
|
||||
* block under the tile */
|
||||
FabricTileId find_tile_by_pb_coordinate(
|
||||
const vtr::Point<size_t>& coord) const;
|
||||
/** @brief Find the id of a tile, with a given coordinate of the connection
|
||||
* block under the tile */
|
||||
FabricTileId find_tile_by_cb_coordinate(
|
||||
const t_rr_type& cb_type, const vtr::Point<size_t>& coord) const;
|
||||
/** @brief Find the id of a tile, with a given coordinate of the switch block
|
||||
* under the tile */
|
||||
FabricTileId find_tile_by_sb_coordinate(
|
||||
const vtr::Point<size_t>& coord) const;
|
||||
/** @brief Find the coordinate of the unique tile w.r.t the tile with a tile
|
||||
* id */
|
||||
vtr::Point<size_t> unique_tile_coordinate(const FabricTileId& tile_id) const;
|
||||
/** @brief Return a list of unique tiles */
|
||||
std::vector<FabricTileId> unique_tiles() const;
|
||||
/** @brief Find the index of a programmable block in the internal list by a
|
||||
* given coordinate. Note that the coord can be either the one in device grid
|
||||
* or the one of gsb which the programmable block belongs to */
|
||||
size_t find_pb_index_in_tile(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord,
|
||||
const bool& use_gsb_coord = false) const;
|
||||
/** @brief Find the index of a switch block in the internal list by a given
|
||||
* coordinate. */
|
||||
size_t find_sb_index_in_tile(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) const;
|
||||
/** @brief Find the index of a connection block in the internal list by a
|
||||
* given coordinate. */
|
||||
size_t find_cb_index_in_tile(const FabricTileId& tile_id,
|
||||
const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& coord) const;
|
||||
/** @brief Find the coodinate of a connection block in its unique tile. For
|
||||
* example, a cbx[1][0] is the 2nd element of the connection block list in
|
||||
* tile[1][1], while the unique tile of tile[1][1] is tile[2][2]. We will find
|
||||
* the 2nd element of the connection block list in tile[2][2] and return its
|
||||
* coordinate. Error out on any exception. */
|
||||
vtr::Point<size_t> find_cb_coordinate_in_unique_tile(
|
||||
const FabricTileId& tile_id, const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& cb_coord) const;
|
||||
/** @brief Find the coodinate of a programmable block in its unique tile. For
|
||||
* example, a pb[1][0] is the 2nd element of the programmable block list in
|
||||
* tile[1][1], while the unique tile of tile[1][1] is tile[2][2]. We will find
|
||||
* the 2nd element of the programmable block list in tile[2][2] and return its
|
||||
* coordinate. Error out on any exception. */
|
||||
vtr::Point<size_t> find_pb_coordinate_in_unique_tile(
|
||||
const FabricTileId& tile_id, const vtr::Point<size_t>& pb_coord) const;
|
||||
/** @brief Find the coodinate of a switch block in its unique tile. For
|
||||
* example, a pb[1][0] is the 2nd element of the switch block list in
|
||||
* tile[1][1], while the unique tile of tile[1][1] is tile[2][2]. We will find
|
||||
* the 2nd element of the switch block list in tile[2][2] and return its
|
||||
* coordinate. Error out on any exception. */
|
||||
vtr::Point<size_t> find_sb_coordinate_in_unique_tile(
|
||||
const FabricTileId& tile_id, const vtr::Point<size_t>& sb_coord) const;
|
||||
/** @brief Check if a programmable block (with a coordinate) exists in a tile.
|
||||
* Note that the coord can be either the one in device grid or the one of gsb
|
||||
* which the programmable block belongs to
|
||||
*/
|
||||
bool pb_in_tile(const FabricTileId& tile_id, const vtr::Point<size_t>& coord,
|
||||
const bool& use_gsb_coord = false) const;
|
||||
/** @brief Check if a switch block (with a coordinate) exists in a tile */
|
||||
bool sb_in_tile(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord) const;
|
||||
/** @brief Check if a connection block (with a coordinate) exists in a tile */
|
||||
bool cb_in_tile(const FabricTileId& tile_id, const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& coord) const;
|
||||
/** @brief Identify if the fabric tile is empty: no tiles are defined */
|
||||
bool empty() const;
|
||||
|
||||
public: /* Mutators */
|
||||
FabricTileId create_tile(const vtr::Point<size_t>& coord);
|
||||
bool set_tile_coordinate(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord);
|
||||
int add_pb_coordinate(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord,
|
||||
const vtr::Point<size_t>& gsb_coord);
|
||||
/* Set the top-right coordinate of a pb. This is mainly for heterogeneous
|
||||
* blocks, whose height or width can be > 1 */
|
||||
int set_pb_max_coordinate(const FabricTileId& tile_id, const size_t& pb_index,
|
||||
const vtr::Point<size_t>& max_coord);
|
||||
int add_cb_coordinate(const FabricTileId& tile_id, const t_rr_type& cb_type,
|
||||
const vtr::Point<size_t>& coord);
|
||||
int add_sb_coordinate(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord);
|
||||
/** @brief Build a list of unique tiles by comparing the coordinates in
|
||||
* DeviceRRGSB */
|
||||
void build_unique_tiles();
|
||||
/** @brief Clear all the content */
|
||||
void clear();
|
||||
/** @brief Initialize the data with a given range. Used by constructors */
|
||||
void init(const vtr::Point<size_t>& max_coord);
|
||||
/** @brief Identify the number of unique tiles and keep in the lookup */
|
||||
int build_unique_tiles(const DeviceGrid& grids,
|
||||
const DeviceRRGSB& device_rr_gsb, const bool& verbose);
|
||||
|
||||
public: /* Validators */
|
||||
bool valid_tile_id(const FabricTileId& tile_id) const;
|
||||
|
||||
private: /* Internal validators */
|
||||
/** @brief Identify if two tile are equivalent in their sub-modules, including
|
||||
* pb, cbx, cby and sb */
|
||||
bool equivalent_tile(const FabricTileId& tile_a, const FabricTileId& tile_b,
|
||||
const DeviceGrid& grids,
|
||||
const DeviceRRGSB& device_rr_gsb) const;
|
||||
|
||||
private: /* Internal builders */
|
||||
void invalidate_tile_in_lookup(const vtr::Point<size_t>& coord);
|
||||
void invalidate_pb_in_lookup(const vtr::Point<size_t>& coord);
|
||||
void invalidate_cbx_in_lookup(const vtr::Point<size_t>& coord);
|
||||
void invalidate_cby_in_lookup(const vtr::Point<size_t>& coord);
|
||||
void invalidate_sb_in_lookup(const vtr::Point<size_t>& coord);
|
||||
bool register_tile_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord);
|
||||
bool register_pb_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord);
|
||||
bool register_cbx_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord);
|
||||
bool register_cby_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord);
|
||||
bool register_sb_in_lookup(const FabricTileId& tile_id,
|
||||
const vtr::Point<size_t>& coord);
|
||||
|
||||
private: /* Internal Data */
|
||||
vtr::vector<FabricTileId, FabricTileId> ids_;
|
||||
vtr::vector<FabricTileId, vtr::Point<size_t>> coords_;
|
||||
/* Coordinates w.r.t. RRGSB
|
||||
* Note that we keep two coordinates for the programmable block: regular one
|
||||
* (in device grid) and the one in gsb. This is to ease the lookup/search for
|
||||
* coordinates through both device grid and gsb. Client functions need one of
|
||||
* the them depending on the scenario. In future, once we refactor the RRGSB
|
||||
* organization (to follow bottom-left corner style). This limitation can be
|
||||
* resolved.
|
||||
*/
|
||||
vtr::vector<FabricTileId, std::vector<vtr::Rect<size_t>>> pb_coords_;
|
||||
vtr::vector<FabricTileId, std::vector<vtr::Point<size_t>>> pb_gsb_coords_;
|
||||
vtr::vector<FabricTileId, std::vector<vtr::Point<size_t>>> cbx_coords_;
|
||||
vtr::vector<FabricTileId, std::vector<vtr::Point<size_t>>> cby_coords_;
|
||||
vtr::vector<FabricTileId, std::vector<vtr::Point<size_t>>> sb_coords_;
|
||||
/* A few fast lookup to spot tile by coordinate of programmable blocks,
|
||||
* connection blocks and switch blocks */
|
||||
std::vector<std::vector<FabricTileId>> pb_coord2id_lookup_;
|
||||
std::vector<std::vector<FabricTileId>> cbx_coord2id_lookup_;
|
||||
std::vector<std::vector<FabricTileId>> cby_coord2id_lookup_;
|
||||
std::vector<std::vector<FabricTileId>> sb_coord2id_lookup_;
|
||||
/* A fast lookup to spot tile by coordinate */
|
||||
std::vector<std::vector<FabricTileId>> tile_coord2id_lookup_;
|
||||
std::vector<std::vector<FabricTileId>>
|
||||
tile_coord2unique_tile_ids_; /* Use [x][y] to get the id of the unique tile
|
||||
with a given coordinate */
|
||||
std::vector<FabricTileId> unique_tile_ids_;
|
||||
};
|
||||
|
||||
} /* End namespace openfpga*/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
/**************************************************
|
||||
* This file includes only declarations for
|
||||
* the data structures for fabric tiles
|
||||
* Please refer to fabric_tiles.h for more details
|
||||
*************************************************/
|
||||
#ifndef FABRIC_TILE_FWD_H
|
||||
#define FABRIC_TILE_FWD_H
|
||||
|
||||
#include "vtr_strong_id.h"
|
||||
|
||||
/* begin namespace openfpga */
|
||||
namespace openfpga {
|
||||
|
||||
/* Strong Ids for ModuleManager */
|
||||
struct fabric_tile_id_tag;
|
||||
|
||||
typedef vtr::StrongId<fabric_tile_id_tag> FabricTileId;
|
||||
|
||||
class FabricTile;
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
|
@ -2,10 +2,10 @@
|
|||
* This file includes functions that are used to annotate routing results
|
||||
* from VPR to OpenFPGA
|
||||
*******************************************************************/
|
||||
/* Headers from vtrutil library */
|
||||
#include "openfpga_annotate_routing.h"
|
||||
|
||||
#include "annotate_routing.h"
|
||||
#include "old_traceback.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_log.h"
|
||||
|
||||
|
@ -134,14 +134,15 @@ void annotate_rr_node_previous_nodes(
|
|||
/* Cache Previous nodes */
|
||||
RRNodeId prev_node = RRNodeId::INVALID();
|
||||
|
||||
t_trace* tptr = routing_ctx.trace[net_id].head;
|
||||
t_trace* tptr = TracebackCompat::traceback_from_route_tree(
|
||||
routing_ctx.route_trees[net_id].value());
|
||||
t_trace* head = tptr;
|
||||
while (tptr != nullptr) {
|
||||
RRNodeId rr_node = RRNodeId(tptr->index);
|
||||
|
||||
/* Find the right previous node */
|
||||
prev_node = find_previous_node_from_routing_traces(
|
||||
device_ctx.rr_graph, routing_ctx.trace[net_id].head, prev_node,
|
||||
rr_node);
|
||||
device_ctx.rr_graph, head, prev_node, rr_node);
|
||||
|
||||
/* Only update mapped nodes */
|
||||
if (prev_node) {
|
||||
|
@ -156,6 +157,7 @@ void annotate_rr_node_previous_nodes(
|
|||
/* Move on to the next */
|
||||
tptr = tptr->next;
|
||||
}
|
||||
free_traceback(head);
|
||||
}
|
||||
|
||||
VTR_LOG("Done with %d nodes mapping\n", counter);
|
||||
|
|
|
@ -32,8 +32,9 @@ void VprPlacementAnnotation::init_mapped_blocks(const DeviceGrid& grids) {
|
|||
for (size_t x = 0; x < grids.width(); ++x) {
|
||||
for (size_t y = 0; y < grids.height(); ++y) {
|
||||
/* Deposit invalid ids and we will fill later */
|
||||
blocks_[x][y].resize(grids[x][y].type->capacity,
|
||||
ClusterBlockId::INVALID());
|
||||
blocks_[x][y].resize(
|
||||
grids.get_physical_type(t_physical_tile_loc(x, y, 0))->capacity,
|
||||
ClusterBlockId::INVALID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ class NetlistManager {
|
|||
SUBMODULE_NETLIST,
|
||||
LOGIC_BLOCK_NETLIST,
|
||||
ROUTING_MODULE_NETLIST,
|
||||
TILE_MODULE_NETLIST,
|
||||
TOP_MODULE_NETLIST,
|
||||
TESTBENCH_NETLIST,
|
||||
NUM_NETLIST_TYPES
|
||||
|
|
|
@ -200,6 +200,11 @@ ShellCommandId add_write_fabric_bitstream_command_template(
|
|||
"Keep don't care bits in bitstream file; If not enabled, don't care bits "
|
||||
"are converted to logic '0' or '1'");
|
||||
|
||||
/* Add an option '--wl_incremental_order' */
|
||||
shell_cmd.add_option(
|
||||
"wl_decremental_order", false,
|
||||
"Generate bitstream in WL decremental addressing order if supported");
|
||||
|
||||
/* Add an option '--no_time_stamp' */
|
||||
shell_cmd.add_option("no_time_stamp", false,
|
||||
"Do not print time stamp in output files");
|
||||
|
|
|
@ -91,6 +91,7 @@ int write_fabric_bitstream_template(const T& openfpga_ctx, const Command& cmd,
|
|||
CommandOptionId opt_file_format = cmd.option("format");
|
||||
CommandOptionId opt_fast_config = cmd.option("fast_configuration");
|
||||
CommandOptionId opt_keep_dont_care_bits = cmd.option("keep_dont_care_bits");
|
||||
CommandOptionId opt_wl_decremental_order = cmd.option("wl_decremental_order");
|
||||
CommandOptionId opt_no_time_stamp = cmd.option("no_time_stamp");
|
||||
|
||||
/* Write fabric bitstream if required */
|
||||
|
@ -127,6 +128,7 @@ int write_fabric_bitstream_template(const T& openfpga_ctx, const Command& cmd,
|
|||
cmd_context.option_value(cmd, opt_file),
|
||||
cmd_context.option_enable(cmd, opt_fast_config),
|
||||
cmd_context.option_enable(cmd, opt_keep_dont_care_bits),
|
||||
!cmd_context.option_enable(cmd, opt_wl_decremental_order),
|
||||
!cmd_context.option_enable(cmd, opt_no_time_stamp),
|
||||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "build_device_module.h"
|
||||
#include "build_fabric_global_port_info.h"
|
||||
#include "build_fabric_io_location_map.h"
|
||||
#include "build_fpga_core_wrapper_module.h"
|
||||
#include "command.h"
|
||||
#include "command_context.h"
|
||||
#include "command_exit_codes.h"
|
||||
|
@ -14,7 +15,10 @@
|
|||
#include "fabric_hierarchy_writer.h"
|
||||
#include "fabric_key_writer.h"
|
||||
#include "globals.h"
|
||||
#include "openfpga_naming.h"
|
||||
#include "read_xml_fabric_key.h"
|
||||
#include "read_xml_io_name_map.h"
|
||||
#include "read_xml_tile_config.h"
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_time.h"
|
||||
|
||||
|
@ -65,21 +69,23 @@ void compress_routing_hierarchy_template(T& openfpga_ctx,
|
|||
"Detected %lu unique switch blocks from a total of %d (compression "
|
||||
"rate=%.2f%)\n",
|
||||
openfpga_ctx.device_rr_gsb().get_num_sb_unique_module(),
|
||||
find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb()),
|
||||
100. *
|
||||
((float)find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb()) /
|
||||
(float)openfpga_ctx.device_rr_gsb().get_num_sb_unique_module() -
|
||||
1.));
|
||||
find_device_rr_gsb_num_sb_modules(openfpga_ctx.device_rr_gsb(),
|
||||
g_vpr_ctx.device().rr_graph),
|
||||
100. * ((float)find_device_rr_gsb_num_sb_modules(
|
||||
openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph) /
|
||||
(float)openfpga_ctx.device_rr_gsb().get_num_sb_unique_module() -
|
||||
1.));
|
||||
|
||||
VTR_LOG(
|
||||
"Detected %lu unique general switch blocks from a total of %d (compression "
|
||||
"rate=%.2f%)\n",
|
||||
openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module(),
|
||||
find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb()),
|
||||
100. *
|
||||
((float)find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb()) /
|
||||
(float)openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module() -
|
||||
1.));
|
||||
find_device_rr_gsb_num_gsb_modules(openfpga_ctx.device_rr_gsb(),
|
||||
g_vpr_ctx.device().rr_graph),
|
||||
100. * ((float)find_device_rr_gsb_num_gsb_modules(
|
||||
openfpga_ctx.device_rr_gsb(), g_vpr_ctx.device().rr_graph) /
|
||||
(float)openfpga_ctx.device_rr_gsb().get_num_gsb_unique_module() -
|
||||
1.));
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
|
@ -95,8 +101,31 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd,
|
|||
cmd.option("generate_random_fabric_key");
|
||||
CommandOptionId opt_write_fabric_key = cmd.option("write_fabric_key");
|
||||
CommandOptionId opt_load_fabric_key = cmd.option("load_fabric_key");
|
||||
CommandOptionId opt_group_tile = cmd.option("group_tile");
|
||||
CommandOptionId opt_group_config_block = cmd.option("group_config_block");
|
||||
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||
|
||||
/* Report conflicts with options:
|
||||
* - group tile does not support duplicate_grid_pin
|
||||
* - group tile requires compress_routing to be enabled
|
||||
*/
|
||||
if (cmd_context.option_enable(cmd, opt_group_tile)) {
|
||||
if (cmd_context.option_enable(cmd, opt_duplicate_grid_pin)) {
|
||||
VTR_LOG_ERROR(
|
||||
"Option '%s' requires options '%s' to be disabled due to a conflict!\n",
|
||||
cmd.option_name(opt_group_tile).c_str(),
|
||||
cmd.option_name(opt_duplicate_grid_pin).c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
if (!cmd_context.option_enable(cmd, opt_compress_routing)) {
|
||||
VTR_LOG_ERROR(
|
||||
"Option '%s' requires options '%s' to be enabled due to a conflict!\n",
|
||||
cmd.option_name(opt_group_tile).c_str(),
|
||||
cmd.option_name(opt_compress_routing).c_str());
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (true == cmd_context.option_enable(cmd, opt_compress_routing)) {
|
||||
compress_routing_hierarchy_template<T>(
|
||||
openfpga_ctx, cmd_context.option_enable(cmd, opt_verbose));
|
||||
|
@ -122,14 +151,34 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd,
|
|||
|
||||
VTR_LOG("\n");
|
||||
|
||||
/* Build tile-level information:
|
||||
* - This feature only supports when compress routing is enabled
|
||||
* - Read the tile organization configuration file
|
||||
* - Build tile info
|
||||
*/
|
||||
TileConfig tile_config;
|
||||
if (cmd_context.option_enable(cmd, opt_group_tile)) {
|
||||
if (!cmd_context.option_enable(cmd, opt_compress_routing)) {
|
||||
VTR_LOG_ERROR(
|
||||
"Group tile is applicable only when compress routing is enabled!\n");
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
curr_status = read_xml_tile_config(
|
||||
cmd_context.option_value(cmd, opt_group_tile).c_str(), tile_config);
|
||||
if (CMD_EXEC_SUCCESS != curr_status) {
|
||||
return CMD_EXEC_FATAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
curr_status = build_device_module_graph(
|
||||
openfpga_ctx.mutable_module_graph(), openfpga_ctx.mutable_decoder_lib(),
|
||||
openfpga_ctx.mutable_blwl_shift_register_banks(),
|
||||
const_cast<const T&>(openfpga_ctx), g_vpr_ctx.device(),
|
||||
cmd_context.option_enable(cmd, opt_frame_view),
|
||||
openfpga_ctx.mutable_fabric_tile(), const_cast<const T&>(openfpga_ctx),
|
||||
g_vpr_ctx.device(), cmd_context.option_enable(cmd, opt_frame_view),
|
||||
cmd_context.option_enable(cmd, opt_compress_routing),
|
||||
cmd_context.option_enable(cmd, opt_duplicate_grid_pin),
|
||||
predefined_fabric_key,
|
||||
predefined_fabric_key, tile_config,
|
||||
cmd_context.option_enable(cmd, opt_group_config_block),
|
||||
cmd_context.option_enable(cmd, opt_gen_random_fabric_key),
|
||||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
|
||||
|
@ -141,7 +190,8 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd,
|
|||
|
||||
/* Build I/O location map */
|
||||
openfpga_ctx.mutable_io_location_map() = build_fabric_io_location_map(
|
||||
openfpga_ctx.module_graph(), g_vpr_ctx.device().grid);
|
||||
openfpga_ctx.module_graph(), g_vpr_ctx.device().grid,
|
||||
cmd_context.option_enable(cmd, opt_group_tile));
|
||||
|
||||
/* Build fabric global port information */
|
||||
openfpga_ctx.mutable_fabric_global_port_info() =
|
||||
|
@ -154,11 +204,11 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd,
|
|||
std::string fkey_fname =
|
||||
cmd_context.option_value(cmd, opt_write_fabric_key);
|
||||
VTR_ASSERT(false == fkey_fname.empty());
|
||||
curr_status =
|
||||
write_fabric_key_to_xml_file(openfpga_ctx.module_graph(), fkey_fname,
|
||||
openfpga_ctx.arch().config_protocol,
|
||||
openfpga_ctx.blwl_shift_register_banks(),
|
||||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
curr_status = write_fabric_key_to_xml_file(
|
||||
openfpga_ctx.module_graph(), fkey_fname,
|
||||
openfpga_ctx.arch().config_protocol,
|
||||
openfpga_ctx.blwl_shift_register_banks(), false,
|
||||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
/* If there is any error, final status cannot be overwritten by a success
|
||||
* flag */
|
||||
if (CMD_EXEC_SUCCESS != curr_status) {
|
||||
|
@ -169,6 +219,32 @@ int build_fabric_template(T& openfpga_ctx, const Command& cmd,
|
|||
return final_status;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Write fabric key of the module graph for FPGA device to a file
|
||||
*******************************************************************/
|
||||
template <class T>
|
||||
int write_fabric_key_template(const T& openfpga_ctx, const Command& cmd,
|
||||
const CommandContext& cmd_context) {
|
||||
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||
CommandOptionId opt_include_module_keys = cmd.option("include_module_keys");
|
||||
|
||||
/* Check the option '--file' is enabled or not
|
||||
* Actually, it must be enabled as the shell interface will check
|
||||
* before reaching this fuction
|
||||
*/
|
||||
CommandOptionId opt_file = cmd.option("file");
|
||||
VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_file));
|
||||
VTR_ASSERT(false == cmd_context.option_value(cmd, opt_file).empty());
|
||||
|
||||
/* Write fabric key to a file */
|
||||
return write_fabric_key_to_xml_file(
|
||||
openfpga_ctx.module_graph(), cmd_context.option_value(cmd, opt_file),
|
||||
openfpga_ctx.arch().config_protocol,
|
||||
openfpga_ctx.blwl_shift_register_banks(),
|
||||
cmd_context.option_enable(cmd, opt_include_module_keys),
|
||||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Write hierarchy of the module graph for FPGA device to a file
|
||||
*******************************************************************/
|
||||
|
@ -231,6 +307,35 @@ int write_fabric_io_info_template(const T& openfpga_ctx, const Command& cmd,
|
|||
cmd_context.option_enable(cmd, opt_verbose));
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Add fpga_core module to the module graph
|
||||
*******************************************************************/
|
||||
template <class T>
|
||||
int add_fpga_core_to_fabric_template(T& openfpga_ctx, const Command& cmd,
|
||||
const CommandContext& cmd_context) {
|
||||
CommandOptionId opt_frame_view = cmd.option("frame_view");
|
||||
bool frame_view = cmd_context.option_enable(cmd, opt_frame_view);
|
||||
CommandOptionId opt_verbose = cmd.option("verbose");
|
||||
bool verbose_output = cmd_context.option_enable(cmd, opt_verbose);
|
||||
|
||||
CommandOptionId opt_inst_name = cmd.option("instance_name");
|
||||
std::string core_inst_name = generate_fpga_core_instance_name();
|
||||
if (true == cmd_context.option_enable(cmd, opt_inst_name)) {
|
||||
core_inst_name = cmd_context.option_value(cmd, opt_inst_name);
|
||||
}
|
||||
|
||||
/* Handle I/O naming rules if defined */
|
||||
CommandOptionId opt_io_naming = cmd.option("io_naming");
|
||||
if (true == cmd_context.option_enable(cmd, opt_io_naming)) {
|
||||
read_xml_io_name_map(cmd_context.option_value(cmd, opt_io_naming).c_str(),
|
||||
openfpga_ctx.mutable_io_name_map());
|
||||
}
|
||||
|
||||
return add_fpga_core_to_device_module_graph(
|
||||
openfpga_ctx.mutable_module_graph(), openfpga_ctx.io_name_map(),
|
||||
core_inst_name, frame_view, verbose_output);
|
||||
}
|
||||
|
||||
} /* end namespace openfpga */
|
||||
|
||||
#endif
|
||||
|
|