diff --git a/.github/scripts/install_dependencies.sh b/.github/scripts/install_dependencies.sh index 71933e09138..351ec7d56a7 100755 --- a/.github/scripts/install_dependencies.sh +++ b/.github/scripts/install_dependencies.sh @@ -10,6 +10,7 @@ sudo apt install -y \ binutils \ binutils-gold \ build-essential \ + capnproto \ cmake \ ctags \ curl \ @@ -20,6 +21,7 @@ sudo apt install -y \ git \ gperf \ libcairo2-dev \ + libcapnp-dev \ libgtk-3-dev \ libevent-dev \ libfontconfig1-dev \ @@ -54,3 +56,9 @@ sudo apt install -y \ # libtbb-dev pip install -r requirements.txt + +git clone https://github.com/capnproto/capnproto-java.git $GITHUB_WORKSPACE/env/capnproto-java +pushd $GITHUB_WORKSPACE/env/capnproto-java +make +sudo make install +popd diff --git a/CMakeLists.txt b/CMakeLists.txt index 40179ed4552..46c077ddf24 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ option(VTR_ENABLE_CAPNPROTO "Enable capnproto binary serialization support in VP #Allow the user to enable/disable VPR analytic placement #VPR option --enable_analytic_placer is also required for Analytic Placement option(VPR_ANALYTIC_PLACE "Enable analytic placement in VPR." ON) +option(VPR_ENABLE_INTERCHANGE "Enable FPGA interchange." ON) option(WITH_BLIFEXPLORER "Enable build with blifexplorer" OFF) @@ -121,6 +122,11 @@ endif() # Build type flags # +set(EXTRA_FLAGS "") +if(VPR_ENABLE_INTERCHANGE) + set(EXTRA_FLAGS "-lz") +endif() + if(NOT MSVC) # for GCC and Clang set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -g3") @@ -295,7 +301,7 @@ endif() # Set final flags # separate_arguments( - ADDITIONAL_FLAGS UNIX_COMMAND "${SANITIZE_FLAGS} ${PROFILING_FLAGS} ${COVERAGE_FLAGS} ${LOGGING_FLAGS} ${COLORED_COMPILE}" + ADDITIONAL_FLAGS UNIX_COMMAND "${SANITIZE_FLAGS} ${PROFILING_FLAGS} ${COVERAGE_FLAGS} ${LOGGING_FLAGS} ${COLORED_COMPILE} ${EXTRA_FLAGS}" ) separate_arguments( WARN_FLAGS UNIX_COMMAND "${WARN_FLAGS}" @@ -366,7 +372,6 @@ if (VPR_USE_EZGL STREQUAL "auto") endif() endif() - #Add the various sub-projects if(${WITH_ABC}) add_subdirectory(abc) diff --git a/dev/subtree_config.xml b/dev/subtree_config.xml index 19875e23c0d..b25af6b85d3 100644 --- a/dev/subtree_config.xml +++ b/dev/subtree_config.xml @@ -1,42 +1,47 @@ - - - - - - - - + diff --git a/libs/EXTERNAL/CMakeLists.txt b/libs/EXTERNAL/CMakeLists.txt index 548cc45551f..a0d0842ac82 100644 --- a/libs/EXTERNAL/CMakeLists.txt +++ b/libs/EXTERNAL/CMakeLists.txt @@ -8,7 +8,7 @@ add_subdirectory(libsdcparse) add_subdirectory(libblifparse) add_subdirectory(libtatum) -#VPR_USE_EZGL is initialized in the root CMakeLists. +#VPR_USE_EZGL is initialized in the root CMakeLists. #compile libezgl only if the user asks for or has its dependencies installed. if(VPR_USE_EZGL STREQUAL "on") add_subdirectory(libezgl) diff --git a/libs/EXTERNAL/libinterchange/.github/workflows/ci.yml b/libs/EXTERNAL/libinterchange/.github/workflows/ci.yml new file mode 100644 index 00000000000..2187914723d --- /dev/null +++ b/libs/EXTERNAL/libinterchange/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +# Copyright (C) 2017-2021 The SymbiFlow Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: CI tests +on: [push, pull_request] +jobs: + Check-schema: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: recursive + + - uses: SymbiFlow/actions/checks@main + + - name: Install + run: | + sudo apt-get install capnproto libcapnp-dev + git clone https://github.com/capnproto/capnproto-java.git $GITHUB_WORKSPACE/env/capnproto-java + cd $GITHUB_WORKSPACE/env/capnproto-java + make + sudo make install + - name: Build schemas + run: | + mkdir build + capnp compile -Iinterchange -oc++:build interchange/References.capnp + capnp compile -Iinterchange -oc++:build interchange/LogicalNetlist.capnp + capnp compile -Iinterchange -oc++:build interchange/PhysicalNetlist.capnp + capnp compile -Iinterchange -oc++:build interchange/DeviceResources.capnp diff --git a/libs/EXTERNAL/libinterchange/.gitignore b/libs/EXTERNAL/libinterchange/.gitignore new file mode 100644 index 00000000000..96935f26b38 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/.gitignore @@ -0,0 +1,120 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +env/ +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Emacs temporary files +*~ diff --git a/libs/EXTERNAL/libinterchange/.readthedocs.yml b/libs/EXTERNAL/libinterchange/.readthedocs.yml new file mode 100644 index 00000000000..80f0f323048 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/.readthedocs.yml @@ -0,0 +1,32 @@ +# Copyright (C) 2017-2021 The SymbiFlow Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +formats: + - htmlzip + +conda: + environment: docs/environment.yml diff --git a/libs/EXTERNAL/libinterchange/LICENSE b/libs/EXTERNAL/libinterchange/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/libs/EXTERNAL/libinterchange/README.md b/libs/EXTERNAL/libinterchange/README.md new file mode 100644 index 00000000000..063ba44f6ba --- /dev/null +++ b/libs/EXTERNAL/libinterchange/README.md @@ -0,0 +1,16 @@ +# FPGA interchange schema definitions + +This repository contains the capnp schema for the FPGA interchange. + +## Repositories that implement tools around the FPGA interchange + +[RapidWright](https://github.com/Xilinx/RapidWright/): + - Provides support for 7-series, UltraScale and UltraScale+ parts + - Generate device database for parts + - Can convert DCPs to logical and physical FPGA interchange files. + - Can convert logical and physical FPGA interchange files to DCPs + +[python-fpga-interchange](https://github.com/SymbiFlow/python-fpga-interchange): + - Implements textual format conversions for FPGA interchange files. + - Provides (partial) generator for nextpnr to generate a nextpnr architecture + for a FPGA interchange device database. diff --git a/libs/EXTERNAL/libinterchange/cmake/cxx_static/CMakeLists.txt b/libs/EXTERNAL/libinterchange/cmake/cxx_static/CMakeLists.txt new file mode 100644 index 00000000000..196c328824f --- /dev/null +++ b/libs/EXTERNAL/libinterchange/cmake/cxx_static/CMakeLists.txt @@ -0,0 +1,48 @@ +find_package(CapnProto REQUIRED) +set(PROTOS LogicalNetlist.capnp PhysicalNetlist.capnp DeviceResources.capnp References.capnp) + +set(INTERCHANGE_SCHEMA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../interchange) + +find_program(WGET wget REQUIRED) + +set(JAVA_SCHEMA ${CMAKE_CURRENT_BINARY_DIR}/schema/capnp/java.capnp) +add_custom_command( + OUTPUT ${JAVA_SCHEMA} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/schema/capnp/ + COMMAND ${WGET} + https://raw.githubusercontent.com/capnproto/capnproto-java/master/compiler/src/main/schema/capnp/java.capnp + -O ${JAVA_SCHEMA} + ) +add_custom_target( + get_java_capnp_schema + DEPENDS ${JAVA_SCHEMA}) + +set(CAPNPC_IMPORT_DIRS) +list(APPEND CAPNPC_IMPORT_DIRS ${CMAKE_CURRENT_BINARY_DIR}/schema) + +set(CAPNPC_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/../../interchange") + +add_custom_command( + OUTPUT ${CAPNPC_OUTPUT_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CAPNPC_OUTPUT_DIR} + ) +add_custom_target( + fpga_interchange_capnp_output_directory + DEPENDS ${CAPNPC_OUTPUT_DIR}) + +set(CAPNPC_SRC_PREFIX ${INTERCHANGE_SCHEMA_DIR}) +set(CAPNP_SRCS) +set(CAPNP_HDRS) +foreach (proto ${PROTOS}) + capnp_generate_cpp(CAPNP_SRC CAPNP_HDR ${INTERCHANGE_SCHEMA_DIR}/${proto}) + list(APPEND CAPNP_HDRS ${CAPNP_HDR}) + list(APPEND CAPNP_SRCS ${CAPNP_SRC}) +endforeach() +add_library(fpga_interchange_capnp STATIC ${CAPNP_SRCS}) +add_dependencies(fpga_interchange_capnp + get_java_capnp_schema + fpga_interchange_capnp_output_directory) +target_link_libraries(fpga_interchange_capnp PRIVATE CapnProto::capnp) + +get_filename_component(FPGA_INTERCHANGE_CAPNP_INCLUDE_DIR "${CAPNPC_OUTPUT_DIR}" ABSOLUTE) +target_include_directories(fpga_interchange_capnp INTERFACE ${FPGA_INTERCHANGE_CAPNP_INCLUDE_DIR}) diff --git a/libs/EXTERNAL/libinterchange/docs/Makefile b/libs/EXTERNAL/libinterchange/docs/Makefile new file mode 100644 index 00000000000..681c4eafc43 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/Makefile @@ -0,0 +1,74 @@ +# Copyright (C) 2017-2021 The SymbiFlow Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Minimal makefile for Sphinx documentation + + +SHELL = /bin/bash +MAKEDIR := $(dir $(lastword $(MAKEFILE_LIST))) + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = [ -e env/bin/activate ] && source env/bin/activate; sphinx-build +SPHINXAUTOBUILD = [ -e env/bin/activate ] && source env/bin/activate; sphinx-autobuild +SPHINXPROJ = SymbiFlowFIF +SOURCEDIR = . +BUILDDIR = _build +OSFLAG = +UNAME_S := $(shell uname -s) +ifneq (, $(findstring Linux, $(UNAME_S))) + OSFLAG := Linux +endif +ifeq ($(UNAME_S), Darwin) + OSFLAG := MacOSX +endif +ifneq (, $(findstring Cygwin, $(UNAME_S))) + OSFLAG := Linux +endif +ifneq (, $(findstring MINGW, $(UNAME_S))) + OSFLAG := Linux +endif + + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +livehtml: + @$(SPHINXAUTOBUILD) -b html --ignore \*.swp --ignore \*~ $(SPHINXOPTS) "$(SOURCEDIR)" "$(BUILDDIR)/html" + +.PHONY: help livehtml Makefile + + +env/Miniconda3-latest-$(OSFLAG)-x86_64.sh: + mkdir env + wget https://repo.anaconda.com/miniconda/Miniconda3-latest-$(OSFLAG)-x86_64.sh -O env/Miniconda3-latest-$(OSFLAG)-x86_64.sh + chmod a+x env/Miniconda3-latest-$(OSFLAG)-x86_64.sh + +env: + rm -rf env + make env/Miniconda3-latest-$(OSFLAG)-x86_64.sh + ./env/Miniconda3-latest-$(OSFLAG)-x86_64.sh -p $(PWD)/env -b -f + source env/bin/activate; conda config --system --add envs_dirs $(PWD)/env/envs + source env/bin/activate; conda config --system --add pkgs_dirs $(PWD)/env/pkgs + source env/bin/activate; conda env update --name base --file ./environment.yml + +.PHONY: env + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/libs/EXTERNAL/libinterchange/docs/_static/.keepme b/libs/EXTERNAL/libinterchange/docs/_static/.keepme new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/EXTERNAL/libinterchange/docs/bel_and_site_design.md b/libs/EXTERNAL/libinterchange/docs/bel_and_site_design.md new file mode 100644 index 00000000000..266ec0ea940 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/bel_and_site_design.md @@ -0,0 +1,258 @@ +# Cell, BEL and Site Design + +One of the key concepts within the FPGA interchange device resources is the +relationship between the cell library and the device BEL and site definitions. +A well designed cell library and a flexible but concise BEL and site +definition is important for exposing the hardware in an efficient way that +enables a place and route tool to succeed. + +Good design is hard to capture, but this document will talk about some of the +considerations. + +### Assumptions about cell placement and driver BEL pins + +One important note is that BELs represent a placable location for a cell, and +only one cell should be placable at a given BEL. This means that the cell +library design and BEL design strongly affects what is expressable by the +place and route tool. There will be some examples highlighted below that +expand on how this is important and relevant when discussing concrete +examples. + +## Granularity of the cell library + +It is important to divide the place and route problem and the synthesis +problem, at least as defined for the purpose of the FPGA interchange. The +synthesis tool operates on the **cell library**, which should be designed to +expose logic elements at a useful level of granularity. + +As a concrete example, a LUT4 element is technically just two LUT3 elements, +connected by a mux (e.g. MUXF4), a LUT3 element is just two LUT2 elements, +connected by a mux (e.g. MUXF3), etc. If the outputs of those interior muxes +are not accessible to the place and route tool, then exposing those interior +function muxes as cells in the cell library is not as useful. + +Cell definitions should be granular enough that the synthesis can map to +them, but not so granular that the place and route tool will be making few if +any choices. If there is only one legal placement of the cell, it's value is +relatively low. + +## Drawing site boundaries + +When designing an FPGA interchange device resource for a new fabric, one +important consideration is where to draw the site boundary. The primary goal +of lumping BELs within a site is to capture some local congestion due to +fanout limitations. Interior static routing muxes and output muxes may +accommodate significantly fewer signals than the possible number of BELs that +drive them. In this case, it is important to draw the site boundary large +enough to capture these cases so as to enable the local congestion to be +resolved during either packing for clustered approaches, or during placement +during unclustered approaches. In either case, local congestion that is +strongly placement dependant must be resolved prior to general routing, +unless a fused placement and routing algorithm is used. + +### FF control sets routing + +A common case worth exploring is FF control sets, e.g. SR type signals and CE +type signals. In most fabric SLICE types, the SR and CE control signals are +shared among multiple rows of the SLICE. This is a common example of local +site congestion, and the site boundary should typically encompass all BELs +that share this kind of local routing for all the reasons discussed above. + +Another consideration with control signals is the presence of control signal +constraints that cannot be expressed as local routing congestion. For +example, if a set of BELs share whether the SR control line is a set or reset +(or async set or async reset), it is common to expand the site boundary to +cover the BELs that share these implicit configurations. The constraint +system in the device resources is designed to handle this kind of non-routing +driven configuration. + +## Drawing BEL boundaries + +BEL definitions require creating a boundary around primitive elements of +the fabric. The choice of where to place that boundary has a strong influence +on the design of the cell library in the FPGA interchange. + +In general, the smaller the BEL boundary, the more complexity is exposed to +the place and route tool. In some cases exposing this complexity is +important, because it enables some goal. For example, leaving static routing +muxes outside of BELs enables a place and route tool to have greater +flexibility when resolving site congestion. But as a counter point, if only +a handful of static mux configurations are useful and those choices can be +made at synthesis time, then lumping those muxes into synthesis reduces the +complexity required in the place and route tool. + +The most common case where the static routing muxes are typically lumped into +the BEL is BRAM's and FIFO's address and routing configuration. At synthesis +time, a choice is made about the address and data widths, which are encoded as +parameters on the cell. The place and route tool does not typically make +meaningful choices on the configuration of those static routing muxes, but +they do exist in the hardware. + +The most common case where the static routing muxes are almost never lumped +into the BEL is SLICE-type situations. The remainder of this document will +show examples of why the BEL boundary should typically exclude the static +routing muxes, and leave the choice to the place and route tooling. + +## Static routing muxes and bitstream formats + +Something to keep in mind when drawing BEL boundaries to include or exclude +static routing muxes is the degree of configurability present in the +underlying bitstream. Some static routing muxes share configuration bits in +the bitstream, and so expressing them as two seperate static routing muxes +potentially gives the place and route tool flexibility than the underlying +fabric cannot express. This will result in physical netlists that cannot be +converted to bitstream. + +In some cases this can be handled through tight coupling of the cell and +BEL library. The idea is to limit cell port to BEL pin mappings that avoid +illegal static routing mux configurations. This approach has its limits. +In general, considering how the bitstream expresses static routing muxes must +be accounted for when drawing BEL boundaries. + +### Stratix II and Stratix 10 ALM + +![Stratix II](stratix2_slice.png-026_rotate.png) + +![Stratix 10](stratix10_slice.png-11.png) + +Consider both Stratix II and Stratix 10 logic sites. The first thing to note +is that the architectures at this level are actually mostly the same. Though +it isn't immediately apparent, both designs are structured around 4 4-LUT +elements. + +Take note that of the following structure: + +![Stratix II fractured LUT4](frac_lut4.png) + +This is actually just two LUT4 elements, where the top select line is +independent. + +See the following two figures: + +![Stratix II fractured LUT4 Top](frac_lut4_a.png) +![Stratix II fractured LUT4 Bottom](frac_lut4_b.png) + +In Stratix 10, the LUT4 element is still present, but the top select line +fracturing was removed. + +So now consider the output paths from the the 4 LUT4 elements in the Stratix +II site. Some of the LUT4 outputs route directly to the carry element, so it +will be important for the place and route tool be able to place a LUT4 or +smaller to access that direct connection. But if the output is not used in +the carry element, then it can only be accessed in Stratix II via the MUXF5 +(blue below) and MUXF6 (red below) elements. + +![Stratix II Highlight MUXF5 and MUXF6](highlight_muxf5_muxf6.png) + +So given the Stratix II site layout, the following BELs will be required: + + - 4 LUT4 BELs that connect to the carry + - 2 LUT6 BELs that connect to the output FF or output MUX. + +The two LUT6 BELs are shown below: + +![Stratix II Top LUT6](highlight_top_lut6.png) +![Stratix II Top LUT6](highlight_bottom_lut6.png) + +Drawing a smaller BEL boundary has little value, because a LUT5 element would +still always require routing through the MUXF6 element. + +Now consider the Stratix 10 output arrangement. The LUT4 elements direct to +the carry element is the same, so those BELs would be identical. The Stratix +10 site now has an output tap directly on the top LUT5, similiar to the Xilinx +Versal LUT6 / LUT5 fracture setup. See diagram below. LUT5 element is shown +in blue, and LUT6 element is shown in red. + +![Stratix 10 2 LUT5](stratix10_highlight_lut5.png) +![Stratix 10 LUT6](stratix10_highlight_lut6.png) + +So given the Stratix 10 site layout, the following BELs will be required: + + - 4 LUT4 BELs that connect to the carry + - 2 LUT5 BELs that connect to the output FF or output MUX + - 1 LUT6 BELs that connect to the output FF or output MUX + +### Versal ACAP + +The Versal ACAP LUT structure is fairly similiar to the Stratix 10 combitorial +elements. + +![Versal ACAP LUTs](versal_luts.png) + +Unlike the Stratix 10 ALM, it appears only 1 of the LUT4's connects to the +carry element (the prop signal). The O6 output also has a dedicate +connection to the carry. See image below: + +![Versal SLICE row](versal_row.png) + +The Versal LUT structure likely should be decomposed into 4 BELs, shown in +the next figures: + +![Versal ACAP LUT4](versal_lut4.png) +![Versal ACAP two LUT5](versal_lut5.png) +![Versal ACAP LUT6](versal_lut6.png) + +So given the Versal site layout, the following BELs will be required (per SLICE row): + + - 1 LUT4 BELs that connect to the carry + - 2 LUT5 BELs that connect to the output FF or output MUX + - 1 LUT6 BELs that connect to the output FF or output MUX + +#### Implication of a wider BEL definition + +Consider the Versal structure, but instead of drawing four BELs per row, only have two +BELs per row. One BEL has the `O5_1` and `prop` output BEL pins and the +other BEL has the `O6` and `O5_2` BEL pin. In this configuration, if the cell +library does not expose a cell that maps to both the `O5_1` and `prop` output +BEL pins, then it will not be possible to map LUTs that leverage both output +BEL pins. + +In theory, the cell port to BEL pin map could map the output pin of a LUT4 +element to both the `prop` and `O5_1` output BEL pins, but then there will be +two output BEL pins driving the net connected to the cell port. Having +multiple BEL pins driving one net is not legal, except for the global logic 0 +and 1. + + +### Quicklogic EOS S3 logic cell + +The Quicklogic EOS S3 logic cell has an interesting LUT design because there +is not LUT element specifically. Instead, the fabric exposes a 8x3 mux, with +inverters at each of the mux inputs, see figure below: + +![Quicklogic EOS S3 logic cell](eos_slice.png) + +The way to approach this fabric is to first draw BEL boundaries around the 4x2 +mux and 8x3 mux present in the fabric: + +![Quicklogic MUX4x2](eos_slice_mux4x2.png) +![Quicklogic MUX8x3](eos_slice_mux8x3.png) + +The cell library should have +3 MUX cell types: + - 4-input 1-output 2-select MUX4x2 (maps to MUX4x2 BEL and MUX8x3 BEL) + - 8-input 1-output 3-select MUX8x3 (maps to MUX8x3 BEL) + - A macro cell that is 2x (4-input 1-output 2-select MUX4x2) 2xMUX4x2 (maps to MUX4x2 *and* MUX8x3 BEL) + +A fourth most general cell type is possible, which is to add a cell that also +has a cell port that maps to `TBS`, instead of tying `TBS` high as the +2xMUX4x2 cell would do. It is unclear how useful such a cell would be. +However given the BEL boundaries, adding such a cell would be easy after the +fact. + +In all of the cells above, all inputs to the muxes have statically +configured inverters. + +So the question becomes, how to model LUT cells in this fabric? The LUT cells +should be the regular LUT1, LUT2 and LUT3 cells. The LUT1 and LUT2 can map to +either the MUX4x2 or MUX8x3 BEL. The LUT3 can map to only the MUX8x3 BEL. +The question is only what is the cell port to BEL pin map? + +The solution is when mapping a LUT cell, to tie all of the MUX BEL pins to VCC +(or GND, whatever the default is) before the inverter. The place and route +tool can treat the BEL as a regular LUT, and only the bitstream generation +step will need to be aware that the inversion control is being used to +encode the LUT equation. + +This configuration allows most (if not all) of the logic to be available +to the place and route tool, without exposing unneeded complexity. diff --git a/libs/EXTERNAL/libinterchange/docs/conf.py b/libs/EXTERNAL/libinterchange/docs/conf.py new file mode 100644 index 00000000000..dff3816eab6 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/conf.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (C) 2017-2021 The SymbiFlow Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# SymbiFlow Interchange-schema documentation build configuration file, +# created by sphinx-quickstart on Mon Feb 5 11:04:37 2018. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import re + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.doctest', + 'sphinx.ext.imgmath', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo', + 'sphinx_markdown_tables', + 'recommonmark', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +source_suffix = ['.rst', '.md'] + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'SymbiFlow FPGA Interchange Format' +copyright = u'2021, SymbiFlow Team' +author = u'SymbiFlow Team' + +# Enable github links when not on readthedocs +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +if not on_rtd: + html_context = { + "display_github": True, # Integrate GitHub + "github_user": "symbiflow", # Username + "github_repo": "fpga-interchange-schema", # Repo name + "github_version": "master", # Version + "conf_py_path": "/docs/", + } +else: + docs_dir = os.path.abspath(os.path.dirname(__file__)) + print("Docs dir is:", docs_dir) + import subprocess + subprocess.call('git fetch origin --unshallow', cwd=docs_dir, shell=True) + subprocess.check_call('git fetch origin --tags', cwd=docs_dir, shell=True) + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The full version, including alpha/beta/rc tags. +release = re.sub('^v', '', os.popen('git describe').read().strip()) +# The short X.Y version. +version = release + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'env', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'default' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_symbiflow_theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { + 'repo_name': 'SymbiFlow/fpga-interchange-schema', + 'github_url': 'https://github.com/SymbiFlow/fpga-interchange-schema', +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'relations.html', # needs 'show_related': True theme option to display + 'searchbox.html', + ] +} + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'fpga-interchange-schema' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( + master_doc, + 'fpga-interchange-schema.tex', + u'SymbiFlow FPGA Interchange Format Documentation', + u'SymbiFlow Team', + 'manual', + ), +] + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ( + master_doc, + 'fpga-interchange-schema', + u'SymbiFlow FPGA Interchange Format Documentation', + [author], + 1, + ), +] + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + 'SymbiFlowFIFM', + u'SymbiFlow FPGA Interchange Format Documentation', + author, + 'SymbiFlowFIF', + 'One line description of project.', + 'Miscellaneous', + ), +] + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} + + +def setup(app): + pass diff --git a/libs/EXTERNAL/libinterchange/docs/device_resources.md b/libs/EXTERNAL/libinterchange/docs/device_resources.md new file mode 100644 index 00000000000..9793098bd58 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/device_resources.md @@ -0,0 +1,374 @@ +# Device Resources + +The device resources schema is intended to be a complete description of an +island-based FPGA design. It is made of many components, but the core +description of the device is shown below. + +``` +┌─────────────────┐ +│ Device │ +│ ┌─────────────┐ │ +│ │ Tile │ │ +│ │ ┌─────────┐ │ │ +│ │ │ Site │ │ │ +│ │ │ ┌─────┐ │ │ │ +│ │ │ │ BEL │ │ │ │ +│ │ │ └─────┘ │ │ │ +│ │ │ │ │ │ +│ │ └─────────┘ │ │ +│ │ │ │ +│ └─────────────┘ │ +│ │ +└─────────────────┘ +``` + +That is: + - A device contains tiles + - Tiles contains sites + - Sites contains BELs + +The schema contains the required information to answer questions such as: + - Where are tiles located? + - How are sites connected to the routing graph? + - How are BELs connected to the boundary of the site? + - How can cells be placed at BELs? + +## Terminology + +- Device - A set of tiles and package pins. +- Tiles - An instance of a tile type which contains wires and sites +- Package pin - A boundry between the "interior" of the device and what is "outside" the package. Generally corresponds to a pin on a package, e.g. pin 1 on SOP-8 or A1 on CSG324. +- Wire - Also known as a "tile wire" . A wire is a piece of conductive material totally contained within a tile. Wires can be part of nodes. Wires can connect to PIPs or site pins. +- Node - A node is a set of 1 or more wires that are connected. Nodes can span multiple tiles. Nodes connect to PIPs or site pins via the wires that are part of the node. +- PIP - PIP is an abbreviation for programable interconnect point. A PIP provides a connection between two wires. PIPs can be unidirectional or bidirectional. Unidirectional PIPs always connect wire0 to wire1. Bidirectional PIPs can connect wire0 to wire1 or wire1 to wire0. +- Site - A collection of site pins, site wires and BELs. +- Site pin - The connection between a site and a wire. Site pins may connect to 0 or more site port BELs. +- Site wire - A piece of conductive material that connects to at most 1 output BEL pin and 0 or more input or inout BEL pins. +- BEL - BEL is an abbreviation of basic logic element. A BEL can be one of 3 types, site port, logic, routing. A BEL contains 1 or more BEL pins. +- BEL pin - A connection between a BEL and a site wire. +- Logic BEL - A placable logic element. May be subject to 0 or more placement constraints. +- Site port BEL - A site port BEL represents a connection to a site pin contained within the parent tile of the site. See [Site port BEL](#site_port_bel). +- Routing BEL - A routing BEL connects at most 1 input BEL pin to the output BEL pin. See [Routing BEL](#routing_bel). +- Site PIP - A pair of input and output BEL pins belonging to a BEL that represents a logically connection. +- Cell - A logical element of a design that contains some number of cell ports and some number of cell instances, and some number of nets. +- Cell port - The boundary between the interior of a cell and the containing cell (if any). +- Cell instance - A instance of a cell. The cell ports of may be connected to nets within the parent cell. +- Net - A set of logically connected cell ports. + +## The place and route problem + +The device resources schema is intended to provide a description for a tool +solving the place and route problem. The definition of the problem used by +this schema is described below: + +There exists exactly 1 **cell instance** (the **top** instance) that contains 1 or +more leaf **cell instances** that must be placed at **BELs**, such that no +**constraints** are violated and the **nets** between the **cell instances** are +**routable**. **Routable** means that **site wires**, **site PIPs**, **nodes**, **PIPs** +can be assigned to at most 1 **net** such that each **net driver BEL pin** can +reach each every **net sink BEL pin** on the **net**. + +The device resource format describes how **cell instances** can be legally +placed at **BELs** and how **cell pins** relate to **BEL pins**. When a +**cell instance** is placed at a **BEL**, it may be subject to 0 or more +**constraints**. + +**Nets** are divided into 3 categories. A **signal** net represents a signal +that is not either the constant logical 0 or constant logical 1 net. +The constant logical 0 and constant logical 1 nets are special because they +can have multiple drivers in the device description. Routing resources that +are always part of the constant logical 0 or constant logical 1 net are +explicitly defined in the device resources schema. The constant logical 0 net +is listed in the schema as the "gnd" type. The constant logical 1 net is +listed in the schema as the "vcc" type. + +### Rules for routing + +Fully routed signal nets always begin at a output/inout BEL pin, and always +end at an input/inout BEL pin. If a net enters a site, that net **must** end +at an input/inout BEL pin. It is not legal for a net to enter and leave a +site. If such a path is required, a pseudo PIP should be added to the schema. + +## Site example + +The following is an example site for a SLICE for a non-existent device. + +``` + ▲ + CO│ │ + ┌──────────────────────────┼─────────────────────────────────────┼──────┐ + │ │ │ │ + B0│ I0┌───────┐ │ BLUT ┌───────┐ ▼ CLK │ +─────┼────►│ │ ┌────────o─────────────────┬────►│ │ ┌────┐ │ + B1│ I1│ │O │ │ │XOR │ │D D│ │Q │ FFOUT +─────┼────►│ BLUT3 ├───┤ │ ┌────────┬──o────►│ FFMUX ├──►│ FF ├───┼───► + B2│ I2│ │ │ │ │ │ |ALUT │ │ │ │ │ +─────┼────►│ │ │ │ │ ┌──┬──o──o────►│ │ └────┘ │ + │ └───────┘ │ ┌───┴───┐ │ │ │ │ │ └───────┘ │ + │ │ SI│ │ │ │ │ │ │ │ + │ └───►│ ├─┘ │ │ │ │ │ + │ DX│ CARRY │ O │ │ │ │ │ + │ ┌───►│ │ │ │ │ │ │ + A0│ I0┌───────┐ │ └───┬───┘ │ │ │ │ ┌────────┐ │ +─────┼────►│ │ │ │ │ │ │ │ BLUT │ │ │ + A1│ I1│ │O │ │ │ │ │ └─────►│ │OUT │ OUT +─────┼────►│ ALUT3 ├───┴────────o────────┘ │ │ XOR │ OUTMUX ├──────────┼────► + B2│ I2│ │ │ │ └────────►│ │ │ +─────┼────►│ │ │ │ ALUT │ │ │ + │ └───────┘ │ └───────────►└────────┘ │ + │ │ │ + │ │ │ + └──────────────────────────┼────────────────────────────────────────────┘ + CI│ + │ +``` + +In the above example, there are 17 BELs: + +| BEL Name | Category | # Input | # Output | +|----------|-----------|---------|----------| +| ALUT3 | Logic | 3 | 1 | +| BLUT3 | Logic | 3 | 1 | +| CARRY | Logic | 3 | 2 | +| FF | Logic | 2 | 1 | +| FFMUX | Routing | 3 | 1 | +| OUTMUX | Routing | 3 | 1 | +| A0 | Site port | 0 | 1 | +| A1 | Site port | 0 | 1 | +| A2 | Site port | 0 | 1 | +| B0 | Site port | 0 | 1 | +| B1 | Site port | 0 | 1 | +| B2 | Site port | 0 | 1 | +| CI | Site port | 0 | 1 | +| CLK | Site port | 0 | 1 | +| CO | Site port | 1 | 0 | +| FFOUT | Site port | 1 | 0 | +| OUT | Site port | 1 | 0 | + +Each site port BEL has a site pin, so the site pins are: + +| Site Pin Name | Direction | +|---------------|-----------| +| A0 | In | +| A1 | In | +| A2 | In | +| B0 | In | +| B1 | In | +| B2 | In | +| CI | In | +| CLK | In | +| CO | Out | +| FFOUT | Out | +| OUT | Out | + +The BEL BLUT3 has 4 BEL pins: + +| BEL pin name | Direction | +|--------------|-----------| +| I0 | In | +| I1 | In | +| I2 | In | +| O | Out | + +The BEL A0 has 1 BEL pin: + +| BEL pin name | Direction | +|--------------|-----------| +| A0 | Out | + +The BEL OUTMUX has 4 BEL pins: + +| BEL pin name | Direction | +|--------------|-----------| +| BLUT | In | +| XOR | In | +| ALUT | In | +| OUT | Out | + + +There are 12 site PIPs: + +| BEL name | In pin | Out pin | +|----------|--------|---------| +| BLUT3 | I0 | O | +| BLUT3 | I1 | O | +| BLUT3 | I2 | O | +| ALUT3 | I0 | O | +| ALUT3 | I1 | O | +| ALUT3 | I2 | O | +| FFMUX | ALUT | D | +| FFMUX | XOR | D | +| FFMUX | BLUT | D | +| OUTMUX | ALUT | OUT | +| OUTMUX | XOR | OUT | +| OUTMUX | BLUT | OUT | + + +The site wire BLUT3\_O has 4 BEL pins: + +| BEL Name | Pin | +|----------|------| +| BLUT3 | O | +| CARRY | SI | +| FFMUX | BLUT | +| OUTMUX | BLUT | + +The site wire B0 has 2 BEL pins: + +| BEL Name | Pin | +|----------|-----| +| B0 | B0 | +| BLUT3 | I0 | + +## Details + +### Net routing summary + +Each net start at the driver output/inout BEL pin. The BEL pin will be +connected to exactly 1 site wire. If the net sink can be reached within the +site, then the net can use site PIPs to reach a site wire connected to the +input/inout BEL pin. + +If the net sink is in another site, then the net must first reach a site port +input BEL pin using site PIPs to reach the site wire connected to the site +port. From there the net leaves the site via the site port and is now on the +first node via the site pin matching the site port. + +From there the net must use PIPs to expand to new nodes until arriving at a +node attached to valid site pin for the sink. This would be a site pin that +is part of the same site that the sink BEL is part of, and that the site port +wire can reach the sink BEL pin (via 0 or more site PIPs). The site can be +entered via the site port corresponding to the site pin. The first site wire +in the site will be the site wire attached to the output BEL pin of the site +port. From there site routing continues per above. + +![Wire and nodes](https://symbiflow.readthedocs.io/projects/arch-defs/en/latest/_images/rrgraph-wire.svg) + +### Use of site PIPs + +It is important to note that site PIPs can only be used to access placed cells +inside that site. Site PIPs cannot be used as general route-thrus, to route +from site input to output. General route-thrus across entire sites should use +tile pseudo PIPs as described below - even if a site pin is being validly used +for one sink pin of a net that is located inside the site; site PIPs cannot +also be used to leave the site again to reach other sinks. + +LUT route-thrus, for example, might require both site PIPs and tile pseudo +PIPs. The site PIP would be used to route through the LUT in order to access +an associated flipflop input inside the site. The tile PIP would be used to +route across the entire site as part of the general, inter-tile, routing +problem. + +A diagram illustrating the legal and illegal uses is shown below. + +![Site PIP usage](site_pip_usage.svg) + +### Tile Types and site types + +To reduce data duplication in the device schema, both tiles and sites have a +type. Most of the definition of the tile and site is in the type rather than +repeated at each instance. This does cause some more complicated +indirection, so the following section provides some additional explanation +here. + +#### Sites, site types and alternative site types + +The most complicated relationship in the schema is likely the relationship +between sites, site types and alternative site types. + +Most of the site type description is independent of the tile / tile type that +the site type is within. See the "SiteType" struct definition for +the independent portion of the schema. The important exception is the +relationship between wires and site pins. + +Each site within a tile has a "primary site type", which is found in the +"SiteTypeInTileType" struct definition, contained in the "TileType" struct. +The site within the tile will specify which "SiteTypeInTileType" to use for +that particular site. + +The primary site type contains a list of "alternative" site types that may be +used ("altSiteTypes" in "SiteType"). The primary site type must always +contains the complete list of site pins used in any of the alternative site +types. + +The site pins to wire relationship is always done via the primary site type. +When an alternative site type is used, the site pins of that alternative site +type must be first be mapped to a site pin of the primary site type. + +The "SiteTypeInTileType" defines the relationship between the primary site +type and the wires. It also defines the relationship from the alternative +site type to the primary site type. + +It first defines the primary site type ("primaryType"). It defines a map +between the site pins in the primary site type and wires in the tile type that +contains the site ("primaryPinsToTileWires"). Last it defines +the map between the alternative site pin and the primary site pin +("altPinsToPrimaryPins"). + +Important: When solving the place and route problem, only the primary or one +of the alternative site types can be used at a time for a particular site. + +### Routing BEL + +A routing BEL represents statically configurable site routing by connecting a +site wire connected to one of the input BEL pins to the output BEL pin of +the BEL. Routing BELs must have 1 output BEL pin. + +#### Inverting routing BELs + +Some routing BELs can invert signals that pass through them. Defined the +"inverting" field in the "BEL" struct with the BEL pins that invert and do +not invert. + +### Site port BEL + +A site port BEL represents a connection to a site pin contained within the +parent tile of the site. Site port BELs have exactly 1 BEL pin. The BEL name +and pin name should be the same. The name of the BEL should match the name of +the site pin that the site port connects too. The direction of the BEL pin +should be the opposite of the site pin direction. + +Examples: + +An input site pin "I0" would have a site port BEL named "I0" with 1 BEL +output pin named "I0". + +## Additional topics + +The device resources schema also covers some important data required for +handling common cases found in island based FPGAs. + +### Signal inversions + +It is fairly common for fabrics to contain site local signal inverters. +Depending on the architecture, the place and route tool may be expected to +leverage inverters or may even require it. The device resources schemas +provides a description for how cells express inversion and how to use site +local inverters to implement the requested inversion. + +### LUT definitions + +LUTs are common to every island-based FPGA, and many place and route tasks +depend on having knowledge of how the LUTs are arranged. The LUT definition +section of the device resources defines where LUTs exist as BELs and what +cells can be placed at those BELs. This is important is at least two place +and route tasks. The first is that LUTs can be trivially turned into site pips +from the input of the LUT to the output of the LUT, subject to **constraints** +and LUT equation sharing. The second is that LUTs can be trivially turned +into constant sources from the output pin. + +### Parameters + +Some parameters attached to cell instances may be relevant for the place and +route problem. A common example is LUT equation sharing, which can happen on +fracturable LUTs. See the schema for details. + +### Pseudo PIPs + +It may be important within a device to represent PIPs that "route-thru" +one or more BELs. This can be modelled as placing a cell in a particular +configuration at a BEL, subject to the normal cell placement rules. The +"PseudoCell" struct defines what resources are used by using PIPs. + +All pseudo PIPs must define at least 1 pseudo cell. Pseudo cells should +include the site port BEL that the pseudo PIP used to enter the site. diff --git a/libs/EXTERNAL/libinterchange/docs/environment.yml b/libs/EXTERNAL/libinterchange/docs/environment.yml new file mode 100644 index 00000000000..490164c1596 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/environment.yml @@ -0,0 +1,32 @@ +# Copyright (C) 2017-2021 The SymbiFlow Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: fpga-interchange-schema-docs +channels: +- SymbiFlow +- conda-forge +- defaults +dependencies: +- python=3.7 +- pip +# ReadTheDoc dependencies +- mock +- pillow +- sphinx +- sphinx_rtd_theme +# Packages installed from PyPI +- pip: + - -r requirements.txt diff --git a/libs/EXTERNAL/libinterchange/docs/eos_slice.png b/libs/EXTERNAL/libinterchange/docs/eos_slice.png new file mode 100644 index 00000000000..cbe8fd26df2 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/eos_slice.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/eos_slice.png-057.png b/libs/EXTERNAL/libinterchange/docs/eos_slice.png-057.png new file mode 100644 index 00000000000..d0597ca5c0d Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/eos_slice.png-057.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/eos_slice_mux4x2.png b/libs/EXTERNAL/libinterchange/docs/eos_slice_mux4x2.png new file mode 100644 index 00000000000..90d49f9ff02 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/eos_slice_mux4x2.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/eos_slice_mux8x3.png b/libs/EXTERNAL/libinterchange/docs/eos_slice_mux8x3.png new file mode 100644 index 00000000000..4ee6a2be13b Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/eos_slice_mux8x3.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/frac_lut4.png b/libs/EXTERNAL/libinterchange/docs/frac_lut4.png new file mode 100644 index 00000000000..8ae555d0321 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/frac_lut4.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/frac_lut4_a.png b/libs/EXTERNAL/libinterchange/docs/frac_lut4_a.png new file mode 100644 index 00000000000..9f700437785 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/frac_lut4_a.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/frac_lut4_b.png b/libs/EXTERNAL/libinterchange/docs/frac_lut4_b.png new file mode 100644 index 00000000000..49747814cdf Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/frac_lut4_b.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/highlight_bottom_lut6.png b/libs/EXTERNAL/libinterchange/docs/highlight_bottom_lut6.png new file mode 100644 index 00000000000..2f823400b92 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/highlight_bottom_lut6.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/highlight_muxf5.png b/libs/EXTERNAL/libinterchange/docs/highlight_muxf5.png new file mode 100644 index 00000000000..94f0228d506 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/highlight_muxf5.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/highlight_muxf5_muxf6.png b/libs/EXTERNAL/libinterchange/docs/highlight_muxf5_muxf6.png new file mode 100644 index 00000000000..551268517d0 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/highlight_muxf5_muxf6.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/highlight_top_lut6.png b/libs/EXTERNAL/libinterchange/docs/highlight_top_lut6.png new file mode 100644 index 00000000000..a78a1c22b6a Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/highlight_top_lut6.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/index.rst b/libs/EXTERNAL/libinterchange/docs/index.rst new file mode 100644 index 00000000000..d8dc21edf8c --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/index.rst @@ -0,0 +1,18 @@ +Welcome to FPGA Interchange documentation! +========================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + device_resources + bel_and_site_design + pseudo_cells + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/libs/EXTERNAL/libinterchange/docs/pseudo_cells.md b/libs/EXTERNAL/libinterchange/docs/pseudo_cells.md new file mode 100644 index 00000000000..65b55d9d44b --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/pseudo_cells.md @@ -0,0 +1,37 @@ +# Pseudo Cells + +Pseudo PIPs and site pseudo PIPs are edges in the device graph that route +through other sites and/or BELs. Pseudo cells are the expression of what +routing resources are "blocked" by the use of a pseudo PIP. + +The device database currently expresses pseudo PIPs as using BEL pins within +the site that the pseudo PIP is attached too. Both input and output BEL pins +are included in the pseudo cell definition, but only output BEL pins "block" +the site wire. + +### Example + +In the case of a Xilinx 7-series `CLBLL`'s `CLBLL_LL_A1` to `CLBLL_LL_A` +pseudo PIP, this PIP connects a signal from an input site port to an output +site port. Each site wire that is consumed has **both** a output BEL pin +(from the site wire source) and a input BEL pin (connected to either a logic +BEL, e.g. `A6LUT` or routing BEL, e.g. `AUSED` or output site port BEL, e.g. +`A`). + +In the case of a `CLBLL`'s `CLBLL_LL_A` to `CLBLL_LL_AMUX` pseudo PIP, this +PIP connects a signal from an output site port to an output site port. In +this case, it is assumed and required that some of the site wires are already +bound to the relevant net (by virtue of the wire `CLBLL_LL_A` already being +part of the net). In this case, the first BEL pin will be an input BEL pin +(specifically `AOUTMUX/O6`) that indicates that a site PIP is used as part of +the pseudo PIP. However in this case, this edge does **not** block the site +wire, instead it only requires it. The following output BEL pin (specifically + `AOUTMUX/OUT`) blocks the site wire `AMUX`. + +### Future enhancements + +Pseudo cells right now only have BEL pins used by the pseudo PIP. This +ignores the fact that some BEL's when route through may have constraint +implications. For example, routing through a LUT BEL requires that it be in +LUT mode. If that BEL is in either a SRL or LUT-RAM mode, the LUT route +through may not operate properly. diff --git a/libs/EXTERNAL/libinterchange/docs/requirements.txt b/libs/EXTERNAL/libinterchange/docs/requirements.txt new file mode 100644 index 00000000000..b83d25184be --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/requirements.txt @@ -0,0 +1,10 @@ +git+https://github.com/SymbiFlow/sphinx_symbiflow_theme.git#egg=sphinx_symbiflow_theme + +docutils +sphinx +sphinx-autobuild + +breathe +recommonmark +sphinx-markdown-tables +sphinxcontrib-napoleon diff --git a/libs/EXTERNAL/libinterchange/docs/site_pip_usage.svg b/libs/EXTERNAL/libinterchange/docs/site_pip_usage.svg new file mode 100644 index 00000000000..ebf5721486c --- /dev/null +++ b/libs/EXTERNAL/libinterchange/docs/site_pip_usage.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + LUT6 + + + LUT5 + + + + + + + + + + + + + + + + DFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LUT6 + + + LUT5 + + + + + + + + + + + + + + + + DFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LUT6 + + + LUT5 + + + + + + + + + + + + + + + + DFF + + + + + + + + + + + + + + + + + + + + + Legal - site routing + used to access DFF + input and output + + + Illegal - not being + used to access bound + bel inside site; tile PIP + should be used instead + + + + + Illegal - red dashed + part not being used to + access bound bel inside + site; tile PIP should be used + instead for that part + + + + + diff --git a/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_lut5.png b/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_lut5.png new file mode 100644 index 00000000000..ae621a6498f Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_lut5.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_lut6.png b/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_lut6.png new file mode 100644 index 00000000000..c14aab13d8e Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_lut6.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_muxf5_muxf6.png b/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_muxf5_muxf6.png new file mode 100644 index 00000000000..3addc520931 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/stratix10_highlight_muxf5_muxf6.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/stratix10_slice.png-11.png b/libs/EXTERNAL/libinterchange/docs/stratix10_slice.png-11.png new file mode 100644 index 00000000000..a84aa6ae27b Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/stratix10_slice.png-11.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/stratix2_slice.png-026.png b/libs/EXTERNAL/libinterchange/docs/stratix2_slice.png-026.png new file mode 100644 index 00000000000..c1efec6f4b6 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/stratix2_slice.png-026.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/stratix2_slice.png-026_rotate.png b/libs/EXTERNAL/libinterchange/docs/stratix2_slice.png-026_rotate.png new file mode 100644 index 00000000000..6021abd3cb9 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/stratix2_slice.png-026_rotate.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/versal_lut4.png b/libs/EXTERNAL/libinterchange/docs/versal_lut4.png new file mode 100644 index 00000000000..47c958a0372 Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/versal_lut4.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/versal_lut5.png b/libs/EXTERNAL/libinterchange/docs/versal_lut5.png new file mode 100644 index 00000000000..edf197743dd Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/versal_lut5.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/versal_lut6.png b/libs/EXTERNAL/libinterchange/docs/versal_lut6.png new file mode 100644 index 00000000000..31c907ab18b Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/versal_lut6.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/versal_luts.png b/libs/EXTERNAL/libinterchange/docs/versal_luts.png new file mode 100644 index 00000000000..94d36e7691a Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/versal_luts.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/versal_row.png b/libs/EXTERNAL/libinterchange/docs/versal_row.png new file mode 100644 index 00000000000..9af681c9d1d Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/versal_row.png differ diff --git a/libs/EXTERNAL/libinterchange/docs/versal_slice.png-12.png b/libs/EXTERNAL/libinterchange/docs/versal_slice.png-12.png new file mode 100644 index 00000000000..84eb163321f Binary files /dev/null and b/libs/EXTERNAL/libinterchange/docs/versal_slice.png-12.png differ diff --git a/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp b/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp new file mode 100644 index 00000000000..09c8d438555 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/interchange/DeviceResources.capnp @@ -0,0 +1,915 @@ +# Copyright 2020-2021 Xilinx, Inc. and Google, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@0x9d262c6ba6512325; +using Java = import "/capnp/java.capnp"; +using Ref = import "References.capnp"; +using Dir = import "LogicalNetlist.capnp"; +$Java.package("com.xilinx.rapidwright.interchange"); +$Java.outerClassname("DeviceResources"); + +using Cxx = import "/capnp/c++.capnp"; +$Cxx.namespace("DeviceResources"); + +struct HashSet { + type @0 : Ref.ImplementationType = enumerator; + hide @1 : Bool = true; +} +annotation hashSet(*) :HashSet; + +struct StringRef { + type @0 :Ref.ReferenceType = rootValue; + field @1 :Text = "strList"; +} +annotation stringRef(*) :StringRef; +using StringIdx = UInt32; + +struct SiteTypeRef { + type @0 :Ref.ReferenceType = root; + field @1 :Text = "siteTypeList"; +} +annotation siteTypeRef(*) :SiteTypeRef; +using SiteTypeIdx = UInt32; + +struct BELPinRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "belPins"; + depth @2 :Int32 = 1; +} +annotation belPinRef(*) :BELPinRef; +using BELPinIdx = UInt32; + +struct WireRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "wires"; + depth @2 :Int32 = 1; +} +annotation wireRef(*) :WireRef; +using WireIdx = UInt32; + +struct WireTypeRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "wireTypes"; + depth @2 :Int32 = 1; +} +annotation wireTypeRef(*) :WireTypeRef; +using WireTypeIdx = UInt32; + +using WireIDInTileType = UInt32; # ID in Tile Type +using SitePinIdx = UInt32; + +struct TileTypeRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "tileTypeList"; + depth @2 :Int32 = 1; +} +annotation tileTypeRef(*) :TileTypeRef; +using TileTypeIdx = UInt32; + +using TileTypeSiteTypeIdx = UInt32; +using TileTypeSubTileIdx = UInt16; + +struct PIPTimingRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "pipTimingList"; + depth @2 :Int32 = 1; + +} +annotation pipTimingRef(*) :PIPTimingRef; +using PipTimingIdx = UInt32; + +struct NodeTimingRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "nodeTimingList"; + depth @2 :Int32 = 1; + +} +annotation nodeTimingRef(*) :NodeTimingRef; +using NodeTimingIdx = UInt32; + +struct Device { + + name @0 : Text; + strList @1 : List(Text) $hashSet(); + siteTypeList @2 : List(SiteType); + tileTypeList @3 : List(TileType); + tileList @4 : List(Tile); + wires @5 : List(Wire); + nodes @6 : List(Node); + # Netlist libraries of Unisim primitives and macros + # The library containing primitives should be called "primitives", and + # the library containing macros called "macros". + primLibs @7 : Dir.Netlist; + exceptionMap @8 : List(PrimToMacroExpansion); # Prims to macros expand w/same name, except these + cellBelMap @9 : List(CellBelMapping); + cellInversions @10 : List(CellInversion); + packages @11 : List(Package); + constants @12 : Constants; + constraints @13 : Constraints; + lutDefinitions @14 : LutDefinitions; + parameterDefs @15 : ParameterDefinitions; + wireTypes @16 : List(WireType); + pipTimings @17 : List(PIPTiming); + nodeTimings @18 : List(NodeTiming); + + ####################################### + # Placement definition objects + ####################################### + struct SiteType { + name @0 : StringIdx $stringRef(); + belPins @1 : List(BELPin); # All BEL Pins in site type + pins @2 : List(SitePin); + lastInput @3 : UInt32; # Index of the last input pin + bels @4 : List(BEL); + sitePIPs @5 : List(SitePIP); + siteWires @6 : List(SiteWire); + altSiteTypes @7 : List(SiteTypeIdx); + } + + # Maps site pins from alternative site types to the parent primary site pins + struct ParentPins { + # pins[0] is the mapping of the siteTypeList[altSiteType].pins[0] to the + # primary site pin index. + # + # To determine the tile wire for a alternative site type, first get the + # site pin index for primary site, then use primaryPinsToTileWires. + pins @0 : List(SitePinIdx); + } + + struct SiteTypeInTileType { + primaryType @0 : SiteTypeIdx $siteTypeRef(); + + # primaryPinsToTileWires[0] is the tile wire that matches + # siteTypeList[primaryType].pins[0], etc. + primaryPinsToTileWires @1 : List(StringIdx) $stringRef(); + + # altPinsToPrimaryPins[0] is the mapping for + # siteTypeList[primaryType].altSiteTypes[0], etc. + altPinsToPrimaryPins @2 : List(ParentPins); + } + + struct TileType { + name @0 : StringIdx $stringRef(); + siteTypes @1 : List(SiteTypeInTileType); + wires @2 : List(StringIdx) $stringRef(); + pips @3 : List(PIP); + constants @4 : List(WireConstantSources); + } + + ####################################### + # Placement instance objects + ####################################### + struct BELInverter { + nonInvertingPin @0 : BELPinIdx $belPinRef(depth = 2); + invertingPin @1 : BELPinIdx $belPinRef(depth = 2); + } + + struct BEL { + name @0 : StringIdx $stringRef(); + type @1 : StringIdx $stringRef(); + pins @2 : List(BELPinIdx) $belPinRef(); + category @3 : BELCategory; # This would be BELClass/class, but conflicts with Java language + union { + nonInverting @4 : Void; + inverting @5 : BELInverter; + } + } + + enum BELCategory { + logic @0; + routing @1; + sitePort @2; + } + + struct Site { + name @0 : StringIdx $stringRef(); + type @1 : TileTypeSiteTypeIdx; # Index into TileType.siteTypes + } + + struct Tile { + name @0 : StringIdx $stringRef(); + type @1 : TileTypeIdx $tileTypeRef(); + sites @2 : List(Site); + row @3 : UInt16; + col @4 : UInt16; + + # Field ordinal 5 was deleted. + deleted @5 : UInt32; + + # Sub-tiles enable PIPs inside a tile to use different FASM prefices + # This is needed for the Nexus, where there can be multiple tiles from a + # bitstream perspective at the same (row, col) grid location. PIP.subTile + # indexes into this list to get a prefix for FASM purposes. + # If sub-tiles are not used; then this list is empty and an implicit + # sub-tile index 0 has the same prefix as the tile name. + subTilesPrefices @6 : List(StringIdx) $stringRef(); + } + + ###################################### + # Intra-site routing resources + ###################################### + struct BELPin { + name @0 : StringIdx $stringRef(); + dir @1 : Dir.Netlist.Direction; + bel @2 : StringIdx $stringRef(); + } + + struct SiteWire { + name @0 : StringIdx $stringRef(); + pins @1 : List(BELPinIdx) $belPinRef(); + } + + struct SitePIP { + inpin @0 : BELPinIdx $belPinRef(); + outpin @1 : BELPinIdx $belPinRef(); + # Interconnect delay + delay @2 : CornerModel; + } + + struct SitePin { + name @0 : StringIdx $stringRef(); + dir @1 : Dir.Netlist.Direction; + belpin @2 : BELPinIdx $belPinRef(); + model : union { + noModel @5 : Void; + resistance @3 : CornerModel; + capacitance @4 : CornerModel; + } + delay @6 : CornerModel; + } + + ###################################### + # Inter-site routing resources + ###################################### + + struct Wire { + tile @0 : StringIdx $stringRef(); + wire @1 : StringIdx $stringRef(); + type @2 : WireTypeIdx $wireTypeRef(); + } + + enum WireCategory { + # general interconnect, usually with many uphill and downhill pips and spanning multiple tiles + general @0; + # pin/local wires, carry chains, dedicated paths, everything else + special @1; + # the global clock network + global @2; + } + + # This is used to distinguish between different types of wires, in order to provide extra hints + # during routing, such as the category of wires. It is also intended to be able to describe + # complex routing requirements, such as global routing which requires a series of different types + # of wires to be used in succession + struct WireType { + name @0 : StringIdx $stringRef(); + category @1 : WireCategory; + } + + struct Node { + wires @0 : List(WireIdx) $wireRef(); + nodeTiming @1 : NodeTimingIdx $nodeTimingRef(); + } + + struct PIP { + wire0 @0 : WireIDInTileType; + wire1 @1 : WireIDInTileType; + directional @2 : Bool; + buffered20 @3 : Bool; + buffered21 @4 : Bool; + union { + conventional @5 : Void; + pseudoCells @6 : List(PseudoCell); + } + subTile @7 : TileTypeSubTileIdx; # Index into Tile.subTilesPrefices + timing @8 : PipTimingIdx $pipTimingRef(); + } + + struct PseudoCell { + bel @0 : StringIdx $stringRef(); + pins @1 : List(StringIdx) $stringRef(); + } + + struct WireConstantSources { + wires @0 : List(WireIDInTileType); + constant @1 : ConstantType; + } + + # Table lookup map, for string parameters + struct ParameterMapEntry { + # If the primitive parameter matches 'from', then the macro + # instance parameter will be set to 'to'. + from @0 : StringIdx $stringRef(); + to @1 : StringIdx $stringRef(); + } + + # This describes how we map a parameter from primitive to its + # macro expansion. + struct ParameterMapRule { + # Name of the parameter in its parent primitive + primParam @0 : StringIdx $stringRef(); + # Name of the cell instance to set the derived parameter on + instName @1 : StringIdx $stringRef(); + # Name of the parameter on the cell instance to set + instParam @2 : StringIdx $stringRef(); + # How to derive the new parameter value + union { + # Copy the value directly across with no transform applied + copyValue @3 : Void; + # Apply an arbitrary mapping of bits while deriving the new value. + # Bit i of the derived value will be taken from bit bitSlice[i] of the + # parent primitive parameter. This way bit ranges; every Nth bit and + # permutation can all be represented. + bitSlice @4 : List(UInt32); + # Use a table lookup to derive a new value for a string parameter. + tableLookup @5 : List(ParameterMapEntry); + } + } + + ###################################### + # Macro expansion exception map for + # primitives that don't expand to a + # macro of the same name. This is also + # used for conditional matches on + # parameter values and parameter + # transforms from primitive to + # expansion. + ###################################### + struct PrimToMacroExpansion { + primName @0 : StringIdx $stringRef(); + macroName @1 : StringIdx $stringRef(); + # Optionally, primitive to macro expansions can be conditional on a + # parameter match. For example, I/O buffer expansions might be + # different between true and pseudo differential IO types. The + # expansion is used if **any** of the parameters specified match. + union { + always @2 : Void; + parameters @3 : List(Dir.Netlist.PropertyMap.Entry); + } + # These rules are used to map parameters from the primitive to the + # constituent cell instances for the macro. For example; a LUTRAM + # primitive might have its init value split up to the init values + # of its constituent LUTs. + paramMapping @4 : List(ParameterMapRule); + } + + # Cell <-> BEL and Cell pin <-> BEL Pin mapping + struct CellBelMapping { + cell @0 : StringIdx $stringRef(); + commonPins @1 : List(CommonCellBelPinMaps); + parameterPins @2 : List(ParameterCellBelPinMaps); + pinsDelay @3 : List(PinsDelay); + } + + # Map one cell pin to one BEL pin. + # Note: There may be more than one BEL pin mapped to one cell pin. + # If cellPin is "GND" then the BEL pin is tied to ground, and if cellPin + # is "VCC" then the BEL pin is tied to Vcc. This is used where BEL pins + # must be tied to a constant, but there is no corresponding cell pin. + struct CellBelPinEntry { + cellPin @0 : StringIdx $stringRef(); + belPin @1 : StringIdx $stringRef(); + } + + # Specifies BELs located in a specific site type. + struct SiteTypeBelEntry { + siteType @0 : StringIdx $stringRef(); + bels @1 : List(StringIdx) $stringRef(); + } + + # This is the portion of Cell <-> BEL pin mapping that is common across all + # parameter settings for a specific site type and BELs within that site + # type. + struct CommonCellBelPinMaps { + siteTypes @0 : List(SiteTypeBelEntry); + pins @1 : List(CellBelPinEntry); + } + + # This is the portion of the Cell <-> BEL pin mapping that is parameter + # specific. + struct ParameterSiteTypeBelEntry { + bel @0 : StringIdx $stringRef(); + siteType @1 : StringIdx $stringRef(); + parameter @2 : Dir.Netlist.PropertyMap.Entry; + } + + struct ParameterCellBelPinMaps { + parametersSiteTypes @0 : List(ParameterSiteTypeBelEntry); + pins @1 : List(CellBelPinEntry); + } + + struct Package { + struct PackagePin { + packagePin @0 : StringIdx $stringRef(); + site : union { + noSite @1 : Void; + site @2 : StringIdx $stringRef(); + } + bel : union { + noBel @3 : Void; + bel @4 : StringIdx $stringRef(); + } + } + + struct Grade { + name @0 : StringIdx $stringRef(); + speedGrade @1 : StringIdx $stringRef(); + temperatureGrade @2 : StringIdx $stringRef(); + } + + name @0 : StringIdx $stringRef(); + packagePins @1 : List(PackagePin); + grades @2 : List(Grade); + } + + # Constants + enum ConstantType { + # Routing a VCC or GND are equal cost. + noPreference @0; + # Routing a GND has the best cost. + gnd @1; + # Routing a VCC has the best cost. + vcc @2; + } + + struct Constants { + struct SitePinConstantExceptions { + siteType @0 : StringIdx $stringRef(); + sitePin @1 : StringIdx $stringRef(); + bestConstant @2 : ConstantType; + } + + struct SiteConstantSource { + siteType @0 : StringIdx $stringRef(); + bel @1 : StringIdx $stringRef(); + belPin @2 : StringIdx $stringRef(); + constant @3 : ConstantType; + } + + struct NodeConstantSource { + tile @0 : StringIdx $stringRef(); + wire @1 : StringIdx $stringRef(); + constant @2 : ConstantType; + } + + # These structures are used to define default constant values for unused + # or missing cell pins. For each cell type, we have a list of pins and + # what to do with those pins (which will be to tie it to 0 or 1 in most + # cases). + enum CellPinValue { + # leave floating + float @0; + # connect to ground + gnd @1; + # connect to vcc + vcc @2; + } + + struct DefaultCellConnection { + # What is the name of this cell pin? + name @0 : StringIdx $stringRef(); + # The default constant value for the pin if missing or disconnected + value @1 : CellPinValue; + } + + struct DefaultCellConnections { + # The type of the cell we're providing a list of defaults for + cellType @0 : StringIdx $stringRef(); + # The list of default cell pin values + pins @1 : List(DefaultCellConnection); + } + + # When either constant signal can be routed to an input site pin, which + # constant should be used by default? + # + # For example, if a site pin has a local inverter and a cell requires a + # constant signal, then either a gnd or vcc could be routed to the site. + # The inverter can be used to select which ever constant is needed, + # regardless of what constant the cell requires. In some fabrics, routing + # a VCC or routing a GND is significantly easier than the other. + defaultBestConstant @0 : ConstantType; + + # If there are exceptions to the default best constant, then this list + # specifies which site pins use a different constant. + bestConstantExceptions @1 : List(SitePinConstantExceptions); + + # List of constants that can be found within the routing graph without + # consuming a BEL. + # + # Tools can always generate a constant source from a LUT BEL type. + siteSources @2 : List(SiteConstantSource); + + # Most tied nodes are handled under TileType.constants, however in some + # exceptional cases, the tying is inconsistent between tile types. + # nodeSources should be used to explicitly list nodes that fall into this + # case. + nodeSources @3 : List(NodeConstantSource); + + # Name of GND and VCC cells and their pins that are tied to GND/VCC. + gndCellType @4 : StringIdx $stringRef(); + gndCellPin @5 : StringIdx $stringRef(); + + vccCellType @6 : StringIdx $stringRef(); + vccCellPin @7 : StringIdx $stringRef(); + + # If the format requires a specific gnd/vcc net name, what name should be + # used? + gndNetName : union { + anyName @8 : Void; + name @9 : StringIdx $stringRef(); + } + + vccNetName : union { + anyName @10 : Void; + name @11 : StringIdx $stringRef(); + } + + # How to treat missing/disconnected cell pins + defaultCellConns @12 : List(DefaultCellConnections); + } + + ###################################### + # Inverting pins description + # + # This block describes local site wire + # inverters, site routing BELs, and + # parameters. + ###################################### + struct CellPinInversionParameter { + union { + # This inverter cannot be controlled by parameter, only via tool merging + # of INV cells or other means. + invOnly @0 : Void; + # This inverter can be controlled by a parameter. + # What parameter value configures this setting? + parameter @1 : Dir.Netlist.PropertyMap.Entry; + } + } + + struct CellPinInversion { + # Which cell pin supports a local site inverter? + cellPin @0 : StringIdx $stringRef(); + + # What parameters are used for the non-inverting case, and how to route + # through the inversion routing bels (if any). + notInverting @1 : CellPinInversionParameter; + + # What parameters are used for the inverting case, and how to route + # through the inversion routing bels (if any). + inverting @2 : CellPinInversionParameter; + } + + struct CellInversion { + # Which cell is being described? + cell @0 : StringIdx $stringRef(); + + # Which cell have site local inverters? + cellPins @1 : List(CellPinInversion); + } + + ###################################### + # Timing modeling + # + # This section defines the timing model represantation + # for the interchange schema. + # + # Even though there is no strict standard to define how many + # corner models need to be defined for a given architecture, + # a timing delay model usually includes a "fast" and a + # "slow" corner process, each with three delay measures: + # - minimum + # - typical + # - maximum + # + # The idea is to have a static definition of the corner models + # so to standardize the interchange format to use at maximum + # two process corner models (fast and slow), with the respective + # delay measures. If an architecture does not include one or two + # (but not all of three) delay measures, the timing model is still valid. + # + # There are three main location where timing delays must be defined: + # - BEL/Cell pins + # - Wires + # - PIPs + # + # * BEL/Cell pins: + # These delays can be sequential (associated to a clock pin) or combinatorial + # (associated to an input/output pair). + # At minimum there are four types of pin delays that need to be considered: + # - comb : input to output delay + # - setup : associated to the input and a clock pin + # - hold : associated to the input and a clock pin + # - clock2q : associated to the clock and the output pin + # + # * Wires: + # These delays are associated to wires, or better nodes which are collection + # of electrically connected wires. + # They are described following the RC (Resistance/Capacitance) modeling. + # + # * PIPs: + # These are delays corresponding to the connections between two wires. + # + ###################################### + + # BEL/Cell pins delays + struct PinsDelay { + firstPin @0 : PinDelay; + secondPin @1 : PinDelay; + cornerModel @2 : CornerModel; + pinsDelayType @3 : PinsDelayType; + site @4 : SiteTypeIdx $siteTypeRef(); + } + + struct PinDelay { + pin @0 : BELPinIdx $belPinRef(); + union { + noClock @1 : Void; + clockEdge @2 : ClockEdge; + } + } + + enum ClockEdge { + rise @0; + fall @1; + } + + enum PinsDelayType { + comb @0; + setup @1; + hold @2; + clk2q @3; + } + + # Wire (nodes) delays + struct NodeTiming { + capacitance @0 : CornerModel; + resistance @1 : CornerModel; + } + + # PIP (switches) delays + struct PIPTiming { + inputCapacitance @0 : CornerModel; + internalCapacitance @1 : CornerModel; + internalDelay @2 : CornerModel; + outputResistance @3 : CornerModel; + outputCapacitance @4 : CornerModel; + } + + struct CornerModel { + slow : union { + noSlow @0 : Void; + slow @1 : CornerModelValues; + } + + fast : union { + noFast @2 : Void; + fast @3 : CornerModelValues; + } + } + + struct CornerModelValues { + min : union { + noMin @0 : Void; + min @1 : Float32; + } + + typ : union { + noTyp @2 : Void; + typ @3 : Float32; + } + + max : union { + noMax @4 : Void; + max @5 : Float32; + } + } + + ###################################### + # Placement constraints + # + # This section defines constraints required for valid placement above and + # beyond routing constraints. + # + # This section has three sections: + # - Tags + # - Routed tags + # - Cell constraints + # + # The tags sections defines a list of tags that take one of an exclusive set + # of states. Tags are attached to an object within the FPGA fabric. + # Currently tags can only be attached to either a site type or tile type. + # All instances of that site type or tile type will have it's own unique + # instance of the tag. In order for the constraints to be considered "met", + # each tag instance must be constrained to either 0 or 1 states. In the + # event that a tag instance is constrained to 0 states, then that tag + # instance value will be the "default" state. + # + # Routed tags are required to express instances where a cell constraint + # relates through a site routing mux to a tag. Routed tags route from a tag + # or routed tag to either another routed tag or a cell constraint. + # + # Cell constraints are the list of constraints that are applied (or removed) + # when a cell is placed (or unplaced). The format of the cell constraint + # can be read as: + # - When a cell of a specific type is (cell/cells field) + # - Is placed at (siteTypes and bel field) + # - Apply the following constraints (implies/requires field). + # + # In the case where a cell constraint references a routed tag, then the + # constraint also applies to the upstream tag or routed tag. + ###################################### + struct Constraints { + struct State { + state @0 :Text; + description @1 :Text; + } + + struct Tag { + tag @0 :Text; + description @1 :Text; + default @2 :Text; + union { + siteTypes @3 :List(Text); + tileTypes @4 :List(Text); + } + states @5 :List(State); + } + + struct RoutedTagPin { + pin @0 :Text; + tag @1 :Text; + } + + struct RoutedTag { + routedTag @0 :Text; + routingBel @1 :Text; + belPins @2 :List(RoutedTagPin); + } + + struct RoutedTagPort { + tag @0 :Text; + port @1 :Text; + } + + struct TagPair { + union { + tag @0 :Text; + routedTag @1 :RoutedTagPort; + } + state @2 :Text; + } + + struct TagStates { + union { + tag @0 :Text; + routedTag @1 :RoutedTagPort; + } + states @2 :List(Text); + } + + struct BELLocation { + union { + anyBel @0 :Void; + name @1 :Text; + bels @2 :List(Text); + } + } + + struct ConstraintLocation { + siteTypes @0 :List(Text); + bel @1 :BELLocation; + union { + implies @2 :List(TagPair); + requires @3 :List(TagStates); + } + } + + struct CellConstraint { + union { + cell @0 :Text; + cells @1 :List(Text); + } + locations @2 :List(ConstraintLocation); + } + + tags @0 :List(Tag); + routedTags @1 :List(RoutedTag); + cellConstraints @2 :List(CellConstraint); + } + + ###################################### + # LUT definitions + ###################################### + struct LutDefinitions { + struct LutCell { + # What cell type is this? + cell @0 : Text; + + # What pins are part of the LUT equations? + # Pins are listed in LSB first order. + inputPins @1 : List(Text); + + # How is the LUT equation stored? + equation : union { + # Equation is stored as an INIT style parameter. + # For a LUT2, INIT is 4 bits. + # INIT[0] is output when all pins are 0. + # INIT[1] is output when first pin is 1 and all other pins are 0. + # INIT[2] is output when second pin is 1 and all other pins are 0. + # INIT[3] is output when both pins are 1. + initParam @2 : Text; + invalid @3 : Void; + } + } + + struct LutBel { + # Name of the BEL that is part of this element + name @0 : Text; + # What pins are part of this BEL? + # Pins are listed in LSB first order. + inputPins @1 : List(Text); + # What is the output pin of this LUT? + outputPin @2 : Text; + # What bits within the LutElement does this BEL use? + # Bits must be consecutive. + lowBit @3 : Int8; + highBit @4 : Int8; + } + + # Each LUT element in the site should have a width. + struct LutElement { + width @0 : Int8; + # If 2 or more BELs share the same underlying equation shortage, + # how are the BELs related? + bels @1 : List(LutBel); + } + + # How are LUT BELs laid out in a site? + struct LutElements { + site @0 : Text; + luts @1 : List(LutElement); + } + + # Which cells are LUT equations? + lutCells @0 : List(LutCell); + + # Which sites have LUT BELs? + lutElements @1 : List(LutElements); + } + + enum ParameterFormat { + string @0; + # TRUE/FALSE/1/0 + boolean @1; + # 0/1/256 + integer @2; + # 0.0 + floatingPoint @3; + # 1'b0 + verilogBinary @4; + # 8'hF + verilogHex @5; + # 0b10 + cBinary @6; + # 0xF + cHex @7; + } + + struct ParameterDefinition { + # What is the name of this parameter? + name @0 : StringIdx; + + # When a parameter is stored as a string, what presentation should it use? + # + # If the default is stored as a string, it must be in this presentation. + # If a logical netlist stores this parameter as a string, it must be in + # this presentation. + format @1 : ParameterFormat; + + # Default parameter value + # + # Note: key should also be set for ease of copy into + # LogicalNetlist. + default @2 : Dir.Netlist.PropertyMap.Entry; + } + + struct CellParameterDefinition { + cellType @0 : StringIdx; + parameters @1 : List(ParameterDefinition); + } + + struct ParameterDefinitions { + cells @0 : List(CellParameterDefinition); + } +} diff --git a/libs/EXTERNAL/libinterchange/interchange/LogicalNetlist.capnp b/libs/EXTERNAL/libinterchange/interchange/LogicalNetlist.capnp new file mode 100644 index 00000000000..eba0feb2b43 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/interchange/LogicalNetlist.capnp @@ -0,0 +1,170 @@ +# Copyright 2020-2021 Xilinx, Inc. and Google, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@0xcb2ccd67aa912967; +using Java = import "/capnp/java.capnp"; +using Ref = import "References.capnp"; +$Java.package("com.xilinx.rapidwright.interchange"); +$Java.outerClassname("LogicalNetlist"); + +using Cxx = import "/capnp/c++.capnp"; +$Cxx.namespace("LogicalNetlist"); + +struct HashSet { + type @0 : Ref.ImplementationType = enumerator; + hide @1 : Bool = true; +} +annotation hashSet(*) :HashSet; + +struct StringRef { + type @0 :Ref.ReferenceType = rootValue; + field @1 :Text = "strList"; +} +annotation stringRef(*) :StringRef; +using StringIdx = UInt32; + +struct PortRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "portList"; + depth @2 :Int32 = 1; +} +annotation portRef(*) :PortRef; +using PortIdx = UInt32; + +struct CellRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "cellDecls"; + depth @2 :Int32 = 1; +} +annotation cellRef(*) :CellRef; +using CellIdx = UInt32; + +struct InstRef { + type @0 :Ref.ReferenceType = parent; + field @1 :Text = "instList"; + depth @2 :Int32 = 1; +} +annotation instRef(*) :InstRef; +using InstIdx = UInt32; + +struct Netlist { + + name @0 : Text; + propMap @1 : PropertyMap; + strList @2 : List(Text) $hashSet(); + portList @3 : List(Port); + cellDecls @4 : List(CellDeclaration); + topInst @5 : CellInstance; + instList @6 : List(CellInstance); + cellList @7 : List(Cell); + + struct CellDeclaration { + name @0 : StringIdx $stringRef(); + propMap @1 : PropertyMap; + view @2 : StringIdx $stringRef(); + lib @3 : StringIdx $stringRef(); + ports @4 : List(PortIdx) $portRef(); + } + + struct CellInstance { + name @0 : StringIdx $stringRef(); + propMap @1 : PropertyMap; + view @2 : StringIdx $stringRef(); + cell @3 : CellIdx $cellRef(); + } + + struct Cell { + index @0 : CellIdx $cellRef(); + insts @1 : List(InstIdx) $instRef(); + nets @2 : List(Net); + } + + struct Net { + name @0 : StringIdx $stringRef(); + propMap @1 : PropertyMap; + portInsts @2 : List(PortInstance); + } + + struct Port { + name @0 : StringIdx $stringRef(); + dir @1 : Direction; + propMap @2 : PropertyMap; + union { + bit @3 : Void; + bus @4 : Bus; + } + } + + enum Direction { + input @0; + output @1; + inout @2; + } + + struct Bus { + busStart @0 : UInt32; + busEnd @1 : UInt32; + } + + struct PortInstance { + port @0 : PortIdx $portRef(depth = 3); + busIdx : union { + singleBit @1 : Void; # Single bit + idx @2 : UInt32; # Index within bussed port + } + union { + extPort @3 : Void; + inst @4 : InstIdx $instRef(depth = 3); + } + } + + # Arbitrary length bitstring, encoded into array of uint8. + # + # width is the width of the bitstring in number of bits. + # Data is required to ceil(width/8) elements long. + # Bits are stored in LSB order. Example. + # + # If bits is a bool vector-like, then: + # + # bits[0] is storage in the LSB of data[0]. + # bits[8] is storage in the LSB of data[1]. + # + # e.g. bit[0] === (data[0] & (1 << 0)) != 0 + # e.g. bit[1] === (data[0] & (1 << 1)) != 0 + # e.g. bit[7] === (data[0] & (1 << 7)) != 0 + # e.g. bit[8] === (data[1] & (1 << 0)) != 0 + # + struct Bitstring { + width @0 : UInt32; + data @1 : List(UInt8); + } + + struct PropertyMap { + entries @0 : List(Entry); + struct Entry { + key @0 : StringIdx $stringRef(); + # Some tools may require a particular presentation. textValue is the + # most general, and should always be accepted. + # + # See DeviceResources.ParameterDefinition for potential string + # presentations. + union { + textValue @1 : StringIdx $stringRef(); + intValue @2 : Int32; + boolValue @3 : Bool; + bitstringValue @4 : Bitstring; + } + } + } +} diff --git a/libs/EXTERNAL/libinterchange/interchange/PhysicalNetlist.capnp b/libs/EXTERNAL/libinterchange/interchange/PhysicalNetlist.capnp new file mode 100644 index 00000000000..1ac268f09f2 --- /dev/null +++ b/libs/EXTERNAL/libinterchange/interchange/PhysicalNetlist.capnp @@ -0,0 +1,164 @@ +# Copyright 2020-2021 Xilinx, Inc. and Google, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +@0xcb2ccd67aa912968; +using Java = import "/capnp/java.capnp"; +$Java.package("com.xilinx.rapidwright.interchange"); +$Java.outerClassname("PhysicalNetlist"); + +using Cxx = import "/capnp/c++.capnp"; +$Cxx.namespace("PhysicalNetlist"); + +using Ref = import "References.capnp"; + +struct StringRef { + type @0 :Ref.ReferenceType = rootValue; + field @1 :Text = "strList"; +} +annotation stringRef(*) :StringRef; +using StringIdx = UInt32; + +struct HashSet { + type @0 : Ref.ImplementationType = enumerator; + hide @1 : Bool = true; +} +annotation hashSet(*) :HashSet; + +struct PhysNetlist { + + part @0 : Text; + placements @1 : List(CellPlacement); + physNets @2 : List(PhysNet); + physCells @3 : List(PhysCell); + strList @4 : List(Text) $hashSet(); + siteInsts @5 : List(SiteInstance); + properties @6 : List(Property); + nullNet @7 : PhysNet; + + struct PinMapping { + cellPin @0 : StringIdx $stringRef(); + bel @1 : StringIdx $stringRef(); + belPin @2 : StringIdx $stringRef(); + isFixed @3 : Bool; + union { + multi @4 : Void; + otherCell @5 : MultiCellPinMapping; + } + } + + struct MultiCellPinMapping { + multiCell @0 : StringIdx $stringRef(); + multiType @1 : StringIdx $stringRef(); + } + + struct CellPlacement { + cellName @0 : StringIdx $stringRef(); + type @1 : StringIdx $stringRef(); + site @2 : StringIdx $stringRef(); + bel @3 : StringIdx $stringRef(); + pinMap @4 : List(PinMapping); + otherBels @5 : List(StringIdx) $stringRef(); + isBelFixed @6 : Bool; + isSiteFixed @7 : Bool; + altSiteType @8 : StringIdx $stringRef(); + } + + struct PhysCell { + cellName @0 : StringIdx $stringRef(); + physType @1 : PhysCellType; + } + + enum PhysCellType { + locked @0; + port @1; + gnd @2; + vcc @3; + } + + struct PhysNet { + name @0 : StringIdx $stringRef(); + sources @1 : List(RouteBranch); + stubs @2 : List(RouteBranch); + type @3 : NetType = signal; + } + + enum NetType { + signal @0; + gnd @1; + vcc @2; + } + + + struct RouteBranch { + routeSegment : union { + belPin @0 : PhysBelPin; + sitePin @1 : PhysSitePin; + pip @2 : PhysPIP; + sitePIP @3 : PhysSitePIP; + } + branches @4 : List(RouteBranch); + } + + struct PhysBel { + site @0 : StringIdx $stringRef(); + bel @1 : StringIdx $stringRef(); + } + + struct PhysBelPin { + site @0 : StringIdx $stringRef(); + bel @1 : StringIdx $stringRef(); + pin @2 : StringIdx $stringRef(); + } + + struct PhysSitePin { + site @0 : StringIdx $stringRef(); + pin @1 : StringIdx $stringRef(); + } + + struct PhysPIP { + tile @0 : StringIdx $stringRef(); + wire0 @1 : StringIdx $stringRef(); + wire1 @2 : StringIdx $stringRef(); + forward @3 : Bool; + isFixed @4 : Bool; + # In case of a pseudo PIP also the traversed site + # needs to be added to the PhysPIP object + union { + noSite @5: Void; + # It is assumed that a pseudo PIP can traverse one + # site only + site @6: StringIdx $stringRef(); + } + } + + struct PhysSitePIP { + site @0 : StringIdx $stringRef(); + bel @1 : StringIdx $stringRef(); + pin @2 : StringIdx $stringRef(); + isFixed @3 : Bool; + union { + isInverting @4 : Bool; + inverts @5 : Void; + } + } + + struct SiteInstance { + site @0 : StringIdx $stringRef(); + type @1 : StringIdx $stringRef(); + } + + struct Property { + key @0 : StringIdx $stringRef(); + value @1 : StringIdx $stringRef(); + } +} diff --git a/libs/EXTERNAL/libinterchange/interchange/References.capnp b/libs/EXTERNAL/libinterchange/interchange/References.capnp new file mode 100644 index 00000000000..8d4a1a9526b --- /dev/null +++ b/libs/EXTERNAL/libinterchange/interchange/References.capnp @@ -0,0 +1,28 @@ +# Copyright 2020-2021 Google, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@0xa0082c6893796a86; +using Java = import "/capnp/java.capnp"; +$Java.package("com.xilinx.rapidwright.interchange"); +$Java.outerClassname("References"); + +enum ReferenceType { + root @0; + rootValue @1; + parent @2; +} + +enum ImplementationType { + enumerator @0; +} diff --git a/libs/libarchfpga/CMakeLists.txt b/libs/libarchfpga/CMakeLists.txt index 37a07f0a771..74523097ef9 100644 --- a/libs/libarchfpga/CMakeLists.txt +++ b/libs/libarchfpga/CMakeLists.txt @@ -12,16 +12,23 @@ list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES}) #Create the library add_library(libarchfpga STATIC - ${LIB_HEADERS} - ${LIB_SOURCES}) + ${LIB_HEADERS} + ${LIB_SOURCES} +) + target_include_directories(libarchfpga PUBLIC ${LIB_INCLUDE_DIRS}) + set_target_properties(libarchfpga PROPERTIES PREFIX "") #Avoid extra 'lib' prefix #Specify link-time dependancies target_link_libraries(libarchfpga libvtrutil libpugixml - libpugiutil) + libpugiutil + libvtrcapnproto +) + +target_compile_definitions(libarchfpga PUBLIC ${INTERCHANGE_SCHEMA_HEADERS}) #Create the test executable add_executable(read_arch ${EXEC_SOURCES}) diff --git a/libs/libarchfpga/src/physical_types.h b/libs/libarchfpga/src/physical_types.h index 8b789bfb9d4..4880d5b9989 100644 --- a/libs/libarchfpga/src/physical_types.h +++ b/libs/libarchfpga/src/physical_types.h @@ -1732,6 +1732,12 @@ struct t_clock_arch_spec { std::vector clock_connections_arch; }; +struct t_lut_cell { + std::string name; + std::string init_param; + std::vector inputs; +}; + /* Detailed routing architecture */ struct t_arch { mutable vtr::string_internment strings; @@ -1750,11 +1756,23 @@ struct t_arch { int num_switches; t_direct_inf* Directs = nullptr; int num_directs = 0; + t_model* models = nullptr; t_model* model_library = nullptr; + t_power_arch* power = nullptr; t_clock_arch* clocks = nullptr; + // Constants + std::string gnd_cell; + std::string vcc_cell; + + std::string gnd_net = "$__gnd_net"; + std::string vcc_net = "$__vcc_net"; + + // Luts + std::vector lut_cells; + //The name of the switch used for the input connection block (i.e. to //connect routing tracks to block pins). //This should correspond to a switch in Switches diff --git a/libs/libarchfpga/src/read_common_func.cpp b/libs/libarchfpga/src/read_common_func.cpp new file mode 100644 index 00000000000..b9436ab7419 --- /dev/null +++ b/libs/libarchfpga/src/read_common_func.cpp @@ -0,0 +1,96 @@ +#include + +#include "vtr_log.h" +#include "arch_error.h" + +#include "read_common_func.h" + +bool check_model_clocks(t_model* model, const char* file, uint32_t line) { + //Collect the ports identified as clocks + std::set clocks; + for (t_model_ports* ports : {model->inputs, model->outputs}) { + for (t_model_ports* port = ports; port != nullptr; port = port->next) { + if (port->is_clock) { + clocks.insert(port->name); + } + } + } + + //Check that any clock references on the ports are to identified clock ports + for (t_model_ports* ports : {model->inputs, model->outputs}) { + for (t_model_ports* port = ports; port != nullptr; port = port->next) { + if (!port->clock.empty() && !clocks.count(port->clock)) { + archfpga_throw(file, line, + "No matching clock port '%s' on model '%s', required for port '%s'", + port->clock.c_str(), model->name, port->name); + } + } + } + return true; +} + +bool check_model_combinational_sinks(const t_model* model, const char* file, uint32_t line) { + //Outputs should have no combinational sinks + for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { + if (port->combinational_sink_ports.size() != 0) { + archfpga_throw(file, line, + "Model '%s' output port '%s' can not have combinational sink ports", + model->name, port->name); + } + } + + //Record the output ports + std::map output_ports; + for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { + output_ports.insert({port->name, port}); + } + + for (t_model_ports* port = model->inputs; port != nullptr; port = port->next) { + for (const std::string& sink_port_name : port->combinational_sink_ports) { + //Check that the input port combinational sinks are all outputs + if (!output_ports.count(sink_port_name)) { + archfpga_throw(file, line, + "Model '%s' input port '%s' can not be combinationally connected to '%s' (not an output port of the model)", + model->name, port->name, sink_port_name.c_str()); + } + + //Check that any output combinational sinks are not clocks + t_model_ports* sink_port = output_ports[sink_port_name]; + VTR_ASSERT(sink_port); + if (sink_port->is_clock) { + archfpga_throw(file, line, + "Model '%s' output port '%s' can not be both: a clock source (is_clock=\"%d\")," + " and combinationally connected to input port '%s' (acting as a clock buffer).", + model->name, sink_port->name, sink_port->is_clock, port->name); + } + } + } + + return true; +} + +void warn_model_missing_timing(const t_model* model, const char* file, uint32_t line) { + //Check whether there are missing edges and warn the user + std::set comb_connected_outputs; + for (t_model_ports* port = model->inputs; port != nullptr; port = port->next) { + if (port->clock.empty() //Not sequential + && port->combinational_sink_ports.empty() //Doesn't drive any combinational outputs + && !port->is_clock //Not an input clock + ) { + VTR_LOGF_WARN(file, line, + "Model '%s' input port '%s' has no timing specification (no clock specified to create a sequential input port, not combinationally connected to any outputs, not a clock input)\n", model->name, port->name); + } + + comb_connected_outputs.insert(port->combinational_sink_ports.begin(), port->combinational_sink_ports.end()); + } + + for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { + if (port->clock.empty() //Not sequential + && !comb_connected_outputs.count(port->name) //Not combinationally drivven + && !port->is_clock //Not an output clock + ) { + VTR_LOGF_WARN(file, line, + "Model '%s' output port '%s' has no timing specification (no clock specified to create a sequential output port, not combinationally connected to any inputs, not a clock output)\n", model->name, port->name); + } + } +} diff --git a/libs/libarchfpga/src/read_common_func.h b/libs/libarchfpga/src/read_common_func.h new file mode 100644 index 00000000000..65121ceaa3d --- /dev/null +++ b/libs/libarchfpga/src/read_common_func.h @@ -0,0 +1,19 @@ +#ifndef READ_COMMON_FUNC_H +#define READ_COMMON_FUNC_H + +#include "arch_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* function declarations */ +bool check_model_clocks(t_model*, const char*, uint32_t); +bool check_model_combinational_sinks(const t_model*, const char*, uint32_t); +void warn_model_missing_timing(const t_model*, const char*, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.cpp b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp new file mode 100644 index 00000000000..88a9e782df3 --- /dev/null +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.cpp @@ -0,0 +1,487 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vtr_assert.h" +#include "vtr_digest.h" +#include "vtr_log.h" +#include "vtr_util.h" + +#include "arch_types.h" +#include "arch_util.h" +#include "arch_error.h" + +#include "read_fpga_interchange_arch.h" +#include "read_common_func.h" + +/* + * FPGA Interchange Device frontend + * + * This file contains functions to read and parse a Cap'n'proto FPGA interchange device description + * and populate the various VTR architecture's internal data structures. + * + * The Device data is, by default, GZipped, hence the requirement of the ZLIB library to allow + * for in-memory decompression of the input file. + */ + +using namespace DeviceResources; +using namespace LogicalNetlist; +using namespace capnp; + +static float get_corner_value(Device::CornerModel::Reader model, const char* speed_model, const char* value) { + bool slow_model = std::string(speed_model) == std::string("slow"); + bool fast_model = std::string(speed_model) == std::string("fast"); + + bool min_corner = std::string(value) == std::string("min"); + bool typ_corner = std::string(value) == std::string("typ"); + bool max_corner = std::string(value) == std::string("max"); + + if (!slow_model && !fast_model) { + archfpga_throw("", __LINE__, + "Wrong speed model `%s`. Expected `slow` or `fast`\n", speed_model); + } + + if (!min_corner && !typ_corner && !max_corner) { + archfpga_throw("", __LINE__, + "Wrong corner model `%s`. Expected `min`, `typ` or `max`\n", value); + } + + bool has_fast = model.getFast().hasFast(); + bool has_slow = model.getSlow().hasSlow(); + + if (slow_model && has_slow) { + auto half = model.getSlow().getSlow(); + if (min_corner && half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (typ_corner && half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (max_corner && half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + if (half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + archfpga_throw("", __LINE__, + "Invalid speed model %s. No value found!\n", speed_model); + } + } + } else if (fast_model && has_fast) { + auto half = model.getFast().getFast(); + if (min_corner && half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (typ_corner && half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (max_corner && half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + if (half.getMin().isMin()) { + return half.getMin().getMin(); + } else if (half.getTyp().isTyp()) { + return half.getTyp().getTyp(); + } else if (half.getMax().isMax()) { + return half.getMax().getMax(); + } else { + archfpga_throw("", __LINE__, + "Invalid speed model %s. No value found!\n", speed_model); + } + } + } + + return 0.; +} + +struct ArchReader { + public: + ArchReader(t_arch* arch, Device::Reader& arch_reader, const char* arch_file /*, std::vector& phys_types, std::vector& logical_types*/) + : arch_(arch) + , arch_file_(arch_file) + , ar_(arch_reader) + /* , ptypes_(phys_types) + * , ltypes_(logical_types)*/ + { + } + + void read_arch() { + process_device(); + process_layout(); + process_models(); + process_luts(); + process_switches(); + } + + private: + t_arch* arch_; + const char* arch_file_; + Device::Reader& ar_; + //std::vector& ptypes_; + //std::vector& ltypes_; + + t_default_fc_spec default_fc_; + + // Model processing + void process_models() { + // Populate the common library, namely .inputs, .outputs, .names, .latches + CreateModelLibrary(arch_); + + t_model* temp = nullptr; + std::map model_name_map; + std::pair::iterator, bool> ret_map_name; + + int model_index = NUM_MODELS_IN_LIBRARY; + arch_->models = nullptr; + + auto strList = ar_.getStrList(); + auto primLib = ar_.getPrimLibs(); + for (auto primitive : primLib.getCellDecls()) { + if (std::string(strList[primitive.getLib()]) == std::string("primitives")) { + try { + temp = new t_model; + temp->index = model_index++; + + temp->never_prune = true; + temp->name = vtr::strdup(std::string(strList[primitive.getName()]).c_str()); + ret_map_name = model_name_map.insert(std::pair(temp->name, 0)); + if (!ret_map_name.second) { + archfpga_throw(arch_file_, __LINE__, + "Duplicate model name: '%s'.\n", temp->name); + } + + process_model_ports(temp, primitive); + + check_model_clocks(temp, arch_file_, __LINE__); + check_model_combinational_sinks(temp, arch_file_, __LINE__); + warn_model_missing_timing(temp, arch_file_, __LINE__); + + } catch (ArchFpgaError& e) { + free_arch_model(temp); + throw; + } + temp->next = arch_->models; + arch_->models = temp; + } + } + return; + } + + void process_model_ports(t_model* model, Netlist::CellDeclaration::Reader primitive) { + auto strList = ar_.getStrList(); + auto primLib = ar_.getPrimLibs(); + auto portList = primLib.getPortList(); + + std::set> port_names; + + for (auto port_idx : primitive.getPorts()) { + auto port = portList[port_idx]; + enum PORTS dir = ERR_PORT; + switch (port.getDir()) { + case LogicalNetlist::Netlist::Direction::INPUT: + dir = IN_PORT; + break; + case LogicalNetlist::Netlist::Direction::OUTPUT: + dir = OUT_PORT; + break; + case LogicalNetlist::Netlist::Direction::INOUT: + dir = INOUT_PORT; + break; + default: + break; + } + t_model_ports* model_port = new t_model_ports; + model_port->dir = dir; + model_port->name = vtr::strdup(std::string(strList[port.getName()]).c_str()); + + // TODO: add parsing of clock port types when the interchange schema allows for it: + // https://github.com/chipsalliance/fpga-interchange-schema/issues/66 + + //Sanity checks + if (model_port->is_clock == true && model_port->is_non_clock_global == true) { + archfpga_throw(arch_file_, __LINE__, + "Model port '%s' cannot be both a clock and a non-clock signal simultaneously", model_port->name); + } + if (model_port->name == nullptr) { + archfpga_throw(arch_file_, __LINE__, + "Model port is missing a name"); + } + if (port_names.count(std::pair(model_port->name, dir)) && dir != INOUT_PORT) { + archfpga_throw(arch_file_, __LINE__, + "Duplicate model port named '%s'", model_port->name); + } + if (dir == OUT_PORT && !model_port->combinational_sink_ports.empty()) { + archfpga_throw(arch_file_, __LINE__, + "Model output ports can not have combinational sink ports"); + } + + model_port->min_size = 1; + model_port->size = 1; + if (port.isBus()) { + int s = port.getBus().getBusStart(); + int e = port.getBus().getBusEnd(); + model_port->size = std::abs(e - s) + 1; + } + + port_names.insert(std::pair(model_port->name, dir)); + //Add the port + if (dir == IN_PORT) { + model_port->next = model->inputs; + model->inputs = model_port; + + } else if (dir == OUT_PORT) { + model_port->next = model->outputs; + model->outputs = model_port; + } + } + } + + void process_luts() { + // Add LUT Cell definitions + // This is helpful to understand which cells are LUTs + auto lut_def = ar_.getLutDefinitions(); + + for (auto lut_cell : lut_def.getLutCells()) { + t_lut_cell cell; + cell.name = lut_cell.getCell().cStr(); + for (auto input : lut_cell.getInputPins()) + cell.inputs.push_back(input.cStr()); + + auto equation = lut_cell.getEquation(); + if (equation.isInitParam()) + cell.init_param = equation.getInitParam().cStr(); + + arch_->lut_cells.push_back(cell); + } + } + + // Layout Processing + void process_layout() { + auto strList = ar_.getStrList(); + auto tileList = ar_.getTileList(); + auto tileTypeList = ar_.getTileTypeList(); + t_grid_def grid_def; + + grid_def.width = grid_def.height = 0; + for (auto tile : tileList) { + grid_def.width = std::max(grid_def.width, tile.getCol() + 1); + grid_def.height = std::max(grid_def.height, tile.getRow() + 1); + } + + grid_def.grid_type = GridDefType::FIXED; + std::string name = std::string(ar_.getName()); + if (name == "auto") { + archfpga_throw(arch_file_, __LINE__, + "The name auto is reserved for auto-size layouts; please choose another name"); + } + grid_def.name = name; + for (auto tile : tileList) { + t_metadata_dict data; + std::string tile_prefix(strList[tile.getName()].cStr()); + auto tileType = tileTypeList[tile.getType()]; + std::string tile_type(strList[tileType.getName()].cStr()); + + size_t pos = tile_prefix.find(tile_type); + if (pos != std::string::npos && pos == 0) + tile_prefix.erase(pos, tile_type.length() + 1); + data.add(arch_->strings.intern_string(vtr::string_view("fasm_prefix")), + arch_->strings.intern_string(vtr::string_view(tile_prefix.c_str()))); + t_grid_loc_def single(tile_type, 1); + single.x.start_expr = tile.getCol(); + single.y.start_expr = tile.getRow(); + single.x.end_expr = single.x.start_expr + " + w - 1"; + single.y.end_expr = single.y.start_expr + " + h - 1"; + single.owned_meta = std::make_unique(data); + single.meta = single.owned_meta.get(); + grid_def.loc_defs.emplace_back(std::move(single)); + } + + arch_->grid_layouts.emplace_back(std::move(grid_def)); + } + + void process_device() { + /* + * The generic architecture data is not currently available in the interchange format + * therefore, for a very initial implementation, the values are taken from the ones + * used primarly in the Xilinx series7 devices, generated using SymbiFlow. + * + * As the interchange format develops further, with possibly more details, this function can + * become dynamic, allowing for different parameters for the different architectures. + */ + arch_->R_minW_nmos = 6065.520020; + arch_->R_minW_pmos = 18138.500000; + arch_->grid_logic_tile_area = 14813.392; + arch_->Chans.chan_x_dist.type = UNIFORM; + arch_->Chans.chan_x_dist.peak = 1; + arch_->Chans.chan_x_dist.width = 0; + arch_->Chans.chan_x_dist.xpeak = 0; + arch_->Chans.chan_x_dist.dc = 0; + arch_->Chans.chan_y_dist.type = UNIFORM; + arch_->Chans.chan_y_dist.peak = 1; + arch_->Chans.chan_y_dist.width = 0; + arch_->Chans.chan_y_dist.xpeak = 0; + arch_->Chans.chan_y_dist.dc = 0; + arch_->ipin_cblock_switch_name = std::string("generic"); + arch_->SBType = WILTON; + arch_->Fs = 3; + default_fc_.specified = true; + default_fc_.in_value_type = e_fc_value_type::FRACTIONAL; + default_fc_.in_value = 1.0; + default_fc_.out_value_type = e_fc_value_type::FRACTIONAL; + default_fc_.out_value = 1.0; + } + + void process_switches() { + std::set> pip_timing_models; + for (auto tile_type : ar_.getTileTypeList()) { + for (auto pip : tile_type.getPips()) { + pip_timing_models.insert(std::pair(pip.getBuffered21(), pip.getTiming())); + if (!pip.getDirectional()) + pip_timing_models.insert(std::pair(pip.getBuffered20(), pip.getTiming())); + } + } + + auto timing_data = ar_.getPipTimings(); + + std::vector> pip_timing_models_list; + pip_timing_models_list.reserve(pip_timing_models.size()); + + for (auto entry : pip_timing_models) { + pip_timing_models_list.push_back(entry); + } + + auto num_switches = pip_timing_models.size() + 2; + std::string switch_name; + + arch_->num_switches = num_switches; + auto* switches = arch_->Switches; + + if (num_switches > 0) { + switches = new t_arch_switch_inf[num_switches]; + } + + float R, Cin, Cint, Cout, Tdel; + for (int i = 0; i < (int)num_switches; ++i) { + t_arch_switch_inf& as = switches[i]; + + R = Cin = Cint = Cout = Tdel = 0.0; + SwitchType type; + + if (i == 0) { + switch_name = "short"; + type = SwitchType::SHORT; + R = 0.0; + } else if (i == 1) { + switch_name = "generic"; + type = SwitchType::MUX; + R = 0.0; + } else { + auto entry = pip_timing_models_list[i - 2]; + auto model = timing_data[entry.second]; + std::stringstream name; + std::string mux_type_string = entry.first ? "mux_" : "passGate_"; + name << mux_type_string; + + R = get_corner_value(model.getOutputResistance(), "slow", "min"); + name << "R" << std::scientific << R; + + Cin = get_corner_value(model.getInputCapacitance(), "slow", "min"); + name << "Cin" << std::scientific << Cin; + + Cout = get_corner_value(model.getOutputCapacitance(), "slow", "min"); + name << "Cout" << std::scientific << Cout; + + if (entry.first) { + Cint = get_corner_value(model.getInternalCapacitance(), "slow", "min"); + name << "Cinternal" << std::scientific << Cint; + } + + Tdel = get_corner_value(model.getInternalDelay(), "slow", "min"); + name << "Tdel" << std::scientific << Tdel; + + switch_name = name.str(); + type = entry.first ? SwitchType::MUX : SwitchType::PASS_GATE; + } + + /* Should never happen */ + if (switch_name == std::string(VPR_DELAYLESS_SWITCH_NAME)) { + archfpga_throw(arch_file_, __LINE__, + "Switch name '%s' is a reserved name for VPR internal usage!", switch_name.c_str()); + } + + as.name = vtr::strdup(switch_name.c_str()); + as.set_type(type); + as.mux_trans_size = as.type() == SwitchType::MUX ? 1 : 0; + + as.R = R; + as.Cin = Cin; + as.Cout = Cout; + as.Cinternal = Cint; + as.set_Tdel(t_arch_switch_inf::UNDEFINED_FANIN, Tdel); + + if (as.type() == SwitchType::SHORT || as.type() == SwitchType::PASS_GATE) { + as.buf_size_type = BufferSize::ABSOLUTE; + as.buf_size = 0; + as.power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; + as.power_buffer_size = 0.; + } else { + as.buf_size_type = BufferSize::AUTO; + as.buf_size = 0.; + as.power_buffer_type = POWER_BUFFER_TYPE_AUTO; + } + } + } +}; + +void FPGAInterchangeReadArch(const char* FPGAInterchangeDeviceFile, + const bool /*timing_enabled*/, + t_arch* arch, + std::vector& /*PhysicalTileTypes*/, + std::vector& /*LogicalBlockTypes*/) { + // Decompress GZipped capnproto device file + gzFile file = gzopen(FPGAInterchangeDeviceFile, "r"); + VTR_ASSERT(file != Z_NULL); + + std::vector output_data; + output_data.resize(4096); + std::stringstream sstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary); + while (true) { + int ret = gzread(file, output_data.data(), output_data.size()); + VTR_ASSERT(ret >= 0); + if (ret > 0) { + sstream.write((const char*)output_data.data(), ret); + VTR_ASSERT(sstream); + } else { + VTR_ASSERT(ret == 0); + int error; + gzerror(file, &error); + VTR_ASSERT(error == Z_OK); + break; + } + } + + VTR_ASSERT(gzclose(file) == Z_OK); + + sstream.seekg(0); + kj::std::StdInputStream istream(sstream); + + // Reader options + capnp::ReaderOptions reader_options; + reader_options.nestingLimit = std::numeric_limits::max(); + reader_options.traversalLimitInWords = std::numeric_limits::max(); + + capnp::InputStreamMessageReader message_reader(istream, reader_options); + + auto device_reader = message_reader.getRoot(); + + arch->architecture_id = vtr::strdup(vtr::secure_digest_file(FPGAInterchangeDeviceFile).c_str()); + + ArchReader reader(arch, device_reader, FPGAInterchangeDeviceFile /*, PhysicalTileTypes, LogicalBlockTypes*/); + reader.read_arch(); +} diff --git a/libs/libarchfpga/src/read_fpga_interchange_arch.h b/libs/libarchfpga/src/read_fpga_interchange_arch.h new file mode 100644 index 00000000000..c8e6b47e60a --- /dev/null +++ b/libs/libarchfpga/src/read_fpga_interchange_arch.h @@ -0,0 +1,36 @@ +#ifndef READ_FPGAINTERCHANGE_ARCH_FILE_H +#define READ_FPGAINTERCHANGE_ARCH_FILE_H + +#include "arch_types.h" + +#include "DeviceResources.capnp.h" +#include "LogicalNetlist.capnp.h" +#include "capnp/serialize.h" +#include "capnp/serialize-packed.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* special type indexes, necessary for initialization, everything afterwards + * should use the pointers to these type indices */ + +#define NUM_MODELS_IN_LIBRARY 4 +#define EMPTY_TYPE_INDEX 0 + +/* function declaration */ +void FPGAInterchangeReadArch(const char* FPGAInterchangeDeviceFile, + const bool timing_enabled, + t_arch* arch, + std::vector& PhysicalTileTypes, + std::vector& LogicalBlockTypes); + +const char* get_fpga_interchange_arch_file_name(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/libarchfpga/src/read_xml_arch_file.cpp b/libs/libarchfpga/src/read_xml_arch_file.cpp index 658c907fa2b..7fea5a38aca 100644 --- a/libs/libarchfpga/src/read_xml_arch_file.cpp +++ b/libs/libarchfpga/src/read_xml_arch_file.cpp @@ -58,6 +58,7 @@ #include "arch_util.h" #include "arch_error.h" +#include "read_common_func.h" #include "read_xml_arch_file.h" #include "read_xml_util.h" #include "parse_switchblocks.h" @@ -256,9 +257,6 @@ static void ProcessClocks(pugi::xml_node Parent, t_clock_arch* clocks, const pug static void ProcessPb_TypePowerEstMethod(pugi::xml_node Parent, t_pb_type* pb_type, const pugiutil::loc_data& loc_data); static void ProcessPb_TypePort_Power(pugi::xml_node Parent, t_port* port, e_power_estimation_method power_method, const pugiutil::loc_data& loc_data); -bool check_model_combinational_sinks(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model); -void warn_model_missing_timing(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model); -bool check_model_clocks(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model); bool check_leaf_pb_model_timing_consistency(const t_pb_type* pb_type, const t_arch& arch); const t_pin_to_pin_annotation* find_sequential_annotation(const t_pb_type* pb_type, const t_model_ports* port, enum e_pin_to_pin_delay_annotations annot_type); const t_pin_to_pin_annotation* find_combinational_annotation(const t_pb_type* pb_type, std::string in_port, std::string out_port); @@ -2356,9 +2354,9 @@ static void ProcessModels(pugi::xml_node Node, t_arch* arch, const pugiutil::loc } //Sanity check the model - check_model_clocks(model, loc_data, temp); - check_model_combinational_sinks(model, loc_data, temp); - warn_model_missing_timing(model, loc_data, temp); + check_model_clocks(temp, loc_data.filename_c_str(), loc_data.line(model)); + check_model_combinational_sinks(temp, loc_data.filename_c_str(), loc_data.line(model)); + warn_model_missing_timing(temp, loc_data.filename_c_str(), loc_data.line(model)); } catch (ArchFpgaError& e) { free_arch_model(temp); throw; @@ -4637,96 +4635,6 @@ const char* get_arch_file_name() { return arch_file_name; } -bool check_model_clocks(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model) { - //Collect the ports identified as clocks - std::set clocks; - for (t_model_ports* ports : {model->inputs, model->outputs}) { - for (t_model_ports* port = ports; port != nullptr; port = port->next) { - if (port->is_clock) { - clocks.insert(port->name); - } - } - } - - //Check that any clock references on the ports are to identified clock ports - for (t_model_ports* ports : {model->inputs, model->outputs}) { - for (t_model_ports* port = ports; port != nullptr; port = port->next) { - if (!port->clock.empty() && !clocks.count(port->clock)) { - archfpga_throw(loc_data.filename_c_str(), loc_data.line(model_tag), - "No matching clock port '%s' on model '%s', required for port '%s'", - port->clock.c_str(), model->name, port->name); - } - } - } - return true; -} - -bool check_model_combinational_sinks(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model) { - //Outputs should have no combinational sinks - for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { - if (port->combinational_sink_ports.size() != 0) { - archfpga_throw(loc_data.filename_c_str(), loc_data.line(model_tag), - "Model '%s' output port '%s' can not have combinational sink ports", - model->name, port->name); - } - } - - //Record the output ports - std::map output_ports; - for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { - output_ports.insert({port->name, port}); - } - - for (t_model_ports* port = model->inputs; port != nullptr; port = port->next) { - for (const std::string& sink_port_name : port->combinational_sink_ports) { - //Check that the input port combinational sinks are all outputs - if (!output_ports.count(sink_port_name)) { - archfpga_throw(loc_data.filename_c_str(), loc_data.line(model_tag), - "Model '%s' input port '%s' can not be combinationally connected to '%s' (not an output port of the model)", - model->name, port->name, sink_port_name.c_str()); - } - - //Check that any output combinational sinks are not clocks - t_model_ports* sink_port = output_ports[sink_port_name]; - VTR_ASSERT(sink_port); - if (sink_port->is_clock) { - archfpga_throw(loc_data.filename_c_str(), loc_data.line(model_tag), - "Model '%s' output port '%s' can not be both: a clock source (is_clock=\"%d\")," - " and combinationally connected to input port '%s' (acting as a clock buffer).", - model->name, sink_port->name, sink_port->is_clock, port->name); - } - } - } - - return true; -} - -void warn_model_missing_timing(pugi::xml_node model_tag, const pugiutil::loc_data& loc_data, const t_model* model) { - //Check whether there are missing edges and warn the user - std::set comb_connected_outputs; - for (t_model_ports* port = model->inputs; port != nullptr; port = port->next) { - if (port->clock.empty() //Not sequential - && port->combinational_sink_ports.empty() //Doesn't drive any combinational outputs - && !port->is_clock //Not an input clock - ) { - VTR_LOGF_WARN(loc_data.filename_c_str(), loc_data.line(model_tag), - "Model '%s' input port '%s' has no timing specification (no clock specified to create a sequential input port, not combinationally connected to any outputs, not a clock input)\n", model->name, port->name); - } - - comb_connected_outputs.insert(port->combinational_sink_ports.begin(), port->combinational_sink_ports.end()); - } - - for (t_model_ports* port = model->outputs; port != nullptr; port = port->next) { - if (port->clock.empty() //Not sequential - && !comb_connected_outputs.count(port->name) //Not combinationally drivven - && !port->is_clock //Not an output clock - ) { - VTR_LOGF_WARN(loc_data.filename_c_str(), loc_data.line(model_tag), - "Model '%s' output port '%s' has no timing specification (no clock specified to create a sequential output port, not combinationally connected to any inputs, not a clock output)\n", model->name, port->name); - } - } -} - bool check_leaf_pb_model_timing_consistency(const t_pb_type* pb_type, const t_arch& arch) { //Normalize the blif model name to match the model name // by removing the leading '.' (.latch, .inputs, .names etc.) diff --git a/libs/libvtrcapnproto/CMakeLists.txt b/libs/libvtrcapnproto/CMakeLists.txt index a362da39df0..cd97b0ec4f1 100644 --- a/libs/libvtrcapnproto/CMakeLists.txt +++ b/libs/libvtrcapnproto/CMakeLists.txt @@ -25,20 +25,74 @@ set(CAPNP_DEFS gen/rr_graph_uxsdcxx.capnp map_lookahead.capnp extended_map_lookahead.capnp - ) +) + capnp_generate_cpp(CAPNP_SRCS CAPNP_HDRS ${CAPNP_DEFS} +) + +if (VPR_ENABLE_INTERCHANGE) + set(IC_DIR ${CMAKE_SOURCE_DIR}/libs/EXTERNAL/libinterchange/interchange) + set(CAPNPC_SRC_PREFIX ${IC_DIR}) + + find_program(WGET wget REQUIRED) + find_package(ZLIB REQUIRED) + + # Add Java schema + set(JAVA_SCHEMA ${CMAKE_CURRENT_BINARY_DIR}/schema/capnp/java.capnp) + add_custom_command( + OUTPUT ${JAVA_SCHEMA} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/schema/capnp/ + COMMAND ${WGET} + https://raw.githubusercontent.com/capnproto/capnproto-java/master/compiler/src/main/schema/capnp/java.capnp + -O ${JAVA_SCHEMA} + ) + + add_custom_target( + get_java_capnp_schema + DEPENDS ${JAVA_SCHEMA} + ) + + set(CAPNPC_IMPORT_DIRS) + list(APPEND CAPNPC_IMPORT_DIRS ${CMAKE_CURRENT_BINARY_DIR}/schema) + + set(IC_PROTOS + LogicalNetlist.capnp + PhysicalNetlist.capnp + DeviceResources.capnp + References.capnp ) + set(IC_SRCS) + set(IC_HDRS) + foreach(PROTO ${IC_PROTOS}) + capnp_generate_cpp( + IC_SRC + IC_HDR + ${IC_DIR}/${PROTO} + ) + list(APPEND IC_SRCS ${IC_SRC}) + list(APPEND IC_HDRS ${IC_HDR}) + list(APPEND CAPNP_DEFS ${IC_DIR}/${PROTO}) + endforeach() +endif() install(FILES ${CAPNP_DEFS} DESTINATION ${CMAKE_INSTALL_DATADIR}/vtr) add_library(libvtrcapnproto STATIC ${CAPNP_SRCS} + ${IC_SRCS} mmap_file.h mmap_file.cpp serdes_utils.h serdes_utils.cpp ) + +if (VPR_ENABLE_INTERCHANGE) + add_dependencies(libvtrcapnproto + get_java_capnp_schema + ) +endif() + target_include_directories(libvtrcapnproto PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} @@ -47,4 +101,4 @@ target_include_directories(libvtrcapnproto PUBLIC target_link_libraries(libvtrcapnproto libvtrutil CapnProto::capnp - ) +) diff --git a/libs/libvtrutil/src/vtr_hash.h b/libs/libvtrutil/src/vtr_hash.h index 99fd55807ac..fad2a2417ba 100644 --- a/libs/libvtrutil/src/vtr_hash.h +++ b/libs/libvtrutil/src/vtr_hash.h @@ -15,6 +15,16 @@ inline void hash_combine(std::size_t& seed, const T& v) { seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } +struct hash_pair { + template + std::size_t operator()(const std::pair& pair) const { + auto hash1 = std::hash{}(pair.first); + auto hash2 = std::hash{}(pair.second); + + return hash1 ^ hash2; + } +}; + } // namespace vtr #endif diff --git a/vpr/src/base/SetupVPR.cpp b/vpr/src/base/SetupVPR.cpp index 09d46df5ed4..888ee805bea 100644 --- a/vpr/src/base/SetupVPR.cpp +++ b/vpr/src/base/SetupVPR.cpp @@ -15,6 +15,7 @@ #include "globals.h" #include "read_xml_arch_file.h" +#include "read_fpga_interchange_arch.h" #include "SetupVPR.h" #include "pb_type_graph.h" #include "pack_types.h" @@ -91,6 +92,8 @@ void SetupVPR(const t_options* Options, FileNameOpts->NetFile = Options->NetFile; FileNameOpts->PlaceFile = Options->PlaceFile; FileNameOpts->RouteFile = Options->RouteFile; + if (Options->FPGAInterchangePhysicalNet) + FileNameOpts->FPGAInterchangePhysicalFile = Options->FPGAInterchangePhysicalFile; FileNameOpts->ActFile = Options->ActFile; FileNameOpts->PowerFile = Options->PowerFile; FileNameOpts->CmosTechFile = Options->CmosTechFile; @@ -109,11 +112,20 @@ void SetupVPR(const t_options* Options, if (readArchFile == true) { vtr::ScopedStartFinishTimer t("Loading Architecture Description"); - XmlReadArch(Options->ArchFile.value().c_str(), - TimingEnabled, - Arch, - device_ctx.physical_tile_types, - device_ctx.logical_block_types); + if (Options->FPGAInterchangeDevice) { + FPGAInterchangeReadArch(Options->FPGAInterchangeDeviceFile().value().c_str(), + TimingEnabled, + Arch, + device_ctx.physical_tile_types, + device_ctx.logical_block_types); + VTR_LOG("Use FPGA Interchange device\n"); + } else { + XmlReadArch(Options->ArchFile.value().c_str(), + TimingEnabled, + Arch, + device_ctx.physical_tile_types, + device_ctx.logical_block_types); + } } VTR_LOG("\n"); diff --git a/vpr/src/base/atom_netlist_utils.cpp b/vpr/src/base/atom_netlist_utils.cpp index 9f73cb84426..34283546a92 100644 --- a/vpr/src/base/atom_netlist_utils.cpp +++ b/vpr/src/base/atom_netlist_utils.cpp @@ -95,7 +95,7 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) { AtomPinId pin = *netlist.block_pins(blk_id).begin(); std::string blk_name = netlist.block_name(blk_id); - std::string out_name(blk_name.begin() + 4, blk_name.end()); //+4 to trim out: prefix + std::string out_name(blk_name.begin(), blk_name.end()); //+4 to trim out: prefix fprintf(f, "%s%s", INDENT, out_name.c_str()); @@ -307,7 +307,7 @@ void print_netlist_as_blif(FILE* f, const AtomNetlist& netlist) { ports.push_back(port_id); } - fprintf(f, ".subckt %s \\\n", blk_model->name); + fprintf(f, ".subckt %s \\\n", netlist.block_name(blk_id).c_str()); for (size_t i = 0; i < ports.size(); i++) { auto width = netlist.port_width(ports[i]); for (size_t j = 0; j < width; ++j) { diff --git a/vpr/src/base/read_circuit.cpp b/vpr/src/base/read_circuit.cpp index cee5fbe9e9a..cf5f12e1ef4 100644 --- a/vpr/src/base/read_circuit.cpp +++ b/vpr/src/base/read_circuit.cpp @@ -1,5 +1,6 @@ #include "read_circuit.h" #include "read_blif.h" +#include "read_interchange_netlist.h" #include "atom_netlist.h" #include "atom_netlist_utils.h" #include "echo_files.h" @@ -21,20 +22,23 @@ static void process_circuit(AtomNetlist& netlist, static void show_circuit_stats(const AtomNetlist& netlist); -AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, - const char* circuit_file, - const t_model* user_models, - const t_model* library_models, - e_const_gen_inference const_gen_inference, - bool should_absorb_buffers, - bool should_sweep_dangling_primary_ios, - bool should_sweep_dangling_nets, - bool should_sweep_dangling_blocks, - bool should_sweep_constant_primary_outputs, - int verbosity) { +AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, t_vpr_setup& vpr_setup, t_arch& arch) { + // Options + const char* circuit_file = vpr_setup.PackerOpts.blif_file_name.c_str(); + const t_model* user_models = vpr_setup.user_models; + const t_model* library_models = vpr_setup.library_models; + e_const_gen_inference const_gen_inference = vpr_setup.NetlistOpts.const_gen_inference; + bool should_absorb_buffers = vpr_setup.NetlistOpts.absorb_buffer_luts; + bool should_sweep_dangling_primary_ios = vpr_setup.NetlistOpts.sweep_dangling_primary_ios; + bool should_sweep_dangling_nets = vpr_setup.NetlistOpts.sweep_dangling_nets; + bool should_sweep_dangling_blocks = vpr_setup.NetlistOpts.sweep_dangling_blocks; + bool should_sweep_constant_primary_outputs = vpr_setup.NetlistOpts.sweep_constant_primary_outputs; + bool verbosity = vpr_setup.NetlistOpts.netlist_verbosity; + if (circuit_format == e_circuit_format::AUTO) { auto name_ext = vtr::split_ext(circuit_file); + VTR_LOG("%s\n", circuit_file); if (name_ext[1] == ".blif") { circuit_format = e_circuit_format::BLIF; } else if (name_ext[1] == ".eblif") { @@ -49,10 +53,18 @@ AtomNetlist read_and_process_circuit(e_circuit_format circuit_format, { vtr::ScopedStartFinishTimer t("Load circuit"); - VTR_ASSERT(circuit_format == e_circuit_format::BLIF - || circuit_format == e_circuit_format::EBLIF); - - netlist = read_blif(circuit_format, circuit_file, user_models, library_models); + switch (circuit_format) { + case e_circuit_format::BLIF: + case e_circuit_format::EBLIF: + netlist = read_blif(circuit_format, circuit_file, user_models, library_models); + break; + case e_circuit_format::FPGA_INTERCHANGE: + netlist = read_interchange_netlist(circuit_format, circuit_file, arch); + break; + default: + VTR_ASSERT(false); + break; + } } if (isEchoFileEnabled(E_ECHO_ATOM_NETLIST_ORIG)) { diff --git a/vpr/src/base/read_circuit.h b/vpr/src/base/read_circuit.h index a7935aa99e4..90be01a3891 100644 --- a/vpr/src/base/read_circuit.h +++ b/vpr/src/base/read_circuit.h @@ -5,20 +5,11 @@ #include "vpr_types.h" enum class e_circuit_format { - AUTO, /// +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LogicalNetlist.capnp.h" +#include "capnp/serialize.h" +#include "capnp/serialize-packed.h" + +#include "atom_netlist.h" + +#include "vtr_assert.h" +#include "vtr_hash.h" +#include "vtr_util.h" +#include "vtr_log.h" +#include "vtr_logic.h" +#include "vtr_time.h" +#include "vtr_digest.h" + +#include "vpr_types.h" +#include "vpr_error.h" +#include "globals.h" +#include "read_interchange_netlist.h" +#include "arch_types.h" + +struct NetlistReader { + public: + NetlistReader(AtomNetlist& main_netlist, + LogicalNetlist::Netlist::Reader& netlist_reader, + const std::string netlist_id, + const char* netlist_file, + const t_arch& arch) + : main_netlist_(main_netlist) + , nr_(netlist_reader) + , netlist_file_(netlist_file) + , arch_(arch) { + // Define top module + top_cell_instance_ = nr_.getTopInst(); + + auto str_list = nr_.getStrList(); + main_netlist_ = AtomNetlist(str_list[top_cell_instance_.getName()], netlist_id); + + inpad_model_ = find_model(MODEL_INPUT); + outpad_model_ = find_model(MODEL_OUTPUT); + main_netlist_.set_block_types(inpad_model_, outpad_model_); + + VTR_LOG("Reading IOs...\n"); + read_ios(); + VTR_LOG("Reading names...\n"); + read_names(); + VTR_LOG("Reading blocks...\n"); + read_blocks(); + } + + private: + AtomNetlist& main_netlist_; + // Netlist Reader + LogicalNetlist::Netlist::Reader& nr_; + + const char* netlist_file_; + + const t_model* inpad_model_; + const t_model* outpad_model_; + const t_arch& arch_; + + LogicalNetlist::Netlist::CellInstance::Reader top_cell_instance_; + + void read_ios() { + const t_model* input_model = find_model(MODEL_INPUT); + const t_model* output_model = find_model(MODEL_OUTPUT); + + auto str_list = nr_.getStrList(); + + auto top_cell_decl = nr_.getCellDecls()[top_cell_instance_.getCell()]; + for (auto top_port : top_cell_decl.getPorts()) { + auto port = nr_.getPortList()[top_port]; + auto name = std::string(str_list[port.getName()].cStr()); + auto dir = port.getDir(); + + int bus_size, start_bit; + std::tie(bus_size, start_bit) = get_bus_size(port); + + for (int bit = start_bit; bit < start_bit + bus_size; bit++) { + auto port_name = name; + if (bus_size > 1) + port_name = name + "[" + std::to_string(bit) + "]"; + + AtomBlockId blk_id; + AtomPortId port_id; + AtomNetId net_id; + + switch (dir) { + case LogicalNetlist::Netlist::Direction::INPUT: + blk_id = main_netlist_.create_block(port_name, input_model); + port_id = main_netlist_.create_port(blk_id, input_model->outputs); + net_id = main_netlist_.create_net(port_name); + main_netlist_.create_pin(port_id, 0, net_id, PinType::DRIVER); + break; + case LogicalNetlist::Netlist::Direction::OUTPUT: + blk_id = main_netlist_.create_block(port_name, output_model); + port_id = main_netlist_.create_port(blk_id, output_model->inputs); + net_id = main_netlist_.create_net(port_name); + main_netlist_.create_pin(port_id, 0, net_id, PinType::SINK); + break; + default: + VTR_ASSERT(0); + break; + } + } + } + } + + void read_names() { + const t_model* blk_model = find_model(MODEL_NAMES); + + // Set the max size of the LUT + int lut_size = 0; + for (auto lut : arch_.lut_cells) + lut_size = std::max((int)lut.inputs.size(), lut_size); + blk_model->inputs[0].size = lut_size; + + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + auto decl_list = nr_.getCellDecls(); + auto inst_list = nr_.getInstList(); + auto port_list = nr_.getPortList(); + auto str_list = nr_.getStrList(); + + std::vector> insts; + for (auto cell_inst : top_cell.getInsts()) { + auto cell = decl_list[inst_list[cell_inst].getCell()]; + + bool is_lut; + int width; + std::string init_param; + std::tie(is_lut, width, init_param) = is_lut_cell(str_list[cell.getName()].cStr()); + + if (is_lut) + insts.emplace_back(cell_inst, width, init_param); + } + + for (auto inst : insts) { + unsigned int inst_idx; + int lut_width; + std::string init_param; + std::tie(inst_idx, lut_width, init_param) = inst; + + std::string inst_name = str_list[inst_list[inst_idx].getName()].cStr(); + + auto props = inst_list[inst_idx].getPropMap().getEntries(); + std::vector init; + for (auto entry : props) { + if (std::string(str_list[entry.getKey()].cStr()) != init_param) + continue; + + if (entry.which() == LogicalNetlist::Netlist::PropertyMap::Entry::TEXT_VALUE) { + const std::regex hex_regex("[0-9]+'h([0-9A-Z]+)"); + const std::regex bit_regex("[0-1]+"); + std::string init_str = std::string(str_list[entry.getTextValue()].cStr()); + std::smatch regex_matches; + + // Fill the init vector + if (std::regex_match(init_str, regex_matches, hex_regex)) { + for (const char& c : regex_matches[1].str()) { + int value = std::stoi(std::string(1, c), 0, 16); + for (int bit = 3; bit >= 0; bit--) { + init.push_back((value >> bit) & 1); + } + } + } else if (std::regex_match(init_str, regex_matches, bit_regex)) { + for (const char& c : init_str) { + init.push_back((bool)std::stoi(std::string(1, c), 0, 2)); + } + } + } + } + + // Add proper LUT mapping function here based on LUT size and init value + AtomNetlist::TruthTable truth_table; + bool is_const = false; + for (int bit = 0; bit < (int)init.size(); bit++) { + bool bit_set = init[init.size() - bit - 1]; + + if (bit_set == 0) + continue; + + is_const = bit == 0; + + truth_table.emplace_back(); + for (int row_bit = lut_width - 1; row_bit >= 0; row_bit--) { + bool row_bit_set = (bit >> row_bit) & 1; + auto log_value = row_bit_set ? vtr::LogicValue::TRUE : vtr::LogicValue::FALSE; + + truth_table[truth_table.size() - 1].push_back(log_value); + } + truth_table[truth_table.size() - 1].push_back(vtr::LogicValue::TRUE); + } + + //Figure out if the output is a constant generator + bool output_is_const = false; + if (truth_table.empty()) { + //An empty truth table in BLIF corresponds to a constant-zero + // e.g. + // + // #gnd is a constant 0 generator + // .names gnd + // + //An single entry truth table with value '0' also corresponds to a constant-zero + // e.g. + // + // #gnd2 is a constant 0 generator + // .names gnd2 + // 0 + // + output_is_const = true; + VTR_LOG("Found constant-zero generator '%s'\n", inst_name.c_str()); + } else if (truth_table.size() == 1 && is_const) { + //A single-entry truth table with value '1' in BLIF corresponds to a constant-one + // e.g. + // + // #vcc is a constant 1 generator + // .names vcc + // 1 + // + output_is_const = true; + VTR_LOG("Found constant-one generator '%s'\n", inst_name.c_str()); + } + + AtomBlockId blk_id = main_netlist_.create_block(inst_name, blk_model, truth_table); + + AtomPortId iport_id = main_netlist_.create_port(blk_id, blk_model->inputs); + AtomPortId oport_id = main_netlist_.create_port(blk_id, blk_model->outputs); + + auto cell_lib = decl_list[inst_list[inst_idx].getCell()]; + std::unordered_map port_net_map; + + for (auto net : top_cell.getNets()) { + std::string net_name = str_list[net.getName()].cStr(); + for (auto port : net.getPortInsts()) { + if (!port.isInst() || port.getInst() != inst_idx) + continue; + + port_net_map.emplace(port.getPort(), net_name); + } + } + + int inum = 0; + for (auto port : cell_lib.getPorts()) { + auto net_name = port_net_map.at(port); + AtomNetId net_id = main_netlist_.create_net(net_name); + + auto dir = port_list[port].getDir(); + switch (dir) { + case LogicalNetlist::Netlist::Direction::INPUT: + if (!output_is_const) main_netlist_.create_pin(iport_id, inum++, net_id, PinType::SINK); + break; + case LogicalNetlist::Netlist::Direction::OUTPUT: + main_netlist_.create_pin(oport_id, 0, net_id, PinType::DRIVER, output_is_const); + break; + default: + VTR_ASSERT(0); + break; + } + } + } + } + + void read_blocks() { + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + auto decl_list = nr_.getCellDecls(); + auto inst_list = nr_.getInstList(); + auto port_list = nr_.getPortList(); + auto str_list = nr_.getStrList(); + + std::vector> insts; + for (auto cell_inst : top_cell.getInsts()) { + auto cell = decl_list[inst_list[cell_inst].getCell()]; + + bool is_lut; + std::tie(is_lut, std::ignore, std::ignore) = is_lut_cell(str_list[cell.getName()].cStr()); + + if (!is_lut) + insts.emplace_back(cell_inst, inst_list[cell_inst].getCell()); + } + + for (auto inst_pair : insts) { + auto inst_idx = inst_pair.first; + auto cell_idx = inst_pair.second; + + auto model_name = str_list[decl_list[cell_idx].getName()].cStr(); + const t_model* blk_model = find_model(model_name); + + std::string inst_name = str_list[inst_list[inst_idx].getName()].cStr(); + VTR_ASSERT(inst_name.empty() == 0); + + //The name for every block should be unique, check that there is no name conflict + AtomBlockId blk_id = main_netlist_.find_block(inst_name); + if (blk_id) { + const t_model* conflicting_model = main_netlist_.block_model(blk_id); + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, + "Duplicate blocks named '%s' found in netlist." + " Existing block of type '%s' conflicts with subckt of type '%s'.", + inst_name.c_str(), conflicting_model->name, blk_model->name); + } + + auto port_net_map = get_port_net_map(inst_idx); + + auto cell = decl_list[inst_list[inst_idx].getCell()]; + if (std::string(str_list[cell.getName()].cStr()) == arch_.vcc_cell) + inst_name = arch_.vcc_cell; + else if (std::string(str_list[cell.getName()].cStr()) == arch_.gnd_cell) + inst_name = arch_.gnd_cell; + + if (main_netlist_.find_block(inst_name)) + continue; + + //Create the block + blk_id = main_netlist_.create_block(inst_name, blk_model); + + std::unordered_set added_ports; + for (auto port_net : port_net_map) { + auto port_idx = port_net.first.first; + auto port_bit = port_net.first.second; + + auto net_name = port_net.second; + if (inst_name == arch_.vcc_cell) + net_name = arch_.vcc_net; + else if (inst_name == arch_.gnd_cell) + net_name = arch_.gnd_net; + + auto port = port_list[port_idx]; + auto port_name = str_list[port.getName()].cStr(); + + //Check for consistency between model and ports + const t_model_ports* model_port = find_model_port(blk_model, std::string(port_name)); + VTR_ASSERT(model_port); + + //Determine the pin type + PinType pin_type = PinType::SINK; + if (model_port->dir == OUT_PORT) { + pin_type = PinType::DRIVER; + } else { + VTR_ASSERT_MSG(model_port->dir == IN_PORT, "Unexpected port type"); + } + + AtomPortId port_id = main_netlist_.create_port(blk_id, model_port); + + //Make the net + AtomNetId net_id = main_netlist_.create_net(std::string(net_name)); + + //Make the pin + main_netlist_.create_pin(port_id, port_bit, net_id, pin_type); + + added_ports.emplace(port_id); + } + + // Bind unconnected ports to VCC by default + for (const t_model_ports* ports : {blk_model->inputs, blk_model->outputs}) { + for (const t_model_ports* port = ports; port != nullptr; port = port->next) { + AtomPortId port_id = main_netlist_.create_port(blk_id, port); + + if (added_ports.count(port_id)) + continue; + + if (port->dir != IN_PORT) + continue; + + //Make the net + AtomNetId net_id = main_netlist_.create_net(arch_.vcc_net); + + PinType pin_type = PinType::SINK; + //Make the pin + for (int i = 0; i < port->size; i++) + main_netlist_.create_pin(port_id, i, net_id, pin_type); + } + } + } + } + + // + // Utilities + // + const t_model* find_model(std::string name) { + for (const auto models : {arch_.models, arch_.model_library}) + for (const t_model* model = models; model != nullptr; model = model->next) + if (name == model->name) + return model; + + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, "Failed to find matching architecture model for '%s'\n", name.c_str()); + } + + const t_model_ports* find_model_port(const t_model* blk_model, std::string name) { + //We now look through all the ports on the model looking for the matching port + for (const t_model_ports* ports : {blk_model->inputs, blk_model->outputs}) + for (const t_model_ports* port = ports; port != nullptr; port = port->next) + if (name == std::string(port->name)) + return port; + + //No match + vpr_throw(VPR_ERROR_IC_NETLIST_F, netlist_file_, -1, + "Found no matching port '%s' on architecture model '%s'\n", + name.c_str(), blk_model->name); + return nullptr; + } + + std::pair get_bus_size(LogicalNetlist::Netlist::Port::Reader port_reader) { + if (port_reader.isBus()) { + int s = port_reader.getBus().getBusStart(); + int e = port_reader.getBus().getBusEnd(); + + if (e < s) + return std::make_pair(s - e + 1, e); + else + return std::make_pair(e - s + 1, s); + } + + return std::make_pair(1, 0); + } + + unsigned int get_port_bit(LogicalNetlist::Netlist::PortInstance::Reader port_inst_reader) { + if (port_inst_reader.getBusIdx().which() == LogicalNetlist::Netlist::PortInstance::BusIdx::IDX) + return port_inst_reader.getBusIdx().getIdx(); + + return 0; + } + + std::unordered_map, std::string, vtr::hash_pair> get_port_net_map(unsigned int inst_idx) { + auto top_cell = nr_.getCellList()[nr_.getTopInst().getCell()]; + auto str_list = nr_.getStrList(); + std::unordered_map, std::string, vtr::hash_pair> map; + for (auto net : top_cell.getNets()) { + std::string net_name = str_list[net.getName()].cStr(); + for (auto port : net.getPortInsts()) { + if (!port.isInst() || port.getInst() != inst_idx) + continue; + + unsigned int port_bit = get_port_bit(port); + auto pair = std::make_pair(port.getPort(), port_bit); + map.emplace(pair, net_name); + } + } + + return map; + } + + std::tuple is_lut_cell(std::string cell_name) { + for (auto lut_cell : arch_.lut_cells) { + if (cell_name == lut_cell.name) { + auto init_param = lut_cell.init_param; + + // Assign default value for the LUT init parameter, if inexistent + if (init_param.empty()) + init_param = "INIT"; + + return std::make_tuple(true, lut_cell.inputs.size(), init_param); + } + } + + return std::make_tuple(false, 0, ""); + } +}; + +AtomNetlist read_interchange_netlist(e_circuit_format circuit_format, + const char* ic_netlist_file, + t_arch& arch) { + AtomNetlist netlist; + std::string netlist_id = vtr::secure_digest_file(ic_netlist_file); + + // Decompress GZipped capnproto device file + gzFile file = gzopen(ic_netlist_file, "r"); + VTR_ASSERT(file != Z_NULL); + + std::vector output_data; + output_data.resize(4096); + std::stringstream sstream(std::ios_base::in | std::ios_base::out | std::ios_base::binary); + while (true) { + int ret = gzread(file, output_data.data(), output_data.size()); + VTR_ASSERT(ret >= 0); + if (ret > 0) { + sstream.write((const char*)output_data.data(), ret); + VTR_ASSERT(sstream); + } else { + VTR_ASSERT(ret == 0); + int error; + gzerror(file, &error); + VTR_ASSERT(error == Z_OK); + break; + } + } + + VTR_ASSERT(gzclose(file) == Z_OK); + + sstream.seekg(0); + kj::std::StdInputStream istream(sstream); + + // Reader options + capnp::ReaderOptions reader_options; + reader_options.nestingLimit = std::numeric_limits::max(); + reader_options.traversalLimitInWords = std::numeric_limits::max(); + + capnp::InputStreamMessageReader message_reader(istream, reader_options); + + auto netlist_reader = message_reader.getRoot(); + + NetlistReader reader(netlist, netlist_reader, netlist_id, ic_netlist_file, arch); + + return netlist; +} diff --git a/vpr/src/base/read_interchange_netlist.h b/vpr/src/base/read_interchange_netlist.h new file mode 100644 index 00000000000..545e3138fad --- /dev/null +++ b/vpr/src/base/read_interchange_netlist.h @@ -0,0 +1,11 @@ +#ifndef READ_INTERCHANGE_NETLIST_H +#define READ_INTERCHANGE_NETLIST_H +#include "logic_types.h" +#include "atom_netlist_fwd.h" +#include "read_circuit.h" + +AtomNetlist read_interchange_netlist(e_circuit_format circuit_format, + const char* ic_netlist_file, + t_arch& arch); + +#endif /*READ_INTERCHANGE_NETLIST_H*/ diff --git a/vpr/src/base/read_options.cpp b/vpr/src/base/read_options.cpp index bb69b986136..87ca957ea58 100644 --- a/vpr/src/base/read_options.cpp +++ b/vpr/src/base/read_options.cpp @@ -69,6 +69,8 @@ struct ParseCircuitFormat { conv_value.set_value(e_circuit_format::BLIF); else if (str == "eblif") conv_value.set_value(e_circuit_format::EBLIF); + else if (str == "fpga-interchange") + conv_value.set_value(e_circuit_format::FPGA_INTERCHANGE); else { std::stringstream msg; msg << "Invalid conversion from '" << str << "' to e_circuit_format (expected one of: " << argparse::join(default_choices(), ", ") << ")"; @@ -84,16 +86,18 @@ struct ParseCircuitFormat { conv_value.set_value("auto"); else if (val == e_circuit_format::BLIF) conv_value.set_value("blif"); - else { - VTR_ASSERT(val == e_circuit_format::EBLIF); + else if (val == e_circuit_format::EBLIF) conv_value.set_value("eblif"); + else { + VTR_ASSERT(val == e_circuit_format::FPGA_INTERCHANGE); + conv_value.set_value("fpga-interchange"); } return conv_value; } std::vector default_choices() { - return {"auto", "blif", "eblif"}; + return {"auto", "blif", "eblif", "fpga-interchange"}; } }; struct ParseRoutePredictor { @@ -1112,7 +1116,9 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg auto& pos_grp = parser.add_argument_group("positional arguments"); pos_grp.add_argument(args.ArchFile, "architecture") - .help("FPGA Architecture description file (XML)"); + .help( + "FPGA Architecture description file \n" + " (XML or FPGA Interchange if --fpga_interchange_device specified)"); pos_grp.add_argument(args.CircuitName, "circuit") .help("Circuit file (or circuit name if --circuit_file specified)"); @@ -1147,6 +1153,23 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg "If the implementation is illegal analysis can be forced by explicitly\n" "specifying the --analysis option."); + auto& FPGAInterchage_grp = parser.add_argument_group("FPGA Interchange options"); + + FPGAInterchage_grp.add_argument(args.FPGAInterchangeDevice, "--fpga_interchange_device") + .help("Read arch file as FPGA Interchange format") + .action(argparse::Action::STORE_TRUE) + .default_value("off"); + + FPGAInterchage_grp.add_argument(args.do_packing, "--fpga_interchange_netlist") + .help("Read circuit file as FPGA Interchange format") + .action(argparse::Action::STORE_TRUE) + .default_value("off"); + + FPGAInterchage_grp.add_argument(args.do_packing, "--fpga_interchange_physical") + .help("Write physical placement in FPGA Interchange format") + .action(argparse::Action::STORE_TRUE) + .default_value("off"); + auto& gfx_grp = parser.add_argument_group("graphics options"); gfx_grp.add_argument(args.show_graphics, "--disp") @@ -1380,7 +1403,8 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg " .conn - Connection between two wires\n" " .cname - Custom name for atom primitive\n" " .param - Parameter on atom primitive\n" - " .attr - Attribute on atom primitive\n") + " .attr - Attribute on atom primitive\n" + " * fpga-interchage: Logical netlist in FPGA Interchange schema format\n") .default_value("auto") .show_in(argparse::ShowIn::HELP_ONLY); @@ -1396,6 +1420,10 @@ argparse::ArgumentParser create_arg_parser(std::string prog_name, t_options& arg .help("Path to routing file") .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.FPGAInterchangePhysicalFile, "--physical_file") + .help("Path to FPGA Interchange Physical file") + .show_in(argparse::ShowIn::HELP_ONLY); + file_grp.add_argument(args.SDCFile, "--sdc_file") .help("Path to timing constraints file in SDC format") .show_in(argparse::ShowIn::HELP_ONLY); @@ -2472,7 +2500,7 @@ void set_conditional_defaults(t_options& args) { } if (args.SDCFile.provenance() != Provenance::SPECIFIED) { - //Use the full path specified in the original circuit name, + //Use the full path specified in the originial circuit name, //and append the expected .sdc extension std::string sdc_file = default_output_name + ".sdc"; args.SDCFile.set(sdc_file, Provenance::INFERRED); @@ -2496,6 +2524,12 @@ void set_conditional_defaults(t_options& args) { args.RouteFile.set(route_file, Provenance::INFERRED); } + if (args.FPGAInterchangePhysicalNet.provenance() == Provenance::SPECIFIED && args.FPGAInterchangePhysicalFile.provenance() != Provenance::SPECIFIED) { + std::string physical_file = args.out_file_prefix; + physical_file += default_output_name + ".phys"; + args.FPGAInterchangePhysicalFile.set(physical_file, Provenance::INFERRED); + } + if (args.ActFile.provenance() != Provenance::SPECIFIED) { std::string activity_file = args.out_file_prefix; activity_file += default_output_name + ".act"; diff --git a/vpr/src/base/read_options.h b/vpr/src/base/read_options.h index aacf3cd824b..60bca3a541a 100644 --- a/vpr/src/base/read_options.h +++ b/vpr/src/base/read_options.h @@ -10,11 +10,14 @@ struct t_options { /* File names */ argparse::ArgValue ArchFile; + const argparse::ArgValue& FPGAInterchangeDeviceFile() const { return ArchFile; } argparse::ArgValue CircuitName; argparse::ArgValue NetFile; argparse::ArgValue PlaceFile; argparse::ArgValue RouteFile; + argparse::ArgValue FPGAInterchangePhysicalFile; argparse::ArgValue BlifFile; + argparse::ArgValue& FPGAInterchangeLogicalFile() { return BlifFile; } argparse::ArgValue ActFile; argparse::ArgValue PowerFile; argparse::ArgValue CmosTechFile; @@ -52,6 +55,9 @@ struct t_options { argparse::ArgValue show_help; argparse::ArgValue show_version; argparse::ArgValue num_workers; + argparse::ArgValue FPGAInterchangeDevice; + argparse::ArgValue FPGAInterchangeLogicalNet; + argparse::ArgValue FPGAInterchangePhysicalNet; argparse::ArgValue timing_analysis; argparse::ArgValue timing_update_type; argparse::ArgValue CreateEchoFile; diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 3250a12cbe1..d1e73eb5c57 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -313,17 +313,7 @@ void vpr_init_with_options(const t_options* options, t_vpr_setup* vpr_setup, t_a /* Read blif file and sweep unused components */ auto& atom_ctx = g_vpr_ctx.mutable_atom(); - atom_ctx.nlist = read_and_process_circuit(options->circuit_format, - vpr_setup->PackerOpts.blif_file_name.c_str(), - vpr_setup->user_models, - vpr_setup->library_models, - vpr_setup->NetlistOpts.const_gen_inference, - vpr_setup->NetlistOpts.absorb_buffer_luts, - vpr_setup->NetlistOpts.sweep_dangling_primary_ios, - vpr_setup->NetlistOpts.sweep_dangling_nets, - vpr_setup->NetlistOpts.sweep_dangling_blocks, - vpr_setup->NetlistOpts.sweep_constant_primary_outputs, - vpr_setup->NetlistOpts.netlist_verbosity); + atom_ctx.nlist = read_and_process_circuit(options->circuit_format, *vpr_setup, *arch); if (vpr_setup->PowerOpts.do_power) { //Load the net activity file for power estimation diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 68ae9b57e6b..d8497195ad0 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -748,6 +748,7 @@ struct t_file_name_opts { std::string NetFile; std::string PlaceFile; std::string RouteFile; + std::string FPGAInterchangePhysicalFile; std::string ActFile; std::string PowerFile; std::string CmosTechFile; diff --git a/vpr/src/util/vpr_error.h b/vpr/src/util/vpr_error.h index 5820ebc0aae..d293a03b94f 100644 --- a/vpr/src/util/vpr_error.h +++ b/vpr/src/util/vpr_error.h @@ -19,6 +19,7 @@ enum e_vpr_error { VPR_ERROR_NET_F, VPR_ERROR_PLACE_F, VPR_ERROR_BLIF_F, + VPR_ERROR_IC_NETLIST_F, VPR_ERROR_IMPL_NETLIST_WRITER, VPR_ERROR_NETLIST, VPR_ERROR_ATOM_NETLIST, diff --git a/vpr/test/lut.netlist b/vpr/test/lut.netlist new file mode 100644 index 00000000000..d8a49d6feab Binary files /dev/null and b/vpr/test/lut.netlist differ diff --git a/vpr/test/test_interchange_device.cpp b/vpr/test/test_interchange_device.cpp new file mode 100644 index 00000000000..5321e6424bb --- /dev/null +++ b/vpr/test/test_interchange_device.cpp @@ -0,0 +1,72 @@ +#include "catch.hpp" + +#include "read_fpga_interchange_arch.h" +#include "arch_util.h" +#include "vpr_api.h" +#include +#include +#include + +namespace { + +using Catch::Matchers::Equals; + +static constexpr const char kArchFile[] = "testarch.device"; + +TEST_CASE("read_interchange_models", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + std::unordered_set models = {"IB", "OB", "LUT", "DFF", "GND", "VCC"}; + + // Check that there are exactly the expected models + for (auto* model = arch.models; model != nullptr; model = model->next) { + std::string name = model->name; + REQUIRE(models.find(name) != models.end()); + models.erase(name); + } + + REQUIRE(models.size() == 0); + + std::unordered_set lib_models = {MODEL_INPUT, MODEL_OUTPUT, MODEL_LATCH, MODEL_NAMES}; + + // Check that there are exactly the expected models + for (auto* model = arch.model_library; model != nullptr; model = model->next) { + std::string name = model->name; + REQUIRE(lib_models.find(name) != lib_models.end()); + lib_models.erase(name); + } + + REQUIRE(lib_models.size() == 0); +} + +TEST_CASE("read_interchange_layout", "[vpr]") { + t_arch arch; + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + auto& gd = arch.grid_layouts[0]; + REQUIRE(gd.grid_type == GridDefType::FIXED); + REQUIRE(gd.height == 10); + REQUIRE(gd.width == 10); + + std::unordered_map tile_types({{"NULL", false}, {"PWR", false}, {"IOB", false}, {"CLB", false}}); + for (auto& loc : gd.loc_defs) { + auto ret = tile_types.find(loc.block_type); + REQUIRE(ret != tile_types.end()); + REQUIRE(loc.priority == 1); + + ret->second = true; + } + + for (auto type : tile_types) { + CHECK(type.second); + } +} + +} // namespace diff --git a/vpr/test/test_interchange_netlist.cpp b/vpr/test/test_interchange_netlist.cpp new file mode 100644 index 00000000000..bc1a60dac20 --- /dev/null +++ b/vpr/test/test_interchange_netlist.cpp @@ -0,0 +1,35 @@ +#include "catch.hpp" + +#include "read_circuit.h" +#include "read_fpga_interchange_arch.h" +#include "arch_util.h" +#include "vpr_api.h" +#include +#include +#include + +namespace { + +using Catch::Matchers::Equals; + +static constexpr const char kArchFile[] = "testarch.device"; + +TEST_CASE("read_interchange_netlist", "[vpr]") { + t_arch arch; + t_vpr_setup vpr_setup; + + std::vector physical_tile_types; + std::vector logical_block_types; + + FPGAInterchangeReadArch(kArchFile, /*timing_enabled=*/true, &arch, physical_tile_types, logical_block_types); + + vpr_setup.user_models = arch.models; + vpr_setup.library_models = arch.model_library; + vpr_setup.PackerOpts.blif_file_name = "lut.netlist"; + + /* Read blif file and sweep unused components */ + auto& atom_ctx = g_vpr_ctx.mutable_atom(); + atom_ctx.nlist = read_and_process_circuit(e_circuit_format::FPGA_INTERCHANGE, vpr_setup, arch); +} + +} // namespace diff --git a/vpr/test/testarch.device b/vpr/test/testarch.device new file mode 100644 index 00000000000..e9b12b1ac89 Binary files /dev/null and b/vpr/test/testarch.device differ