diff --git a/.clang-format b/.clang-format index 28d13da2564..c86fa8c1cda 100644 --- a/.clang-format +++ b/.clang-format @@ -6,7 +6,7 @@ BreakBeforeBraces: Linux AllowShortIfStatementsOnASingleLine: false IndentCaseLabels: false -# From CodingReadme +# From guidelines/CodingStyle TabWidth: 8 ContinuationIndentWidth: 2 ColumnLimit: 150 diff --git a/.dockerignore b/.dockerignore index 9910e9954e9..9f1da94da71 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,8 +6,7 @@ Dockerfile README.md manual -CodingReadme +guidelines CodeOfConduct .travis .travis.yml - diff --git a/.gitattributes b/.gitattributes index f85ae06c930..5e568606e6e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.v linguist-language=Verilog +/.gitcommit export-subst diff --git a/.gitcommit b/.gitcommit new file mode 100644 index 00000000000..46b7856fbc8 --- /dev/null +++ b/.gitcommit @@ -0,0 +1 @@ +$Format:%h$ diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..66c0b19715a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,76 @@ +name: Bug Report +description: Report an issue or regression with Yosys +labels: ["pending-verification"] +body: + - type: markdown + attributes: + value: > + + If you have a general question, please ask it in the [Discussions](https://github.com/YosysHQ/yosys/discussions) area + or join our [IRC Channel](https://web.libera.chat/#yosys) or [Community Slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA). + + + If you have a feature request, please fill out the appropriate issue form, this form is for bugs and/or regressions. + + + Please contact [YosysHQ GmbH](https://www.yosyshq.com/) if you need + commercial support for Yosys. + + - type: input + id: yosys_version + attributes: + label: Version + description: "The version of yosys this bug was encountered on." + placeholder: "The output of `yosys --version`" + validations: + required: true + + - type: dropdown + id: os + attributes: + label: On which OS did this happen? + options: + - Linux + - macOS + - Windows + - BSD + - WebAssembly + multiple: true + validations: + required: true + + - type: markdown + attributes: + value: > + When providing steps to reproduce the issue, please ensure that the issue + is reproducible in the current git master of Yosys. Also ensure to + provide all necessary source files needed. + + + Please see [https://stackoverflow.com/help/mcve](https://stackoverflow.com/help/mcve) + for information on how to create a Minimal, Complete, and Verifiable Example + (MCVE). + + - type: textarea + id: reproduction_steps + attributes: + label: Reproduction Steps + description: "Please provide clear and concise steps to reproduce the issue." + validations: + required: true + + - type: textarea + id: expected_behavior + attributes: + label: Expected Behavior + description: "Please describe the behavior you would have expected from the tool." + validations: + required: true + + - type: textarea + id: actual_behavior + attributes: + label: Actual Behavior + description: "Please describe how the behavior you see differs from the expected behavior." + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..bef410a3c92 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +contact_links: + - name: Discussions + url: https://github.com/YosysHQ/yosys/discussions + about: "Have a question? Ask it on our discussions page!" + - name: Community Slack + url: https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA + about: "Yosys Community Slack" + - name: IRC Channel + url: https://web.libera.chat/#yosys + about: "#yosys on irc.libera.chat" + diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..c521b529631 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,26 @@ +name: Feature Request +description: "Submit a feature request for Yosys" +labels: ["feature-request"] +body: + - type: markdown + attributes: + value: > + + If you have a general question, please ask it in the [Discussions](https://github.com/YosysHQ/yosys/discussions) area + or join our [IRC Channel](https://web.libera.chat/#yosys) or [Community Slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA). + + + If you have a bug report, please fill out the appropriate issue form, this form is for feature requests. + + + Please contact [YosysHQ GmbH](https://www.yosyshq.com/) if you need + commercial support or work done for Yosys. + + - type: textarea + id: feature_description + attributes: + label: Feature Description + description: "A clear and detailed description of the feature." + validations: + required: true + diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 5a0723c3e0a..00000000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,24 +0,0 @@ -## Steps to reproduce the issue - -*Provide instructions for reproducing the issue. Make sure to include -all necessary source files. (You can simply drag&drop a .zip file into -the issue editor.)* - -Also, make sure that the issue is actually reproducable in current git -master of Yosys. - -See https://stackoverflow.com/help/mcve for some information on how to -create a Minimal, Complete, and Verifiable example (MCVE). - -Please do not waste our time with issues that lack sufficient information -to reproduce the issue easily. We will simply close those issues. - -Contact https://www.symbioticeda.com/ if you need commercial support for Yosys. - -## Expected behavior - -*Please describe the behavior you would have expected from the tool.* - -## Actual behavior - -*Please describe how the behavior you see differs from the expected behavior.* diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..4a77d223429 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,29 @@ +name: "CodeQL" + +on: + workflow_dispatch: + schedule: + - cron: '0 3 * * *' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + steps: + - name: Install deps + run: sudo apt-get install bison flex libreadline-dev tcl-dev libffi-dev + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: cpp + queries: security-extended,security-and-quality + + - name: Build + run: make yosys -j6 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/emcc.yml b/.github/workflows/emcc.yml new file mode 100644 index 00000000000..7c6409c1be2 --- /dev/null +++ b/.github/workflows/emcc.yml @@ -0,0 +1,18 @@ +name: Emscripten Build + +on: [push, pull_request] + +jobs: + emcc: + runs-on: ubuntu-latest + steps: + - uses: mymindstorm/setup-emsdk@v14 + - uses: actions/checkout@v4 + - name: Build + run: | + make config-emcc + make YOSYS_VER=latest + - uses: actions/upload-artifact@v4 + with: + name: yosysjs + path: yosysjs-latest.zip diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml new file mode 100644 index 00000000000..e8064e485ae --- /dev/null +++ b/.github/workflows/test-docs.yml @@ -0,0 +1,42 @@ +name: Build and test doc code samples + +on: + pull_request: + branches: + - master + +jobs: + test-docs: + runs-on: ubuntu-latest + steps: + - name: Install Dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get install gperf build-essential bison flex libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev + + - name: Setup GCC + uses: Dup4/actions-setup-gcc@v1 + + - name: Runtime environment + shell: bash + env: + WORKSPACE: ${{ github.workspace }} + run: | + echo "GITHUB_WORKSPACE=`pwd`" >> $GITHUB_ENV + echo "$GITHUB_WORKSPACE/.local/bin" >> $GITHUB_PATH + echo "procs=$(nproc)" >> $GITHUB_ENV + + - name: Checkout Yosys + uses: actions/checkout@v3 + + - name: Build yosys + shell: bash + run: | + make config-gcc + make -j${{ env.procs }} + + - name: Run tests + shell: bash + run: | + make -C docs test -j${{ env.procs }} diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml new file mode 100644 index 00000000000..c4441a8843e --- /dev/null +++ b/.github/workflows/test-linux.yml @@ -0,0 +1,130 @@ +name: Build and run tests (Linux) + +on: [push, pull_request] + +jobs: + test-linux: + runs-on: ${{ matrix.os.id }} + strategy: + matrix: + os: + - { id: ubuntu-20.04, name: focal } + compiler: + - 'clang-12' + - 'gcc-11' + cpp_std: + - 'c++11' + - 'c++14' + - 'c++17' + - 'c++20' + include: + # Limit the older compilers to C++11 mode + - os: { id: ubuntu-20.04, name: focal } + compiler: 'clang-11' + cpp_std: 'c++11' + - os: { id: ubuntu-20.04, name: focal } + compiler: 'gcc-10' + cpp_std: 'c++11' + fail-fast: false + steps: + - name: Install Dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get install gperf build-essential bison flex libreadline-dev gawk tcl-dev libffi-dev git graphviz xdot pkg-config python python3 libboost-system-dev libboost-python-dev libboost-filesystem-dev zlib1g-dev + + - name: Setup GCC + if: startsWith(matrix.compiler, 'gcc') + shell: bash + run: | + CXX=${CC/#gcc/g++} + sudo apt-add-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install $CC $CXX + echo "CC=$CC" >> $GITHUB_ENV + echo "CXX=$CXX" >> $GITHUB_ENV + echo "CXXFLAGS=-Wp,-D_GLIBCXX_ASSERTIONS" >> $GITHUB_ENV + env: + CC: ${{ matrix.compiler }} + + - name: Setup Clang + if: startsWith(matrix.compiler, 'clang') + shell: bash + run: | + wget https://apt.llvm.org/llvm-snapshot.gpg.key + sudo apt-key add llvm-snapshot.gpg.key + rm llvm-snapshot.gpg.key + sudo apt-add-repository "deb https://apt.llvm.org/${{ matrix.os.name }}/ llvm-toolchain-${{ matrix.os.name }} main" + sudo apt-get update + CXX=${CC/#clang/clang++} + sudo apt-get install $CC $CXX + echo "CC=$CC" >> $GITHUB_ENV + echo "CXX=$CXX" >> $GITHUB_ENV + env: + CC: ${{ matrix.compiler }} + + - name: Runtime environment + shell: bash + env: + WORKSPACE: ${{ github.workspace }} + run: | + echo "GITHUB_WORKSPACE=`pwd`" >> $GITHUB_ENV + echo "$GITHUB_WORKSPACE/.local/bin" >> $GITHUB_PATH + echo "procs=$(nproc)" >> $GITHUB_ENV + + - name: Tool versions + shell: bash + run: | + $CC --version + $CXX --version + + - name: Checkout Yosys + uses: actions/checkout@v4 + + - name: Get iverilog + shell: bash + run: | + git clone https://github.com/steveicarus/iverilog.git + cd iverilog + echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV + + - name: Cache iverilog + id: cache-iverilog + uses: actions/cache@v4 + with: + path: .local/ + key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }} + + - name: Build iverilog + if: steps.cache-iverilog.outputs.cache-hit != 'true' + shell: bash + run: | + mkdir -p $GITHUB_WORKSPACE/.local/ + cd iverilog + autoconf + CC=gcc CXX=g++ ./configure --prefix=$GITHUB_WORKSPACE/.local + make -j${{ env.procs }} + make install + + - name: Build yosys + shell: bash + run: | + make config-${CC%%-*} + make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC + + - name: Store build artifact + if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11') + uses: actions/upload-artifact@v4 + with: + name: compiled-yosys + path: yosys + + - name: Run tests + if: (matrix.cpp_std == 'c++11') && (matrix.compiler == 'gcc-11') + shell: bash + run: | + make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=$CC CXX=$CC LD=$CC + + - name: Log yosys-config output + run: | + ./yosys-config || true diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml new file mode 100644 index 00000000000..8ca658c3951 --- /dev/null +++ b/.github/workflows/test-macos.yml @@ -0,0 +1,75 @@ +name: Build and run tests (macOS) + +on: [push, pull_request] + +jobs: + test-macos: + runs-on: ${{ matrix.os.id }} + strategy: + matrix: + os: + - { id: macos-13, name: 'Ventura' } + cpp_std: + - 'c++11' + - 'c++17' + fail-fast: false + steps: + - name: Install Dependencies + run: | + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash + + - name: Runtime environment + shell: bash + env: + WORKSPACE: ${{ github.workspace }} + run: | + echo "GITHUB_WORKSPACE=`pwd`" >> $GITHUB_ENV + echo "$GITHUB_WORKSPACE/.local/bin" >> $GITHUB_PATH + echo "$(brew --prefix bison)/bin" >> $GITHUB_PATH + echo "$(brew --prefix flex)/bin" >> $GITHUB_PATH + echo "procs=$(sysctl -n hw.ncpu)" >> $GITHUB_ENV + + - name: Tool versions + shell: bash + run: | + cc --version + + - name: Checkout Yosys + uses: actions/checkout@v4 + + - name: Get iverilog + shell: bash + run: | + git clone https://github.com/steveicarus/iverilog.git + cd iverilog + echo "IVERILOG_GIT=$(git rev-parse HEAD)" >> $GITHUB_ENV + + - name: Cache iverilog + id: cache-iverilog + uses: actions/cache@v4 + with: + path: .local/ + key: ${{ matrix.os.id }}-${{ env.IVERILOG_GIT }} + + - name: Build iverilog + if: steps.cache-iverilog.outputs.cache-hit != 'true' + shell: bash + run: | + mkdir -p $GITHUB_WORKSPACE/.local/ + cd iverilog + autoconf + CC=gcc CXX=g++ ./configure --prefix=$GITHUB_WORKSPACE/.local/ + make -j${{ env.procs }} + make install + + - name: Build yosys + shell: bash + run: | + make config-clang + make -j${{ env.procs }} CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc + + - name: Run tests + if: matrix.cpp_std == 'c++11' + shell: bash + run: | + make -j${{ env.procs }} test CXXSTD=${{ matrix.cpp_std }} CC=cc CXX=cc LD=cc diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml new file mode 100644 index 00000000000..47a7fe1a39f --- /dev/null +++ b/.github/workflows/version.yml @@ -0,0 +1,34 @@ +name: Bump version + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + +jobs: + bump-version: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Take last commit + id: log + run: echo "message=$(git log --no-merges -1 --oneline)" >> $GITHUB_OUTPUT + - name: Take repository + id: repo + run: echo "message=$GITHUB_REPOSITORY" >> $GITHUB_OUTPUT + - name: Bump version + if: "!contains(steps.log.outputs.message, 'Bump version') && contains(steps.repo.outputs.message, 'YosysHQ/yosys')" + run: | + make bumpversion + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git add Makefile + git commit -m "Bump version" + - name: Push changes # push the output folder to your repo + if: "!contains(steps.log.outputs.message, 'Bump version') && contains(steps.repo.outputs.message, 'YosysHQ/yosys')" + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/vs.yml b/.github/workflows/vs.yml new file mode 100644 index 00000000000..799c5f259f4 --- /dev/null +++ b/.github/workflows/vs.yml @@ -0,0 +1,31 @@ +name: Visual Studio Build + +on: [push, pull_request] + +jobs: + yosys-vcxsrc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build + run: make vcxsrc YOSYS_VER=latest + - uses: actions/upload-artifact@v4 + with: + name: vcxsrc + path: yosys-win32-vcxsrc-latest.zip + + build: + runs-on: windows-2019 + needs: yosys-vcxsrc + steps: + - uses: actions/download-artifact@v4 + with: + name: vcxsrc + path: . + - name: unzip + run: unzip yosys-win32-vcxsrc-latest.zip + - name: setup-msbuild + uses: microsoft/setup-msbuild@v1 + - name: MSBuild + working-directory: yosys-win32-vcxsrc-latest + run: msbuild YosysVS.sln /p:PlatformToolset=v142 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.17763.0 diff --git a/.github/workflows/wasi.yml b/.github/workflows/wasi.yml new file mode 100644 index 00000000000..d6d200d92c2 --- /dev/null +++ b/.github/workflows/wasi.yml @@ -0,0 +1,30 @@ +name: WASI Build + +on: [push, pull_request] + +jobs: + wasi: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build + run: | + WASI_SDK=wasi-sdk-19.0 + WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz + if ! [ -d ${WASI_SDK} ]; then curl -L ${WASI_SDK_URL} | tar xzf -; fi + + mkdir -p build + cat > build/Makefile.conf < +Marcelina Kościelnicka +Marcelina Kościelnicka +Claire Xenia Wolf +Claire Xenia Wolf +Claire Xenia Wolf +Claire Xenia Wolf diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 09f3808313a..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,144 +0,0 @@ -sudo: false -language: cpp - -cache: - ccache: true - directories: - - ~/.local-bin - - -env: - global: - - MAKEFLAGS="-j 2" - -matrix: - include: - # Latest gcc-4.8, earliest version supported by Travis - - os: linux - addons: - apt: - packages: - - g++-4.8 - - gperf - - build-essential - - bison - - flex - - libreadline-dev - - gawk - - tcl-dev - - libffi-dev - - git - - graphviz - - xdot - - pkg-config - - python - - python3 - - libboost-system-dev - - libboost-python-dev - - libboost-filesystem-dev - - zlib1g-dev - env: - - MATRIX_EVAL="CONFIG=gcc && CC=gcc-4.8 && CXX=g++-4.8" - - # Latest gcc supported on Travis Linux - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-9 - - gperf - - build-essential - - bison - - flex - - libreadline-dev - - gawk - - tcl-dev - - libffi-dev - - git - - graphviz - - xdot - - pkg-config - - python - - python3 - - libboost-system-dev - - libboost-python-dev - - libboost-filesystem-dev - - zlib1g-dev - env: - - MATRIX_EVAL="CONFIG=gcc && CC=gcc-9 && CXX=g++-9" - - # Clang which ships on Trusty Linux - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 - packages: - - clang-3.8 - - gperf - - build-essential - - bison - - flex - - libreadline-dev - - gawk - - tcl-dev - - libffi-dev - - git - - graphviz - - xdot - - pkg-config - - python - - python3 - - libboost-system-dev - - libboost-python-dev - - libboost-filesystem-dev - - zlib1g-dev - env: - - MATRIX_EVAL="CONFIG=clang && CC=clang-3.8 && CXX=clang++-3.8" - - # Latest clang supported by Travis Linux - - os: linux - addons: - apt: - sources: - - llvm-toolchain-xenial-8 - packages: - - clang-8 - - gperf - - build-essential - - bison - - flex - - libreadline-dev - - gawk - - tcl-dev - - libffi-dev - - git - - graphviz - - xdot - - pkg-config - - python - - python3 - - libboost-system-dev - - libboost-python-dev - - libboost-filesystem-dev - - zlib1g-dev - env: - - MATRIX_EVAL="CONFIG=clang && CC=clang-8 && CXX=clang++-8" - -# # Latest clang on Mac OS X -# - os: osx -# osx_image: xcode9.4 -# env: -# - MATRIX_EVAL="CONFIG=clang && CC=clang && CXX=clang++" - -before_install: - - ./.travis/setup.sh - -script: - - ./.travis/build-and-test.sh - -after_success: - - ./.travis/deploy-after-success.sh diff --git a/.travis/build-and-test.sh b/.travis/build-and-test.sh deleted file mode 100755 index 801407d1e8f..00000000000 --- a/.travis/build-and-test.sh +++ /dev/null @@ -1,51 +0,0 @@ -#! /bin/bash - -set -e - -source .travis/common.sh - -########################################################################## - -echo -echo 'Configuring...' && echo -en 'travis_fold:start:script.configure\\r' -echo - -if [ "$CONFIG" = "gcc" ]; then - echo "Configuring for gcc." - make config-gcc -elif [ "$CONFIG" = "clang" ]; then - echo "Configuring for clang." - make config-clang -fi - -echo -echo -en 'travis_fold:end:script.configure\\r' -echo - -########################################################################## - -echo -echo 'Building...' && echo -en 'travis_fold:start:script.build\\r' -echo - -make CC=$CC CXX=$CC LD=$CC - -echo -echo -en 'travis_fold:end:script.build\\r' -echo - -########################################################################## - -./yosys tests/simple/fiedler-cooley.v - -echo -echo 'Testing...' && echo -en 'travis_fold:start:script.test\\r' -echo - -make test - -echo -echo -en 'travis_fold:end:script.test\\r' -echo - -########################################################################## diff --git a/.travis/common.sh b/.travis/common.sh deleted file mode 100644 index 8eecc4c099f..00000000000 --- a/.travis/common.sh +++ /dev/null @@ -1,15 +0,0 @@ -#! /bin/bash - -# Setup the CC / CXX from the matrix config -eval "${MATRIX_EVAL}" - -# Look for location binaries first -export PATH="$HOME/.local-bin/bin:$PATH" - -# OS X specific common setup -if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - export PATH="/usr/local/opt/ccache/libexec:$PATH" -fi - -# Parallel builds! -MAKEFLAGS="-j 2" diff --git a/.travis/deploy-after-success.sh b/.travis/deploy-after-success.sh deleted file mode 100755 index d64e95244f6..00000000000 --- a/.travis/deploy-after-success.sh +++ /dev/null @@ -1,6 +0,0 @@ -#! /bin/bash - -set -x -set -e - -# FIXME: Upload the build results somewhere... diff --git a/.travis/setup.sh b/.travis/setup.sh deleted file mode 100755 index 02879b97436..00000000000 --- a/.travis/setup.sh +++ /dev/null @@ -1,63 +0,0 @@ -#! /bin/bash - -set -e - -source .travis/common.sh - -########################################################################## - -# Output status information. -( - set +e - set -x - git status - git branch -v - git log -n 5 --graph - git log --format=oneline -n 20 --graph -) -echo -echo -en 'travis_fold:end:before_install.git\\r' -echo - -########################################################################## - -# Mac OS X specific setup. -if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - ( - echo - echo 'Setting up brew...' && echo -en 'travis_fold:start:before_install.brew\\r' - echo - brew update - brew tap Homebrew/bundle - brew bundle - brew install ccache - echo - echo -en 'travis_fold:end:before_install.brew\\r' - echo - ) -fi - -########################################################################## - -# Install iverilog -( - if [ ! -e ~/.local-bin/bin/iverilog ]; then - echo - echo 'Building iverilog...' && echo -en 'travis_fold:start:before_install.iverilog\\r' - echo - mkdir -p ~/.local-src - mkdir -p ~/.local-bin - cd ~/.local-src - git clone git://github.com/steveicarus/iverilog.git - cd iverilog - autoconf - CC=gcc CXX=g++ ./configure --prefix=$HOME/.local-bin - make - make install - echo - echo -en 'travis_fold:end:before_install.iverilog\\r' - echo - fi -) - -########################################################################## diff --git a/Brewfile b/Brewfile index 2a985f09e65..b50c70a4b0e 100644 --- a/Brewfile +++ b/Brewfile @@ -9,3 +9,4 @@ brew "python3" brew "tcl-tk" brew "xdot" brew "bash" +brew 'boost-python3' diff --git a/CHANGELOG b/CHANGELOG index 6dcd05de665..b172988d5db 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,74 +2,682 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.39 .. Yosys 0.40-dev +-------------------------- + +Yosys 0.38 .. Yosys 0.39 +-------------------------- + * New commands and options + - Added option "-extra-map" to "synth" pass. + - Added option "-dont_use" to "dfflibmap" pass. + - Added option "-href" to "show" command. + - Added option "-noscopeinfo" to "flatten" pass. + - Added option "-scopename" to "flatten" pass. + + * SystemVerilog + - Added support for packed multidimensional arrays. + + * Various + - Added "$scopeinfo" cells to preserve information about + the hierarchy during flattening. + - Added sequential area output to "stat -liberty". + - Added ability to record/replay diagnostics in cxxrtl backend. + + * Verific support + - Added attributes to module instantiation. + +Yosys 0.37 .. Yosys 0.38 +-------------------------- + * New commands and options + - Added option "-tech" to "opt_lut" pass. + - Added option "-nokeep_prints" to "hierarchy" pass. + - Added option "-nolower" to "async2sync" and "clk2fflogic" pass. + - Added option "-lower" to "chformal" pass. + + * Various + - Added $check cell to represent assertions with messages. + - Allow capturing $print cell output in CXXRTL. + - Added API to overwrite existing pass from plugin. + - Follow the XDG Base Directory Specification for storing history files. + - Without a known top module, derive all deferred modules (hierarchy pass). + - Detect and error out on combinational loops in write_aiger. + + * Verific support + - Added option "-no-split-complex-ports" to "verific -import". + +Yosys 0.36 .. Yosys 0.37 +-------------------------- + * New commands and options + - Added option "-nodisplay" to read_verilog. + + * SystemVerilog + - Correct hierarchical path names for structs and unions. + + * Various + - Print hierarchy for failed assertions in "sim" pass. + - Add "--present-only" option to "yosys-witness" to omit unused signals. + - Implement a generic record/replay interface for CXXRTL. + - Improved readability of emitted code with "write_verilog". + +Yosys 0.35 .. Yosys 0.36 +-------------------------- + * New commands and options + - Added option "--" to pass arguments down to tcl when using -c option. + - Added ability on MacOS and Windows to pass options after arguments on cli. + - Added option "-cmp2softlogic" to synth_lattice. + - Added option "-lowpower" to "booth" pass. + + * QuickLogic support + - Added "K6N10f" support. + - Added "-nodsp", "-nocarry", "-nobram" and "-bramtypes" options to + "synth_quicklogic" pass. + - Added "ql_bram_merge" pass to merge 18K BRAM cells into TDP36K. + - Added "ql_bram_types" pass to change TDP36K depending on configuration. + - Added "ql_dsp_io_regs" pass to update QL_DSP2 depending on configuration. + - Added "ql_dsp_macc" pass to infer multiplier-accumulator DSP cells. + - Added "ql_dsp_simd" pass to merge DSP pairs to operate in SIMD mode. + + * ECP5,iCE40 and Gowin support + - Enabled abc9 by default, added "-noabc9" option to disable. + + * MachXO3 support + - Quality of results improvements. + - Enabled "booth" pass by default for it in "synth_lattice". + + * Various + - Improved "peepopt" by adding shiftadd pattern support. + - Added "--incremental" mode to smtbmc. + +Yosys 0.34 .. Yosys 0.35 +-------------------------- + * Various + - Improvements on "peepopt" shiftmul matcher. + - Improvements on "ram_style" attributes handling. + + * Verific support + - Improved static elaboration for VHDL and mixed HDL designs. + - Expose "hdlname" attribute with original module name. + - Expose "architecture" attribute with VHDL architecture name. + +Yosys 0.33 .. Yosys 0.34 +-------------------------- + * New commands and options + - Added option "-assert" to "sim" pass. + - Added option "-noinitstate" to "sim" pass. + - Added option "-dont_use" to "abc" pass. + - Added "dft_tag" pass to create tagging logic for data flow tracking. + - Added "future" pass to resolve future sampled value functions. + - Added "booth" pass to map $mul cells to Booth multipliers. + - Added option "-booth" to "synth" pass. + + * SystemVerilog + - Added support for assignments within expressions, e.g., `x[y++] = z;` or + `x = (y *= 2) - 1;`. + + * Verific support + - "src" attribute contain full location info. + - module parameters are kept after import. + - accurate access order semantics in memory inference. + - better "bind" support for mixed language projects. + + * Various + - "show" command displays dot instead of box for wire aliases. + +Yosys 0.32 .. Yosys 0.33 +-------------------------- + * Various + - Added "$print" cell, produced by "$display" and "$write" + Verilog tasks. + - Added "$print" cell handling in CXXRTL. + + * Lattice FPGA support + - Added generic "synth_lattice" pass (for now MachXO2/XO3/XO3D) + - Removed "synth_machxo2" pass + - Pass "ecp5_gsr" renamed to "lattice_gsr" + - "synth_machxo2" equivalent is "synth_lattice -family xo2" + +Yosys 0.31 .. Yosys 0.32 +-------------------------- + * Verific support + - Added sub option "-lib" to reading commands for VHDL and + SystemVerilog, that will later import all units/modules from + marked files as blackboxes. + + * Various + - Added support for $lt, $le, $gt, $ge to the code generating AIGs. + +Yosys 0.30 .. Yosys 0.31 +-------------------------- + * New commands and options + - Added option "-lsbidx" to "write_edif" pass. + + * Various + - Added support for $divfloor operator to cxxrtl backend. + - dfflegalize: allow setting mince and minsrst args from scratchpad. + +Yosys 0.29 .. Yosys 0.30 +-------------------------- + * New commands and options + - Added "recover_names" pass to recover names post-mapping. + + * Gowin support + - Added remaining primitives blackboxes. + + * Various + - "show -colorattr" will now color the cells, wires, and + connection arrows. + - "show -viewer none" will not execute viewer. + +Yosys 0.28 .. Yosys 0.29 +-------------------------- + * New commands and options + - Added "synthprop" pass for synthesizable properties. + + * Verific support + - Handle conditions on clocked concurrent assertions in unclocked + procedural contexts. + + * Verilog + - Fix const eval of unbased unsized constants. + - Handling of attributes for struct / union variables. + +Yosys 0.27 .. Yosys 0.28 +-------------------------- + * Verilog + - Out of bounds checking for struct/union members. + + * Verific support + - Fix enum_values support and signed attribute values. + + * ECP5 support + - Added "synth_ecp5 -iopad" + + * MachXO2 support + - Added "synth_machxo2 -ccu2" + +Yosys 0.26 .. Yosys 0.27 +-------------------------- + * New commands and options + - Added option "-make_assert" to "equiv_make" pass. + - Added option "-coverenable" to "chformal" pass. + + * Verilog + - Resolve package types in interfaces. + - Handle range offsets in packed arrays within packed structs. + - Support for data and array queries on struct/union item expressions. + + * GateMate support + - Enable register initialization. + +Yosys 0.25 .. Yosys 0.26 +-------------------------- + * New commands and options + - Added "bwmuxmap" pass to replace $bwmux cells with equivalent logic. + - Added "xprop" experimental pass for formal x propagation. + - Added "splitcells" pass to split up multi-bit cells. + - Added "viz" pass to visualize data flow graph. + - Added option "-make_cover" to "miter" pass. + - Added option "-noparallelcase" to "write_verilog" pass. + - Added option "-chain" to "insbuf" pass. + - Added options "-hierarchy" and "-assume" to "formalff" pass. + - Added options "-append" and "-summary" to "sim" pass. + - Added option "-ywmap" to "write_btor" pass. + - Added option "-ignore-self-reset" to "fsm_detect" pass. + + * Verilog + - Support for struct members of union type. + - Support for struct member package types. + + * Various + - Added Yosys witness (.yw) cosimulation. + - GCC 4.8 is deprecated, compiler with full C++11 support is required. + +Yosys 0.24 .. Yosys 0.25 +-------------------------- + * Verific support + - Respect "noblackbox" attribute for modules. + + * Various + - Documentation is hosted at https://yosyshq.readthedocs.io/projects/yosys/en/latest/ + +Yosys 0.23 .. Yosys 0.24 +-------------------------- + * New commands and options + - Added option "-set-def-formal" to "sat" pass. + - Added option "-s" to "tee" command. + + * Verilog + - Support for module-scoped identifiers referring to tasks and functions. + - Support for arrays with swapped ranges within structs. + + * Verific support + - Support for importing verilog configurations per name. + - "verific -set-XXXXX" commands are now able to set severity to all messages + of certain type (errors, warnings, infos and comments) + + * Various + - TCL shell support (use "yosys -C") + - Added FABulous eFPGA frontend + +Yosys 0.22 .. Yosys 0.23 +-------------------------- + * New commands and options + - Added option "-cross" to "miter" pass. + - Added option "-nocheck" to "equiv_opt" pass. + + * Formal Verification + - yosys-smtbmc: Added "--detect-loops" option for checking if states are + unique in temporal induction counter examples. + + * Verific support + - Added support for reading Liberty files using Verific library. + (Optinally enabled with ENABLE_VERIFIC_LIBERTY) + - Added option "-cells" to "verific -import" enabling import of + all cells from verific design. + + * Various + - MinGW build (Windows) plugin support. + - Added YOSYS_ABORT_ON_LOG_ERROR environment variable for debugging. + Setting it to 1 causes abort() to be called when Yosys terminates with an + error message. + +Yosys 0.21 .. Yosys 0.22 +-------------------------- + * Verific support + - Added support for here-document for "verific" command (for reading + source files). + - Added support for reading EDIF files using Verific library. + (Optinally enabled with ENABLE_VERIFIC_EDIF) + + * Various + - Added tech specific utilization to "stat" json. + +Yosys 0.20 .. Yosys 0.21 +-------------------------- + * New commands and options + - Added "formalff" pass - transforms FFs for formal verification + - Added option "-formal" to "memory_map" pass + - Added option "-witness" to "rename" - give public names to all signals + present in yosys witness traces + - Added option "-hdlname" to "sim" pass - preserves hiearachy when writing + simulation output for a flattened design + - Addded option "-scramble-name" to "rename" pass + + * Formal Verification + - Added $anyinit cell to directly represent FFs with an unconstrained + initialization value. These can be generated by the new formalff pass. + - New JSON based yosys witness format for formal verification traces. + - yosys-smtbmc: Reading and writing of yosys witness traces. + - write_smt2: Emit inline metadata to support yosys witness trace. + - yosys-witness is a new tool to inspect and convert yosys witness traces. + - write_aiger: Option to write a map file for yosys witness trace + conversion. + - yosys-witness: Conversion from and to AIGER witness traces. + + * Verific support + - Filename re-writing support for "verific" pass. + + * Various + - ABC performance improvements + - Filename re-writing added for "show -lib". + + * SmartFusion2 support + - Added $alu support + - Added SYSRESET and XTLOSC cells + - Compatible now with LiberoSoc flow + +Yosys 0.19 .. Yosys 0.20 +-------------------------- + * New commands and options + - Added option "-wb" to "read_liberty" pass + + * Various + - Added support for $modfloor operator to cxxrtl backend + - Support build on OpenBSD + - Fixed smt2 backend use of $shift/$shiftx with negative shift amounts, + which affects bit/part-select assignments with a dynamic index. Shift + operators were not affected. + + * Verific support + - Proper import of port ranges into Yosys, may result in reversed + bit-order of top-level ports for some synthesis flows. + +Yosys 0.18 .. Yosys 0.19 +-------------------------- + * New commands and options + - Added option "-rom-only" to "memory_libmap" pass + - Added option "-smtcheck" to "hierarchy" pass + - Added option "-keepdc" to "memory_libmap" pass + - Added option "-suffix" to "rename" pass + - Added "gatemate_foldinv" pass + + * Formal Verification + - Added support for $pos cell in btor backend + - Added the "smtlib2_module" and "smtlib2_comb_expr" attributes + + * GateMate support + - Added LUT tree mapping + + * Verific support + - Added option "-pp" to "verific -import" + +Yosys 0.17 .. Yosys 0.18 +-------------------------- + * Various + - Migrated most flows to use memory_libmap based memory inference + + * New commands and options + - Added "memory_libmap" pass + - Added "memory_bmux2rom" pass - converts muxes to ROMs + - Added "memory_dff -no-rw-check" + - Added "opt_ffinv" pass - push inverters through FFs + - Added "proc_rom" pass - convert switches to ROMs + - Added "proc -norom" option - will omit the proc_rom pass + - Added option "-no-rw-check" to synth passes + - Added "synth_ice40 -spram" option for automatic inference of SB_SPRAM256KA + - Added options "-nobram" and "-nolutram" to synth_machxo2 pass + + * Formal Verification + - Fixed the signedness of $past's return value to be the same as the + argument's instead of always unsigned. + + * Verilog + - Fixed an issue where simplifying case statements by removing unreachable + cases could result in the wrong signedness being used for comparison with + the remaining cases + - Fixed size and signedness computation for expressions containing array + querying functions + - Fixed size and signedness computation of functions used in ternary + expressions or case item expressions + + * Verific support + - Proper file location for readmem commands + - Added "-vlog-libext" option to specify search extension for libraries + +Yosys 0.16 .. Yosys 0.17 +-------------------------- + * New commands and options + - Added "write_jny" ( JSON netlist metadata format ) + - Added "tribuf -formal" + + * SystemVerilog + - Fixed automatic `nosync` inference for local variables in `always_comb` + procedures not applying to nested blocks and blocks in functions + +Yosys 0.15 .. Yosys 0.16 +-------------------------- + * Various + - Added BTOR2 witness file co-simulation. + - Simulation calls external vcd2fst for VCD conversion. + - Added fst2tb pass - generates testbench for the circuit using + the given top-level module and simulus signal from FST file. + - yosys-smtbmc: Option to keep going after failed assertions in BMC mode + + * Verific support + - Import modules in alphabetic (reproducable) order. + +Yosys 0.14 .. Yosys 0.15 +-------------------------- + + * Various + - clk2fflogic: nice names for autogenerated signals + - simulation include support for all flip-flop types. + - Added AIGER witness file co-simulation. + + * Verilog + - Fixed evaluation of constant functions with variables or arguments with + reversed dimensions + - Fixed elaboration of dynamic range assignments where the vector is + reversed or is not zero-indexed + - Added frontend support for time scale delay values (e.g., `#1ns`) + + * SystemVerilog + - Added support for accessing whole sub-structures in expressions + + * New commands and options + - Added glift command, used to create gate-level information flow tracking + (GLIFT) models by the "constructive mapping" approach + + * Verific support + - Ability to override default parser mode for verific -f command. + +Yosys 0.13 .. Yosys 0.14 +-------------------------- + + * Various + - Added $bmux and $demux cells and related optimization patterns. + + * New commands and options + - Added "bmuxmap" and "dmuxmap" passes + - Added "-fst" option to "sim" pass for writing FST files + - Added "-r", "-scope", "-start", "-stop", "-at", "-sim", "-sim-gate", + "-sim-gold" options to "sim" pass for co-simulation + + * Anlogic support + - Added support for BRAMs + +Yosys 0.12 .. Yosys 0.13 +-------------------------- + + * Various + - Use "read" command to parse HDL files from Yosys command-line + - Added "yosys -r " command line option + - write_verilog: dump zero width sigspecs correctly + + * SystemVerilog + - Fixed regression preventing the use array querying functions in case + expressions and case item expressions + - Fixed static size casts inadvertently limiting the result width of binary + operations + - Fixed static size casts ignoring expression signedness + - Fixed static size casts not extending unbased unsized literals + - Added automatic `nosync` inference for local variables in `always_comb` + procedures which are always assigned before they are used to avoid errant + latch inference + + * New commands and options + - Added "clean_zerowidth" pass + + * Verific support + - Add YOSYS to the implicitly defined verilog macros in verific + +Yosys 0.11 .. Yosys 0.12 +-------------------------- + + * Various + - Added iopadmap native support for negative-polarity output enable + - ABC update + + * SystemVerilog + - Support parameters using struct as a wiretype + + * New commands and options + - Added "-genlib" option to "abc" pass + - Added "sta" very crude static timing analysis pass + + * Verific support + - Fixed memory block size in import + + * New back-ends + - Added support for GateMate FPGA from Cologne Chip AG + + * Intel ALM support + - Added preliminary Arria V support + + +Yosys 0.10 .. Yosys 0.11 +-------------------------- -Yosys 0.9 .. Yosys 0.9-dev + * Various + - Added $aldff and $aldffe (flip-flops with async load) cells + + * SystemVerilog + - Fixed an issue which prevented writing directly to a memory word via a + connection to an output port + - Fixed an issue which prevented unbased unsized literals (e.g., `'1`) from + filling the width of a cell input + - Fixed an issue where connecting a slice covering the entirety of a signed + signal to a cell input would cause a failed assertion + + * Verific support + - Importer support for {PRIM,WIDE_OPER}_DFF + - Importer support for PRIM_BUFIF1 + - Option to use Verific without VHDL support + - Importer support for {PRIM,WIDE_OPER}_DLATCH{,RS} + - Added -cfg option for getting/setting Verific runtime flags + +Yosys 0.9 .. Yosys 0.10 -------------------------- * Various + - Added automatic gzip decompression for frontends + - Added $_NMUX_ cell type + - Added automatic gzip compression (based on filename extension) for backends + - Improve attribute and parameter encoding in JSON to avoid ambiguities between + bit vectors and strings containing [01xz]* + - Improvements in pmgen: subpattern and recursive matches + - Support explicit FIRRTL properties + - Improvements in pmgen: slices, choices, define, generate + - Added "_TECHMAP_WIREINIT_*_" parameter and "_TECHMAP_REMOVEINIT_*_" wire for "techmap" pass + - Added +/mul2dsp.v for decomposing wide multipliers to custom-sized ones + - Added new frontend: rpc + - Added --version and -version as aliases for -V + - Improve yosys-smtbmc "solver not found" handling + - Improved support of $readmem[hb] Memory Content File inclusion + - Added CXXRTL backend + - Use YosysHQ/abc instead of upstream berkeley-abc/abc + - Added WASI platform support. + - Added extmodule support to firrtl backend + - Added $divfloor and $modfloor cells + - Added $adffe, $dffsre, $sdff, $sdffe, $sdffce, $adlatch cells + - Added "_TECHMAP_CELLNAME_" parameter for "techmap" pass + - Added firrtl backend support for generic parameters in blackbox components + - Added $meminit_v2 cells (with support for write mask) + - Added $mem_v2, $memrd_v2, $memwr_v2, with the following features: + - write priority masks, per write/write port pair + - transparency and undefined collision behavior masks, per read/write port pair + - read port reset and initialization + - wide ports (accessing a naturally aligned power-of-two number of memory cells) + + * New commands and options - Added "write_xaiger" backend + - Added "read_xaiger" - Added "abc9" pass for timing-aware techmapping (experimental, FPGA only) - - Added "synth_xilinx -abc9" (experimental) - - Added "synth_ice40 -abc9" (experimental) - Added "synth -abc9" (experimental) - Added "script -scriptwire" + - Added "clkbufmap" pass + - Added "extractinv" pass and "invertible_pin" attribute + - Added "proc_clean -quiet" + - Added "proc_prune" pass + - Added "stat -tech cmos" + - Added "opt_share" pass, run as part of "opt -full" + - Added "-match-init" option to "dff2dffs" pass + - Added "equiv_opt -multiclock" + - Added "techmap_autopurge" support to techmap + - Added "add -mod " + - Added "paramap" pass + - Added "portlist" command + - Added "check -mapped" + - Added "check -allow-tbuf" + - Added "autoname" pass + - Added "write_verilog -extmem" + - Added "opt_mem" pass + - Added "scratchpad" pass + - Added "fminit" pass + - Added "opt_lut_ins" pass + - Added "logger" pass + - Added "show -nobg" + - Added "exec" command + - Added "design -delete" + - Added "design -push-copy" + - Added "qbfsat" command + - Added "select -unset" + - Added "dfflegalize" pass + - Removed "opt_expr -clkinv" option, made it the default + - Added "proc -nomux + - Merged "dffsr2dff", "opt_rmdff", "dff2dffe", "dff2dffs", "peepopt.dffmux" passes into a new "opt_dff" pass + + * SystemVerilog + - Added checking of always block types (always_comb, always_latch and always_ff) + - Added support for wildcard port connections (.*) + - Added support for enum typedefs + - Added support for structs and packed unions. + - Allow constant function calls in for loops and generate if and case + - Added support for static cast + - Added support for logic typed parameters + - Fixed generate scoping issues + - Added support for real-valued parameters + - Allow localparams in constant functions + - Module name scope support + - Support recursive functions using ternary expressions + - Extended support for integer types + - Support for parameters without default values + - Allow globals in one file to depend on globals in another + - Added support for: *=, /=, %=, <<=, >>=, <<<=, >>>= + - Added support for parsing the 'bind' construct + - support declaration in procedural for initialization + - support declaration in generate for initialization + - Support wand and wor of data types + + * Verific support + - Added "verific -L" + - Add Verific SVA support for "always" properties + - Add Verific support for SVA nexttime properties + - Improve handling of verific primitives in "verific -import -V" mode + - Import attributes for wires + - Support VHDL enums + - Added support for command files + + * New back-ends + - Added initial EFINIX support + - Added Intel ALM: alternative synthesis for Intel FPGAs + - Added initial Nexus support + - Added initial MachXO2 support + - Added initial QuickLogic PolarPro 3 support + + * ECP5 support + - Renamed labels/options in synth_ecp5 (e.g. dram -> map_lutram; -nodram -> -nolutram) + - Added "synth_ecp5 -abc9" (experimental) + - Added "synth_ecp5 -nowidelut" + - "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental) + + * iCE40 support + - Added "synth_ice40 -abc9" (experimental) + - Added "synth_ice40 -device" + - Renamed labels/options in synth_ice40 (e.g. dram -> map_lutram; -nodram -> -nolutram) + - Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping + - Removed "ice40_unlut" + - Added "ice40_dsp" for Lattice iCE40 DSP packing + - "synth_ice40 -dsp" to infer DSP blocks + + * Xilinx support + - Added "synth_xilinx -abc9" (experimental) - Added "synth_xilinx -nocarry" - Added "synth_xilinx -nowidelut" - - Added "synth_ecp5 -nowidelut" - "synth_xilinx" to now infer wide multiplexers (-widemux to enable) - - Renamed labels/options in synth_ice40 (e.g. dram -> map_lutram; -nodram -> -nolutram) - - Renamed labels/options in synth_ecp5 (e.g. dram -> map_lutram; -nodram -> -nolutram) - - Renamed labels in synth_intel (e.g. bram -> map_bram) - Renamed labels/options in synth_xilinx (e.g. dram -> map_lutram; -nodram -> -nolutram) - - Added automatic gzip decompression for frontends - - Added $_NMUX_ cell type - - Added automatic gzip compression (based on filename extension) for backends - - Improve attribute and parameter encoding in JSON to avoid ambiguities between - bit vectors and strings containing [01xz]* - - Added "clkbufmap" pass - - Added "extractinv" pass and "invertible_pin" attribute - Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental) - Added "synth_xilinx -ise" (experimental) - Added "synth_xilinx -iopad" - "synth_xilinx" now automatically inserts clock buffers (add -noclkbuf to disable) - - Improvements in pmgen: subpattern and recursive matches - - Added "opt_share" pass, run as part of "opt -full" - - Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping - - Removed "ice40_unlut" - - Improvements in pmgen: slices, choices, define, generate - Added "xilinx_srl" for Xilinx shift register extraction - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl") - - Added "_TECHMAP_WIREINIT_*_" parameter and "_TECHMAP_REMOVEINIT_*_" wire for "techmap" pass - - Added "-match-init" option to "dff2dffs" pass - - Added "techmap_autopurge" support to techmap - - Added "add -mod " - - Added +/mul2dsp.v for decomposing wide multipliers to custom-sized ones - - Added "ice40_dsp" for Lattice iCE40 DSP packing - Added "xilinx_dsp" for Xilinx DSP packing - "synth_xilinx" to now infer DSP blocks (-nodsp to disable) - - "synth_ecp5" to now infer DSP blocks (-nodsp to disable, experimental) - - "synth_ice40 -dsp" to infer DSP blocks - Added latch support to synth_xilinx - Added support for flip-flops with synchronous reset to synth_xilinx - Added support for flip-flops with reset and enable to synth_xilinx - - Added "check -mapped" - - Added checking of SystemVerilog always block types (always_comb, - always_latch and always_ff) - - Added support for SystemVerilog wildcard port connections (.*) - Added "xilinx_dffopt" pass - - Added "scratchpad" pass - Added "synth_xilinx -dff" - - Improved support of $readmem[hb] Memory Content File inclusion - - Added "opt_lut_ins" pass - - Added "logger" pass - - Added "design -delete" - - Added "select -unset" - - Use YosysHQ/abc instead of upstream berkeley-abc/abc - - Added $divfloor and $modfloor cells - - Added $adffe, $dffsre, $sdff, $sdffe, $sdffce, $adlatch cells - - Added "dfflegalize" pass - - Added "_TECHMAP_CELLNAME_" parameter for "techmap" pass - - Merged "dffsr2dff", "opt_rmdff", "dff2dffe", "dff2dffs", "peepopt.dffmux" passes into a new "opt_dff" pass + + * Intel support + - Renamed labels in synth_intel (e.g. bram -> map_bram) + - synth_intel: cyclone10 -> cyclone10lp, a10gx -> arria10gx + - Added "intel_alm -abc9" (experimental) + + * CoolRunner2 support + - Separate and improve buffer cell insertion pass + - Use extract_counter to optimize counters Yosys 0.8 .. Yosys 0.9 ---------------------- diff --git a/CODEOWNERS b/CODEOWNERS index 350a62120b5..7d680e9f2e4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -16,6 +16,9 @@ backends/cxxrtl/ @whitequark passes/cmds/bugpoint.cc @whitequark passes/techmap/flowmap.cc @whitequark passes/opt/opt_lut.cc @whitequark +passes/techmap/abc9*.cc @eddiehung @Ravenslofty +backends/aiger/xaiger.cc @eddiehung +docs/ @KrystalDelusion ## External Contributors @@ -25,7 +28,12 @@ passes/opt/opt_lut.cc @whitequark # These still override previous lines, so be careful not to # accidentally disable any of the above rules. -techlibs/intel_alm/ @ZirconiumX +frontends/verilog/ @zachjs +frontends/ast/ @zachjs + +techlibs/intel_alm/ @Ravenslofty +techlibs/gowin/ @pepijndevos +techlibs/gatemate/ @pu-cc # pyosys misc/*.py @btut @@ -35,4 +43,5 @@ backends/firrtl @ucbjrl @azidar passes/sat/qbfsat.cc @boqwxp passes/sat/qbfsat.h @boqwxp passes/cmds/exec.cc @boqwxp +passes/cmds/glift.cc @boqwxp passes/cmds/printattrs.cc @boqwxp diff --git a/COPYING b/COPYING index 7cd2464cd6f..e8b123be234 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,6 @@ ISC License -Copyright (C) 2012 - 2020 Claire Wolf +Copyright (C) 2012 - 2020 Claire Xenia Wolf Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/Makefile b/Makefile index 97a6370d9ec..776e49a79e8 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,11 @@ CONFIG := clang # CONFIG := gcc -# CONFIG := gcc-4.8 # CONFIG := afl-gcc # CONFIG := emcc # CONFIG := wasi # CONFIG := mxe -# CONFIG := msys2 +# CONFIG := msys2-32 # CONFIG := msys2-64 # features (the more the better) @@ -16,10 +15,14 @@ ENABLE_GLOB := 1 ENABLE_PLUGINS := 1 ENABLE_READLINE := 1 ENABLE_EDITLINE := 0 +ENABLE_GHDL := 0 ENABLE_VERIFIC := 0 +ENABLE_VERIFIC_EDIF := 0 +ENABLE_VERIFIC_LIBERTY := 0 +DISABLE_VERIFIC_EXTENSIONS := 0 +DISABLE_VERIFIC_VHDL := 0 ENABLE_COVER := 1 ENABLE_LIBYOSYS := 0 -ENABLE_PROTOBUF := 0 ENABLE_ZLIB := 1 # python wrappers @@ -31,6 +34,8 @@ ENABLE_GPROF := 0 ENABLE_DEBUG := 0 ENABLE_NDEBUG := 0 ENABLE_CCACHE := 0 +# sccache is not always a drop-in replacement for ccache in practice +ENABLE_SCCACHE := 0 LINK_CURSES := 0 LINK_TERMCAP := 0 LINK_ABC := 0 @@ -51,6 +56,9 @@ PROGRAM_PREFIX := OS := $(shell uname -s) PREFIX ?= /usr/local INSTALL_SUDO := +ifneq ($(filter MINGW%,$(OS)),) +OS := MINGW +endif ifneq ($(wildcard Makefile.conf),) include Makefile.conf @@ -82,9 +90,17 @@ all: top-all YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST))) VPATH := $(YOSYS_SRC) +CXXSTD ?= c++11 CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include -LDLIBS := $(LDLIBS) -lstdc++ -lm -PLUGIN_LDFLAGS := +LIBS := $(LIBS) -lstdc++ -lm +PLUGIN_LINKFLAGS := +PLUGIN_LIBS := +EXE_LINKFLAGS := +ifeq ($(OS), MINGW) +EXE_LINKFLAGS := -Wl,--export-all-symbols -Wl,--out-implib,libyosys_exe.a +PLUGIN_LINKFLAGS += -L"$(LIBDIR)" +PLUGIN_LIBS := -lyosys_exe +endif PKG_CONFIG ?= pkg-config SED ?= sed @@ -93,7 +109,7 @@ STRIP ?= strip AWK ?= awk ifeq ($(OS), Darwin) -PLUGIN_LDFLAGS += -undefined dynamic_lookup +PLUGIN_LINKFLAGS += -undefined dynamic_lookup # homebrew search paths ifneq ($(shell :; command -v brew),) @@ -101,10 +117,10 @@ BREW_PREFIX := $(shell brew --prefix)/opt $(info $$BREW_PREFIX is [${BREW_PREFIX}]) ifeq ($(ENABLE_PYOSYS),1) CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost -LDFLAGS += -L$(BREW_PREFIX)/boost/lib +LINKFLAGS += -L$(BREW_PREFIX)/boost/lib endif CXXFLAGS += -I$(BREW_PREFIX)/readline/include -LDFLAGS += -L$(BREW_PREFIX)/readline/lib +LINKFLAGS += -L$(BREW_PREFIX)/readline/lib PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH) PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH) export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH) @@ -113,22 +129,35 @@ export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX else ifneq ($(shell :; command -v port),) PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port)) CXXFLAGS += -I$(PORT_PREFIX)/include -LDFLAGS += -L$(PORT_PREFIX)/lib +LINKFLAGS += -L$(PORT_PREFIX)/lib PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) export PATH := $(PORT_PREFIX)/bin:$(PATH) endif else -LDFLAGS += -rdynamic -LDLIBS += -lrt +LINKFLAGS += -rdynamic +ifneq ($(OS), OpenBSD) +LIBS += -lrt +endif +endif + +YOSYS_VER := 0.39+124 + +# Note: We arrange for .gitcommit to contain the (short) commit hash in +# tarballs generated with git-archive(1) using .gitattributes. The git repo +# will have this file in its unexpanded form tough, in which case we fall +# back to calling git directly. +TARBALL_GIT_REV := $(shell cat $(YOSYS_SRC)/.gitcommit) +ifeq ($(TARBALL_GIT_REV),$$Format:%h$$) +GIT_REV := $(shell GIT_DIR=$(YOSYS_SRC)/.git git rev-parse --short=9 HEAD || echo UNKNOWN) +else +GIT_REV := $(TARBALL_GIT_REV) endif -YOSYS_VER := 0.9+3683 -GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) OBJS = kernel/version_$(GIT_REV).o bumpversion: - sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8a4c6e6.. | wc -l`/;" Makefile + sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 0033808.. | wc -l`/;" Makefile # set 'ABCREV = default' to use abc/ as it is # @@ -136,10 +165,10 @@ bumpversion: # is just a symlink to your actual ABC working directory, as 'make mrproper' # will remove the 'abc' directory and you do not want to accidentally # delete your work on ABC.. -ABCREV = 341db25 +ABCREV = 0cd90d0 ABCPULL = 1 ABCURL ?= https://github.com/YosysHQ/abc -ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 +ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABC_USE_NAMESPACE=abc VERBOSE=$(Q) # set ABCEXTERNAL = to use an external ABC instance # Note: The in-tree ABC (yosys-abc) will not be installed when ABCEXTERNAL is set. @@ -180,79 +209,73 @@ endif endif +ABC_ARCHFLAGS = "" +ifeq ($(OS), OpenBSD) +ABC_ARCHFLAGS += "-DABC_NO_RLIMIT" +endif + ifeq ($(CONFIG),clang) -CXX = clang -LD = clang++ -CXXFLAGS += -std=c++11 -Os -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" +CXX = clang++ +CXXFLAGS += -std=$(CXXSTD) -Os +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -Wno-c++11-narrowing $(ABC_ARCHFLAGS)" ifneq ($(SANITIZER),) $(info [Clang Sanitizer] $(SANITIZER)) CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER) -LDFLAGS += -g -fsanitize=$(SANITIZER) -ifeq ($(SANITIZER),address) +LINKFLAGS += -g -fsanitize=$(SANITIZER) +ifneq ($(findstring address,$(SANITIZER)),) ENABLE_COVER := 0 endif -ifeq ($(SANITIZER),memory) +ifneq ($(findstring memory,$(SANITIZER)),) CXXFLAGS += -fPIE -fsanitize-memory-track-origins -LDFLAGS += -fPIE -fsanitize-memory-track-origins +LINKFLAGS += -fPIE -fsanitize-memory-track-origins endif -ifeq ($(SANITIZER),cfi) +ifneq ($(findstring cfi,$(SANITIZER)),) CXXFLAGS += -flto -LDFLAGS += -flto +LINKFLAGS += -flto endif endif else ifeq ($(CONFIG),gcc) -CXX = gcc -LD = gcc -CXXFLAGS += -std=c++11 -Os -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" +CXX = g++ +CXXFLAGS += -std=$(CXXSTD) -Os +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" else ifeq ($(CONFIG),gcc-static) -LD = $(CXX) -LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static -LDLIBS := $(filter-out -lrt,$(LDLIBS)) +LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -static +LIBS := $(filter-out -lrt,$(LIBS)) CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) -CXXFLAGS += -std=c++11 -Os -ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(LD)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \ +CXXFLAGS += -std=$(CXXSTD) -Os +ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(CXX)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \ ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1 ifeq ($(DISABLE_ABC_THREADS),1) ABCMKARGS += "ABC_USE_NO_PTHREADS=1" endif -else ifeq ($(CONFIG),gcc-4.8) -CXX = gcc-4.8 -LD = gcc-4.8 -CXXFLAGS += -std=c++11 -Os -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" - else ifeq ($(CONFIG),afl-gcc) CXX = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc -LD = AFL_QUIET=1 AFL_HARDEN=1 afl-gcc -CXXFLAGS += -std=c++11 -Os +CXXFLAGS += -std=$(CXXSTD) -Os ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" else ifeq ($(CONFIG),cygwin) -CXX = gcc -LD = gcc +CXX = g++ CXXFLAGS += -std=gnu++11 -Os ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" else ifeq ($(CONFIG),emcc) CXX = emcc -LD = emcc -CXXFLAGS := -std=c++11 $(filter-out -fPIC -ggdb,$(CXXFLAGS)) -ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8" -EMCCFLAGS := -Os -Wno-warn-absolute-paths -EMCCFLAGS += --memory-init-file 0 --embed-file share -s NO_EXIT_RUNTIME=1 -EMCCFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']" -EMCCFLAGS += -s TOTAL_MEMORY=134217728 -EMCCFLAGS += -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' +CXXFLAGS := -std=$(CXXSTD) $(filter-out -fPIC -ggdb,$(CXXFLAGS)) +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8 -Wno-c++11-narrowing" +EMCC_CXXFLAGS := -Os -Wno-warn-absolute-paths +EMCC_LINKFLAGS := --embed-file share +EMCC_LINKFLAGS += -s NO_EXIT_RUNTIME=1 +EMCC_LINKFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg','_memset']" +EMCC_LINKFLAGS += -s TOTAL_MEMORY=134217728 +EMCC_LINKFLAGS += -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' # https://github.com/kripken/emscripten/blob/master/src/settings.js -CXXFLAGS += $(EMCCFLAGS) -LDFLAGS += $(EMCCFLAGS) -LDLIBS = +CXXFLAGS += $(EMCC_CXXFLAGS) +LINKFLAGS += $(EMCC_LINKFLAGS) +LIBS = EXE = .js DISABLE_SPAWN := 1 @@ -269,7 +292,7 @@ viz.js: wget -O viz.js.part https://github.com/mdaines/viz.js/releases/download/0.0.3/viz.js mv viz.js.part viz.js -yosysjs-$(YOSYS_VER).zip: yosys.js yosys.wasm viz.js misc/yosysjs/* +yosysjs-$(YOSYS_VER).zip: yosys.js viz.js misc/yosysjs/* rm -rf yosysjs-$(YOSYS_VER) yosysjs-$(YOSYS_VER).zip mkdir -p yosysjs-$(YOSYS_VER) cp viz.js misc/yosysjs/* yosys.js yosys.wasm yosysjs-$(YOSYS_VER)/ @@ -280,23 +303,21 @@ yosys.html: misc/yosys.html else ifeq ($(CONFIG),wasi) ifeq ($(WASI_SDK),) -CXX = clang -LD = clang++ +CXX = clang++ AR = llvm-ar RANLIB = llvm-ranlib WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS) else -CXX = $(WASI_SDK)/bin/clang -LD = $(WASI_SDK)/bin/clang++ +CXX = $(WASI_SDK)/bin/clang++ AR = $(WASI_SDK)/bin/ar RANLIB = $(WASI_SDK)/bin/ranlib WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS) endif -CXXFLAGS := $(WASIFLAGS) -std=c++11 -Os $(filter-out -fPIC,$(CXXFLAGS)) -LDFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LDFLAGS)) -LDLIBS := $(filter-out -lrt,$(LDLIBS)) +CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS)) +LINKFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LINKFLAGS)) +LIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LIBS)) ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)" -ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING" +ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing" ABCMKARGS += OPTFLAGS="-Os" EXE = .wasm @@ -310,40 +331,37 @@ endif else ifeq ($(CONFIG),mxe) PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ -LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ -CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes +CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) -LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s -LDLIBS := $(filter-out -lrt,$(LDLIBS)) +LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s +LIBS := $(filter-out -lrt,$(LIBS)) ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" # TODO: Try to solve pthread linking issue in more appropriate way -ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LDFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc" +ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" LINKFLAGS="-Wl,--allow-multiple-definition" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc" EXE = .exe -else ifeq ($(CONFIG),msys2) +else ifeq ($(CONFIG),msys2-32) CXX = i686-w64-mingw32-g++ -LD = i686-w64-mingw32-g++ -CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR +CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) -LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s -LDLIBS := $(filter-out -lrt,$(LDLIBS)) +LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s +LIBS := $(filter-out -lrt,$(LIBS)) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" -ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)" +ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)" EXE = .exe else ifeq ($(CONFIG),msys2-64) CXX = x86_64-w64-mingw32-g++ -LD = x86_64-w64-mingw32-g++ -CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR +CXXFLAGS += -std=$(CXXSTD) -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) -LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s -LDLIBS := $(filter-out -lrt,$(LDLIBS)) +LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s +LIBS := $(filter-out -lrt,$(LIBS)) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" -ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)" +ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)" EXE = .exe else ifneq ($(CONFIG),none) -$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.8, emcc, mxe, msys2, msys2-64) +$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, emcc, mxe, msys2-32, msys2-64) endif ifeq ($(ENABLE_LIBYOSYS),1) @@ -351,75 +369,51 @@ TARGETS += libyosys.so endif ifeq ($(ENABLE_PYOSYS),1) - -#Detect name of boost_python library. Some distros usbe boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers -ifeq ($(OS), Darwin) +# Detect name of boost_python library. Some distros use boost_python-py, other boost_python, some only use the major version number, some a concatenation of major and minor version numbers +CHECK_BOOST_PYTHON = (echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") BOOST_PYTHON_LIB ?= $(shell \ - if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \ - if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \ - if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \ - if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null $(shell $(PYTHON_CONFIG) --ldflags) -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \ - echo ""; fi; fi; fi; fi;) -else -BOOST_PYTHON_LIB ?= $(shell \ - if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python-py$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_VERSION))"; else \ - if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python-py$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \ - if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python$(subst .,,$(PYTHON_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_VERSION))"; else \ - if echo "int main(int argc, char ** argv) {return 0;}" | $(CXX) -xc -o /dev/null `$(PYTHON_CONFIG) --libs` -lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION)) - > /dev/null 2>&1; then echo "-lboost_python$(subst .,,$(PYTHON_MAJOR_VERSION))"; else \ - echo ""; fi; fi; fi; fi;) -endif + $(call CHECK_BOOST_PYTHON,boost_python-py$(subst .,,$(PYTHON_VERSION))) || \ + $(call CHECK_BOOST_PYTHON,boost_python-py$(PYTHON_MAJOR_VERSION)) || \ + $(call CHECK_BOOST_PYTHON,boost_python$(subst .,,$(PYTHON_VERSION))) || \ + $(call CHECK_BOOST_PYTHON,boost_python$(PYTHON_MAJOR_VERSION)) \ +) ifeq ($(BOOST_PYTHON_LIB),) $(error BOOST_PYTHON_LIB could not be detected. Please define manually) endif -ifeq ($(OS), Darwin) -ifeq ($(PYTHON_MAJOR_VERSION),3) -LDLIBS += $(shell $(PYTHON_CONFIG) --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem -CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON -else -LDLIBS += $(shell $(PYTHON_CONFIG) --ldflags) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem -CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON -endif -else -ifeq ($(PYTHON_MAJOR_VERSION),3) -LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem -CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON -else -LDLIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem +LIBS += $(shell $(PYTHON_CONFIG) --libs) $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem +# python-config --ldflags includes LIBS for some reason +LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON -endif -endif -ifeq ($(ENABLE_PYOSYS),1) PY_WRAPPER_FILE = kernel/python_wrappers OBJS += $(PY_WRAPPER_FILE).o PY_GEN_SCRIPT= py_wrap_generator PY_WRAP_INCLUDES := $(shell python$(PYTHON_VERSION) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()") -endif -endif +endif # ENABLE_PYOSYS ifeq ($(ENABLE_READLINE),1) CXXFLAGS += -DYOSYS_ENABLE_READLINE -ifeq ($(OS), FreeBSD) +ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) CXXFLAGS += -I/usr/local/include endif -LDLIBS += -lreadline +LIBS += -lreadline ifeq ($(LINK_CURSES),1) -LDLIBS += -lcurses +LIBS += -lcurses ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline" endif ifeq ($(LINK_TERMCAP),1) -LDLIBS += -ltermcap +LIBS += -ltermcap ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap" endif ifeq ($(CONFIG),mxe) -LDLIBS += -ltermcap +LIBS += -ltermcap endif else ifeq ($(ENABLE_EDITLINE),1) CXXFLAGS += -DYOSYS_ENABLE_EDITLINE -LDLIBS += -ledit -ltinfo -lbsd +LIBS += -ledit -ltinfo -lbsd else ABCMKARGS += "ABC_USE_NO_READLINE=1" endif @@ -435,9 +429,12 @@ endif ifeq ($(ENABLE_PLUGINS),1) CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS -LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ifneq ($(OS), FreeBSD) -LDLIBS += -ldl +ifeq ($(OS), MINGW) +CXXFLAGS += -Ilibs/dlfcn-win32 +endif +LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) +ifneq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD MINGW)) +LIBS += -ldl endif endif @@ -447,40 +444,38 @@ endif ifeq ($(ENABLE_ZLIB),1) CXXFLAGS += -DYOSYS_ENABLE_ZLIB -LDLIBS += -lz +LIBS += -lz endif ifeq ($(ENABLE_TCL),1) TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')") -ifeq ($(OS), FreeBSD) +ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) +# BSDs usually use tcl8.6, but the lib is named "libtcl86" TCL_INCLUDE ?= /usr/local/include/$(TCL_VERSION) +TCL_LIBS ?= -l$(subst .,,$(TCL_VERSION)) else TCL_INCLUDE ?= /usr/include/$(TCL_VERSION) +TCL_LIBS ?= -l$(TCL_VERSION) endif ifeq ($(CONFIG),mxe) CXXFLAGS += -DYOSYS_ENABLE_TCL -LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv +LIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz -luserenv else CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL -ifeq ($(OS), FreeBSD) -# FreeBSD uses tcl8.6, but lib is named "libtcl86" -LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo -l$(TCL_VERSION) | tr -d '.') -else -LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo -l$(TCL_VERSION)) -endif +LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS)) endif endif ifeq ($(ENABLE_GCOV),1) CXXFLAGS += --coverage -LDFLAGS += --coverage +LINKFLAGS += --coverage endif ifeq ($(ENABLE_GPROF),1) CXXFLAGS += -pg -LDFLAGS += -pg +LINKFLAGS += -pg endif ifeq ($(ENABLE_NDEBUG),1) @@ -500,7 +495,7 @@ CXXFLAGS += -DYOSYS_ENABLE_ABC ifeq ($(LINK_ABC),1) CXXFLAGS += -DYOSYS_LINK_ABC ifeq ($(DISABLE_ABC_THREADS),0) -LDLIBS += -lpthread +LIBS += -lpthread endif else ifeq ($(ABCEXTERNAL),) @@ -509,20 +504,46 @@ endif endif endif +ifeq ($(ENABLE_GHDL),1) +GHDL_PREFIX ?= $(PREFIX) +GHDL_INCLUDE_DIR ?= $(GHDL_PREFIX)/include +GHDL_LIB_DIR ?= $(GHDL_PREFIX)/lib +CXXFLAGS += -I$(GHDL_INCLUDE_DIR) -DYOSYS_ENABLE_GHDL +LIBS += $(GHDL_LIB_DIR)/libghdl.a $(file <$(GHDL_LIB_DIR)/libghdl.link) +endif + +LIBS_VERIFIC = ifeq ($(ENABLE_VERIFIC),1) VERIFIC_DIR ?= /usr/local/src/verific_lib -VERIFIC_COMPONENTS ?= verilog vhdl database util containers hier_tree +VERIFIC_COMPONENTS ?= verilog database util containers hier_tree +ifneq ($(DISABLE_VERIFIC_VHDL),1) +VERIFIC_COMPONENTS += vhdl +CXXFLAGS += -DVERIFIC_VHDL_SUPPORT +else +ifneq ($(wildcard $(VERIFIC_DIR)/vhdl),) +VERIFIC_COMPONENTS += vhdl +endif +endif +ifeq ($(ENABLE_VERIFIC_EDIF),1) +VERIFIC_COMPONENTS += edif +CXXFLAGS += -DVERIFIC_EDIF_SUPPORT +endif +ifeq ($(ENABLE_VERIFIC_LIBERTY),1) +VERIFIC_COMPONENTS += synlib +CXXFLAGS += -DVERIFIC_LIBERTY_SUPPORT +endif +ifneq ($(DISABLE_VERIFIC_EXTENSIONS),1) +VERIFIC_COMPONENTS += extensions +CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS +endif CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC ifeq ($(OS), Darwin) -LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)) -lz +LIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz else -LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -lz +LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz endif endif -ifeq ($(ENABLE_PROTOBUF),1) -LDLIBS += $(shell pkg-config --cflags --libs protobuf) -endif ifeq ($(ENABLE_COVER),1) CXXFLAGS += -DYOSYS_ENABLE_COVER @@ -530,6 +551,10 @@ endif ifeq ($(ENABLE_CCACHE),1) CXX := ccache $(CXX) +else +ifeq ($(ENABLE_SCCACHE),1) +CXX := sccache $(CXX) +endif endif define add_share_file @@ -570,54 +595,76 @@ Q = S = endif -$(eval $(call add_include_file,kernel/yosys.h)) -$(eval $(call add_include_file,kernel/hashlib.h)) -$(eval $(call add_include_file,kernel/log.h)) -$(eval $(call add_include_file,kernel/rtlil.h)) -$(eval $(call add_include_file,kernel/register.h)) -$(eval $(call add_include_file,kernel/celltypes.h)) +$(eval $(call add_include_file,kernel/binding.h)) +$(eval $(call add_include_file,kernel/cellaigs.h)) $(eval $(call add_include_file,kernel/celledges.h)) +$(eval $(call add_include_file,kernel/celltypes.h)) $(eval $(call add_include_file,kernel/consteval.h)) $(eval $(call add_include_file,kernel/constids.inc)) -$(eval $(call add_include_file,kernel/sigtools.h)) -$(eval $(call add_include_file,kernel/modtools.h)) -$(eval $(call add_include_file,kernel/macc.h)) -$(eval $(call add_include_file,kernel/utils.h)) -$(eval $(call add_include_file,kernel/satgen.h)) +$(eval $(call add_include_file,kernel/cost.h)) $(eval $(call add_include_file,kernel/ff.h)) $(eval $(call add_include_file,kernel/ffinit.h)) +$(eval $(call add_include_file,kernel/ffmerge.h)) +$(eval $(call add_include_file,kernel/fmt.h)) +ifeq ($(ENABLE_ZLIB),1) +$(eval $(call add_include_file,kernel/fstdata.h)) +endif +$(eval $(call add_include_file,kernel/hashlib.h)) +$(eval $(call add_include_file,kernel/json.h)) +$(eval $(call add_include_file,kernel/log.h)) +$(eval $(call add_include_file,kernel/macc.h)) +$(eval $(call add_include_file,kernel/modtools.h)) $(eval $(call add_include_file,kernel/mem.h)) +$(eval $(call add_include_file,kernel/qcsat.h)) +$(eval $(call add_include_file,kernel/register.h)) +$(eval $(call add_include_file,kernel/rtlil.h)) +$(eval $(call add_include_file,kernel/satgen.h)) +$(eval $(call add_include_file,kernel/scopeinfo.h)) +$(eval $(call add_include_file,kernel/sigtools.h)) +$(eval $(call add_include_file,kernel/timinginfo.h)) +$(eval $(call add_include_file,kernel/utils.h)) +$(eval $(call add_include_file,kernel/yosys.h)) +$(eval $(call add_include_file,kernel/yw.h)) $(eval $(call add_include_file,libs/ezsat/ezsat.h)) $(eval $(call add_include_file,libs/ezsat/ezminisat.h)) +ifeq ($(ENABLE_ZLIB),1) +$(eval $(call add_include_file,libs/fst/fstapi.h)) +endif $(eval $(call add_include_file,libs/sha1/sha1.h)) $(eval $(call add_include_file,libs/json11/json11.hpp)) $(eval $(call add_include_file,passes/fsm/fsmdata.h)) $(eval $(call add_include_file,frontends/ast/ast.h)) +$(eval $(call add_include_file,frontends/ast/ast_binding.h)) +$(eval $(call add_include_file,frontends/blif/blifparse.h)) $(eval $(call add_include_file,backends/rtlil/rtlil_backend.h)) -$(eval $(call add_include_file,backends/cxxrtl/cxxrtl.h)) -$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd.h)) -$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_capi.cc)) -$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_capi.h)) -$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.cc)) -$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.h)) OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o -OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/mem.o +OBJS += kernel/binding.o +OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o +ifeq ($(ENABLE_ZLIB),1) +OBJS += kernel/fstdata.o +endif +ifeq ($(ENABLE_PLUGINS),1) +ifeq ($(OS), MINGW) +OBJS += libs/dlfcn-win32/dlfcn.o +endif +endif kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"' kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"' -DYOSYS_PROGRAM_PREFIX='"$(PROGRAM_PREFIX)"' +ifeq ($(ENABLE_ABC),1) +ifneq ($(ABCEXTERNAL),) +kernel/yosys.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' +endif +endif OBJS += libs/bigint/BigIntegerAlgorithms.o libs/bigint/BigInteger.o libs/bigint/BigIntegerUtils.o OBJS += libs/bigint/BigUnsigned.o libs/bigint/BigUnsignedInABase.o OBJS += libs/sha1/sha1.o -ifneq ($(SMALL),1) - OBJS += libs/json11/json11.o -OBJS += libs/subcircuit/subcircuit.o - OBJS += libs/ezsat/ezsat.o OBJS += libs/ezsat/ezminisat.o @@ -626,6 +673,16 @@ OBJS += libs/minisat/SimpSolver.o OBJS += libs/minisat/Solver.o OBJS += libs/minisat/System.o +ifeq ($(ENABLE_ZLIB),1) +OBJS += libs/fst/fstapi.o +OBJS += libs/fst/fastlz.o +OBJS += libs/fst/lz4.o +endif + +ifneq ($(SMALL),1) + +OBJS += libs/subcircuit/subcircuit.o + include $(YOSYS_SRC)/frontends/*/Makefile.inc include $(YOSYS_SRC)/passes/*/Makefile.inc include $(YOSYS_SRC)/backends/*/Makefile.inc @@ -634,6 +691,9 @@ include $(YOSYS_SRC)/techlibs/*/Makefile.inc else include $(YOSYS_SRC)/frontends/verilog/Makefile.inc +ifeq ($(ENABLE_VERIFIC),1) +include $(YOSYS_SRC)/frontends/verific/Makefile.inc +endif include $(YOSYS_SRC)/frontends/rtlil/Makefile.inc include $(YOSYS_SRC)/frontends/ast/Makefile.inc include $(YOSYS_SRC)/frontends/blif/Makefile.inc @@ -675,13 +735,13 @@ yosys.js: $(filter-out yosysjs-$(YOSYS_VER).zip,$(EXTRA_TARGETS)) endif $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) - $(P) $(LD) -o $(PROGRAM_PREFIX)yosys$(EXE) $(LDFLAGS) $(OBJS) $(LDLIBS) + $(P) $(CXX) -o $(PROGRAM_PREFIX)yosys$(EXE) $(EXE_LINKFLAGS) $(LINKFLAGS) $(OBJS) $(LIBS) $(LIBS_VERIFIC) libyosys.so: $(filter-out kernel/driver.o,$(OBJS)) ifeq ($(OS), Darwin) - $(P) $(LD) -o libyosys.so -shared -Wl,-install_name,$(DESTDIR)$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) + $(P) $(CXX) -o libyosys.so -shared -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) else - $(P) $(LD) -o libyosys.so -shared -Wl,-soname,$(DESTDIR)$(LIBDIR)/libyosys.so $(LDFLAGS) $^ $(LDLIBS) + $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) endif %.o: %.cc @@ -690,7 +750,7 @@ endif %.pyh: %.h $(Q) mkdir -p $(dir $@) - $(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(LD) $(CXXFLAGS) -x c++ -o $@ -E -P - + $(P) cat $< | grep -E -v "#[ ]*(include|error)" | $(CXX) $(CXXFLAGS) -x c++ -o $@ -E -P - ifeq ($(ENABLE_PYOSYS),1) $(PY_WRAPPER_FILE).cc: misc/$(PY_GEN_SCRIPT).py $(PY_WRAP_INCLUDES) @@ -711,15 +771,15 @@ kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile ifeq ($(ENABLE_VERIFIC),1) CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) -LDLIBS_NOVERIFIC = $(foreach v,$(LDLIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) +LIBS_NOVERIFIC = $(foreach v,$(LIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) else CXXFLAGS_NOVERIFIC = $(CXXFLAGS) -LDLIBS_NOVERIFIC = $(LDLIBS) +LIBS_NOVERIFIC = $(LIBS) endif $(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in - $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC)))#;' \ - -e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC))#;' \ + $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -Ilibs/dlfcn-win32,,$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC))))#;' \ + -e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LINKFLAGS@#$(strip $(LINKFLAGS) $(PLUGIN_LINKFLAGS))#;' -e 's#@LIBS@#$(strip $(LIBS_NOVERIFIC) $(PLUGIN_LIBS))#;' \ -e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > $(PROGRAM_PREFIX)yosys-config $(Q) chmod +x $(PROGRAM_PREFIX)yosys-config @@ -729,11 +789,16 @@ ifneq ($(ABCREV),default) $(Q) if test -d abc/.hg; then \ echo 'REEBE: NOP qverpgbel vf n ut jbexvat pbcl! Erzbir nop/ naq er-eha "znxr".' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ fi - $(Q) if ( cd abc 2> /dev/null && ! git diff-index --quiet HEAD; ); then \ + $(Q) if test -d abc && test -d abc/.git && ! git -C abc diff-index --quiet HEAD; then \ echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ fi + $(Q) if test -d abc && ! test -d abc/.git && ! test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \ + echo 'REEBE: Qbjaybnqrq NOP irefvbaf qbrf abg zngpu! Qbjaybnq sebz:' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; echo $(ABCURL)/archive/$(ABCREV).tar.gz; false; \ + fi # set a variable so the test fails if git fails to run - when comparing outputs directly, empty string would match empty string - $(Q) if ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" == "$$rev"); then \ + $(Q) if test -d abc && ! test -d abc/.git && test "`cat abc/.gitcommit | cut -c1-7`" = "$(ABCREV)"; then \ + echo "Compiling local copy of ABC"; \ + elif ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" = "$$rev"); then \ test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \ echo "Pulling ABC from $(ABCURL):"; set -x; \ test -d abc || git clone $(ABCURL) abc; \ @@ -741,7 +806,7 @@ ifneq ($(ABCREV),default) fi endif $(Q) rm -f abc/abc-[0-9a-f]* - $(Q) cd abc && $(MAKE) $(S) $(ABCMKARGS) $(if $(filter %.a,$@),PROG="abc-$(ABCREV)",PROG="abc-$(ABCREV)$(EXE)") MSG_PREFIX="$(eval P_OFFSET = 5)$(call P_SHOW)$(eval P_OFFSET = 10) ABC: " $(if $(filter %.a,$@),libabc-$(ABCREV).a) + $(Q) $(MAKE) -C abc $(S) $(ABCMKARGS) $(if $(filter %.a,$@),PROG="abc-$(ABCREV)",PROG="abc-$(ABCREV)$(EXE)") MSG_PREFIX="$(eval P_OFFSET = 5)$(call P_SHOW)$(eval P_OFFSET = 10) ABC: " $(if $(filter %.a,$@),libabc-$(ABCREV).a) ifeq ($(ABCREV),default) .PHONY: abc/abc-$(ABCREV)$(EXE) @@ -766,7 +831,23 @@ else ABCOPT="" endif +# When YOSYS_NOVERIFIC is set as a make variable, also export it to the +# enviornment, so that `YOSYS_NOVERIFIC=1 make test` _and_ +# `make test YOSYS_NOVERIFIC=1` will run with verific disabled. +ifeq ($(YOSYS_NOVERIFIC),1) +export YOSYS_NOVERIFIC +endif + test: $(TARGETS) $(EXTRA_TARGETS) +ifeq ($(ENABLE_VERIFIC),1) +ifeq ($(YOSYS_NOVERIFIC),1) + @echo + @echo "Running tests without verific support due to YOSYS_NOVERIFIC=1" + @echo +else + +cd tests/verific && bash run-test.sh $(SEEDOPT) +endif +endif +cd tests/simple && bash run-test.sh $(SEEDOPT) +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT) +cd tests/hana && bash run-test.sh $(SEEDOPT) @@ -777,27 +858,37 @@ test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/fsm && bash run-test.sh $(SEEDOPT) +cd tests/techmap && bash run-test.sh +cd tests/memories && bash run-test.sh $(ABCOPT) $(SEEDOPT) + +cd tests/memlib && bash run-test.sh $(SEEDOPT) +cd tests/bram && bash run-test.sh $(SEEDOPT) +cd tests/various && bash run-test.sh +cd tests/select && bash run-test.sh +cd tests/sat && bash run-test.sh + +cd tests/sim && bash run-test.sh +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) +cd tests/svtypes && bash run-test.sh $(SEEDOPT) +cd tests/proc && bash run-test.sh + +cd tests/blif && bash run-test.sh +cd tests/opt && bash run-test.sh +cd tests/aiger && bash run-test.sh $(ABCOPT) +cd tests/arch && bash run-test.sh +cd tests/arch/ice40 && bash run-test.sh $(SEEDOPT) +cd tests/arch/xilinx && bash run-test.sh $(SEEDOPT) +cd tests/arch/ecp5 && bash run-test.sh $(SEEDOPT) + +cd tests/arch/machxo2 && bash run-test.sh $(SEEDOPT) +cd tests/arch/efinix && bash run-test.sh $(SEEDOPT) +cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT) +cd tests/arch/gowin && bash run-test.sh $(SEEDOPT) +cd tests/arch/intel_alm && bash run-test.sh $(SEEDOPT) +cd tests/arch/nexus && bash run-test.sh $(SEEDOPT) + +cd tests/arch/quicklogic/pp3 && bash run-test.sh $(SEEDOPT) + +cd tests/arch/quicklogic/qlf_k6n10f && bash run-test.sh $(SEEDOPT) + +cd tests/arch/gatemate && bash run-test.sh $(SEEDOPT) +cd tests/rpc && bash run-test.sh +cd tests/memfile && bash run-test.sh +cd tests/verilog && bash run-test.sh + +cd tests/xprop && bash run-test.sh $(SEEDOPT) + +cd tests/fmt && bash run-test.sh + +cd tests/cxxrtl && bash run-test.sh @echo "" @echo " Passed \"make test\"." @echo "" @@ -827,7 +918,7 @@ ystests: $(TARGETS) $(EXTRA_TARGETS) # Unit test unit-test: libyosys.so @$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \ - CXXFLAGS="$(CXXFLAGS)" LDLIBS="$(LDLIBS)" ROOTPATH="$(CURDIR)" + CXXFLAGS="$(CXXFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)" clean-unit-test: @$(MAKE) -C $(UNITESTPATH) clean @@ -856,6 +947,12 @@ ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) cp misc/__init__.py $(DESTDIR)$(PYTHON_DESTDIR)/$(subst -,_,$(PROGRAM_PREFIX))pyosys/ endif endif +ifeq ($(ENABLE_PLUGINS),1) +ifeq ($(OS), MINGW) + $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR) + $(INSTALL_SUDO) cp libyosys_exe.a $(DESTDIR)$(LIBDIR)/ +endif +endif uninstall: $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR)/,$(notdir $(TARGETS))) @@ -869,18 +966,40 @@ ifeq ($(ENABLE_PYOSYS),1) endif endif -update-manual: $(TARGETS) $(EXTRA_TARGETS) - cd manual && ../$(PROGRAM_PREFIX)yosys -p 'help -write-tex-command-reference-manual' +# also others, but so long as it doesn't fail this is enough to know we tried +docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) + mkdir -p docs/source/cmd + ./$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' + +PHONY: docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs +docs/gen_examples: + $(Q) $(MAKE) -C docs examples + +docs/gen_images: + $(Q) $(MAKE) -C docs images -manual: $(TARGETS) $(EXTRA_TARGETS) - cd manual && bash appnotes.sh - cd manual && bash presentation.sh - cd manual && bash manual.sh +DOCS_GUIDELINE_FILES := GettingStarted CodingStyle +docs/guidelines: + $(Q) mkdir -p docs/source/temp + $(Q) cp -f $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) docs/source/temp + +# many of these will return an error which can be safely ignored, so we prefix +# the command with a '-' +DOCS_USAGE_PROGS := yosys yosys-config yosys-filterlib yosys-abc yosys-smtbmc yosys-witness +docs/usage: $(addprefix docs/source/temp/,$(DOCS_USAGE_PROGS)) +docs/source/temp/%: docs/guidelines + -$(Q) ./$(PROGRAM_PREFIX)$* --help > $@ 2>&1 + +docs/reqs: + $(Q) $(MAKE) -C docs reqs + +DOC_TARGET ?= html +docs: docs/source/cmd/abc.rst docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs + $(Q) $(MAKE) -C docs $(DOC_TARGET) clean: rm -rf share rm -rf kernel/*.pyh - if test -d manual; then cd manual && sh clean.sh; fi rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) $(PY_WRAP_INCLUDES) $(PY_WRAPPER_FILE).cc rm -f kernel/version_*.o kernel/version_*.cc rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d @@ -889,10 +1008,11 @@ clean: rm -rf tests/simple/*.out tests/simple/*.log rm -rf tests/memories/*.out tests/memories/*.log tests/memories/*.dmp rm -rf tests/sat/*.log tests/techmap/*.log tests/various/*.log - rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp + rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp tests/various/temp rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_* rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff rm -f tests/tools/cmp_tbdata + $(MAKE) -C docs clean clean-abc: $(MAKE) -C abc DEP= clean @@ -933,7 +1053,7 @@ ifeq ($(ENABLE_ABC),1) cp -r $(PROGRAM_PREFIX)yosys-abc.exe abc/lib/x86/pthreadVC2.dll yosys-win32-mxebin-$(YOSYS_VER)/ endif echo -en 'This is Yosys $(YOSYS_VER) for Win32.\r\n' > yosys-win32-mxebin-$(YOSYS_VER)/readme.txt - echo -en 'Documentation at http://www.clifford.at/yosys/.\r\n' >> yosys-win32-mxebin-$(YOSYS_VER)/readme.txt + echo -en 'Documentation at https://yosyshq.net/yosys/.\r\n' >> yosys-win32-mxebin-$(YOSYS_VER)/readme.txt zip -r yosys-win32-mxebin-$(YOSYS_VER).zip yosys-win32-mxebin-$(YOSYS_VER)/ endif @@ -952,9 +1072,6 @@ config-gcc-static: clean echo 'ENABLE_READLINE := 0' >> Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf -config-gcc-4.8: clean - echo 'CONFIG := gcc-4.8' > Makefile.conf - config-afl-gcc: clean echo 'CONFIG := afl-gcc' > Makefile.conf @@ -978,13 +1095,13 @@ config-mxe: clean echo 'CONFIG := mxe' > Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf -config-msys2: clean - echo 'CONFIG := msys2' > Makefile.conf - echo 'ENABLE_PLUGINS := 0' >> Makefile.conf +config-msys2-32: clean + echo 'CONFIG := msys2-32' > Makefile.conf + echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf config-msys2-64: clean echo 'CONFIG := msys2-64' > Makefile.conf - echo 'ENABLE_PLUGINS := 0' >> Makefile.conf + echo "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf config-cygwin: clean echo 'CONFIG := cygwin' > Makefile.conf @@ -1017,6 +1134,5 @@ echo-abc-rev: -include kernel/*.d -include techlibs/*/*.d -.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator coverage vcxsrc mxebin -.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-afl-gcc config-gprof config-sudo - +.PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc mxebin +.PHONY: config-clean config-clang config-gcc config-gcc-static config-afl-gcc config-gprof config-sudo diff --git a/README.md b/README.md index 203a292d1b9..c330d4f74f0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ``` yosys -- Yosys Open SYnthesis Suite -Copyright (C) 2012 - 2020 Claire Wolf +Copyright (C) 2012 - 2024 Claire Xenia Wolf Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -38,13 +38,13 @@ Web Site and Other Resources ============================ More information and documentation can be found on the Yosys web site: -- http://www.clifford.at/yosys/ +- https://yosyshq.net/yosys/ The "Documentation" page on the web site contains links to more resources, including a manual that even describes some of the Yosys internals: -- http://www.clifford.at/yosys/documentation.html +- https://yosyshq.net/yosys/documentation.html -The file `CodingReadme` in this directory contains additional information +The directory `guidelines` contains additional information for people interested in using the Yosys C++ APIs. Users interested in formal verification might want to use the formal verification @@ -53,8 +53,23 @@ front-end for Yosys, SymbiYosys: - https://github.com/YosysHQ/SymbiYosys -Setup -====== +Installation +============ + +Yosys is part of the [Tabby CAD Suite](https://www.yosyshq.com/tabby-cad-datasheet) and the [OSS CAD Suite](https://github.com/YosysHQ/oss-cad-suite-build)! The easiest way to use yosys is to install the binary software suite, which contains all required dependencies and related tools. + +* [Contact YosysHQ](https://www.yosyshq.com/contact) for a [Tabby CAD Suite](https://www.yosyshq.com/tabby-cad-datasheet) Evaluation License and download link +* OR go to https://github.com/YosysHQ/oss-cad-suite-build/releases to download the free OSS CAD Suite +* Follow the [Install Instructions on GitHub](https://github.com/YosysHQ/oss-cad-suite-build#installation) + +Make sure to get a Tabby CAD Suite Evaluation License if you need features such as industry-grade SystemVerilog and VHDL parsers! + +For more information about the difference between Tabby CAD Suite and the OSS CAD Suite, please visit https://www.yosyshq.com/tabby-cad-datasheet + +Many Linux distributions also provide Yosys binaries, some more up to date than others. Check with your package manager! + +Building from Source +==================== You need a C++ compiler with C++11 support (up-to-date CLANG or GCC is recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make. @@ -90,10 +105,6 @@ For Cygwin use the following command to install all prerequisites, or select the setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel,boost-build,zlib-devel -There are also pre-compiled Yosys binary packages for Ubuntu and Win32 as well -as a source distribution for Visual Studio. Visit the Yosys download page for -more information: http://www.clifford.at/yosys/download.html - To configure the build system to use a specific compiler, use one of $ make config-clang @@ -145,9 +156,10 @@ reading and elaborating the design using the Verilog frontend: yosys> read -sv tests/simple/fiedler-cooley.v yosys> hierarchy -top up3down5 -writing the design to the console in Yosys's internal format: +writing the design to the console in the RTLIL format used by Yosys +internally: - yosys> write_ilang + yosys> write_rtlil convert processes (``always`` blocks) to netlist elements and perform some simple optimizations: @@ -489,6 +501,23 @@ Verilog Attributes and non-standard features for use in blackboxes and whiteboxes. Use ``read_verilog -specify`` to enable this functionality. (By default these blocks are ignored.) +- The ``reprocess_after`` internal attribute is used by the Verilog frontend to + mark cells with bindings which might depend on the specified instantiated + module. Modules with such cells will be reprocessed during the ``hierarchy`` + pass once the referenced module definition(s) become available. + +- The ``smtlib2_module`` attribute can be set on a blackbox module to specify a + formal model directly using SMT-LIB 2. For such a module, the + ``smtlib2_comb_expr`` attribute can be used on output ports to define their + value using an SMT-LIB 2 expression. For example: + + (* blackbox *) + (* smtlib2_module *) + module submod(a, b); + input [7:0] a; + (* smtlib2_comb_expr = "(bvnot a)" *) + output [7:0] b; + endmodule Non-standard or SystemVerilog features for formal verification ============================================================== @@ -558,41 +587,42 @@ from SystemVerilog: - enums are supported (including inside packages) - but are currently not strongly typed -- packed structs and unions are supported. +- packed structs and unions are supported + - arrays of packed structs/unions are currently not supported + - structure literals are currently not supported + +- multidimensional arrays are supported + - array assignment of unpacked arrays is currently not supported + - array literals are currently not supported - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether ports are inputs or outputs are supported. +- Assignments within expressions are supported. + Building the documentation ========================== Note that there is no need to build the manual if you just want to read it. -Simply download the PDF from http://www.clifford.at/yosys/documentation.html -instead. - -On Ubuntu, texlive needs these packages to be able to build the manual: +Simply visit https://yosys.readthedocs.io/en/latest/ instead. - sudo apt-get install texlive-binaries - sudo apt-get install texlive-science # install algorithm2e.sty - sudo apt-get install texlive-bibtex-extra # gets multibib.sty - sudo apt-get install texlive-fonts-extra # gets skull.sty and dsfont.sty - sudo apt-get install texlive-publishers # IEEEtran.cls +In addition to those packages listed above for building Yosys from source, the +following are used for building the website: -Also the non-free font luximono should be installed, there is unfortunately -no Ubuntu package for this so it should be installed separately using -`getnonfreefonts`: + $ sudo apt install pdf2svg faketime - wget https://tug.org/fonts/getnonfreefonts/install-getnonfreefonts - sudo texlua install-getnonfreefonts # will install to /usr/local by default, can be changed by editing BINDIR at MANDIR at the top of the script - getnonfreefonts luximono # installs to /home/user/texmf +PDFLaTeX, included with most LaTeX distributions, is also needed during the +build process for the website. Or, run the following: -Then execute, from the root of the repository: + $ sudo apt install texlive-latex-base texlive-latex-extra latexmk - make manual +The Python package, Sphinx, is needed along with those listed in +`docs/source/requirements.txt`: -Notes: + $ pip install -U sphinx -r docs/source/requirements.txt -- To run `make manual` you need to have installed Yosys with `make install`, - otherwise it will fail on finding `kernel/yosys.h` while building - `PRESENTATION_Prog`. +From the root of the repository, run `make docs`. This will build/rebuild yosys +as necessary before generating the website documentation from the yosys help +commands. To build for pdf instead of html, call +`make docs DOC_TARGET=latexpdf`. diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index 476b30488d2..f2cb5d9bcc7 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,6 +19,9 @@ #include "kernel/yosys.h" #include "kernel/sigtools.h" +#include "kernel/json.h" +#include "kernel/yw.h" +#include "libs/json11/json11.hpp" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -51,6 +54,8 @@ struct AigerWriter vector> aig_gates; vector aig_latchin, aig_latchinit, aig_outputs; + vector bit2aig_stack; + size_t next_loop_check = 1024; int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0; @@ -61,6 +66,10 @@ struct AigerWriter dict init_inputs; int initstate_ff = 0; + dict ywmap_clocks; + vector ywmap_asserts; + vector ywmap_assumes; + int mkgate(int a0, int a1) { aig_m++, aig_a++; @@ -76,6 +85,23 @@ struct AigerWriter return it->second; } + if (bit2aig_stack.size() == next_loop_check) { + for (size_t i = 0; i < next_loop_check; ++i) + { + SigBit report_bit = bit2aig_stack[i]; + if (report_bit != bit) + continue; + for (size_t j = i; j < next_loop_check; ++j) { + report_bit = bit2aig_stack[j]; + if (report_bit.is_wire() && report_bit.wire->name.isPublic()) + break; + } + log_error("Found combinational logic loop while processing signal %s.\n", log_signal(report_bit)); + } + next_loop_check *= 2; + } + bit2aig_stack.push_back(bit); + // NB: Cannot use iterator returned from aig_map.insert() // since this function is called recursively @@ -96,6 +122,8 @@ struct AigerWriter a = initstate_ff; } + bit2aig_stack.pop_back(); + if (bit == State::Sx || bit == State::Sz) log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); @@ -114,14 +142,14 @@ struct AigerWriter if (wire->name.isPublic()) sigmap.add(wire); - // promote input wires + // promote output wires for (auto wire : module->wires()) - if (wire->port_input) + if (wire->port_output) sigmap.add(wire); - // promote output wires + // promote input wires for (auto wire : module->wires()) - if (wire->port_output) + if (wire->port_input) sigmap.add(wire); for (auto wire : module->wires()) @@ -159,6 +187,17 @@ struct AigerWriter output_bits.insert(wirebit); } } + + if (wire->width == 1) { + auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); + if (gclk_attr != wire->attributes.end()) { + SigBit bit = sigmap(wire); + if (gclk_attr->second == State::S1) + ywmap_clocks[bit] |= 1; + else if (gclk_attr->second == State::S0) + ywmap_clocks[bit] |= 2; + } + } } for (auto bit : input_bits) @@ -186,6 +225,22 @@ struct AigerWriter unused_bits.erase(D); undriven_bits.erase(Q); ff_map[Q] = D; + + if (cell->type != ID($_FF_)) { + auto sig_clk = sigmap(cell->getPort(ID::C).as_bit()); + ywmap_clocks[sig_clk] |= cell->type == ID($_DFF_N_) ? 2 : 1; + } + continue; + } + + if (cell->type == ID($anyinit)) + { + auto sig_d = sigmap(cell->getPort(ID::D)); + auto sig_q = sigmap(cell->getPort(ID::Q)); + for (int i = 0; i < sig_d.size(); i++) { + undriven_bits.erase(sig_q[i]); + ff_map[sig_q[i]] = sig_d[i]; + } continue; } @@ -216,6 +271,7 @@ struct AigerWriter unused_bits.erase(A); unused_bits.erase(EN); asserts.push_back(make_pair(A, EN)); + ywmap_asserts.push_back(cell); continue; } @@ -226,6 +282,7 @@ struct AigerWriter unused_bits.erase(A); unused_bits.erase(EN); assumes.push_back(make_pair(A, EN)); + ywmap_assumes.push_back(cell); continue; } @@ -267,6 +324,9 @@ struct AigerWriter continue; } + if (cell->type == ID($scopeinfo)) + continue; + log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); } @@ -606,7 +666,7 @@ struct AigerWriter f << stringf("c\nGenerated by %s\n", yosys_version_str); } - void write_map(std::ostream &f, bool verbose_map) + void write_map(std::ostream &f, bool verbose_map, bool no_startoffset) { dict input_lines; dict init_lines; @@ -627,32 +687,33 @@ struct AigerWriter continue; int a = aig_map.at(sig[i]); + int index = no_startoffset ? i : (wire->start_offset+i); if (verbose_map) - wire_lines[a] += stringf("wire %d %d %s\n", a, wire->start_offset+i, log_id(wire)); + wire_lines[a] += stringf("wire %d %d %s\n", a, index, log_id(wire)); if (wire->port_input) { log_assert((a & 1) == 0); - input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire)); + input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, index, log_id(wire)); } if (wire->port_output) { int o = ordered_outputs.at(sig[i]); - output_lines[o] += stringf("output %d %d %s\n", o, wire->start_offset+i, log_id(wire)); + output_lines[o] += stringf("output %d %d %s\n", o, index, log_id(wire)); } if (init_inputs.count(sig[i])) { int a = init_inputs.at(sig[i]); log_assert((a & 1) == 0); - init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire)); + init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, index, log_id(wire)); } if (ordered_latches.count(sig[i])) { int l = ordered_latches.at(sig[i]); if (zinit_mode && (aig_latchinit.at(l) == 1)) - latch_lines[l] += stringf("invlatch %d %d %s\n", l, wire->start_offset+i, log_id(wire)); + latch_lines[l] += stringf("invlatch %d %d %s\n", l, index, log_id(wire)); else - latch_lines[l] += stringf("latch %d %d %s\n", l, wire->start_offset+i, log_id(wire)); + latch_lines[l] += stringf("latch %d %d %s\n", l, index, log_id(wire)); } } } @@ -673,10 +734,144 @@ struct AigerWriter for (auto &it : latch_lines) f << it.second; + if (initstate_ff) + f << stringf("ninitff %d\n", ((initstate_ff >> 1)-1-aig_i)); + wire_lines.sort(); for (auto &it : wire_lines) f << it.second; } + + void write_ywmap(PrettyJson &json) + { + json.begin_object(); + json.entry("version", "Yosys Witness Aiger map"); + json.entry("gennerator", yosys_version_str); + + json.entry("latch_count", aig_l); + json.entry("input_count", aig_i); + + dict clock_lines; + dict input_lines; + dict init_lines; + dict seq_lines; + + for (auto cell : module->cells()) + { + if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_), ID($anyinit), ID($anyconst), ID($anyseq))) + { + // Use sig_q to get the FF output name, but sig to lookup aiger bits + auto sig_qy = cell->getPort(cell->type.in(ID($anyconst), ID($anyseq)) ? ID::Y : ID::Q); + SigSpec sig = sigmap(sig_qy); + + if (cell->get_bool_attribute(ID(clk2fflogic))) + sig_qy = cell->getPort(ID::D); // For a clk2fflogic $_FF_ the named signal is the D input not the Q output + + for (int i = 0; i < GetSize(sig_qy); i++) { + if (sig_qy[i].wire == nullptr || sig[i].wire == nullptr) + continue; + + auto wire = sig_qy[i].wire; + + if (init_inputs.count(sig[i])) { + int a = init_inputs.at(sig[i]); + log_assert((a & 1) == 0); + init_lines[a] = json11::Json(json11::Json::object { + { "path", witness_path(wire) }, + { "input", (a >> 1) - 1 }, + { "offset", sig_qy[i].offset }, + }); + } + + if (input_bits.count(sig[i])) { + int a = aig_map.at(sig[i]); + log_assert((a & 1) == 0); + seq_lines[a] = json11::Json(json11::Json::object { + { "path", witness_path(wire) }, + { "input", (a >> 1) - 1 }, + { "offset", sig_qy[i].offset }, + }); + } + } + } + } + + for (auto wire : module->wires()) + { + SigSpec sig = sigmap(wire); + if (wire->port_input) + { + auto path = witness_path(wire); + for (int i = 0; i < GetSize(wire); i++) { + if (aig_map.count(sig[i]) == 0 || sig[i].wire == nullptr) + continue; + + int a = aig_map.at(sig[i]); + log_assert((a & 1) == 0); + input_lines[a] = json11::Json(json11::Json::object { + { "path", path }, + { "input", (a >> 1) - 1 }, + { "offset", i }, + }); + + if (ywmap_clocks.count(sig[i])) { + int clock_mode = ywmap_clocks[sig[i]]; + if (clock_mode != 3) { + clock_lines[a] = json11::Json(json11::Json::object { + { "path", path }, + { "input", (a >> 1) - 1 }, + { "offset", i }, + { "edge", clock_mode == 1 ? "posedge" : "negedge" }, + }); + } + } + } + } + } + + json.name("clocks"); + json.begin_array(); + clock_lines.sort(); + for (auto &it : clock_lines) + json.value(it.second); + json.end_array(); + + json.name("inputs"); + json.begin_array(); + input_lines.sort(); + for (auto &it : input_lines) + json.value(it.second); + json.end_array(); + + json.name("seqs"); + json.begin_array(); + input_lines.sort(); + for (auto &it : seq_lines) + json.value(it.second); + json.end_array(); + + json.name("inits"); + json.begin_array(); + input_lines.sort(); + for (auto &it : init_lines) + json.value(it.second); + json.end_array(); + + json.name("asserts"); + json.begin_array(); + for (Cell *cell : ywmap_asserts) + json.value(witness_path(cell)); + json.end_array(); + + json.name("assumes"); + json.begin_array(); + for (Cell *cell : ywmap_assumes) + json.value(witness_path(cell)); + json.end_array(); + + json.end_object(); + } + }; struct AigerBackend : public Backend { @@ -713,6 +908,12 @@ struct AigerBackend : public Backend { log(" -vmap \n"); log(" like -map, but more verbose\n"); log("\n"); + log(" -no-startoffset\n"); + log(" make indexes zero based, enable using map files with smt solvers.\n"); + log("\n"); + log(" -ywmap \n"); + log(" write a map file for conversion to and from yosys witness traces.\n"); + log("\n"); log(" -I, -O, -B, -L\n"); log(" If the design contains no input/output/assert/flip-flop then create one\n"); log(" dummy input/output/bad_state-pin or latch to make the tools reading the\n"); @@ -730,7 +931,9 @@ struct AigerBackend : public Backend { bool omode = false; bool bmode = false; bool lmode = false; + bool no_startoffset = false; std::string map_filename; + std::string yw_map_filename; log_header(design, "Executing AIGER backend.\n"); @@ -762,6 +965,14 @@ struct AigerBackend : public Backend { verbose_map = true; continue; } + if (yw_map_filename.empty() && args[argidx] == "-ywmap" && argidx+1 < args.size()) { + yw_map_filename = args[++argidx]; + continue; + } + if (args[argidx] == "-no-startoffset") { + no_startoffset = true; + continue; + } if (args[argidx] == "-I") { imode = true; continue; @@ -782,6 +993,9 @@ struct AigerBackend : public Backend { } extra_args(f, filename, args, argidx, !ascii_mode); + if (!yw_map_filename.empty() && !zinit_mode) + log_error("Currently -ywmap requires -zinit.\n"); + Module *top_module = design->top_module(); if (top_module == nullptr) @@ -804,7 +1018,18 @@ struct AigerBackend : public Backend { mapf.open(map_filename.c_str(), std::ofstream::trunc); if (mapf.fail()) log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); - writer.write_map(mapf, verbose_map); + writer.write_map(mapf, verbose_map, no_startoffset); + } + + if (!yw_map_filename.empty()) { + std::ofstream mapf; + mapf.open(yw_map_filename.c_str(), std::ofstream::trunc); + + PrettyJson json; + + if (!json.write_to_file(yw_map_filename)) + log_error("Can't open file `%s' for writing: %s\n", yw_map_filename.c_str(), strerror(errno)); + writer.write_ywmap(json); } } } AigerBackend; diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index 27499b64a7a..68c2ff52f55 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * 2019 Eddie Hung * * Permission to use, copy, modify, and/or distribute this software for any @@ -156,7 +156,7 @@ struct XAigerWriter // promote keep wires for (auto wire : module->wires()) - if (wire->get_bool_attribute(ID::keep) || wire->get_bool_attribute(ID::abc9_keep)) + if (wire->get_bool_attribute(ID::keep)) sigmap.add(wire); for (auto wire : module->wires()) { @@ -177,11 +177,10 @@ struct XAigerWriter undriven_bits.insert(bit); unused_bits.insert(bit); - bool keep = wire->get_bool_attribute(ID::abc9_keep); - if (wire->port_input || keep) + if (wire->port_input) input_bits.insert(bit); - keep = keep || wire->get_bool_attribute(ID::keep); + bool keep = wire->get_bool_attribute(ID::keep); if (wire->port_output || keep) { if (bit != wirebit) alias_map[wirebit] = bit; @@ -262,26 +261,31 @@ struct XAigerWriter if (!timing.count(inst_module->name)) timing.setup_module(inst_module); - auto &t = timing.at(inst_module->name).arrival; - for (const auto &conn : cell->connections()) { - auto port_wire = inst_module->wire(conn.first); - if (!port_wire->port_output) + + for (auto &i : timing.at(inst_module->name).arrival) { + if (!cell->hasPort(i.first.name)) continue; - for (int i = 0; i < GetSize(conn.second); i++) { - auto d = t.at(TimingInfo::NameBit(conn.first,i), 0); - if (d == 0) - continue; + auto port_wire = inst_module->wire(i.first.name); + log_assert(port_wire->port_output); + + auto d = i.second.first; + if (d == 0) + continue; + auto offset = i.first.offset; + + auto rhs = cell->getPort(i.first.name); + if (offset >= rhs.size()) + continue; #ifndef NDEBUG - if (ys_debug(1)) { - static std::set> seen; - if (seen.emplace(inst_module->name, conn.first, i).second) log("%s.%s[%d] abc9_arrival = %d\n", - log_id(cell->type), log_id(conn.first), i, d); - } -#endif - arrival_times[conn.second[i]] = d; + if (ys_debug(1)) { + static pool> seen; + if (seen.emplace(inst_module->name, i.first).second) log("%s.%s[%d] abc9_arrival = %d\n", + log_id(cell->type), log_id(i.first.name), offset, d); } +#endif + arrival_times[rhs[offset]] = d; } if (abc9_flop) @@ -432,7 +436,8 @@ struct XAigerWriter // that has been padded to its full width if (bit == State::Sx) continue; - log_assert(!aig_map.count(bit)); + if (aig_map.count(bit)) + log_error("Visited AIG node more than once; this could be a combinatorial loop that has not been broken\n"); aig_map[bit] = 2*aig_m; } diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index 08881904256..788b7f951f2 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -226,6 +226,9 @@ struct BlifDumper for (auto cell : module->cells()) { + if (cell->type == ID($scopeinfo)) + continue; + if (config->unbuf_types.count(cell->type)) { auto portnames = config->unbuf_types.at(cell->type); f << stringf(".names %s %s\n1 1\n", @@ -512,8 +515,8 @@ struct BlifBackend : public Backend { log(" suppresses the generation of this nets without fanout.\n"); log("\n"); log("The following options can be useful when the generated file is not going to be\n"); - log("read by a BLIF parser but a custom tool. It is recommended to not name the output\n"); - log("file *.blif when any of this options is used.\n"); + log("read by a BLIF parser but a custom tool. It is recommended not to name the\n"); + log("output file *.blif when any of these options are used.\n"); log("\n"); log(" -icells\n"); log(" do not translate Yosys's internal gates to generic BLIF logic\n"); diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 639c6f12950..9cfd967e581 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,7 +18,7 @@ */ // [[CITE]] Btor2 , BtorMC and Boolector 3.0 -// Aina Niemetz, Mathias Preiner, Clifford Wolf, Armin Biere +// Aina Niemetz, Mathias Preiner, C. Wolf, Armin Biere // Computer Aided Verification - 30th International Conference, CAV 2018 // https://cs.stanford.edu/people/niemetz/publication/2018/niemetzpreinerwolfbiere-cav18/ @@ -28,6 +28,8 @@ #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" +#include "kernel/json.h" +#include "kernel/yw.h" #include USING_YOSYS_NAMESPACE @@ -83,6 +85,22 @@ struct BtorWorker vector info_lines; dict info_clocks; + struct ywmap_btor_sig { + SigSpec sig; + Cell *cell = nullptr; + + ywmap_btor_sig(const SigSpec &sig) : sig(sig) {} + ywmap_btor_sig(Cell *cell) : cell(cell) {} + }; + + vector ywmap_inputs; + vector ywmap_states; + dict ywmap_clock_bits; + dict ywmap_clock_inputs; + + + PrettyJson ywmap_json; + void btorf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3)) { va_list ap; @@ -126,6 +144,50 @@ struct BtorWorker return " " + infostr; } + void ywmap_state(const SigSpec &sig) { + if (ywmap_json.active()) + ywmap_states.emplace_back(sig); + } + + void ywmap_state(Cell *cell) { + if (ywmap_json.active()) + ywmap_states.emplace_back(cell); + } + + void ywmap_input(const SigSpec &sig) { + if (ywmap_json.active()) + ywmap_inputs.emplace_back(sig); + } + + void emit_ywmap_btor_sig(const ywmap_btor_sig &btor_sig) { + if (btor_sig.cell == nullptr) { + if (btor_sig.sig.empty()) { + ywmap_json.value(nullptr); + return; + } + ywmap_json.begin_array(); + ywmap_json.compact(); + for (auto &chunk : btor_sig.sig.chunks()) { + log_assert(chunk.is_wire()); + + ywmap_json.begin_object(); + ywmap_json.entry("path", witness_path(chunk.wire)); + ywmap_json.entry("width", chunk.width); + ywmap_json.entry("offset", chunk.offset); + ywmap_json.end_object(); + } + ywmap_json.end_array(); + } else { + ywmap_json.begin_object(); + ywmap_json.compact(); + ywmap_json.entry("path", witness_path(btor_sig.cell)); + Mem *mem = mem_cells[btor_sig.cell]; + ywmap_json.entry("width", mem->width); + ywmap_json.entry("size", mem->size); + ywmap_json.end_object(); + } + } + void btorf_push(const string &id) { if (verbose) { @@ -446,25 +508,28 @@ struct BtorWorker goto okay; } - if (cell->type.in(ID($not), ID($neg), ID($_NOT_))) + if (cell->type.in(ID($not), ID($neg), ID($_NOT_), ID($pos))) { string btor_op; if (cell->type.in(ID($not), ID($_NOT_))) btor_op = "not"; if (cell->type == ID($neg)) btor_op = "neg"; - log_assert(!btor_op.empty()); int width = std::max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::Y))); bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; - - int sid = get_bv_sid(width); int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); - - int nid = next_nid++; - btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); - SigSpec sig = sigmap(cell->getPort(ID::Y)); + // the $pos cell just passes through, all other cells need an actual operation applied + int nid = nid_a; + if (cell->type != ID($pos)) + { + log_assert(!btor_op.empty()); + int sid = get_bv_sid(width); + nid = next_nid++; + btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); + } + if (GetSize(sig) < width) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; @@ -609,12 +674,12 @@ struct BtorWorker goto okay; } - if (cell->type.in(ID($dff), ID($ff), ID($_DFF_P_), ID($_DFF_N), ID($_FF_))) + if (cell->type.in(ID($dff), ID($ff), ID($anyinit), ID($_DFF_P_), ID($_DFF_N), ID($_FF_))) { SigSpec sig_d = sigmap(cell->getPort(ID::D)); SigSpec sig_q = sigmap(cell->getPort(ID::Q)); - if (!info_filename.empty() && cell->type.in(ID($dff), ID($_DFF_P_), ID($_DFF_N_))) + if ((!info_filename.empty() || ywmap_json.active()) && cell->type.in(ID($dff), ID($_DFF_P_), ID($_DFF_N_))) { SigSpec sig_c = sigmap(cell->getPort(cell->type == ID($dff) ? ID::CLK : ID::C)); int nid = get_sig_nid(sig_c); @@ -626,7 +691,11 @@ struct BtorWorker if (cell->type == ID($dff) && !cell->getParam(ID::CLK_POLARITY).as_bool()) negedge = true; - info_clocks[nid] |= negedge ? 2 : 1; + if (!info_filename.empty()) + info_clocks[nid] |= negedge ? 2 : 1; + + if (ywmap_json.active()) + ywmap_clock_bits[sig_c] |= negedge ? 2 : 1; } IdString symbol; @@ -659,6 +728,11 @@ struct BtorWorker else btorf("%d state %d %s\n", nid, sid, log_id(symbol)); + if (cell->get_bool_attribute(ID(clk2fflogic))) + ywmap_state(cell->getPort(ID::D)); // For a clk2fflogic FF the named signal is the D input not the Q output + else + ywmap_state(sig_q); + if (nid_init_val >= 0) { int nid_init = next_nid++; if (verbose) @@ -678,7 +752,9 @@ struct BtorWorker int sid = get_bv_sid(GetSize(sig_y)); int nid = next_nid++; - btorf("%d state %d\n", nid, sid); + btorf("%d state %d%s\n", nid, sid, getinfo(cell).c_str()); + + ywmap_state(sig_y); if (cell->type == ID($anyconst)) { int nid2 = next_nid++; @@ -699,16 +775,18 @@ struct BtorWorker int one_nid = get_sig_nid(State::S1); int zero_nid = get_sig_nid(State::S0); initstate_nid = next_nid++; - btorf("%d state %d\n", initstate_nid, sid); + btorf("%d state %d%s\n", initstate_nid, sid, getinfo(cell).c_str()); btorf("%d init %d %d %d\n", next_nid++, sid, initstate_nid, one_nid); btorf("%d next %d %d %d\n", next_nid++, sid, initstate_nid, zero_nid); + + ywmap_state(sig_y); } add_nid_sig(initstate_nid, sig_y); goto okay; } - if (cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit))) + if (cell->is_mem_cell()) { Mem *mem = mem_cells[cell]; @@ -728,10 +806,11 @@ struct BtorWorker log_error("Memory %s.%s has mixed async/sync write ports.\n", log_id(module), log_id(mem->memid)); - for (auto &port : mem->rd_ports) + for (auto &port : mem->rd_ports) { if (port.clk_enable) - log_error("Memory %s.%s has sync read ports.\n", + log_error("Memory %s.%s has sync read ports. Please use memory_nordff to convert them first.\n", log_id(module), log_id(mem->memid)); + } int data_sid = get_bv_sid(mem->width); int bool_sid = get_bv_sid(1); @@ -764,6 +843,8 @@ struct BtorWorker nid_init_val = next_nid++; btorf("%d state %d\n", nid_init_val, sid); + ywmap_state(nullptr); + for (int i = 0; i < mem->size; i++) { Const thisword = initdata.extract(i*mem->width, mem->width); if (thisword.is_fully_undef()) @@ -789,6 +870,8 @@ struct BtorWorker else btorf("%d state %d %s\n", nid, sid, log_id(mem->memid)); + ywmap_state(cell); + if (nid_init_val >= 0) { int nid_init = next_nid++; @@ -860,7 +943,20 @@ struct BtorWorker goto okay; } - log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); + if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { + log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_btor`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_btor`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_btor`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + log_error("Unsupported cell type %s for cell %s.%s.\n", + log_id(cell->type), log_id(module), log_id(cell)); okay: btorf_pop(log_id(cell)); @@ -898,10 +994,13 @@ struct BtorWorker int sid = get_bv_sid(GetSize(sig)); int nid_input = next_nid++; - if (is_init) + if (is_init) { btorf("%d state %d\n", nid_input, sid); - else + ywmap_state(sig); + } else { btorf("%d input %d\n", nid_input, sid); + ywmap_input(sig); + } int nid_masked_input; if (sig_mask_undef.is_fully_ones()) { @@ -976,6 +1075,7 @@ struct BtorWorker int sid = get_bv_sid(GetSize(s)); int nid = next_nid++; btorf("%d input %d\n", nid, sid); + ywmap_input(s); nid_width[nid] = GetSize(s); for (int j = 0; j < GetSize(s); j++) @@ -1058,23 +1158,42 @@ struct BtorWorker return nid; } - BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, bool print_internal_names, string info_filename) : + BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, bool print_internal_names, string info_filename, string ywmap_filename) : f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), info_filename(info_filename) { if (!info_filename.empty()) infof("name %s\n", log_id(module)); + if (!ywmap_filename.empty()) + ywmap_json.write_to_file(ywmap_filename); + memories = Mem::get_all_memories(module); dict mem_dict; - for (auto &mem : memories) + for (auto &mem : memories) { + mem.narrow(); mem_dict[mem.memid] = &mem; + } for (auto cell : module->cells()) - if (cell->type.in(ID($mem), ID($memwr), ID($memrd), ID($meminit))) + if (cell->is_mem_cell()) mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()]; btorf_push("inputs"); + if (ywmap_json.active()) { + for (auto wire : module->wires()) + { + auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); + if (gclk_attr == wire->attributes.end()) + continue; + SigSpec sig = sigmap(wire); + if (gclk_attr->second == State::S1) + ywmap_clock_bits[sig] |= 1; + else if (gclk_attr->second == State::S0) + ywmap_clock_bits[sig] |= 2; + } + } + for (auto wire : module->wires()) { if (wire->attributes.count(ID::init)) { @@ -1092,7 +1211,28 @@ struct BtorWorker int nid = next_nid++; btorf("%d input %d%s\n", nid, sid, getinfo(wire).c_str()); + ywmap_input(wire); add_nid_sig(nid, sig); + + if (!info_filename.empty()) { + auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); + if (gclk_attr != wire->attributes.end()) { + if (gclk_attr->second == State::S1) + info_clocks[nid] |= 1; + else if (gclk_attr->second == State::S0) + info_clocks[nid] |= 2; + } + } + + if (ywmap_json.active()) { + for (int i = 0; i < GetSize(sig); i++) { + auto input_bit = SigBit(wire, i); + auto bit = sigmap(input_bit); + if (!ywmap_clock_bits.count(bit)) + continue; + ywmap_clock_inputs[input_bit] = ywmap_clock_bits[bit]; + } + } } btorf_pop("inputs"); @@ -1204,6 +1344,8 @@ struct BtorWorker int this_nid = next_nid++; btorf("%d uext %d %d %d%s\n", this_nid, sid, nid, 0, getinfo(wire).c_str()); + if (info_clocks.count(nid)) + info_clocks[this_nid] |= info_clocks[nid]; btorf_pop(stringf("wire %s", log_id(wire))); continue; @@ -1347,6 +1489,42 @@ struct BtorWorker f << it; f.close(); } + + if (ywmap_json.active()) + { + ywmap_json.begin_object(); + ywmap_json.entry("version", "Yosys Witness BTOR map"); + ywmap_json.entry("generator", yosys_version_str); + + ywmap_json.name("clocks"); + ywmap_json.begin_array(); + for (auto &entry : ywmap_clock_inputs) { + if (entry.second != 1 && entry.second != 2) + continue; + log_assert(entry.first.is_wire()); + ywmap_json.begin_object(); + ywmap_json.compact(); + ywmap_json.entry("path", witness_path(entry.first.wire)); + ywmap_json.entry("offset", entry.first.offset); + ywmap_json.entry("edge", entry.second == 1 ? "posedge" : "negedge"); + ywmap_json.end_object(); + } + ywmap_json.end_array(); + + ywmap_json.name("inputs"); + ywmap_json.begin_array(); + for (auto &entry : ywmap_inputs) + emit_ywmap_btor_sig(entry); + ywmap_json.end_array(); + + ywmap_json.name("states"); + ywmap_json.begin_array(); + for (auto &entry : ywmap_states) + emit_ywmap_btor_sig(entry); + ywmap_json.end_array(); + + ywmap_json.end_object(); + } } }; @@ -1375,14 +1553,24 @@ struct BtorBackend : public Backend { log(" -x\n"); log(" Output symbols for internal netnames (starting with '$')\n"); log("\n"); + log(" -ywmap \n"); + log(" Create a map file for conversion to and from Yosys witness traces\n"); + log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool verbose = false, single_bad = false, cover_mode = false, print_internal_names = false; string info_filename; + string ywmap_filename; log_header(design, "Executing BTOR backend.\n"); + log_push(); + Pass::call(design, "bmuxmap"); + Pass::call(design, "demuxmap"); + Pass::call(design, "bwmuxmap"); + log_pop(); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -1406,6 +1594,10 @@ struct BtorBackend : public Backend { print_internal_names = true; continue; } + if (args[argidx] == "-ywmap" && argidx+1 < args.size()) { + ywmap_filename = args[++argidx]; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -1418,7 +1610,7 @@ struct BtorBackend : public Backend { *f << stringf("; BTOR description generated by %s for module %s.\n", yosys_version_str, log_id(topmod)); - BtorWorker(*f, topmod, verbose, single_bad, cover_mode, print_internal_names, info_filename); + BtorWorker(*f, topmod, verbose, single_bad, cover_mode, print_internal_names, info_filename, ywmap_filename); *f << stringf("; end of yosys output\n"); } diff --git a/backends/btor/test_cells.sh b/backends/btor/test_cells.sh index 0a011932d22..f8bd797825e 100755 --- a/backends/btor/test_cells.sh +++ b/backends/btor/test_cells.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex diff --git a/backends/cxxrtl/Makefile.inc b/backends/cxxrtl/Makefile.inc index aaa304502e0..dd77d2ad360 100644 --- a/backends/cxxrtl/Makefile.inc +++ b/backends/cxxrtl/Makefile.inc @@ -1,2 +1,11 @@ OBJS += backends/cxxrtl/cxxrtl_backend.o + +$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl.h)) +$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h)) +$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h)) +$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h)) +$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc)) +$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h)) +$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc)) +$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h)) diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index a48ea5b23a8..877a829d37a 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -24,6 +24,8 @@ #include "kernel/celltypes.h" #include "kernel/mem.h" #include "kernel/log.h" +#include "kernel/fmt.h" +#include "kernel/scopeinfo.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -185,7 +187,7 @@ bool is_binary_cell(RTLIL::IdString type) ID($and), ID($or), ID($xor), ID($xnor), ID($logic_and), ID($logic_or), ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx), ID($eq), ID($ne), ID($eqx), ID($nex), ID($gt), ID($ge), ID($lt), ID($le), - ID($add), ID($sub), ID($mul), ID($div), ID($mod)); + ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($modfloor), ID($divfloor)); } bool is_extending_cell(RTLIL::IdString type) @@ -195,10 +197,10 @@ bool is_extending_cell(RTLIL::IdString type) ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool)); } -bool is_elidable_cell(RTLIL::IdString type) +bool is_inlinable_cell(RTLIL::IdString type) { return is_unary_cell(type) || is_binary_cell(type) || type.in( - ID($mux), ID($concat), ID($slice), ID($pmux)); + ID($mux), ID($concat), ID($slice), ID($pmux), ID($bmux), ID($demux)); } bool is_ff_cell(RTLIL::IdString type) @@ -206,12 +208,18 @@ bool is_ff_cell(RTLIL::IdString type) return type.in( ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), + ID($aldff), ID($aldffe), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr)); } bool is_internal_cell(RTLIL::IdString type) { - return type[0] == '$' && !type.begins_with("$paramod"); + return !type.isPublic() && !type.begins_with("$paramod"); +} + +bool is_effectful_cell(RTLIL::IdString type) +{ + return type.in(ID($print), ID($check)); } bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell) @@ -221,24 +229,29 @@ bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell) return cell_module->get_bool_attribute(ID(cxxrtl_blackbox)); } +bool is_memwr_process(const RTLIL::Process *process) +{ + for (auto sync : process->syncs) + if (!sync->mem_write_actions.empty()) + return true; + return false; +} + enum class CxxrtlPortType { UNKNOWN = 0, // or mixed comb/sync COMB = 1, SYNC = 2, }; -CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port) +CxxrtlPortType cxxrtl_port_type(RTLIL::Module *module, RTLIL::IdString port) { - RTLIL::Module *cell_module = cell->module->design->module(cell->type); - if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) - return CxxrtlPortType::UNKNOWN; - RTLIL::Wire *cell_output_wire = cell_module->wire(port); - log_assert(cell_output_wire != nullptr); - bool is_comb = cell_output_wire->get_bool_attribute(ID(cxxrtl_comb)); - bool is_sync = cell_output_wire->get_bool_attribute(ID(cxxrtl_sync)); + RTLIL::Wire *output_wire = module->wire(port); + log_assert(output_wire != nullptr); + bool is_comb = output_wire->get_bool_attribute(ID(cxxrtl_comb)); + bool is_sync = output_wire->get_bool_attribute(ID(cxxrtl_sync)); if (is_comb && is_sync) log_cmd_error("Port `%s.%s' is marked as both `cxxrtl_comb` and `cxxrtl_sync`.\n", - log_id(cell_module), log_signal(cell_output_wire)); + log_id(module), log_signal(output_wire)); else if (is_comb) return CxxrtlPortType::COMB; else if (is_sync) @@ -246,6 +259,14 @@ CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port) return CxxrtlPortType::UNKNOWN; } +CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port) +{ + RTLIL::Module *cell_module = cell->module->design->module(cell->type); + if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) + return CxxrtlPortType::UNKNOWN; + return cxxrtl_port_type(cell_module, port); +} + bool is_cxxrtl_comb_port(const RTLIL::Cell *cell, RTLIL::IdString port) { return cxxrtl_port_type(cell, port) == CxxrtlPortType::COMB; @@ -262,18 +283,27 @@ struct FlowGraph { CONNECT, CELL_SYNC, CELL_EVAL, - PROCESS + EFFECT_SYNC, + PROCESS_SYNC, + PROCESS_CASE, + MEM_RDPORT, + MEM_WRPORTS, }; Type type; RTLIL::SigSig connect = {}; - const RTLIL::Cell *cell = NULL; - const RTLIL::Process *process = NULL; + const RTLIL::Cell *cell = nullptr; + std::vector cells; + const RTLIL::Process *process = nullptr; + const Mem *mem = nullptr; + int portidx; }; std::vector nodes; dict> wire_comb_defs, wire_sync_defs, wire_uses; - dict wire_def_elidable, wire_use_elidable; + dict, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses; + dict wire_def_inlinable; + dict> wire_use_inlinable; dict bit_has_state; ~FlowGraph() @@ -282,7 +312,7 @@ struct FlowGraph { delete node; } - void add_defs(Node *node, const RTLIL::SigSpec &sig, bool is_ff, bool elidable) + void add_defs(Node *node, const RTLIL::SigSpec &sig, bool is_ff, bool inlinable) { for (auto chunk : sig.chunks()) if (chunk.wire) { @@ -290,17 +320,25 @@ struct FlowGraph { // A sync def means that a wire holds design state because it is driven directly by // a flip-flop output. Such a wire can never be unbuffered. wire_sync_defs[chunk.wire].insert(node); + node_sync_defs[node].insert(chunk.wire); } else { // A comb def means that a wire doesn't hold design state. It might still be connected, // indirectly, to a flip-flop output. wire_comb_defs[chunk.wire].insert(node); + node_comb_defs[node].insert(chunk.wire); } } for (auto bit : sig.bits()) bit_has_state[bit] |= is_ff; - // Only comb defs of an entire wire in the right order can be elided. - if (!is_ff && sig.is_wire()) - wire_def_elidable[sig.as_wire()] = elidable; + // Only comb defs of an entire wire in the right order can be inlined. + if (!is_ff && sig.is_wire()) { + // Only a single def of a wire can be inlined. (Multiple defs of a wire are unsound, but we + // handle them anyway to avoid assertion failures later.) + if (!wire_def_inlinable.count(sig.as_wire())) + wire_def_inlinable[sig.as_wire()] = inlinable; + else + wire_def_inlinable[sig.as_wire()] = false; + } } void add_uses(Node *node, const RTLIL::SigSpec &sig) @@ -308,26 +346,41 @@ struct FlowGraph { for (auto chunk : sig.chunks()) if (chunk.wire) { wire_uses[chunk.wire].insert(node); - // Only a single use of an entire wire in the right order can be elided. - // (But the use can include other chunks.) - if (!wire_use_elidable.count(chunk.wire)) - wire_use_elidable[chunk.wire] = true; + node_uses[node].insert(chunk.wire); + // Only a single use of an entire wire in the right order can be inlined. (But the use can include + // other chunks.) This is tracked per-node because a wire used by multiple nodes can still be inlined + // if all but one of those nodes is dead. + if (!wire_use_inlinable[chunk.wire].count(node)) + wire_use_inlinable[chunk.wire][node] = true; else - wire_use_elidable[chunk.wire] = false; + wire_use_inlinable[chunk.wire][node] = false; } } - bool is_elidable(const RTLIL::Wire *wire) const + bool is_inlinable(const RTLIL::Wire *wire) const + { + // Can the wire be inlined at all? + if (wire_def_inlinable.count(wire)) + return wire_def_inlinable.at(wire); + return false; + } + + bool is_inlinable(const RTLIL::Wire *wire, const pool &nodes) const { - if (wire_def_elidable.count(wire) && wire_use_elidable.count(wire)) - return wire_def_elidable.at(wire) && wire_use_elidable.at(wire); + // Can the wire be inlined, knowing that the given nodes are reachable? + if (nodes.size() != 1) + return false; + Node *node = *nodes.begin(); + log_assert(node_uses.at(node).count(wire)); + if (is_inlinable(wire) && wire_use_inlinable.count(wire) && wire_use_inlinable.at(wire).count(node)) + return wire_use_inlinable.at(wire).at(node); return false; } // Connections void add_connect_defs_uses(Node *node, const RTLIL::SigSig &conn) { - add_defs(node, conn.first, /*is_ff=*/false, /*elidable=*/true); + add_defs(node, conn.first, /*is_ff=*/false, /*inlinable=*/true); add_uses(node, conn.second); } @@ -373,8 +426,8 @@ struct FlowGraph { for (auto conn : cell->connections()) if (cell->output(conn.first)) if (is_cxxrtl_sync_port(cell, conn.first)) { - // See note regarding elidability below. - add_defs(node, conn.second, /*is_ff=*/false, /*elidable=*/false); + // See note regarding inlinability below. + add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); } } @@ -382,19 +435,19 @@ struct FlowGraph { { for (auto conn : cell->connections()) { if (cell->output(conn.first)) { - if (is_elidable_cell(cell->type)) - add_defs(node, conn.second, /*is_ff=*/false, /*elidable=*/true); - else if (is_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool())) - add_defs(node, conn.second, /*is_ff=*/true, /*elidable=*/false); + if (is_inlinable_cell(cell->type)) + add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/true); + else if (is_ff_cell(cell->type)) + add_defs(node, conn.second, /*is_ff=*/true, /*inlinable=*/false); else if (is_internal_cell(cell->type)) - add_defs(node, conn.second, /*is_ff=*/false, /*elidable=*/false); + add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); else if (!is_cxxrtl_sync_port(cell, conn.first)) { - // Although at first it looks like outputs of user-defined cells may always be elided, the reality is - // more complex. Fully sync outputs produce no defs and so don't participate in elision. Fully comb + // Although at first it looks like outputs of user-defined cells may always be inlined, the reality is + // more complex. Fully sync outputs produce no defs and so don't participate in inlining. Fully comb // outputs are assigned in a different way depending on whether the cell's eval() immediately converged. - // Unknown/mixed outputs could be elided, but should be rare in practical designs and don't justify - // the infrastructure required to elide outputs of cells with many of them. - add_defs(node, conn.second, /*is_ff=*/false, /*elidable=*/false); + // Unknown/mixed outputs could be inlined, but should be rare in practical designs and don't justify + // the infrastructure required to inline outputs of cells with many of them. + add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); } } if (cell->input(conn.first)) @@ -428,11 +481,20 @@ struct FlowGraph { return node; } + Node *add_effect_sync_node(std::vector cells) + { + Node *node = new Node; + node->type = Node::Type::EFFECT_SYNC; + node->cells = cells; + nodes.push_back(node); + return node; + } + // Processes - void add_case_defs_uses(Node *node, const RTLIL::CaseRule *case_) + void add_case_rule_defs_uses(Node *node, const RTLIL::CaseRule *case_) { for (auto &action : case_->actions) { - add_defs(node, action.first, /*is_ff=*/false, /*elidable=*/false); + add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false); add_uses(node, action.second); } for (auto sub_switch : case_->switches) { @@ -440,33 +502,89 @@ struct FlowGraph { for (auto sub_case : sub_switch->cases) { for (auto &compare : sub_case->compare) add_uses(node, compare); - add_case_defs_uses(node, sub_case); + add_case_rule_defs_uses(node, sub_case); } } } - void add_process_defs_uses(Node *node, const RTLIL::Process *process) + void add_sync_rules_defs_uses(Node *node, const RTLIL::Process *process) { - add_case_defs_uses(node, &process->root_case); - for (auto sync : process->syncs) - for (auto action : sync->actions) { + for (auto sync : process->syncs) { + for (auto &action : sync->actions) { if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe) - add_defs(node, action.first, /*is_ff=*/true, /*elidable=*/false); + add_defs(node, action.first, /*is_ff=*/true, /*inlinable=*/false); else - add_defs(node, action.first, /*is_ff=*/false, /*elidable=*/false); + add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false); add_uses(node, action.second); } + for (auto &memwr : sync->mem_write_actions) { + add_uses(node, memwr.address); + add_uses(node, memwr.data); + add_uses(node, memwr.enable); + } + } } Node *add_node(const RTLIL::Process *process) { Node *node = new Node; - node->type = Node::Type::PROCESS; + node->type = Node::Type::PROCESS_SYNC; + node->process = process; + nodes.push_back(node); + add_sync_rules_defs_uses(node, process); + + node = new Node; + node->type = Node::Type::PROCESS_CASE; node->process = process; nodes.push_back(node); - add_process_defs_uses(node, process); + add_case_rule_defs_uses(node, &process->root_case); return node; } + + // Memories + void add_node(const Mem *mem) { + for (int i = 0; i < GetSize(mem->rd_ports); i++) { + auto &port = mem->rd_ports[i]; + Node *node = new Node; + node->type = Node::Type::MEM_RDPORT; + node->mem = mem; + node->portidx = i; + nodes.push_back(node); + add_defs(node, port.data, /*is_ff=*/port.clk_enable, /*inlinable=*/false); + add_uses(node, port.clk); + add_uses(node, port.en); + add_uses(node, port.arst); + add_uses(node, port.srst); + add_uses(node, port.addr); + bool transparent = false; + for (int j = 0; j < GetSize(mem->wr_ports); j++) { + auto &wrport = mem->wr_ports[j]; + if (port.transparency_mask[j]) { + // Our implementation of transparent read ports reads en, addr and data from every write port + // the read port is transparent with. + add_uses(node, wrport.en); + add_uses(node, wrport.addr); + add_uses(node, wrport.data); + transparent = true; + } + } + // Also we read the read address twice in this case (prevent inlining). + if (transparent) + add_uses(node, port.addr); + } + if (!mem->wr_ports.empty()) { + Node *node = new Node; + node->type = Node::Type::MEM_WRPORTS; + node->mem = mem; + nodes.push_back(node); + for (auto &port : mem->wr_ports) { + add_uses(node, port.clk); + add_uses(node, port.en); + add_uses(node, port.addr); + add_uses(node, port.data); + } + } + } }; std::vector split_by(const std::string &str, const std::string &sep) @@ -511,6 +629,20 @@ std::string escape_cxx_string(const std::string &input) return output; } +std::string basename(const std::string &filepath) +{ +#ifdef _WIN32 + const std::string dir_seps = "\\/"; +#else + const std::string dir_seps = "/"; +#endif + size_t sep_pos = filepath.find_last_of(dir_seps); + if (sep_pos != std::string::npos) + return filepath.substr(sep_pos + 1); + else + return filepath; +} + template std::string get_hdl_name(T *object) { @@ -520,13 +652,68 @@ std::string get_hdl_name(T *object) return object->name.str().substr(1); } +struct WireType { + enum Type { + // Non-referenced wire; is not a part of the design. + UNUSED, + // Double-buffered wire; is a class member, and holds design state. + BUFFERED, + // Single-buffered wire; is a class member, but holds no state. + MEMBER, + // Single-buffered wire; is a class member, and is computed on demand. + OUTLINE, + // Local wire; is a local variable in eval method. + LOCAL, + // Inline wire; is an unnamed temporary in eval method. + INLINE, + // Alias wire; is replaced with aliasee, except in debug info. + ALIAS, + // Const wire; is replaced with constant, except in debug info. + CONST, + }; + + Type type = UNUSED; + const RTLIL::Cell *cell_subst = nullptr; // for INLINE + RTLIL::SigSpec sig_subst = {}; // for INLINE, ALIAS, and CONST + + WireType() = default; + + WireType(Type type) : type(type) { + log_assert(type == UNUSED || type == BUFFERED || type == MEMBER || type == OUTLINE || type == LOCAL); + } + + WireType(Type type, const RTLIL::Cell *cell) : type(type), cell_subst(cell) { + log_assert(type == INLINE && is_inlinable_cell(cell->type)); + } + + WireType(Type type, RTLIL::SigSpec sig) : type(type), sig_subst(sig) { + log_assert(type == INLINE || (type == ALIAS && sig.is_wire()) || (type == CONST && sig.is_fully_const())); + } + + bool is_buffered() const { return type == BUFFERED; } + bool is_member() const { return type == BUFFERED || type == MEMBER || type == OUTLINE; } + bool is_outline() const { return type == OUTLINE; } + bool is_named() const { return is_member() || type == LOCAL; } + bool is_local() const { return type == LOCAL || type == INLINE; } + bool is_exact() const { return type == ALIAS || type == CONST; } +}; + +// Tests for a SigSpec that is a valid clock input, clocks have to have a backing wire and be a single bit +// using this instead of sig.is_wire() solves issues when the clock is a slice instead of a full wire +bool is_valid_clock(const RTLIL::SigSpec& sig) { + return sig.is_chunk() && sig.is_bit() && sig[0].wire; +} + struct CxxrtlWorker { bool split_intf = false; std::string intf_filename; std::string design_ns = "cxxrtl_design"; + std::string print_output = "std::cout"; std::ostream *impl_f = nullptr; std::ostream *intf_f = nullptr; + bool print_wire_types = false; + bool print_debug_wire_types = false; bool run_hierarchy = false; bool run_flatten = false; bool run_proc = false; @@ -535,26 +722,26 @@ struct CxxrtlWorker { bool unbuffer_public = false; bool localize_internal = false; bool localize_public = false; - bool elide_internal = false; - bool elide_public = false; + bool inline_internal = false; + bool inline_public = false; bool debug_info = false; + bool debug_member = false; + bool debug_alias = false; + bool debug_eval = false; std::ostringstream f; std::string indent; int temporary = 0; dict sigmaps; + dict> mod_memories; + pool> writable_memories; pool edge_wires; + dict wire_init; dict edge_types; - pool writable_memories; - dict> transparent_for; - dict elided_wires; - dict> schedule; - pool unbuffered_wires; - pool localized_wires; - dict debug_alias_wires; - dict debug_const_wires; + dict> schedule, debug_schedule; + dict wire_types, debug_wire_types; dict bit_has_state; dict> blackbox_specializations; dict eval_converges; @@ -636,6 +823,11 @@ struct CxxrtlWorker { return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox))); } + std::string mangle(const Mem *mem) + { + return mangle_memory_name(mem->memid); + } + std::string mangle(const RTLIL::Memory *memory) { return mangle_memory_name(memory->name); @@ -770,11 +962,6 @@ struct CxxrtlWorker { f << "}"; } - void dump_const_init(const RTLIL::Const &data) - { - dump_const_init(data, data.size()); - } - void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false) { f << "value<" << width << ">"; @@ -786,30 +973,37 @@ struct CxxrtlWorker { dump_const(data, data.size()); } - bool dump_sigchunk(const RTLIL::SigChunk &chunk, bool is_lhs) + bool dump_sigchunk(const RTLIL::SigChunk &chunk, bool is_lhs, bool for_debug = false) { if (chunk.wire == NULL) { dump_const(chunk.data, chunk.width, chunk.offset); return false; } else { - if (elided_wires.count(chunk.wire)) { - log_assert(!is_lhs); - const FlowGraph::Node &node = elided_wires[chunk.wire]; - switch (node.type) { - case FlowGraph::Node::Type::CONNECT: - dump_connect_elided(node.connect); - break; - case FlowGraph::Node::Type::CELL_EVAL: - log_assert(is_elidable_cell(node.cell->type)); - dump_cell_elided(node.cell); + const auto &wire_type = (for_debug ? debug_wire_types : wire_types)[chunk.wire]; + switch (wire_type.type) { + case WireType::BUFFERED: + f << mangle(chunk.wire) << (is_lhs ? ".next" : ".curr"); + break; + case WireType::MEMBER: + case WireType::LOCAL: + case WireType::OUTLINE: + f << mangle(chunk.wire); + break; + case WireType::INLINE: + log_assert(!is_lhs); + if (wire_type.cell_subst != nullptr) { + dump_cell_expr(wire_type.cell_subst, for_debug); break; - default: - log_assert(false); - } - } else if (unbuffered_wires[chunk.wire]) { - f << mangle(chunk.wire); - } else { - f << mangle(chunk.wire) << (is_lhs ? ".next" : ".curr"); + } + YS_FALLTHROUGH + case WireType::ALIAS: + case WireType::CONST: + log_assert(!is_lhs); + return dump_sigspec(wire_type.sig_subst.extract(chunk.offset, chunk.width), is_lhs, for_debug); + case WireType::UNUSED: + log_assert(is_lhs); + f << "value<" << chunk.width << ">()"; + return false; } if (chunk.width == chunk.wire->width && chunk.offset == 0) return false; @@ -821,92 +1015,116 @@ struct CxxrtlWorker { } } - bool dump_sigspec(const RTLIL::SigSpec &sig, bool is_lhs) + bool dump_sigspec(const RTLIL::SigSpec &sig, bool is_lhs, bool for_debug = false) { if (sig.empty()) { f << "value<0>()"; return false; } else if (sig.is_chunk()) { - return dump_sigchunk(sig.as_chunk(), is_lhs); + return dump_sigchunk(sig.as_chunk(), is_lhs, for_debug); } else { - dump_sigchunk(*sig.chunks().rbegin(), is_lhs); - for (auto it = sig.chunks().rbegin() + 1; it != sig.chunks().rend(); ++it) { - f << ".concat("; - dump_sigchunk(*it, is_lhs); - f << ")"; + bool first = true; + auto chunks = sig.chunks(); + for (auto it = chunks.rbegin(); it != chunks.rend(); it++) { + if (!first) + f << ".concat("; + bool is_complex = dump_sigchunk(*it, is_lhs, for_debug); + if (!is_lhs && it->width == 1) { + size_t repeat = 1; + while ((it + repeat) != chunks.rend() && *(it + repeat) == *it) + repeat++; + if (repeat > 1) { + if (is_complex) + f << ".val()"; + f << ".repeat<" << repeat << ">()"; + } + it += repeat - 1; + } + if (!first) + f << ")"; + first = false; } return true; } } - void dump_sigspec_lhs(const RTLIL::SigSpec &sig) + void dump_sigspec_lhs(const RTLIL::SigSpec &sig, bool for_debug = false) { - dump_sigspec(sig, /*is_lhs=*/true); + dump_sigspec(sig, /*is_lhs=*/true, for_debug); } - void dump_sigspec_rhs(const RTLIL::SigSpec &sig) + void dump_sigspec_rhs(const RTLIL::SigSpec &sig, bool for_debug = false) { // In the contexts where we want template argument deduction to occur for `template ... value`, // it is necessary to have the argument to already be a `value`, since template argument deduction and implicit // type conversion are mutually exclusive. In these contexts, we use dump_sigspec_rhs() to emit an explicit // type conversion, but only if the expression needs it. - bool is_complex = dump_sigspec(sig, /*is_lhs=*/false); + bool is_complex = dump_sigspec(sig, /*is_lhs=*/false, for_debug); if (is_complex) f << ".val()"; } - void collect_sigspec_rhs(const RTLIL::SigSpec &sig, std::vector &cells) + void dump_inlined_cells(const std::vector &cells) + { + if (cells.empty()) { + f << indent << "// connection\n"; + } else if (cells.size() == 1) { + dump_attrs(cells.front()); + f << indent << "// cell " << cells.front()->name.str() << "\n"; + } else { + f << indent << "// cells"; + for (auto cell : cells) + f << " " << cell->name.str(); + f << "\n"; + } + } + + void collect_sigspec_rhs(const RTLIL::SigSpec &sig, bool for_debug, std::vector &cells) { for (auto chunk : sig.chunks()) { - if (!chunk.wire || !elided_wires.count(chunk.wire)) + if (!chunk.wire) continue; - - const FlowGraph::Node &node = elided_wires[chunk.wire]; - switch (node.type) { - case FlowGraph::Node::Type::CONNECT: - collect_connect(node.connect, cells); - break; - case FlowGraph::Node::Type::CELL_EVAL: - collect_cell_eval(node.cell, cells); + const auto &wire_type = wire_types[chunk.wire]; + switch (wire_type.type) { + case WireType::INLINE: + if (wire_type.cell_subst != nullptr) { + collect_cell_eval(wire_type.cell_subst, for_debug, cells); + break; + } + YS_FALLTHROUGH + case WireType::ALIAS: + collect_sigspec_rhs(wire_type.sig_subst, for_debug, cells); break; default: - log_assert(false); + break; } } } - void dump_connect_elided(const RTLIL::SigSig &conn) - { - dump_sigspec_rhs(conn.second); - } - - bool is_connect_elided(const RTLIL::SigSig &conn) - { - return conn.first.is_wire() && elided_wires.count(conn.first.as_wire()); - } - - void collect_connect(const RTLIL::SigSig &conn, std::vector &cells) + void dump_connect_expr(const RTLIL::SigSig &conn, bool for_debug = false) { - if (!is_connect_elided(conn)) - return; - - collect_sigspec_rhs(conn.second, cells); + dump_sigspec_rhs(conn.second, for_debug); } - void dump_connect(const RTLIL::SigSig &conn) + void dump_connect(const RTLIL::SigSig &conn, bool for_debug = false) { - if (is_connect_elided(conn)) - return; + std::vector inlined_cells; + collect_sigspec_rhs(conn.second, for_debug, inlined_cells); + dump_inlined_cells(inlined_cells); - f << indent << "// connection\n"; f << indent; - dump_sigspec_lhs(conn.first); + dump_sigspec_lhs(conn.first, for_debug); f << " = "; - dump_connect_elided(conn); + dump_connect_expr(conn, for_debug); f << ";\n"; } - void dump_cell_sync(const RTLIL::Cell *cell) + void collect_connect(const RTLIL::SigSig &conn, bool for_debug, std::vector &cells) + { + collect_sigspec_rhs(conn.second, for_debug, cells); + } + + void dump_cell_sync(const RTLIL::Cell *cell, bool for_debug = false) { const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; f << indent << "// cell " << cell->name.str() << " syncs\n"; @@ -914,12 +1132,12 @@ struct CxxrtlWorker { if (cell->output(conn.first)) if (is_cxxrtl_sync_port(cell, conn.first)) { f << indent; - dump_sigspec_lhs(conn.second); + dump_sigspec_lhs(conn.second, for_debug); f << " = " << mangle(cell) << access << mangle_wire_name(conn.first) << ".curr;\n"; } } - void dump_cell_elided(const RTLIL::Cell *cell) + void dump_cell_expr(const RTLIL::Cell *cell, bool for_debug = false) { // Unary cells if (is_unary_cell(cell->type)) { @@ -927,7 +1145,7 @@ struct CxxrtlWorker { if (is_extending_cell(cell->type)) f << '_' << (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u'); f << "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">("; - dump_sigspec_rhs(cell->getPort(ID::A)); + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ")"; // Binary cells } else if (is_binary_cell(cell->type)) { @@ -936,18 +1154,18 @@ struct CxxrtlWorker { f << '_' << (cell->getParam(ID::A_SIGNED).as_bool() ? 's' : 'u') << (cell->getParam(ID::B_SIGNED).as_bool() ? 's' : 'u'); f << "<" << cell->getParam(ID::Y_WIDTH).as_int() << ">("; - dump_sigspec_rhs(cell->getPort(ID::A)); + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ", "; - dump_sigspec_rhs(cell->getPort(ID::B)); + dump_sigspec_rhs(cell->getPort(ID::B), for_debug); f << ")"; // Muxes } else if (cell->type == ID($mux)) { f << "("; - dump_sigspec_rhs(cell->getPort(ID::S)); + dump_sigspec_rhs(cell->getPort(ID::S), for_debug); f << " ? "; - dump_sigspec_rhs(cell->getPort(ID::B)); + dump_sigspec_rhs(cell->getPort(ID::B), for_debug); f << " : "; - dump_sigspec_rhs(cell->getPort(ID::A)); + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ")"; // Parallel (one-hot) muxes } else if (cell->type == ID($pmux)) { @@ -955,24 +1173,40 @@ struct CxxrtlWorker { int s_width = cell->getParam(ID::S_WIDTH).as_int(); for (int part = 0; part < s_width; part++) { f << "("; - dump_sigspec_rhs(cell->getPort(ID::S).extract(part)); + dump_sigspec_rhs(cell->getPort(ID::S).extract(part), for_debug); f << " ? "; - dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width)); + dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width), for_debug); f << " : "; } - dump_sigspec_rhs(cell->getPort(ID::A)); + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); for (int part = 0; part < s_width; part++) { f << ")"; } + // Big muxes + } else if (cell->type == ID($bmux)) { + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); + f << ".bmux<"; + f << cell->getParam(ID::WIDTH).as_int(); + f << ">("; + dump_sigspec_rhs(cell->getPort(ID::S), for_debug); + f << ").val()"; + // Demuxes + } else if (cell->type == ID($demux)) { + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); + f << ".demux<"; + f << GetSize(cell->getPort(ID::Y)); + f << ">("; + dump_sigspec_rhs(cell->getPort(ID::S), for_debug); + f << ").val()"; // Concats } else if (cell->type == ID($concat)) { - dump_sigspec_rhs(cell->getPort(ID::B)); + dump_sigspec_rhs(cell->getPort(ID::B), for_debug); f << ".concat("; - dump_sigspec_rhs(cell->getPort(ID::A)); + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ").val()"; // Slices } else if (cell->type == ID($slice)) { - dump_sigspec_rhs(cell->getPort(ID::A)); + dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ".slice<"; f << cell->getParam(ID::OFFSET).as_int() + cell->getParam(ID::Y_WIDTH).as_int() - 1; f << ","; @@ -983,61 +1217,203 @@ struct CxxrtlWorker { } } - bool is_cell_elided(const RTLIL::Cell *cell) + void dump_print(const RTLIL::Cell *cell) { - return is_elidable_cell(cell->type) && cell->hasPort(ID::Y) && cell->getPort(ID::Y).is_wire() && - elided_wires.count(cell->getPort(ID::Y).as_wire()); + Fmt fmt; + fmt.parse_rtlil(cell); + + f << indent << "if ("; + dump_sigspec_rhs(cell->getPort(ID::EN)); + f << " == value<1>{1u}) {\n"; + inc_indent(); + dict fmt_args; + f << indent << "struct : public lazy_fmt {\n"; + inc_indent(); + f << indent << "std::string operator() () const override {\n"; + inc_indent(); + fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) { + if (sig.size() == 0) + f << "value<0>()"; + else { + std::string arg_name = "arg" + std::to_string(fmt_args.size()); + fmt_args[arg_name] = sig; + f << arg_name; + } + }, "performer"); + dec_indent(); + f << indent << "}\n"; + f << indent << "struct performer *performer;\n"; + for (auto arg : fmt_args) + f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n"; + dec_indent(); + f << indent << "} formatter;\n"; + f << indent << "formatter.performer = performer;\n"; + for (auto arg : fmt_args) { + f << indent << "formatter." << arg.first << " = "; + dump_sigspec_rhs(arg.second); + f << ";\n"; + } + f << indent << "if (performer) {\n"; + inc_indent(); + f << indent << "static const metadata_map attributes = "; + dump_metadata_map(cell->attributes); + f << ";\n"; + f << indent << "performer->on_print(formatter, attributes);\n"; + dec_indent(); + f << indent << "} else {\n"; + inc_indent(); + f << indent << print_output << " << formatter();\n"; + dec_indent(); + f << indent << "}\n"; + dec_indent(); + f << indent << "}\n"; } - void collect_cell_eval(const RTLIL::Cell *cell, std::vector &cells) + void dump_effect(const RTLIL::Cell *cell) { - if (!is_cell_elided(cell)) - return; + Fmt fmt; + fmt.parse_rtlil(cell); - cells.push_back(cell->name); - for (auto port : cell->connections()) - if (port.first != ID::Y) - collect_sigspec_rhs(port.second, cells); + f << indent << "if ("; + dump_sigspec_rhs(cell->getPort(ID::EN)); + f << ") {\n"; + inc_indent(); + dict fmt_args; + f << indent << "struct : public lazy_fmt {\n"; + inc_indent(); + f << indent << "std::string operator() () const override {\n"; + inc_indent(); + fmt.emit_cxxrtl(f, indent, [&](const RTLIL::SigSpec &sig) { + if (sig.size() == 0) + f << "value<0>()"; + else { + std::string arg_name = "arg" + std::to_string(fmt_args.size()); + fmt_args[arg_name] = sig; + f << arg_name; + } + }, "performer"); + dec_indent(); + f << indent << "}\n"; + f << indent << "struct performer *performer;\n"; + for (auto arg : fmt_args) + f << indent << "value<" << arg.second.size() << "> " << arg.first << ";\n"; + dec_indent(); + f << indent << "} formatter;\n"; + f << indent << "formatter.performer = performer;\n"; + for (auto arg : fmt_args) { + f << indent << "formatter." << arg.first << " = "; + dump_sigspec_rhs(arg.second); + f << ";\n"; + } + if (cell->hasPort(ID::A)) { + f << indent << "bool condition = (bool)"; + dump_sigspec_rhs(cell->getPort(ID::A)); + f << ";\n"; + } + f << indent << "if (performer) {\n"; + inc_indent(); + f << indent << "static const metadata_map attributes = "; + dump_metadata_map(cell->attributes); + f << ";\n"; + if (cell->type == ID($print)) { + f << indent << "performer->on_print(formatter, attributes);\n"; + } else if (cell->type == ID($check)) { + std::string flavor = cell->getParam(ID::FLAVOR).decode_string(); + f << indent << "performer->on_check("; + if (flavor == "assert") + f << "flavor::ASSERT"; + else if (flavor == "assume") + f << "flavor::ASSUME"; + else if (flavor == "live") + f << "flavor::ASSERT_EVENTUALLY"; + else if (flavor == "fair") + f << "flavor::ASSUME_EVENTUALLY"; + else if (flavor == "cover") + f << "flavor::COVER"; + else log_assert(false); + f << ", condition, formatter, attributes);\n"; + } else log_assert(false); + dec_indent(); + f << indent << "} else {\n"; + inc_indent(); + if (cell->type == ID($print)) { + f << indent << print_output << " << formatter();\n"; + } else if (cell->type == ID($check)) { + std::string flavor = cell->getParam(ID::FLAVOR).decode_string(); + if (flavor == "assert" || flavor == "assume") { + f << indent << "if (!condition) {\n"; + inc_indent(); + f << indent << "std::cerr << formatter();\n"; + dec_indent(); + f << indent << "}\n"; + f << indent << "CXXRTL_ASSERT(condition && \"Check failed\");\n"; + } + } else log_assert(false); + dec_indent(); + f << indent << "}\n"; + dec_indent(); + f << indent << "}\n"; } - void dump_cell_eval(const RTLIL::Cell *cell) + void dump_cell_eval(const RTLIL::Cell *cell, bool for_debug = false) { - if (is_cell_elided(cell)) - return; - if (cell->type == ID($meminit)) - return; // Handled elsewhere. - - std::vector elided_cells; - if (is_elidable_cell(cell->type)) { - for (auto port : cell->connections()) - if (port.first != ID::Y) - collect_sigspec_rhs(port.second, elided_cells); - } - if (elided_cells.empty()) { - dump_attrs(cell); - f << indent << "// cell " << cell->name.str() << "\n"; - } else { - f << indent << "// cells"; - for (auto elided_cell : elided_cells) - f << " " << elided_cell.str(); - f << "\n"; - } + std::vector inlined_cells; + collect_cell_eval(cell, for_debug, inlined_cells); + dump_inlined_cells(inlined_cells); // Elidable cells - if (is_elidable_cell(cell->type)) { + if (is_inlinable_cell(cell->type)) { f << indent; - dump_sigspec_lhs(cell->getPort(ID::Y)); + dump_sigspec_lhs(cell->getPort(ID::Y), for_debug); f << " = "; - dump_cell_elided(cell); + dump_cell_expr(cell, for_debug); f << ";\n"; + // Effectful cells + } else if (is_effectful_cell(cell->type)) { + log_assert(!for_debug); + + // Sync effectful cells are grouped into EFFECT_SYNC nodes in the FlowGraph. + log_assert(!cell->getParam(ID::TRG_ENABLE).as_bool() || (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0)); + + if (!cell->getParam(ID::TRG_ENABLE).as_bool()) { // async effectful cell + f << indent << "auto " << mangle(cell) << "_next = "; + dump_sigspec_rhs(cell->getPort(ID::EN)); + f << ".concat("; + if (cell->type == ID($print)) + dump_sigspec_rhs(cell->getPort(ID::ARGS)); + else if (cell->type == ID($check)) + dump_sigspec_rhs(cell->getPort(ID::A)); + else log_assert(false); + f << ").val();\n"; + + f << indent << "if (" << mangle(cell) << " != " << mangle(cell) << "_next) {\n"; + inc_indent(); + dump_effect(cell); + f << indent << mangle(cell) << " = " << mangle(cell) << "_next;\n"; + dec_indent(); + f << indent << "}\n"; + } else { // initial effectful cell + f << indent << "if (!" << mangle(cell) << ") {\n"; + inc_indent(); + dump_effect(cell); + f << indent << mangle(cell) << " = value<1>{1u};\n"; + dec_indent(); + f << indent << "}\n"; + } // Flip-flops } else if (is_ff_cell(cell->type)) { - if (cell->hasPort(ID::CLK) && cell->getPort(ID::CLK).is_wire()) { + log_assert(!for_debug); + // Clocks might be slices of larger signals but should only ever be single bit + if (cell->hasPort(ID::CLK) && is_valid_clock(cell->getPort(ID::CLK))) { // Edge-sensitive logic RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0]; clk_bit = sigmaps[clk_bit.wire->module](clk_bit); - f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_") - << mangle(clk_bit) << ") {\n"; + if (clk_bit.wire) { + f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_") + << mangle(clk_bit) << ") {\n"; + } else { + f << indent << "if (false) {\n"; + } inc_indent(); if (cell->hasPort(ID::EN)) { f << indent << "if ("; @@ -1101,12 +1477,26 @@ struct CxxrtlWorker { dec_indent(); f << indent << "}\n"; } + if (cell->hasPort(ID::ALOAD)) { + // Asynchronous load + f << indent << "if ("; + dump_sigspec_rhs(cell->getPort(ID::ALOAD)); + f << " == value<1> {" << cell->getParam(ID::ALOAD_POLARITY).as_bool() << "u}) {\n"; + inc_indent(); + f << indent; + dump_sigspec_lhs(cell->getPort(ID::Q)); + f << " = "; + dump_sigspec_rhs(cell->getPort(ID::AD)); + f << ";\n"; + dec_indent(); + f << indent << "}\n"; + } if (cell->hasPort(ID::SET)) { // Asynchronous set (for individual bits) f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_sigspec_lhs(cell->getPort(ID::Q)); + dump_sigspec_rhs(cell->getPort(ID::Q)); f << ".update("; dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int())); f << ", "; @@ -1118,127 +1508,37 @@ struct CxxrtlWorker { f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; - dump_sigspec_lhs(cell->getPort(ID::Q)); + dump_sigspec_rhs(cell->getPort(ID::Q)); f << ".update("; dump_const(RTLIL::Const(RTLIL::S0, cell->getParam(ID::WIDTH).as_int())); f << ", "; dump_sigspec_rhs(cell->getPort(ID::CLR)); f << (cell->getParam(ID::CLR_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n"; } - // Memory ports - } else if (cell->type.in(ID($memrd), ID($memwr))) { - if (cell->getParam(ID::CLK_ENABLE).as_bool()) { - RTLIL::SigBit clk_bit = cell->getPort(ID::CLK)[0]; - clk_bit = sigmaps[clk_bit.wire->module](clk_bit); - f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_") - << mangle(clk_bit) << ") {\n"; - inc_indent(); - } - RTLIL::Memory *memory = cell->module->memories[cell->getParam(ID::MEMID).decode_string()]; - std::string valid_index_temp = fresh_temporary(); - f << indent << "auto " << valid_index_temp << " = memory_index("; - dump_sigspec_rhs(cell->getPort(ID::ADDR)); - f << ", " << memory->start_offset << ", " << memory->size << ");\n"; - if (cell->type == ID($memrd)) { - bool has_enable = cell->getParam(ID::CLK_ENABLE).as_bool() && !cell->getPort(ID::EN).is_fully_ones(); - if (has_enable) { - f << indent << "if ("; - dump_sigspec_rhs(cell->getPort(ID::EN)); - f << ") {\n"; - inc_indent(); - } - // The generated code has two bounds checks; one in an assertion, and another that guards the read. - // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless - // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DNDEBUG not - // just for release builds, but also to make sure the simulator (which is presumably embedded in some - // larger program) will never crash the code that calls into it. - // - // If assertions are disabled, out of bounds reads are defined to return zero. - f << indent << "assert(" << valid_index_temp << ".valid && \"out of bounds read\");\n"; - f << indent << "if(" << valid_index_temp << ".valid) {\n"; - inc_indent(); - if (writable_memories[memory]) { - std::string lhs_temp = fresh_temporary(); - f << indent << "value<" << memory->width << "> " << lhs_temp << " = " - << mangle(memory) << "[" << valid_index_temp << ".index];\n"; - std::vector memwr_cells(transparent_for[cell].begin(), transparent_for[cell].end()); - if (!memwr_cells.empty()) { - std::string addr_temp = fresh_temporary(); - f << indent << "const value<" << cell->getPort(ID::ADDR).size() << "> &" << addr_temp << " = "; - dump_sigspec_rhs(cell->getPort(ID::ADDR)); - f << ";\n"; - std::sort(memwr_cells.begin(), memwr_cells.end(), - [](const RTLIL::Cell *a, const RTLIL::Cell *b) { - return a->getParam(ID::PRIORITY).as_int() < b->getParam(ID::PRIORITY).as_int(); - }); - for (auto memwr_cell : memwr_cells) { - f << indent << "if (" << addr_temp << " == "; - dump_sigspec_rhs(memwr_cell->getPort(ID::ADDR)); - f << ") {\n"; - inc_indent(); - f << indent << lhs_temp << " = " << lhs_temp; - f << ".update("; - dump_sigspec_rhs(memwr_cell->getPort(ID::DATA)); - f << ", "; - dump_sigspec_rhs(memwr_cell->getPort(ID::EN)); - f << ");\n"; - dec_indent(); - f << indent << "}\n"; - } - } - f << indent; - dump_sigspec_lhs(cell->getPort(ID::DATA)); - f << " = " << lhs_temp << ";\n"; - } else { - f << indent; - dump_sigspec_lhs(cell->getPort(ID::DATA)); - f << " = " << mangle(memory) << "[" << valid_index_temp << ".index];\n"; - } - dec_indent(); - f << indent << "} else {\n"; - inc_indent(); - f << indent; - dump_sigspec_lhs(cell->getPort(ID::DATA)); - f << " = value<" << memory->width << "> {};\n"; - dec_indent(); - f << indent << "}\n"; - if (has_enable) { - dec_indent(); - f << indent << "}\n"; - } - } else /*if (cell->type == ID($memwr))*/ { - log_assert(writable_memories[memory]); - // See above for rationale of having both the assert and the condition. - // - // If assertions are disabled, out of bounds writes are defined to do nothing. - f << indent << "assert(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; - f << indent << "if (" << valid_index_temp << ".valid) {\n"; - inc_indent(); - f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, "; - dump_sigspec_rhs(cell->getPort(ID::DATA)); - f << ", "; - dump_sigspec_rhs(cell->getPort(ID::EN)); - f << ", " << cell->getParam(ID::PRIORITY).as_int() << ");\n"; - dec_indent(); - f << indent << "}\n"; - } - if (cell->getParam(ID::CLK_ENABLE).as_bool()) { - dec_indent(); - f << indent << "}\n"; - } // Internal cells } else if (is_internal_cell(cell->type)) { log_cmd_error("Unsupported internal cell `%s'.\n", cell->type.c_str()); // User cells + } else if (for_debug) { + // Outlines are called on demand when computing the value of a debug item. Nothing to do here. } else { log_assert(cell->known()); + bool buffered_inputs = false; const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; for (auto conn : cell->connections()) - if (cell->input(conn.first) && !cell->output(conn.first)) { - f << indent << mangle(cell) << access << mangle_wire_name(conn.first) << " = "; + if (cell->input(conn.first)) { + RTLIL::Module *cell_module = cell->module->design->module(cell->type); + log_assert(cell_module != nullptr && cell_module->wire(conn.first)); + RTLIL::Wire *cell_module_wire = cell_module->wire(conn.first); + f << indent << mangle(cell) << access << mangle_wire_name(conn.first); + if (!is_cxxrtl_blackbox_cell(cell) && wire_types[cell_module_wire].is_buffered()) { + buffered_inputs = true; + f << ".next"; + } + f << " = "; dump_sigspec_rhs(conn.second); f << ";\n"; - if (getenv("CXXRTL_VOID_MY_WARRANTY")) { + if (getenv("CXXRTL_VOID_MY_WARRANTY") && conn.second.is_wire()) { // Until we have proper clock tree detection, this really awful hack that opportunistically // propagates prev_* values for clocks can be used to estimate how much faster a design could // be if only one clock edge was simulated by replacing: @@ -1247,19 +1547,11 @@ struct CxxrtlWorker { // with: // top.prev_p_clk = value<1>{0u}; top.p_clk = value<1>{1u}; top.step(); // Don't rely on this; it will be removed without warning. - RTLIL::Module *cell_module = cell->module->design->module(cell->type); - if (cell_module != nullptr && cell_module->wire(conn.first) && conn.second.is_wire()) { - RTLIL::Wire *cell_module_wire = cell_module->wire(conn.first); - if (edge_wires[conn.second.as_wire()] && edge_wires[cell_module_wire]) { - f << indent << mangle(cell) << access << "prev_" << mangle(cell_module_wire) << " = "; - f << "prev_" << mangle(conn.second.as_wire()) << ";\n"; - } + if (edge_wires[conn.second.as_wire()] && edge_wires[cell_module_wire]) { + f << indent << mangle(cell) << access << "prev_" << mangle(cell_module_wire) << " = "; + f << "prev_" << mangle(conn.second.as_wire()) << ";\n"; } } - } else if (cell->input(conn.first)) { - f << indent << mangle(cell) << access << mangle_wire_name(conn.first) << ".next = "; - dump_sigspec_rhs(conn.second); - f << ";\n"; } auto assign_from_outputs = [&](bool cell_converged) { for (auto conn : cell->connections()) { @@ -1277,9 +1569,9 @@ struct CxxrtlWorker { // have any buffered wires if they were not output ports. Imagine inlining the cell's eval() function, // and consider the fate of the localized wires that used to be output ports.) // - // Unlike cell inputs (which are never buffered), it is not possible to know apriori whether the cell - // (which may be late bound) will converge immediately. Because of this, the choice between using .curr - // (appropriate for buffered outputs) and .next (appropriate for unbuffered outputs) is made at runtime. + // It is not possible to know apriori whether the cell (which may be late bound) will converge immediately. + // Because of this, the choice between using .curr (appropriate for buffered outputs) and .next (appropriate + // for unbuffered outputs) is made at runtime. if (cell_converged && is_cxxrtl_comb_port(cell, conn.first)) f << ".next;\n"; else @@ -1287,43 +1579,58 @@ struct CxxrtlWorker { } } }; - f << indent << "if (" << mangle(cell) << access << "eval()) {\n"; - inc_indent(); - assign_from_outputs(/*cell_converged=*/true); - dec_indent(); - f << indent << "} else {\n"; - inc_indent(); + if (buffered_inputs) { + // If we have any buffered inputs, there's no chance of converging immediately. + f << indent << mangle(cell) << access << "eval(performer);\n"; f << indent << "converged = false;\n"; assign_from_outputs(/*cell_converged=*/false); - dec_indent(); - f << indent << "}\n"; + } else { + f << indent << "if (" << mangle(cell) << access << "eval(performer)) {\n"; + inc_indent(); + assign_from_outputs(/*cell_converged=*/true); + dec_indent(); + f << indent << "} else {\n"; + inc_indent(); + f << indent << "converged = false;\n"; + assign_from_outputs(/*cell_converged=*/false); + dec_indent(); + f << indent << "}\n"; + } } } - void dump_assign(const RTLIL::SigSig &sigsig) + void collect_cell_eval(const RTLIL::Cell *cell, bool for_debug, std::vector &cells) + { + cells.push_back(cell); + for (auto port : cell->connections()) + if (cell->input(port.first)) + collect_sigspec_rhs(port.second, for_debug, cells); + } + + void dump_assign(const RTLIL::SigSig &sigsig, bool for_debug = false) { f << indent; - dump_sigspec_lhs(sigsig.first); + dump_sigspec_lhs(sigsig.first, for_debug); f << " = "; - dump_sigspec_rhs(sigsig.second); + dump_sigspec_rhs(sigsig.second, for_debug); f << ";\n"; } - void dump_case_rule(const RTLIL::CaseRule *rule) + void dump_case_rule(const RTLIL::CaseRule *rule, bool for_debug = false) { for (auto action : rule->actions) - dump_assign(action); + dump_assign(action, for_debug); for (auto switch_ : rule->switches) - dump_switch_rule(switch_); + dump_switch_rule(switch_, for_debug); } - void dump_switch_rule(const RTLIL::SwitchRule *rule) + void dump_switch_rule(const RTLIL::SwitchRule *rule, bool for_debug = false) { // The switch attributes are printed before the switch condition is captured. dump_attrs(rule); std::string signal_temp = fresh_temporary(); f << indent << "const value<" << rule->signal.size() << "> &" << signal_temp << " = "; - dump_sigspec(rule->signal, /*is_lhs=*/false); + dump_sigspec(rule->signal, /*is_lhs=*/false, for_debug); f << ";\n"; bool first = true; @@ -1343,7 +1650,7 @@ struct CxxrtlWorker { first = false; if (compare.is_fully_def()) { f << signal_temp << " == "; - dump_sigspec(compare, /*is_lhs=*/false); + dump_sigspec(compare, /*is_lhs=*/false, for_debug); } else if (compare.is_fully_const()) { RTLIL::Const compare_mask, compare_value; for (auto bit : compare.as_const()) { @@ -1377,24 +1684,34 @@ struct CxxrtlWorker { } f << "{\n"; inc_indent(); - dump_case_rule(case_); + dump_case_rule(case_, for_debug); dec_indent(); } f << indent << "}\n"; } - void dump_process(const RTLIL::Process *proc) + void dump_process_case(const RTLIL::Process *proc, bool for_debug = false) { dump_attrs(proc); - f << indent << "// process " << proc->name.str() << "\n"; + f << indent << "// process " << proc->name.str() << " case\n"; // The case attributes (for root case) are always empty. log_assert(proc->root_case.attributes.empty()); - dump_case_rule(&proc->root_case); + dump_case_rule(&proc->root_case, for_debug); + } + + void dump_process_syncs(const RTLIL::Process *proc, bool for_debug = false) + { + dump_attrs(proc); + f << indent << "// process " << proc->name.str() << " syncs\n"; for (auto sync : proc->syncs) { + log_assert(!for_debug || sync->type == RTLIL::STa); + RTLIL::SigBit sync_bit; if (!sync->signal.empty()) { sync_bit = sync->signal[0]; sync_bit = sigmaps[sync_bit.wire->module](sync_bit); + if (!sync_bit.is_wire()) + continue; // a clock, or more commonly a reset, can be tied to a constant driver } pool events; @@ -1434,129 +1751,409 @@ struct CxxrtlWorker { } f << ") {\n"; inc_indent(); - for (auto action : sync->actions) - dump_assign(action); + for (auto &action : sync->actions) + dump_assign(action, for_debug); + for (auto &memwr : sync->mem_write_actions) { + RTLIL::Memory *memory = proc->module->memories.at(memwr.memid); + std::string valid_index_temp = fresh_temporary(); + f << indent << "auto " << valid_index_temp << " = memory_index("; + dump_sigspec_rhs(memwr.address); + f << ", " << memory->start_offset << ", " << memory->size << ");\n"; + // See below for rationale of having both the assert and the condition. + // + // If assertions are disabled, out of bounds writes are defined to do nothing. + f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; + f << indent << "if (" << valid_index_temp << ".valid) {\n"; + inc_indent(); + f << indent << mangle(memory) << ".update(" << valid_index_temp << ".index, "; + dump_sigspec_rhs(memwr.data); + f << ", "; + dump_sigspec_rhs(memwr.enable); + f << ");\n"; + dec_indent(); + f << indent << "}\n"; + } dec_indent(); f << indent << "}\n"; } } } - void dump_wire(const RTLIL::Wire *wire, bool is_local_context) + void dump_cell_effect_sync(std::vector &cells) { - if (elided_wires.count(wire)) - return; + log_assert(!cells.empty()); + const auto &trg = cells[0]->getPort(ID::TRG); + const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY); - if (localized_wires[wire] && is_local_context) { - dump_attrs(wire); - f << indent << "value<" << wire->width << "> " << mangle(wire) << ";\n"; - } - if (!localized_wires[wire] && !is_local_context) { - std::string width; - if (wire->module->has_attribute(ID(cxxrtl_blackbox)) && wire->has_attribute(ID(cxxrtl_width))) { - width = wire->get_string_attribute(ID(cxxrtl_width)); - } else { - width = std::to_string(wire->width); + f << indent << "if ("; + for (int i = 0; i < trg.size(); i++) { + RTLIL::SigBit trg_bit = trg[i]; + trg_bit = sigmaps[trg_bit.wire->module](trg_bit); + log_assert(trg_bit.wire); + + if (i != 0) + f << " || "; + + if (trg_polarity[i] == State::S1) + f << "posedge_"; + else + f << "negedge_"; + f << mangle(trg_bit); + } + f << ") {\n"; + inc_indent(); + std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) { + return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int(); + }); + for (auto cell : cells) { + log_assert(cell->getParam(ID::TRG_ENABLE).as_bool()); + log_assert(cell->getPort(ID::TRG) == trg); + log_assert(cell->getParam(ID::TRG_POLARITY) == trg_polarity); + + std::vector inlined_cells; + collect_cell_eval(cell, /*for_debug=*/false, inlined_cells); + dump_inlined_cells(inlined_cells); + dump_effect(cell); } + dec_indent(); - dump_attrs(wire); - f << indent; - if (wire->port_input && wire->port_output) - f << "/*inout*/ "; - else if (wire->port_input) - f << "/*input*/ "; - else if (wire->port_output) - f << "/*output*/ "; - f << (unbuffered_wires[wire] ? "value" : "wire") << "<" << width << "> " << mangle(wire); - if (wire->has_attribute(ID::init)) { - f << " "; - dump_const_init(wire->attributes.at(ID::init)); + f << indent << "}\n"; + } + + void dump_mem_rdport(const Mem *mem, int portidx, bool for_debug = false) + { + auto &port = mem->rd_ports[portidx]; + dump_attrs(&port); + f << indent << "// memory " << mem->memid.str() << " read port " << portidx << "\n"; + if (port.clk_enable) { + log_assert(!for_debug); + RTLIL::SigBit clk_bit = port.clk[0]; + clk_bit = sigmaps[clk_bit.wire->module](clk_bit); + if (clk_bit.wire) { + f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_") + << mangle(clk_bit) << ") {\n"; + } else { + f << indent << "if (false) {\n"; } - f << ";\n"; - if (edge_wires[wire]) { - if (unbuffered_wires[wire]) { - f << indent << "value<" << width << "> prev_" << mangle(wire); - if (wire->has_attribute(ID::init)) { - f << " "; - dump_const_init(wire->attributes.at(ID::init)); - } + inc_indent(); + } + std::vector inlined_cells_addr; + collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr); + if (!inlined_cells_addr.empty()) + dump_inlined_cells(inlined_cells_addr); + std::string valid_index_temp = fresh_temporary(); + f << indent << "auto " << valid_index_temp << " = memory_index("; + // Almost all non-elidable cells cannot appear in debug_eval(), but $memrd is an exception; asynchronous + // memory read ports can. + dump_sigspec_rhs(port.addr, for_debug); + f << ", " << mem->start_offset << ", " << mem->size << ");\n"; + bool has_enable = port.clk_enable && !port.en.is_fully_ones(); + if (has_enable) { + std::vector inlined_cells_en; + collect_sigspec_rhs(port.en, for_debug, inlined_cells_en); + if (!inlined_cells_en.empty()) + dump_inlined_cells(inlined_cells_en); + f << indent << "if ("; + dump_sigspec_rhs(port.en); + f << ") {\n"; + inc_indent(); + } + // The generated code has two bounds checks; one in an assertion, and another that guards the read. + // This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless + // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DCXXRTL_NDEBUG + // not only for release builds, but also to make sure the simulator (which is presumably embedded in some + // larger program) will never crash the code that calls into it. + // + // If assertions are disabled, out of bounds reads are defined to return zero. + f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds read\");\n"; + f << indent << "if(" << valid_index_temp << ".valid) {\n"; + inc_indent(); + if (!mem->wr_ports.empty()) { + std::string lhs_temp = fresh_temporary(); + f << indent << "value<" << mem->width << "> " << lhs_temp << " = " + << mangle(mem) << "[" << valid_index_temp << ".index];\n"; + bool transparent = false; + for (auto bit : port.transparency_mask) + if (bit) + transparent = true; + if (transparent) { + std::string addr_temp = fresh_temporary(); + f << indent << "const value<" << port.addr.size() << "> &" << addr_temp << " = "; + dump_sigspec_rhs(port.addr); f << ";\n"; - } - for (auto edge_type : edge_types) { - if (edge_type.first.wire == wire) { - std::string prev, next; - if (unbuffered_wires[wire]) { - prev = "prev_" + mangle(edge_type.first.wire); - next = mangle(edge_type.first.wire); - } else { - prev = mangle(edge_type.first.wire) + ".curr"; - next = mangle(edge_type.first.wire) + ".next"; - } - prev += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()"; - next += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()"; - if (edge_type.second != RTLIL::STn) { - f << indent << "bool posedge_" << mangle(edge_type.first) << "() const {\n"; - inc_indent(); - f << indent << "return !" << prev << " && " << next << ";\n"; - dec_indent(); - f << indent << "}\n"; - } - if (edge_type.second != RTLIL::STp) { - f << indent << "bool negedge_" << mangle(edge_type.first) << "() const {\n"; - inc_indent(); - f << indent << "return " << prev << " && !" << next << ";\n"; - dec_indent(); - f << indent << "}\n"; - } + for (int i = 0; i < GetSize(mem->wr_ports); i++) { + auto &wrport = mem->wr_ports[i]; + if (!port.transparency_mask[i]) + continue; + f << indent << "if (" << addr_temp << " == "; + dump_sigspec_rhs(wrport.addr); + f << ") {\n"; + inc_indent(); + f << indent << lhs_temp << " = " << lhs_temp; + f << ".update("; + dump_sigspec_rhs(wrport.data); + f << ", "; + dump_sigspec_rhs(wrport.en); + f << ");\n"; + dec_indent(); + f << indent << "}\n"; } } + f << indent; + dump_sigspec_lhs(port.data); + f << " = " << lhs_temp << ";\n"; + } else { + f << indent; + dump_sigspec_lhs(port.data); + f << " = " << mangle(mem) << "[" << valid_index_temp << ".index];\n"; } + dec_indent(); + f << indent << "} else {\n"; + inc_indent(); + f << indent; + dump_sigspec_lhs(port.data); + f << " = value<" << mem->width << "> {};\n"; + dec_indent(); + f << indent << "}\n"; + if (has_enable && !port.ce_over_srst) { + dec_indent(); + f << indent << "}\n"; + } + if (port.srst != State::S0) { + // Synchronous reset + std::vector inlined_cells_srst; + collect_sigspec_rhs(port.srst, for_debug, inlined_cells_srst); + if (!inlined_cells_srst.empty()) + dump_inlined_cells(inlined_cells_srst); + f << indent << "if ("; + dump_sigspec_rhs(port.srst); + f << " == value<1> {1u}) {\n"; + inc_indent(); + f << indent; + dump_sigspec_lhs(port.data); + f << " = "; + dump_const(port.srst_value); + f << ";\n"; + dec_indent(); + f << indent << "}\n"; + } + if (has_enable && port.ce_over_srst) { + dec_indent(); + f << indent << "}\n"; + } + if (port.clk_enable) { + dec_indent(); + f << indent << "}\n"; + } + if (port.arst != State::S0) { + // Asynchronous reset + std::vector inlined_cells_arst; + collect_sigspec_rhs(port.arst, for_debug, inlined_cells_arst); + if (!inlined_cells_arst.empty()) + dump_inlined_cells(inlined_cells_arst); + f << indent << "if ("; + dump_sigspec_rhs(port.arst); + f << " == value<1> {1u}) {\n"; + inc_indent(); + f << indent; + dump_sigspec_lhs(port.data); + f << " = "; + dump_const(port.arst_value); + f << ";\n"; + dec_indent(); + f << indent << "}\n"; } } - void dump_memory(RTLIL::Module *module, const RTLIL::Memory *memory) + void dump_mem_wrports(const Mem *mem, bool for_debug = false) { - vector init_cells; - for (auto cell : module->cells()) - if (cell->type == ID($meminit) && cell->getParam(ID::MEMID).decode_string() == memory->name.str()) - init_cells.push_back(cell); + log_assert(!for_debug); + for (int portidx = 0; portidx < GetSize(mem->wr_ports); portidx++) { + auto &port = mem->wr_ports[portidx]; + dump_attrs(&port); + f << indent << "// memory " << mem->memid.str() << " write port " << portidx << "\n"; + if (port.clk_enable) { + RTLIL::SigBit clk_bit = port.clk[0]; + clk_bit = sigmaps[clk_bit.wire->module](clk_bit); + if (clk_bit.wire) { + f << indent << "if (" << (port.clk_polarity ? "posedge_" : "negedge_") + << mangle(clk_bit) << ") {\n"; + } else { + f << indent << "if (false) {\n"; + } + inc_indent(); + } + std::vector inlined_cells_addr; + collect_sigspec_rhs(port.addr, for_debug, inlined_cells_addr); + if (!inlined_cells_addr.empty()) + dump_inlined_cells(inlined_cells_addr); + std::string valid_index_temp = fresh_temporary(); + f << indent << "auto " << valid_index_temp << " = memory_index("; + dump_sigspec_rhs(port.addr); + f << ", " << mem->start_offset << ", " << mem->size << ");\n"; + // See above for rationale of having both the assert and the condition. + // + // If assertions are disabled, out of bounds writes are defined to do nothing. + f << indent << "CXXRTL_ASSERT(" << valid_index_temp << ".valid && \"out of bounds write\");\n"; + f << indent << "if (" << valid_index_temp << ".valid) {\n"; + inc_indent(); + std::vector inlined_cells; + collect_sigspec_rhs(port.data, for_debug, inlined_cells); + collect_sigspec_rhs(port.en, for_debug, inlined_cells); + if (!inlined_cells.empty()) + dump_inlined_cells(inlined_cells); + f << indent << mangle(mem) << ".update(" << valid_index_temp << ".index, "; + dump_sigspec_rhs(port.data); + f << ", "; + dump_sigspec_rhs(port.en); + f << ", " << portidx << ");\n"; + dec_indent(); + f << indent << "}\n"; + if (port.clk_enable) { + dec_indent(); + f << indent << "}\n"; + } + } + } - std::sort(init_cells.begin(), init_cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) { - int a_addr = a->getPort(ID::ADDR).as_int(), b_addr = b->getPort(ID::ADDR).as_int(); - int a_prio = a->getParam(ID::PRIORITY).as_int(), b_prio = b->getParam(ID::PRIORITY).as_int(); - return a_prio > b_prio || (a_prio == b_prio && a_addr < b_addr); - }); + void dump_wire(const RTLIL::Wire *wire, bool is_local) + { + const auto &wire_type = wire_types[wire]; + if (!wire_type.is_named() || wire_type.is_local() != is_local) + return; - dump_attrs(memory); - f << indent << "memory<" << memory->width << "> " << mangle(memory) - << " { " << memory->size << "u"; - if (init_cells.empty()) { - f << " };\n"; + dump_attrs(wire); + f << indent; + if (wire->port_input && wire->port_output) + f << "/*inout*/ "; + else if (wire->port_input) + f << "/*input*/ "; + else if (wire->port_output) + f << "/*output*/ "; + f << (wire_type.is_buffered() ? "wire" : "value"); + if (wire->module->has_attribute(ID(cxxrtl_blackbox)) && wire->has_attribute(ID(cxxrtl_width))) { + f << "<" << wire->get_string_attribute(ID(cxxrtl_width)) << ">"; } else { - f << ",\n"; - inc_indent(); - for (auto cell : init_cells) { - dump_attrs(cell); - RTLIL::Const data = cell->getPort(ID::DATA).as_const(); - size_t width = cell->getParam(ID::WIDTH).as_int(); - size_t words = cell->getParam(ID::WORDS).as_int(); - f << indent << "memory<" << memory->width << ">::init<" << words << "> { " - << stringf("%#x", cell->getPort(ID::ADDR).as_int()) << ", {"; + f << "<" << wire->width << ">"; + } + f << " " << mangle(wire) << ";\n"; + if (edge_wires[wire]) { + if (!wire_type.is_buffered()) { + f << indent << "value<" << wire->width << "> prev_" << mangle(wire) << ";\n"; + } + for (auto edge_type : edge_types) { + if (edge_type.first.wire == wire) { + std::string prev, next; + if (!wire_type.is_buffered()) { + prev = "prev_" + mangle(edge_type.first.wire); + next = mangle(edge_type.first.wire); + } else { + prev = mangle(edge_type.first.wire) + ".curr"; + next = mangle(edge_type.first.wire) + ".next"; + } + prev += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()"; + next += ".slice<" + std::to_string(edge_type.first.offset) + ">().val()"; + if (edge_type.second != RTLIL::STn) { + f << indent << "bool posedge_" << mangle(edge_type.first) << "() const {\n"; + inc_indent(); + f << indent << "return !" << prev << " && " << next << ";\n"; + dec_indent(); + f << indent << "}\n"; + } + if (edge_type.second != RTLIL::STp) { + f << indent << "bool negedge_" << mangle(edge_type.first) << "() const {\n"; + inc_indent(); + f << indent << "return " << prev << " && !" << next << ";\n"; + dec_indent(); + f << indent << "}\n"; + } + } + } + } + } + + void dump_debug_wire(const RTLIL::Wire *wire, bool is_local) + { + const auto &wire_type = wire_types[wire]; + if (wire_type.is_member()) + return; + + const auto &debug_wire_type = debug_wire_types[wire]; + if (!debug_wire_type.is_named() || debug_wire_type.is_local() != is_local) + return; + + dump_attrs(wire); + f << indent; + if (debug_wire_type.is_outline()) + f << "/*outline*/ "; + f << "value<" << wire->width << "> " << mangle(wire) << ";\n"; + } + + void dump_reset_method(RTLIL::Module *module) + { + int mem_init_idx = 0; + inc_indent(); + for (auto wire : module->wires()) { + const auto &wire_type = wire_types[wire]; + if (!wire_type.is_named() || wire_type.is_local()) continue; + if (!wire_init.count(wire)) continue; + + f << indent << mangle(wire) << " = "; + if (wire_types[wire].is_buffered()) { + f << "wire<" << wire->width << ">"; + } else { + f << "value<" << wire->width << ">"; + } + dump_const_init(wire_init.at(wire), wire->width); + f << ";\n"; + + if (edge_wires[wire] && !wire_types[wire].is_buffered()) { + f << indent << "prev_" << mangle(wire) << " = "; + dump_const(wire_init.at(wire), wire->width); + f << ";\n"; + } + } + for (auto &mem : mod_memories[module]) { + for (auto &init : mem.inits) { + if (init.removed) + continue; + dump_attrs(&init); + int words = GetSize(init.data) / mem.width; + f << indent << "static const value<" << mem.width << "> "; + f << "mem_init_" << ++mem_init_idx << "[" << words << "] {"; inc_indent(); - for (size_t n = 0; n < words; n++) { + for (int n = 0; n < words; n++) { if (n % 4 == 0) f << "\n" << indent; else f << " "; - dump_const(data, width, n * width, /*fixed_width=*/true); + dump_const(init.data, mem.width, n * mem.width, /*fixed_width=*/true); f << ","; } dec_indent(); - f << "\n" << indent << "}},\n"; + f << "\n"; + f << indent << "};\n"; + f << indent << "std::copy(std::begin(mem_init_" << mem_init_idx << "), "; + f << "std::end(mem_init_" << mem_init_idx << "), "; + f << "&" << mangle(&mem) << ".data[" << stringf("%#x", init.addr.as_int()) << "]);\n"; } - dec_indent(); - f << indent << "};\n"; - } + } + for (auto cell : module->cells()) { + // Async and initial effectful cells have additional state, which must be reset as well. + if (is_effectful_cell(cell->type)) + if (!cell->getParam(ID::TRG_ENABLE).as_bool() || cell->getParam(ID::TRG_WIDTH).as_int() == 0) + f << indent << mangle(cell) << " = {};\n"; + if (is_internal_cell(cell->type)) + continue; + f << indent << mangle(cell); + RTLIL::Module *cell_module = module->design->module(cell->type); + if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) { + f << "->reset();\n"; + } else { + f << ".reset();\n"; + } + } + dec_indent(); } void dump_eval_method(RTLIL::Module *module) @@ -1581,7 +2178,7 @@ struct CxxrtlWorker { } } for (auto wire : module->wires()) - dump_wire(wire, /*is_local_context=*/true); + dump_wire(wire, /*is_local=*/true); for (auto node : schedule[module]) { switch (node.type) { case FlowGraph::Node::Type::CONNECT: @@ -1593,8 +2190,20 @@ struct CxxrtlWorker { case FlowGraph::Node::Type::CELL_EVAL: dump_cell_eval(node.cell); break; - case FlowGraph::Node::Type::PROCESS: - dump_process(node.process); + case FlowGraph::Node::Type::EFFECT_SYNC: + dump_cell_effect_sync(node.cells); + break; + case FlowGraph::Node::Type::PROCESS_CASE: + dump_process_case(node.process); + break; + case FlowGraph::Node::Type::PROCESS_SYNC: + dump_process_syncs(node.process); + break; + case FlowGraph::Node::Type::MEM_RDPORT: + dump_mem_rdport(node.mem, node.portidx); + break; + case FlowGraph::Node::Type::MEM_WRPORTS: + dump_mem_wrports(node.mem); break; } } @@ -1603,183 +2212,321 @@ struct CxxrtlWorker { dec_indent(); } + void dump_debug_eval_method(RTLIL::Module *module) + { + inc_indent(); + for (auto wire : module->wires()) + dump_debug_wire(wire, /*is_local=*/true); + for (auto node : debug_schedule[module]) { + switch (node.type) { + case FlowGraph::Node::Type::CONNECT: + dump_connect(node.connect, /*for_debug=*/true); + break; + case FlowGraph::Node::Type::CELL_SYNC: + dump_cell_sync(node.cell, /*for_debug=*/true); + break; + case FlowGraph::Node::Type::CELL_EVAL: + dump_cell_eval(node.cell, /*for_debug=*/true); + break; + case FlowGraph::Node::Type::PROCESS_CASE: + dump_process_case(node.process, /*for_debug=*/true); + break; + case FlowGraph::Node::Type::PROCESS_SYNC: + dump_process_syncs(node.process, /*for_debug=*/true); + break; + case FlowGraph::Node::Type::MEM_RDPORT: + dump_mem_rdport(node.mem, node.portidx, /*for_debug=*/true); + break; + case FlowGraph::Node::Type::MEM_WRPORTS: + dump_mem_wrports(node.mem, /*for_debug=*/true); + break; + default: + log_abort(); + } + } + dec_indent(); + } + void dump_commit_method(RTLIL::Module *module) { inc_indent(); f << indent << "bool changed = false;\n"; for (auto wire : module->wires()) { - if (elided_wires.count(wire)) - continue; - if (unbuffered_wires[wire]) { - if (edge_wires[wire]) - f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n"; - continue; - } - if (!module->get_bool_attribute(ID(cxxrtl_blackbox)) || wire->port_id != 0) - f << indent << "changed |= " << mangle(wire) << ".commit();\n"; + const auto &wire_type = wire_types[wire]; + if (wire_type.type == WireType::MEMBER && edge_wires[wire]) + f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n"; + if (wire_type.is_buffered()) + f << indent << "if (" << mangle(wire) << ".commit(observer)) changed = true;\n"; } if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { - for (auto memory : module->memories) { - if (!writable_memories[memory.second]) + for (auto &mem : mod_memories[module]) { + if (!writable_memories.count({module, mem.memid})) continue; - f << indent << "changed |= " << mangle(memory.second) << ".commit();\n"; + f << indent << "if (" << mangle(&mem) << ".commit(observer)) changed = true;\n"; } for (auto cell : module->cells()) { if (is_internal_cell(cell->type)) continue; const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; - f << indent << "changed |= " << mangle(cell) << access << "commit();\n"; + f << indent << "if (" << mangle(cell) << access << "commit(observer)) changed = true;\n"; } } f << indent << "return changed;\n"; dec_indent(); } + void dump_metadata_map(const dict &metadata_map) + { + if (metadata_map.empty()) { + f << "metadata_map()"; + return; + } + f << "metadata_map({\n"; + inc_indent(); + for (auto metadata_item : metadata_map) { + if (!metadata_item.first.isPublic()) + continue; + if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) { + f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n"; + continue; + } + f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", "; + // In Yosys, a real is a type of string. + if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { + f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint; + } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { + f << escape_cxx_string(metadata_item.second.decode_string()); + } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) { + f << "INT64_C(" << metadata_item.second.as_int(/*is_signed=*/true) << ")"; + } else { + f << "UINT64_C(" << metadata_item.second.as_int(/*is_signed=*/false) << ")"; + } + f << " },\n"; + } + dec_indent(); + f << indent << "})"; + } + + void dump_debug_attrs(const RTLIL::AttrObject *object) + { + dict attributes = object->attributes; + // Inherently necessary to get access to the object, so a waste of space to emit. + attributes.erase(ID::hdlname); + // Internal Yosys attribute that should be removed but isn't. + attributes.erase(ID::module_not_derived); + dump_metadata_map(attributes); + } + void dump_debug_info_method(RTLIL::Module *module) { + size_t count_scopes = 0; size_t count_public_wires = 0; - size_t count_const_wires = 0; - size_t count_alias_wires = 0; size_t count_member_wires = 0; - size_t count_skipped_wires = 0; + size_t count_undriven = 0; size_t count_driven_sync = 0; size_t count_driven_comb = 0; - size_t count_undriven = 0; size_t count_mixed_driver = 0; + size_t count_alias_wires = 0; + size_t count_const_wires = 0; + size_t count_inline_wires = 0; + size_t count_skipped_wires = 0; inc_indent(); f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n"; - for (auto wire : module->wires()) { - if (wire->name[0] != '\\') - continue; - if (module->get_bool_attribute(ID(cxxrtl_blackbox)) && (wire->port_id == 0)) - continue; - count_public_wires++; - if (debug_const_wires.count(wire)) { - // Wire tied to a constant - f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = "; - dump_const(debug_const_wires[wire]); - f << ";\n"; - f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); - f << ", debug_item(const_" << mangle(wire) << ", "; - f << wire->start_offset << "));\n"; - count_const_wires++; - } else if (debug_alias_wires.count(wire)) { - // Alias of a member wire - f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); - f << ", debug_item(debug_alias(), " << mangle(debug_alias_wires[wire]) << ", "; - f << wire->start_offset << "));\n"; - count_alias_wires++; - } else if (!localized_wires.count(wire)) { - // Member wire - std::vector flags; - - if (wire->port_input && wire->port_output) - flags.push_back("INOUT"); - else if (wire->port_input) - flags.push_back("INPUT"); - else if (wire->port_output) - flags.push_back("OUTPUT"); - - bool has_driven_sync = false; - bool has_driven_comb = false; - bool has_undriven = false; - SigSpec sig(wire); - for (auto bit : sig.bits()) - if (!bit_has_state.count(bit)) - has_undriven = true; - else if (bit_has_state[bit]) - has_driven_sync = true; - else - has_driven_comb = true; - if (has_driven_sync) - flags.push_back("DRIVEN_SYNC"); - if (has_driven_sync && !has_driven_comb && !has_undriven) - count_driven_sync++; - if (has_driven_comb) - flags.push_back("DRIVEN_COMB"); - if (!has_driven_sync && has_driven_comb && !has_undriven) - count_driven_comb++; - if (has_undriven) - flags.push_back("UNDRIVEN"); - if (!has_driven_sync && !has_driven_comb && has_undriven) - count_undriven++; - if (has_driven_sync + has_driven_comb + has_undriven > 1) - count_mixed_driver++; - - f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire)); - f << ", debug_item(" << mangle(wire) << ", "; - f << wire->start_offset; - bool first = true; - for (auto flag : flags) { - if (first) { - first = false; - f << ", "; + f << indent << "if (scopes) {\n"; + inc_indent(); + // The module is responsible for adding its own scope. + f << indent << "scopes->add(path.empty() ? path : path.substr(0, path.size() - 1), "; + f << escape_cxx_string(get_hdl_name(module)) << ", "; + dump_debug_attrs(module); + f << ", std::move(cell_attrs));\n"; + count_scopes++; + // If there were any submodules that were flattened, the module is also responsible for adding them. + for (auto cell : module->cells()) { + if (cell->type != ID($scopeinfo)) continue; + if (cell->getParam(ID::TYPE).decode_string() == "module") { + auto module_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Module); + auto cell_attrs = scopeinfo_attributes(cell, ScopeinfoAttrs::Cell); + cell_attrs.erase(ID::module_not_derived); + f << indent << "scopes->add(path + " << escape_cxx_string(get_hdl_name(cell)) << ", "; + f << escape_cxx_string(cell->get_string_attribute(ID(module))) << ", "; + dump_metadata_map(module_attrs); + f << ", "; + dump_metadata_map(cell_attrs); + f << ");\n"; + } else log_assert(false && "Unknown $scopeinfo type"); + count_scopes++; + } + dec_indent(); + f << indent << "}\n"; + f << indent << "if (items) {\n"; + inc_indent(); + for (auto wire : module->wires()) { + const auto &debug_wire_type = debug_wire_types[wire]; + if (!wire->name.isPublic()) + continue; + count_public_wires++; + switch (debug_wire_type.type) { + case WireType::BUFFERED: + case WireType::MEMBER: { + // Member wire + std::vector flags; + + if (wire->port_input && wire->port_output) + flags.push_back("INOUT"); + else if (wire->port_output) + flags.push_back("OUTPUT"); + else if (wire->port_input) + flags.push_back("INPUT"); + + bool has_driven_sync = false; + bool has_driven_comb = false; + bool has_undriven = false; + if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { + for (auto bit : SigSpec(wire)) + if (!bit_has_state.count(bit)) + has_undriven = true; + else if (bit_has_state[bit]) + has_driven_sync = true; + else + has_driven_comb = true; + } else if (wire->port_output) { + switch (cxxrtl_port_type(module, wire->name)) { + case CxxrtlPortType::SYNC: + has_driven_sync = true; + break; + case CxxrtlPortType::COMB: + has_driven_comb = true; + break; + case CxxrtlPortType::UNKNOWN: + has_driven_sync = has_driven_comb = true; + break; + } + } else { + has_undriven = true; + } + if (has_undriven) + flags.push_back("UNDRIVEN"); + if (!has_driven_sync && !has_driven_comb && has_undriven) + count_undriven++; + if (has_driven_sync) + flags.push_back("DRIVEN_SYNC"); + if (has_driven_sync && !has_driven_comb && !has_undriven) + count_driven_sync++; + if (has_driven_comb) + flags.push_back("DRIVEN_COMB"); + if (!has_driven_sync && has_driven_comb && !has_undriven) + count_driven_comb++; + if (has_driven_sync + has_driven_comb + has_undriven > 1) + count_mixed_driver++; + + f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire)); + f << ", debug_item(" << mangle(wire) << ", " << wire->start_offset; + bool first = true; + for (auto flag : flags) { + if (first) { + first = false; + f << ", "; + } else { + f << "|"; + } + f << "debug_item::" << flag; + } + f << "), "; + dump_debug_attrs(wire); + f << ");\n"; + count_member_wires++; + break; + } + case WireType::ALIAS: { + // Alias of a member wire + const RTLIL::Wire *aliasee = debug_wire_type.sig_subst.as_wire(); + f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire)); + f << ", debug_item("; + // If the aliasee is an outline, then the alias must be an outline, too; otherwise downstream + // tooling has no way to find out about the outline. + if (debug_wire_types[aliasee].is_outline()) + f << "debug_eval_outline"; + else + f << "debug_alias()"; + f << ", " << mangle(aliasee) << ", " << wire->start_offset << "), "; + dump_debug_attrs(aliasee); + f << ");\n"; + count_alias_wires++; + break; + } + case WireType::CONST: { + // Wire tied to a constant + f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = "; + dump_const(debug_wire_type.sig_subst.as_const()); + f << ";\n"; + f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire)); + f << ", debug_item(const_" << mangle(wire) << ", " << wire->start_offset << "), "; + dump_debug_attrs(wire); + f << ");\n"; + count_const_wires++; + break; + } + case WireType::OUTLINE: { + // Localized or inlined, but rematerializable wire + f << indent << "items->add(path + " << escape_cxx_string(get_hdl_name(wire)); + f << ", debug_item(debug_eval_outline, " << mangle(wire) << ", " << wire->start_offset << "), "; + dump_debug_attrs(wire); + f << ");\n"; + count_inline_wires++; + break; + } + default: { + // Localized or inlined wire with no debug information + count_skipped_wires++; + break; + } + } + } + if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { + for (auto &mem : mod_memories[module]) { + if (!mem.memid.isPublic()) + continue; + f << indent << "items->add(path + " << escape_cxx_string(mem.packed ? get_hdl_name(mem.cell) : get_hdl_name(mem.mem)); + f << ", debug_item(" << mangle(&mem) << ", "; + f << mem.start_offset << "), "; + if (mem.packed) { + dump_debug_attrs(mem.cell); } else { - f << "|"; + dump_debug_attrs(mem.mem); } - f << "debug_item::" << flag; + f << ");\n"; } - f << "));\n"; - count_member_wires++; - } else { - count_skipped_wires++; } - } + dec_indent(); + f << indent << "}\n"; if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { - for (auto &memory_it : module->memories) { - if (memory_it.first[0] != '\\') - continue; - f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(memory_it.second)); - f << ", debug_item(" << mangle(memory_it.second) << ", "; - f << memory_it.second->start_offset << "));\n"; - } for (auto cell : module->cells()) { if (is_internal_cell(cell->type)) continue; const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : "."; - f << indent << mangle(cell) << access << "debug_info(items, "; - f << "path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ");\n"; + f << indent << mangle(cell) << access; + f << "debug_info(items, scopes, path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ", "; + dump_debug_attrs(cell); + f << ");\n"; } } dec_indent(); log_debug("Debug information statistics for module `%s':\n", log_id(module)); + log_debug(" Scopes: %zu", count_scopes); log_debug(" Public wires: %zu, of which:\n", count_public_wires); - log_debug(" Const wires: %zu\n", count_const_wires); - log_debug(" Alias wires: %zu\n", count_alias_wires); log_debug(" Member wires: %zu, of which:\n", count_member_wires); + log_debug(" Undriven: %zu (incl. inputs)\n", count_undriven); log_debug(" Driven sync: %zu\n", count_driven_sync); log_debug(" Driven comb: %zu\n", count_driven_comb); - log_debug(" Undriven: %zu\n", count_undriven); log_debug(" Mixed driver: %zu\n", count_mixed_driver); - log_debug(" Other wires: %zu (no debug information)\n", count_skipped_wires); - } - - void dump_metadata_map(const dict &metadata_map) - { - if (metadata_map.empty()) { - f << "metadata_map()"; - return; + if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { + log_debug(" Inline wires: %zu\n", count_inline_wires); + log_debug(" Alias wires: %zu\n", count_alias_wires); + log_debug(" Const wires: %zu\n", count_const_wires); + log_debug(" Other wires: %zu%s\n", count_skipped_wires, + count_skipped_wires > 0 ? " (debug unavailable)" : ""); } - f << "metadata_map({\n"; - inc_indent(); - for (auto metadata_item : metadata_map) { - if (!metadata_item.first.begins_with("\\")) - continue; - f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", "; - if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { - f << std::showpoint << std::stod(metadata_item.second.decode_string()) << std::noshowpoint; - } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { - f << escape_cxx_string(metadata_item.second.decode_string()); - } else { - f << metadata_item.second.as_int(/*is_signed=*/metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED); - if (!(metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED)) - f << "u"; - } - f << " },\n"; - } - dec_indent(); - f << indent << "})"; } void dump_module_intf(RTLIL::Module *module) @@ -1792,23 +2539,34 @@ struct CxxrtlWorker { inc_indent(); for (auto wire : module->wires()) { if (wire->port_id != 0) - dump_wire(wire, /*is_local_context=*/false); + dump_wire(wire, /*is_local=*/false); } f << "\n"; - f << indent << "bool eval() override {\n"; + f << indent << "void reset() override {\n"; + dump_reset_method(module); + f << indent << "}\n"; + f << "\n"; + // No default argument, to prevent unintentional `return bb_foo::eval();` calls that drop performer. + f << indent << "bool eval(performer *performer) override {\n"; dump_eval_method(module); f << indent << "}\n"; f << "\n"; - f << indent << "bool commit() override {\n"; + f << indent << "virtual bool commit(observer &observer) {\n"; dump_commit_method(module); f << indent << "}\n"; f << "\n"; + f << indent << "bool commit() override {\n"; + f << indent << indent << "observer observer;\n"; + f << indent << indent << "return commit(observer);\n"; + f << indent << "}\n"; if (debug_info) { - f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n"; + f << "\n"; + f << indent << "void debug_info(debug_items *items, debug_scopes *scopes, " + << "std::string path, metadata_map &&cell_attrs = {}) override {\n"; dump_debug_info_method(module); f << indent << "}\n"; - f << "\n"; } + f << "\n"; f << indent << "static std::unique_ptr<" << mangle(module); f << template_params(module, /*is_decl=*/false) << "> "; f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n"; @@ -1838,17 +2596,29 @@ struct CxxrtlWorker { f << indent << "struct " << mangle(module) << " : public module {\n"; inc_indent(); for (auto wire : module->wires()) - dump_wire(wire, /*is_local_context=*/false); - f << "\n"; + dump_wire(wire, /*is_local=*/false); + for (auto wire : module->wires()) + dump_debug_wire(wire, /*is_local=*/false); bool has_memories = false; - for (auto memory : module->memories) { - dump_memory(module, memory.second); + for (auto &mem : mod_memories[module]) { + dump_attrs(&mem); + f << indent << "memory<" << mem.width << "> " << mangle(&mem) + << " { " << mem.size << "u };\n"; has_memories = true; } if (has_memories) f << "\n"; bool has_cells = false; for (auto cell : module->cells()) { + // Async and initial effectful cells have additional state, which requires storage. + if (is_effectful_cell(cell->type)) { + if (cell->getParam(ID::TRG_ENABLE).as_bool() && cell->getParam(ID::TRG_WIDTH).as_int() == 0) + f << indent << "value<1> " << mangle(cell) << ";\n"; // async initial cell + if (!cell->getParam(ID::TRG_ENABLE).as_bool() && cell->type == ID($print)) + f << indent << "value<" << (1 + cell->getParam(ID::ARGS_WIDTH).as_int()) << "> " << mangle(cell) << ";\n"; // {EN, ARGS} + if (!cell->getParam(ID::TRG_ENABLE).as_bool() && cell->type == ID($check)) + f << indent << "value<2> " << mangle(cell) << ";\n"; // {EN, A} + } if (is_internal_cell(cell->type)) continue; dump_attrs(cell); @@ -1863,16 +2633,47 @@ struct CxxrtlWorker { dump_metadata_map(cell->attributes); f << ");\n"; } else { - f << indent << mangle(cell_module) << " " << mangle(cell) << ";\n"; + f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n"; } has_cells = true; } if (has_cells) f << "\n"; - f << indent << "bool eval() override;\n"; - f << indent << "bool commit() override;\n"; - if (debug_info) - f << indent << "void debug_info(debug_items &items, std::string path = \"\") override;\n"; + f << indent << mangle(module) << "(interior) {}\n"; + f << indent << mangle(module) << "() {\n"; + inc_indent(); + f << indent << "reset();\n"; + dec_indent(); + f << indent << "};\n"; + f << "\n"; + f << indent << "void reset() override;\n"; + f << "\n"; + f << indent << "bool eval(performer *performer = nullptr) override;\n"; + f << "\n"; + f << indent << "template\n"; + f << indent << "bool commit(ObserverT &observer) {\n"; + dump_commit_method(module); + f << indent << "}\n"; + f << "\n"; + f << indent << "bool commit() override {\n"; + f << indent << indent << "observer observer;\n"; + f << indent << indent << "return commit<>(observer);\n"; + f << indent << "}\n"; + if (debug_info) { + if (debug_eval) { + f << "\n"; + f << indent << "void debug_eval();\n"; + for (auto wire : module->wires()) + if (debug_wire_types[wire].is_outline()) { + f << indent << "debug_outline debug_eval_outline { std::bind(&" + << mangle(module) << "::debug_eval, this) };\n"; + break; + } + } + f << "\n"; + f << indent << "void debug_info(debug_items *items, debug_scopes *scopes, " + << "std::string path, metadata_map &&cell_attrs = {}) override;\n"; + } dec_indent(); f << indent << "}; // struct " << mangle(module) << "\n"; f << "\n"; @@ -1883,20 +2684,28 @@ struct CxxrtlWorker { { if (module->get_bool_attribute(ID(cxxrtl_blackbox))) return; - f << indent << "bool " << mangle(module) << "::eval() {\n"; - dump_eval_method(module); + f << indent << "void " << mangle(module) << "::reset() {\n"; + dump_reset_method(module); f << indent << "}\n"; f << "\n"; - f << indent << "bool " << mangle(module) << "::commit() {\n"; - dump_commit_method(module); + f << indent << "bool " << mangle(module) << "::eval(performer *performer) {\n"; + dump_eval_method(module); f << indent << "}\n"; - f << "\n"; if (debug_info) { - f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n"; + if (debug_eval) { + f << "\n"; + f << indent << "void " << mangle(module) << "::debug_eval() {\n"; + dump_debug_eval_method(module); + f << indent << "}\n"; + } + f << "\n"; + f << indent << "CXXRTL_EXTREMELY_COLD\n"; + f << indent << "void " << mangle(module) << "::debug_info(debug_items *items, debug_scopes *scopes, " + << "std::string path, metadata_map &&cell_attrs) {\n"; dump_debug_info_method(module); f << indent << "}\n"; - f << "\n"; } + f << "\n"; } void dump_design(RTLIL::Design *design) @@ -1936,7 +2745,7 @@ struct CxxrtlWorker { f << "#define " << include_guard << "\n"; f << "\n"; if (top_module != nullptr && debug_info) { - f << "#include \n"; + f << "#include \n"; f << "\n"; f << "#ifdef __cplusplus\n"; f << "extern \"C\" {\n"; @@ -1954,7 +2763,7 @@ struct CxxrtlWorker { } f << "#ifdef __cplusplus\n"; f << "\n"; - f << "#include \n"; + f << "#include \n"; f << "\n"; f << "using namespace cxxrtl;\n"; f << "\n"; @@ -1971,17 +2780,17 @@ struct CxxrtlWorker { } if (split_intf) - f << "#include \"" << intf_filename << "\"\n"; + f << "#include \"" << basename(intf_filename) << "\"\n"; else - f << "#include \n"; + f << "#include \n"; f << "\n"; f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n"; f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n"; - f << "#include \n"; + f << "#include \n"; f << "#endif\n"; f << "\n"; f << "#if defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n"; - f << "#include \n"; + f << "#include \n"; f << "#endif\n"; f << "\n"; f << "using namespace cxxrtl_yosys;\n"; @@ -2021,7 +2830,9 @@ struct CxxrtlWorker { void register_edge_signal(SigMap &sigmap, RTLIL::SigSpec signal, RTLIL::SyncType type) { signal = sigmap(signal); - log_assert(signal.is_wire() && signal.is_bit()); + if (signal.is_fully_const()) + return; // a clock, or more commonly a reset, can be tied to a constant driver + log_assert(is_valid_clock(signal)); log_assert(type == RTLIL::STp || type == RTLIL::STn || type == RTLIL::STe); RTLIL::SigBit sigbit = signal[0]; @@ -2029,7 +2840,8 @@ struct CxxrtlWorker { edge_types[sigbit] = type; else if (edge_types[sigbit] != type) edge_types[sigbit] = RTLIL::STe; - edge_wires.insert(signal.as_wire()); + // Cannot use as_wire because signal might not be a full wire, instead extract the wire from the sigbit + edge_wires.insert(sigbit.wire); } void analyze_design(RTLIL::Design *design) @@ -2044,11 +2856,21 @@ struct CxxrtlWorker { SigMap &sigmap = sigmaps[module]; sigmap.set(module); + std::vector &memories = mod_memories[module]; + memories = Mem::get_all_memories(module); + for (auto &mem : memories) { + mem.narrow(); + mem.coalesce_inits(); + } + if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto port : module->ports) { RTLIL::Wire *wire = module->wire(port); - if (wire->port_input && !wire->port_output) - unbuffered_wires.insert(wire); + if (wire->port_input && !wire->port_output) { + wire_types[wire] = debug_wire_types[wire] = {WireType::MEMBER}; + } else if (wire->port_input || wire->port_output) { + wire_types[wire] = debug_wire_types[wire] = {WireType::BUFFERED}; + } if (wire->has_attribute(ID(cxxrtl_edge))) { RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl_edge)]; if (!(edge_attr.flags & RTLIL::CONST_FLAG_STRING) || (int)edge_attr.decode_string().size() != GetSize(wire)) @@ -2078,18 +2900,24 @@ struct CxxrtlWorker { continue; } + for (auto wire : module->wires()) + if (wire->has_attribute(ID::init)) + wire_init[wire] = wire->attributes.at(ID::init); + + // Construct a flow graph where each node is a basic computational operation generally corresponding + // to a fragment of the RTLIL netlist. FlowGraph flow; for (auto conn : module->connections()) flow.add_node(conn); - dict memrw_cell_nodes; - dict, - pool> memwr_per_domain; for (auto cell : module->cells()) { if (!cell->known()) log_cmd_error("Unknown cell `%s'.\n", log_id(cell->type)); + if (cell->is_mem_cell()) + continue; + RTLIL::Module *cell_module = design->module(cell->type); if (cell_module && cell_module->get_blackbox_attribute() && @@ -2101,58 +2929,64 @@ struct CxxrtlWorker { cell_module->get_bool_attribute(ID(cxxrtl_template))) blackbox_specializations[cell_module].insert(template_args(cell)); - FlowGraph::Node *node = flow.add_node(cell); + flow.add_node(cell); // Various DFF cells are treated like posedge/negedge processes, see above for details. - if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) { - if (cell->getPort(ID::CLK).is_wire()) + if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) { + if (is_valid_clock(cell->getPort(ID::CLK))) register_edge_signal(sigmap, cell->getPort(ID::CLK), cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); } - // Similar for memory port cells. - if (cell->type.in(ID($memrd), ID($memwr))) { - if (cell->getParam(ID::CLK_ENABLE).as_bool()) { - if (cell->getPort(ID::CLK).is_wire()) - register_edge_signal(sigmap, cell->getPort(ID::CLK), - cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn); + + // Effectful cells may be triggered on posedge/negedge events. + if (is_effectful_cell(cell->type) && cell->getParam(ID::TRG_ENABLE).as_bool()) { + for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) { + RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1); + if (is_valid_clock(trg)) + register_edge_signal(sigmap, trg, + cell->parameters[ID::TRG_POLARITY][i] == RTLIL::S1 ? RTLIL::STp : RTLIL::STn); } - memrw_cell_nodes[cell] = node; - } - // Optimize access to read-only memories. - if (cell->type == ID($memwr)) - writable_memories.insert(module->memories[cell->getParam(ID::MEMID).decode_string()]); - // Collect groups of memory write ports in the same domain. - if (cell->type == ID($memwr) && cell->getParam(ID::CLK_ENABLE).as_bool() && cell->getPort(ID::CLK).is_wire()) { - RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0]; - const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()]; - memwr_per_domain[{clk_bit, memory}].insert(cell); } - // Handling of packed memories is delegated to the `memory_unpack` pass, so we can rely on the presence - // of RTLIL memory objects and $memrd/$memwr/$meminit cells. - if (cell->type.in(ID($mem))) - log_assert(false); } - for (auto cell : module->cells()) { - // Collect groups of memory write ports read by every transparent read port. - if (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool() && cell->getPort(ID::CLK).is_wire() && - cell->getParam(ID::TRANSPARENT).as_bool()) { - RTLIL::SigBit clk_bit = sigmap(cell->getPort(ID::CLK))[0]; - const RTLIL::Memory *memory = module->memories[cell->getParam(ID::MEMID).decode_string()]; - for (auto memwr_cell : memwr_per_domain[{clk_bit, memory}]) { - transparent_for[cell].insert(memwr_cell); - // Our implementation of transparent $memrd cells reads \EN, \ADDR and \DATA from every $memwr cell - // in the same domain, which isn't directly visible in the netlist. Add these uses explicitly. - flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::EN)); - flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::ADDR)); - flow.add_uses(memrw_cell_nodes[cell], memwr_cell->getPort(ID::DATA)); + + for (auto &mem : memories) { + flow.add_node(&mem); + + // Clocked memory cells are treated like posedge/negedge processes as well. + for (auto &port : mem.rd_ports) { + if (port.clk_enable) + if (is_valid_clock(port.clk)) + register_edge_signal(sigmap, port.clk, + port.clk_polarity ? RTLIL::STp : RTLIL::STn); + // For read ports, also move initial value to wire_init (if any). + for (int i = 0; i < GetSize(port.data); i++) { + if (port.init_value[i] != State::Sx) { + SigBit bit = port.data[i]; + if (bit.wire) { + auto &init = wire_init[bit.wire]; + if (init == RTLIL::Const()) { + init = RTLIL::Const(State::Sx, GetSize(bit.wire)); + } + init[bit.offset] = port.init_value[i]; + } + } } } + for (auto &port : mem.wr_ports) { + if (port.clk_enable) + if (is_valid_clock(port.clk)) + register_edge_signal(sigmap, port.clk, + port.clk_polarity ? RTLIL::STp : RTLIL::STn); + } + + if (!mem.wr_ports.empty()) + writable_memories.insert({module, mem.memid}); } for (auto proc : module->processes) { flow.add_node(proc.second); - for (auto sync : proc.second->syncs) + for (auto sync : proc.second->syncs) { switch (sync->type) { // Edge-type sync rules require pre-registration. case RTLIL::STp: @@ -2175,61 +3009,46 @@ struct CxxrtlWorker { case RTLIL::STi: log_assert(false); } + for (auto &memwr : sync->mem_write_actions) { + writable_memories.insert({module, memwr.memid}); + } + } } - for (auto wire : module->wires()) { - if (!flow.is_elidable(wire)) continue; - if (wire->port_id != 0) continue; - if (wire->get_bool_attribute(ID::keep)) continue; - if (wire->name.begins_with("$") && !elide_internal) continue; - if (wire->name.begins_with("\\") && !elide_public) continue; - if (edge_wires[wire]) continue; - if (flow.wire_comb_defs[wire].size() > 1) - log_cmd_error("Wire %s.%s has multiple drivers.\n", log_id(module), log_id(wire)); - log_assert(flow.wire_comb_defs[wire].size() == 1); - elided_wires[wire] = **flow.wire_comb_defs[wire].begin(); - } - - dict, hash_ptr_ops> node_defs; - for (auto wire_comb_def : flow.wire_comb_defs) - for (auto node : wire_comb_def.second) - node_defs[node].insert(wire_comb_def.first); - + // Construct a linear order of the flow graph that minimizes the amount of feedback arcs. A flow graph + // without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only + // a single delta cycle. Scheduler scheduler; - dict::Vertex*, hash_ptr_ops> node_map; + dict::Vertex*, hash_ptr_ops> node_vertex_map; for (auto node : flow.nodes) - node_map[node] = scheduler.add(node); - for (auto node_def : node_defs) { - auto vertex = node_map[node_def.first]; - for (auto wire : node_def.second) + node_vertex_map[node] = scheduler.add(node); + for (auto node_comb_def : flow.node_comb_defs) { + auto vertex = node_vertex_map[node_comb_def.first]; + for (auto wire : node_comb_def.second) for (auto succ_node : flow.wire_uses[wire]) { - auto succ_vertex = node_map[succ_node]; + auto succ_vertex = node_vertex_map[succ_node]; vertex->succs.insert(succ_vertex); succ_vertex->preds.insert(vertex); } } - auto eval_order = scheduler.schedule(); - pool evaluated; + // Find out whether the order includes any feedback arcs. + std::vector node_order; + pool evaluated_nodes; pool feedback_wires; - for (auto vertex : eval_order) { + for (auto vertex : scheduler.schedule()) { auto node = vertex->data; - schedule[module].push_back(*node); + node_order.push_back(node); // Any wire that is an output of node vo and input of node vi where vo is scheduled later than vi // is a feedback wire. Feedback wires indicate apparent logic loops in the design, which may be // caused by a true logic loop, but usually are a benign result of dependency tracking that works - // on wire, not bit, level. Nevertheless, feedback wires cannot be localized. - evaluated.insert(node); - for (auto wire : node_defs[node]) + // on wire, not bit, level. Nevertheless, feedback wires cannot be unbuffered. + evaluated_nodes.insert(node); + for (auto wire : flow.node_comb_defs[node]) for (auto succ_node : flow.wire_uses[wire]) - if (evaluated[succ_node]) { + if (evaluated_nodes[succ_node]) feedback_wires.insert(wire); - // Feedback wires may never be elided because feedback requires state, but the point of elision - // (and localization) is to eliminate state. - elided_wires.erase(wire); - } } - if (!feedback_wires.empty()) { has_feedback_arcs = true; log("Module `%s' contains feedback arcs through wires:\n", log_id(module)); @@ -2237,19 +3056,108 @@ struct CxxrtlWorker { log(" %s\n", log_id(wire)); } + // Conservatively assign wire types. Assignment of types BUFFERED and MEMBER is final, but assignment + // of type LOCAL may be further refined to UNUSED or INLINE. for (auto wire : module->wires()) { + auto &wire_type = wire_types[wire]; + wire_type = {WireType::BUFFERED}; + if (feedback_wires[wire]) continue; if (wire->port_output && !module->get_bool_attribute(ID::top)) continue; - if (wire->name.begins_with("$") && !unbuffer_internal) continue; - if (wire->name.begins_with("\\") && !unbuffer_public) continue; + if (!wire->name.isPublic() && !unbuffer_internal) continue; + if (wire->name.isPublic() && !unbuffer_public) continue; if (flow.wire_sync_defs.count(wire) > 0) continue; - unbuffered_wires.insert(wire); + wire_type = {WireType::MEMBER}; + if (edge_wires[wire]) continue; if (wire->get_bool_attribute(ID::keep)) continue; if (wire->port_input || wire->port_output) continue; - if (wire->name.begins_with("$") && !localize_internal) continue; - if (wire->name.begins_with("\\") && !localize_public) continue; - localized_wires.insert(wire); + if (!wire->name.isPublic() && !localize_internal) continue; + if (wire->name.isPublic() && !localize_public) continue; + wire_type = {WireType::LOCAL}; + } + + // Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users. + pool worklist; + for (auto node : flow.nodes) { + if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type)) + worklist.insert(node); // node evaluates a submodule + else if (node->type == FlowGraph::Node::Type::CELL_EVAL && is_effectful_cell(node->cell->type)) + worklist.insert(node); // node has async effects + else if (node->type == FlowGraph::Node::Type::EFFECT_SYNC) + worklist.insert(node); // node has sync effects + else if (node->type == FlowGraph::Node::Type::MEM_WRPORTS) + worklist.insert(node); // node is memory write + else if (node->type == FlowGraph::Node::Type::PROCESS_SYNC && is_memwr_process(node->process)) + worklist.insert(node); // node is memory write + else if (flow.node_sync_defs.count(node)) + worklist.insert(node); // node is a flip-flop + else if (flow.node_comb_defs.count(node)) { + for (auto wire : flow.node_comb_defs[node]) + if (wire_types[wire].is_member()) + worklist.insert(node); // node drives public wires + } + } + dict> live_wires; + pool live_nodes; + while (!worklist.empty()) { + auto node = worklist.pop(); + live_nodes.insert(node); + for (auto wire : flow.node_uses[node]) { + live_wires[wire].insert(node); + for (auto pred_node : flow.wire_comb_defs[wire]) + if (!live_nodes[pred_node]) + worklist.insert(pred_node); + } + } + + // Refine wire types taking into account the amount of uses from reachable nodes only. + for (auto wire : module->wires()) { + auto &wire_type = wire_types[wire]; + if (!wire_type.is_local()) continue; + if (live_wires[wire].empty()) { + wire_type = {WireType::UNUSED}; // wire never used + continue; + } + + if (!wire->name.isPublic() && !inline_internal) continue; + if (wire->name.isPublic() && !inline_public) continue; + if (flow.is_inlinable(wire, live_wires[wire])) { + if (flow.wire_comb_defs[wire].size() > 1) + log_cmd_error("Wire %s.%s has multiple drivers!\n", log_id(module), log_id(wire)); + log_assert(flow.wire_comb_defs[wire].size() == 1); + FlowGraph::Node *node = *flow.wire_comb_defs[wire].begin(); + switch (node->type) { + case FlowGraph::Node::Type::CELL_EVAL: + if (!is_inlinable_cell(node->cell->type)) continue; + wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell + break; + case FlowGraph::Node::Type::CONNECT: + wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig + break; + default: continue; + } + live_nodes.erase(node); + } + } + + // Emit reachable nodes in eval(). + // Accumulate sync effectful cells per trigger condition. + dict, std::vector> effect_sync_cells; + for (auto node : node_order) + if (live_nodes[node]) { + if (node->type == FlowGraph::Node::Type::CELL_EVAL && + is_effectful_cell(node->cell->type) && + node->cell->getParam(ID::TRG_ENABLE).as_bool() && + node->cell->getParam(ID::TRG_WIDTH).as_int() != 0) + effect_sync_cells[make_pair(node->cell->getPort(ID::TRG), node->cell->getParam(ID::TRG_POLARITY))].push_back(node->cell); + else + schedule[module].push_back(*node); + } + + for (auto &it : effect_sync_cells) { + auto node = flow.add_effect_sync_node(it.second); + schedule[module].push_back(*node); } // For maximum performance, the state of the simulation (which is the same as the set of its double buffered @@ -2259,10 +3167,9 @@ struct CxxrtlWorker { // as a wire with multiple drivers where one of them is combinatorial and the other is synchronous. Such designs // also require more than one delta cycle to converge. pool buffered_comb_wires; - for (auto wire : module->wires()) { - if (flow.wire_comb_defs[wire].size() > 0 && !unbuffered_wires[wire] && !feedback_wires[wire]) + for (auto wire : module->wires()) + if (wire_types[wire].is_buffered() && !feedback_wires[wire] && flow.wire_comb_defs[wire].size() > 0) buffered_comb_wires.insert(wire); - } if (!buffered_comb_wires.empty()) { has_buffered_comb_wires = true; log("Module `%s' contains buffered combinatorial wires:\n", log_id(module)); @@ -2270,47 +3177,154 @@ struct CxxrtlWorker { log(" %s\n", log_id(wire)); } + // Record whether eval() requires only one delta cycle in this module. eval_converges[module] = feedback_wires.empty() && buffered_comb_wires.empty(); - for (auto item : flow.bit_has_state) - bit_has_state.insert(item); - if (debug_info) { - // Find wires that alias other wires or are tied to a constant; debug information can be enriched with these - // at essentially zero additional cost. - // - // Note that the information collected here can't be used for optimizing the netlist: debug information queries - // are pure and run on a design in a stable state, which allows assumptions that do not otherwise hold. + // Annotate wire bits with the type of their driver; this is exposed in the debug metadata. + for (auto item : flow.bit_has_state) + bit_has_state.insert(item); + + // Assign debug information wire types to public wires according to the chosen debug level. + // Unlike with optimized wire types, all assignments here are final. for (auto wire : module->wires()) { - if (wire->name[0] != '\\') - continue; - if (!unbuffered_wires[wire]) - continue; - const RTLIL::Wire *wire_it = wire; - while (1) { - if (!(flow.wire_def_elidable.count(wire_it) && flow.wire_def_elidable[wire_it])) - break; // not an alias: complex def - log_assert(flow.wire_comb_defs[wire_it].size() == 1); - FlowGraph::Node *node = *flow.wire_comb_defs[wire_it].begin(); - if (node->type != FlowGraph::Node::Type::CONNECT) - break; // not an alias: def by cell - RTLIL::SigSpec rhs_sig = node->connect.second; - if (rhs_sig.is_wire()) { - RTLIL::Wire *rhs_wire = rhs_sig.as_wire(); - if (unbuffered_wires[rhs_wire]) { - wire_it = rhs_wire; // maybe an alias - } else { - debug_alias_wires[wire] = rhs_wire; // is an alias + const auto &wire_type = wire_types[wire]; + auto &debug_wire_type = debug_wire_types[wire]; + + if (!debug_info) continue; + if (wire->port_input || wire_type.is_buffered()) + debug_wire_type = wire_type; // wire contains state + else if (!wire->name.isPublic()) + continue; // internal and stateless + + if (!debug_member) continue; + if (wire_type.is_member()) + debug_wire_type = wire_type; // wire is a member + + if (!debug_alias) continue; + const RTLIL::Wire *it = wire; + while (flow.is_inlinable(it)) { + log_assert(flow.wire_comb_defs[it].size() == 1); + FlowGraph::Node *node = *flow.wire_comb_defs[it].begin(); + if (node->type != FlowGraph::Node::Type::CONNECT) break; // not an alias + RTLIL::SigSpec rhs = node->connect.second; + if (rhs.is_fully_const()) { + debug_wire_type = {WireType::CONST, rhs}; // wire replaced with const + } else if (rhs.is_wire()) { + if (wire_types[rhs.as_wire()].is_member()) + debug_wire_type = {WireType::ALIAS, rhs}; // wire replaced with wire + else if (debug_eval && rhs.as_wire()->name.isPublic()) + debug_wire_type = {WireType::ALIAS, rhs}; // wire replaced with outline + it = rhs.as_wire(); // and keep looking + continue; + } + break; + } + + if (!debug_eval) continue; + if (!debug_wire_type.is_exact() && !wire_type.is_member()) + debug_wire_type = {WireType::OUTLINE}; // wire is local or inlined + } + + // Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members) + // and collect reachable wire users. + pool worklist; + for (auto node : flow.nodes) { + if (flow.node_comb_defs.count(node)) + for (auto wire : flow.node_comb_defs[node]) + if (debug_wire_types[wire].is_outline()) + worklist.insert(node); // node drives outline + } + dict> debug_live_wires; + pool debug_live_nodes; + while (!worklist.empty()) { + auto node = worklist.pop(); + debug_live_nodes.insert(node); + for (auto wire : flow.node_uses[node]) { + if (debug_wire_types[wire].is_member()) + continue; // node uses member + if (debug_wire_types[wire].is_exact()) + continue; // node uses alias or const + debug_live_wires[wire].insert(node); + for (auto pred_node : flow.wire_comb_defs[wire]) + if (!debug_live_nodes[pred_node]) + worklist.insert(pred_node); + } + } + + // Assign debug information wire types to internal wires used by reachable nodes. This is similar + // to refining optimized wire types with the exception that the assignments here are first and final. + for (auto wire : module->wires()) { + const auto &wire_type = wire_types[wire]; + auto &debug_wire_type = debug_wire_types[wire]; + if (wire->name.isPublic()) continue; + + if (debug_live_wires[wire].empty()) { + continue; // wire never used + } else if (flow.is_inlinable(wire, debug_live_wires[wire])) { + log_assert(flow.wire_comb_defs[wire].size() == 1); + FlowGraph::Node *node = *flow.wire_comb_defs[wire].begin(); + switch (node->type) { + case FlowGraph::Node::Type::CELL_EVAL: + if (!is_inlinable_cell(node->cell->type)) continue; + debug_wire_type = {WireType::INLINE, node->cell}; // wire replaced with cell break; + case FlowGraph::Node::Type::CONNECT: + debug_wire_type = {WireType::INLINE, node->connect.second}; // wire replaced with sig + break; + default: continue; + } + debug_live_nodes.erase(node); + } else if (wire_type.is_member() || wire_type.type == WireType::LOCAL) { + debug_wire_type = wire_type; // wire not inlinable + } else { + log_assert(wire_type.type == WireType::INLINE || + wire_type.type == WireType::UNUSED); + if (flow.wire_comb_defs[wire].size() == 0) { + if (wire_init.count(wire)) { // wire never modified + debug_wire_type = {WireType::CONST, wire_init.at(wire)}; + } else { + debug_wire_type = {WireType::CONST, RTLIL::SigSpec(RTLIL::S0, wire->width)}; } - } else if (rhs_sig.is_fully_const()) { - debug_const_wires[wire] = rhs_sig.as_const(); // is a const - break; } else { - break; // not an alias: complex rhs + debug_wire_type = {WireType::LOCAL}; // wire used only for debug } } } + + // Emit reachable nodes in debug_eval(). + for (auto node : node_order) + if (debug_live_nodes[node]) + debug_schedule[module].push_back(*node); + } + + auto show_wire_type = [&](const RTLIL::Wire* wire, const WireType &wire_type) { + const char *type_str; + switch (wire_type.type) { + case WireType::UNUSED: type_str = "UNUSED"; break; + case WireType::BUFFERED: type_str = "BUFFERED"; break; + case WireType::MEMBER: type_str = "MEMBER"; break; + case WireType::OUTLINE: type_str = "OUTLINE"; break; + case WireType::LOCAL: type_str = "LOCAL"; break; + case WireType::INLINE: type_str = "INLINE"; break; + case WireType::ALIAS: type_str = "ALIAS"; break; + case WireType::CONST: type_str = "CONST"; break; + default: type_str = "(invalid)"; + } + if (wire_type.sig_subst.empty()) + log_debug(" %s: %s\n", log_signal((RTLIL::Wire*)wire), type_str); + else + log_debug(" %s: %s = %s\n", log_signal((RTLIL::Wire*)wire), type_str, log_signal(wire_type.sig_subst)); + }; + if (print_wire_types && !wire_types.empty()) { + log_debug("Wire types:\n"); + for (auto wire_type : wire_types) + show_wire_type(wire_type.first, wire_type.second); + } + if (print_debug_wire_types && !debug_wire_types.empty()) { + log_debug("Debug wire types:\n"); + for (auto debug_wire_type : debug_wire_types) + show_wire_type(debug_wire_type.first, debug_wire_type.second); } } if (has_feedback_arcs || has_buffered_comb_wires) { @@ -2330,9 +3344,9 @@ struct CxxrtlWorker { } } - void check_design(RTLIL::Design *design, bool &has_top, bool &has_sync_init, bool &has_packed_mem) + void check_design(RTLIL::Design *design, bool &has_sync_init) { - has_sync_init = has_packed_mem = has_top = false; + has_sync_init = false; for (auto module : design->modules()) { if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox))) @@ -2344,28 +3358,20 @@ struct CxxrtlWorker { if (!design->selected_module(module)) continue; - if (module->get_bool_attribute(ID::top)) - has_top = true; - for (auto proc : module->processes) for (auto sync : proc.second->syncs) if (sync->type == RTLIL::STi) has_sync_init = true; - - // The Mem constructor also checks for well-formedness of $meminit cells, if any. - for (auto &mem : Mem::get_all_memories(module)) - if (mem.packed) - has_packed_mem = true; } } void prepare_design(RTLIL::Design *design) { bool did_anything = false; - bool has_top, has_sync_init, has_packed_mem; + bool has_sync_init; log_push(); - check_design(design, has_top, has_sync_init, has_packed_mem); - if (run_hierarchy && !has_top) { + check_design(design, has_sync_init); + if (run_hierarchy) { Pass::call(design, "hierarchy -auto-top"); did_anything = true; } @@ -2384,14 +3390,10 @@ struct CxxrtlWorker { Pass::call(design, "proc_init"); did_anything = true; } - if (has_packed_mem) { - Pass::call(design, "memory_unpack"); - did_anything = true; - } // Recheck the design if it was modified. if (did_anything) - check_design(design, has_top, has_sync_init, has_packed_mem); - log_assert(has_top && !has_sync_init && !has_packed_mem); + check_design(design, has_sync_init); + log_assert(!has_sync_init); log_pop(); if (did_anything) log_spacer(); @@ -2401,8 +3403,7 @@ struct CxxrtlWorker { struct CxxrtlBackend : public Backend { static const int DEFAULT_OPT_LEVEL = 6; - static const int OPT_LEVEL_DEBUG = 4; - static const int DEFAULT_DEBUG_LEVEL = 1; + static const int DEFAULT_DEBUG_LEVEL = 4; CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { } void help() override @@ -2461,7 +3462,8 @@ struct CxxrtlBackend : public Backend { log(" value<8> p_i_data;\n"); log(" wire<8> p_o_data;\n"); log("\n"); - log(" bool eval() override;\n"); + log(" bool eval(performer *performer) override;\n"); + log(" virtual bool commit(observer &observer);\n"); log(" bool commit() override;\n"); log("\n"); log(" static std::unique_ptr\n"); @@ -2474,11 +3476,11 @@ struct CxxrtlBackend : public Backend { log(" namespace cxxrtl_design {\n"); log("\n"); log(" struct stderr_debug : public bb_p_debug {\n"); - log(" bool eval() override {\n"); + log(" bool eval(performer *performer) override {\n"); log(" if (posedge_p_clk() && p_en)\n"); log(" fprintf(stderr, \"debug: %%02x\\n\", p_i_data.data[0]);\n"); log(" p_o_data.next = p_i_data;\n"); - log(" return bb_p_debug::eval();\n"); + log(" return bb_p_debug::eval(performer);\n"); log(" }\n"); log(" };\n"); log("\n"); @@ -2563,6 +3565,9 @@ struct CxxrtlBackend : public Backend { log("\n"); log("The following options are supported by this backend:\n"); log("\n"); + log(" -print-wire-types, -print-debug-wire-types\n"); + log(" enable additional debug logging, for pass developers.\n"); + log("\n"); log(" -header\n"); log(" generate separate interface (.h) and implementation (.cc) files.\n"); log(" if specified, the backend must be called with a filename, and filename\n"); @@ -2573,6 +3578,11 @@ struct CxxrtlBackend : public Backend { log(" place the generated code into namespace . if not specified,\n"); log(" \"cxxrtl_design\" is used.\n"); log("\n"); + log(" -print-output \n"); + log(" $print cells in the generated code direct their output to .\n"); + log(" must be one of \"std::cout\", \"std::cerr\". if not specified,\n"); + log(" \"std::cout\" is used. explicitly provided performer overrides this.\n"); + log("\n"); log(" -nohierarchy\n"); log(" use design hierarchy as-is. in most designs, a top module should be\n"); log(" present as it is exposed through the C API and has unbuffered outputs\n"); @@ -2598,13 +3608,13 @@ struct CxxrtlBackend : public Backend { log(" no optimization.\n"); log("\n"); log(" -O1\n"); - log(" localize internal wires if possible.\n"); + log(" unbuffer internal wires if possible.\n"); log("\n"); log(" -O2\n"); - log(" like -O1, and unbuffer internal wires if possible.\n"); + log(" like -O1, and localize internal wires if possible.\n"); log("\n"); log(" -O3\n"); - log(" like -O2, and elide internal wires if possible.\n"); + log(" like -O2, and inline internal wires if possible.\n"); log("\n"); log(" -O4\n"); log(" like -O3, and unbuffer public wires not marked (*keep*) if possible.\n"); @@ -2613,27 +3623,37 @@ struct CxxrtlBackend : public Backend { log(" like -O4, and localize public wires not marked (*keep*) if possible.\n"); log("\n"); log(" -O6\n"); - log(" like -O5, and elide public wires not marked (*keep*) if possible.\n"); - log("\n"); - log(" -Og\n"); - log(" highest optimization level that provides debug information for all\n"); - log(" public wires. currently, alias for -O%d.\n", OPT_LEVEL_DEBUG); + log(" like -O5, and inline public wires not marked (*keep*) if possible.\n"); log("\n"); log(" -g \n"); log(" set the debug level. the default is -g%d. higher debug levels provide\n", DEFAULT_DEBUG_LEVEL); log(" more visibility and generate more code, but do not pessimize evaluation.\n"); log("\n"); log(" -g0\n"); - log(" no debug information.\n"); + log(" no debug information. the C API is disabled.\n"); log("\n"); log(" -g1\n"); - log(" debug information for non-optimized public wires. this also makes it\n"); - log(" possible to use the C API.\n"); + log(" include bare minimum of debug information necessary to access all design\n"); + log(" state. the C API is enabled.\n"); + log("\n"); + log(" -g2\n"); + log(" like -g1, but include debug information for all public wires that are\n"); + log(" directly accessible through the C++ interface.\n"); + log("\n"); + log(" -g3\n"); + log(" like -g2, and include debug information for public wires that are tied\n"); + log(" to a constant or another public wire.\n"); + log("\n"); + log(" -g4\n"); + log(" like -g3, and compute debug information on demand for all public wires\n"); + log(" that were optimized out.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { + bool print_wire_types = false; + bool print_debug_wire_types = false; bool nohierarchy = false; bool noflatten = false; bool noproc = false; @@ -2646,6 +3666,14 @@ struct CxxrtlBackend : public Backend { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-print-wire-types") { + print_wire_types = true; + continue; + } + if (args[argidx] == "-print-debug-wire-types") { + print_debug_wire_types = true; + continue; + } if (args[argidx] == "-nohierarchy") { nohierarchy = true; continue; @@ -2659,12 +3687,14 @@ struct CxxrtlBackend : public Backend { continue; } if (args[argidx] == "-Og") { - opt_level = OPT_LEVEL_DEBUG; + log_warning("The `-Og` option has been removed. Use `-g3` instead for complete " + "design coverage regardless of optimization level.\n"); continue; } if (args[argidx] == "-O" && argidx+1 < args.size() && args[argidx+1] == "g") { argidx++; - opt_level = OPT_LEVEL_DEBUG; + log_warning("The `-Og` option has been removed. Use `-g3` instead for complete " + "design coverage regardless of optimization level.\n"); continue; } if (args[argidx] == "-O" && argidx+1 < args.size()) { @@ -2691,17 +3721,27 @@ struct CxxrtlBackend : public Backend { worker.design_ns = args[++argidx]; continue; } + if (args[argidx] == "-print-output" && argidx+1 < args.size()) { + worker.print_output = args[++argidx]; + if (!(worker.print_output == "std::cout" || worker.print_output == "std::cerr")) { + log_cmd_error("Invalid output stream \"%s\".\n", worker.print_output.c_str()); + worker.print_output = "std::cout"; + } + continue; + } break; } extra_args(f, filename, args, argidx); + worker.print_wire_types = print_wire_types; + worker.print_debug_wire_types = print_debug_wire_types; worker.run_hierarchy = !nohierarchy; worker.run_flatten = !noflatten; worker.run_proc = !noproc; switch (opt_level) { // the highest level here must match DEFAULT_OPT_LEVEL case 6: - worker.elide_public = true; + worker.inline_public = true; YS_FALLTHROUGH case 5: worker.localize_public = true; @@ -2710,7 +3750,7 @@ struct CxxrtlBackend : public Backend { worker.unbuffer_public = true; YS_FALLTHROUGH case 3: - worker.elide_internal = true; + worker.inline_internal = true; YS_FALLTHROUGH case 2: worker.localize_internal = true; @@ -2725,6 +3765,15 @@ struct CxxrtlBackend : public Backend { } switch (debug_level) { // the highest level here must match DEFAULT_DEBUG_LEVEL + case 4: + worker.debug_eval = true; + YS_FALLTHROUGH + case 3: + worker.debug_alias = true; + YS_FALLTHROUGH + case 2: + worker.debug_member = true; + YS_FALLTHROUGH case 1: worker.debug_info = true; YS_FALLTHROUGH diff --git a/backends/cxxrtl/cxxrtl_capi.cc b/backends/cxxrtl/cxxrtl_capi.cc deleted file mode 100644 index b77e4c49141..00000000000 --- a/backends/cxxrtl/cxxrtl_capi.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2020 whitequark - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_capi.h`. - -#include -#include - -struct _cxxrtl_handle { - std::unique_ptr module; - cxxrtl::debug_items objects; -}; - -// Private function for use by other units of the C API. -const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle) { - return handle->objects; -} - -cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design) { - cxxrtl_handle handle = new _cxxrtl_handle; - handle->module = std::move(design->module); - handle->module->debug_info(handle->objects); - delete design; - return handle; -} - -void cxxrtl_destroy(cxxrtl_handle handle) { - delete handle; -} - -int cxxrtl_eval(cxxrtl_handle handle) { - return handle->module->eval(); -} - -int cxxrtl_commit(cxxrtl_handle handle) { - return handle->module->commit(); -} - -size_t cxxrtl_step(cxxrtl_handle handle) { - return handle->module->step(); -} - -struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts) { - auto it = handle->objects.table.find(name); - if (it == handle->objects.table.end()) - return nullptr; - *parts = it->second.size(); - return static_cast(&it->second[0]); -} - -void cxxrtl_enum(cxxrtl_handle handle, void *data, - void (*callback)(void *data, const char *name, - cxxrtl_object *object, size_t parts)) { - for (auto &it : handle->objects.table) - callback(data, it.first.c_str(), static_cast(&it.second[0]), it.second.size()); -} diff --git a/backends/cxxrtl/runtime/README.txt b/backends/cxxrtl/runtime/README.txt new file mode 100644 index 00000000000..9ae7051cdcc --- /dev/null +++ b/backends/cxxrtl/runtime/README.txt @@ -0,0 +1,18 @@ +This directory contains the runtime components of CXXRTL and should be placed on the include path +when building the simulation using the `-I${YOSYS}/backends/cxxrtl/runtime` option. These components +are not used in the Yosys binary; they are only built as a part of the simulation binary. + +The interfaces declared in `cxxrtl_capi*.h` contain the stable C API. These interfaces will not be +changed in backward-incompatible ways unless no other option is available, and any breaking changes +will be made in a way that causes the downstream code to fail in a visible way. The ABI of these +interfaces is considered stable as well, and it will not use features complicating its use via +libraries such as libffi or ctypes. + +The implementations in `cxxrtl_capi*.cc` are considered private; they are still placed in the include +path to enable build-system-less builds (where the CXXRTL runtime component is included in the C++ +file of the simulation toplevel). + +The interfaces declared in `cxxrtl*.h` (without `capi`) are unstable and may change without notice. + +For clarity, all of the files in this directory and its subdirectories have unique names regardless +of the directory where they are placed. \ No newline at end of file diff --git a/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc new file mode 100644 index 00000000000..3c62401ddea --- /dev/null +++ b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc @@ -0,0 +1,135 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 whitequark + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl/capi/cxxrtl_capi.h`. + +#include +#include + +struct _cxxrtl_handle { + std::unique_ptr module; + cxxrtl::debug_items objects; +}; + +// Private function for use by other units of the C API. +const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle) { + return handle->objects; +} + +cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design) { + return cxxrtl_create_at(design, ""); +} + +cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path_) { + std::string top_path = top_path_; + if (!top_path.empty()) { + // module::debug_info() accepts either an empty path, or a path ending in space to simplify + // the logic in generated code. While this is sketchy at best to expose in the C++ API, this + // would be a lot worse in the C API, so don't expose it here. + assert(top_path.back() != ' '); + top_path += ' '; + } + + cxxrtl_handle handle = new _cxxrtl_handle; + handle->module = std::move(design->module); + handle->module->debug_info(handle->objects, top_path); + delete design; + return handle; +} + +void cxxrtl_destroy(cxxrtl_handle handle) { + delete handle; +} + +void cxxrtl_reset(cxxrtl_handle handle) { + handle->module->reset(); +} + +int cxxrtl_eval(cxxrtl_handle handle) { + return handle->module->eval(); +} + +int cxxrtl_commit(cxxrtl_handle handle) { + return handle->module->commit(); +} + +size_t cxxrtl_step(cxxrtl_handle handle) { + return handle->module->step(); +} + +struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts) { + auto it = handle->objects.table.find(name); + if (it == handle->objects.table.end()) + return nullptr; + *parts = it->second.size(); + return static_cast(&it->second[0]); +} + +void cxxrtl_enum(cxxrtl_handle handle, void *data, + void (*callback)(void *data, const char *name, + cxxrtl_object *object, size_t parts)) { + for (auto &it : handle->objects.table) + callback(data, it.first.c_str(), static_cast(&it.second[0]), it.second.size()); +} + +void cxxrtl_outline_eval(cxxrtl_outline outline) { + outline->eval(); +} + +int cxxrtl_attr_type(cxxrtl_attr_set attrs_, const char *name) { + auto attrs = (cxxrtl::metadata_map*)attrs_; + if (!attrs->count(name)) + return CXXRTL_ATTR_NONE; + switch (attrs->at(name).value_type) { + case cxxrtl::metadata::UINT: + return CXXRTL_ATTR_UNSIGNED_INT; + case cxxrtl::metadata::SINT: + return CXXRTL_ATTR_SIGNED_INT; + case cxxrtl::metadata::STRING: + return CXXRTL_ATTR_STRING; + case cxxrtl::metadata::DOUBLE: + return CXXRTL_ATTR_DOUBLE; + default: + // Present unsupported attribute type the same way as no attribute at all. + return CXXRTL_ATTR_NONE; + } +} + +uint64_t cxxrtl_attr_get_unsigned_int(cxxrtl_attr_set attrs_, const char *name) { + auto &attrs = *(cxxrtl::metadata_map*)attrs_; + assert(attrs.count(name) && attrs.at(name).value_type == cxxrtl::metadata::UINT); + return attrs[name].as_uint(); +} + +int64_t cxxrtl_attr_get_signed_int(cxxrtl_attr_set attrs_, const char *name) { + auto &attrs = *(cxxrtl::metadata_map*)attrs_; + assert(attrs.count(name) && attrs.at(name).value_type == cxxrtl::metadata::SINT); + return attrs[name].as_sint(); +} + +const char *cxxrtl_attr_get_string(cxxrtl_attr_set attrs_, const char *name) { + auto &attrs = *(cxxrtl::metadata_map*)attrs_; + assert(attrs.count(name) && attrs.at(name).value_type == cxxrtl::metadata::STRING); + return attrs[name].as_string().c_str(); +} + +double cxxrtl_attr_get_double(cxxrtl_attr_set attrs_, const char *name) { + auto &attrs = *(cxxrtl::metadata_map*)attrs_; + assert(attrs.count(name) && attrs.at(name).value_type == cxxrtl::metadata::DOUBLE); + return attrs[name].as_double(); +} diff --git a/backends/cxxrtl/cxxrtl_capi.h b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h similarity index 64% rename from backends/cxxrtl/cxxrtl_capi.h rename to backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h index 385d6dcf386..ae42733ad7f 100644 --- a/backends/cxxrtl/cxxrtl_capi.h +++ b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h @@ -52,9 +52,23 @@ typedef struct _cxxrtl_handle *cxxrtl_handle; // The `design` is consumed by this operation and cannot be used afterwards. cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design); +// Create a design handle at a given hierarchy position from a design toplevel. +// +// This operation is similar to `cxxrtl_create`, except the full hierarchical name of every object +// is prepended with `top_path`. +cxxrtl_handle cxxrtl_create_at(cxxrtl_toplevel design, const char *top_path); + // Release all resources used by a design and its handle. void cxxrtl_destroy(cxxrtl_handle handle); +// Reinitialize the design, replacing the internal state with the reset values while preserving +// black boxes. +// +// This operation is essentially equivalent to a power-on reset. Values, wires, and memories are +// returned to their reset state while preserving the state of black boxes and keeping all of +// the interior pointers obtained with e.g. `cxxrtl_get` valid. +void cxxrtl_reset(cxxrtl_handle handle); + // Evaluate the design, propagating changes on inputs to the `next` value of internal state and // output wires. // @@ -114,6 +128,18 @@ enum cxxrtl_type { // pointer is always NULL. CXXRTL_ALIAS = 3, + // Outlines correspond to netlist nodes that were optimized in a way that makes them inaccessible + // outside of a module's `eval()` function. At the highest debug information level, every inlined + // node has a corresponding outline object. + // + // Outlines can be inspected via the `curr` pointer and can never be modified; the `next` pointer + // is always NULL. Unlike all other objects, the bits of an outline object are meaningful only + // after a call to `cxxrtl_outline_eval` and until any subsequent modification to the netlist. + // Observing this requirement is the responsibility of the caller; it is not enforced. + // + // Outlines always correspond to combinatorial netlist nodes that are not ports. + CXXRTL_OUTLINE = 4, + // More object types may be added in the future, but the existing ones will never change. }; @@ -157,8 +183,8 @@ enum cxxrtl_flag { // Node has bits that are driven by a combinatorial cell or another node. // - // This flag can be set on objects of type `CXXRTL_VALUE` and `CXXRTL_WIRE`. It may be combined - // with `CXXRTL_DRIVEN_SYNC` and `CXXRTL_UNDRIVEN`, as well as other flags. + // This flag can be set on objects of type `CXXRTL_VALUE`, `CXXRTL_WIRE`, and `CXXRTL_OUTLINE`. + // It may be combined with `CXXRTL_DRIVEN_SYNC` and `CXXRTL_UNDRIVEN`, as well as other flags. // // This flag is set on objects that have bits connected to the output of a combinatorial cell, // or directly to another node. For designs without combinatorial loops, writing to such bits @@ -179,8 +205,8 @@ enum cxxrtl_flag { // Description of a simulated object. // -// The `data` array can be accessed directly to inspect and, if applicable, modify the bits -// stored in the object. +// The `curr` and `next` arrays can be accessed directly to inspect and, if applicable, modify +// the bits stored in the object. struct cxxrtl_object { // Type of the object. // @@ -214,9 +240,29 @@ struct cxxrtl_object { // through wires, the bits are double buffered. To avoid race conditions, user code should // always read from `curr` and write to `next`. The `curr` pointer is always valid; for objects // that cannot be modified, or cannot be modified in a race-free way, `next` is NULL. + // + // In case where `width == 0`, `curr` is a non-NULL pointer unique for the wire. That is, + // there is a 1-to-1 correspondence between simulation objects and `curr` pointers, regardless + // of whether they have storage or not. (Aliases' `curr` pointer equals that of some other + // simulated object.) uint32_t *curr; uint32_t *next; + // Opaque reference to an outline. Only meaningful for outline objects. + // + // See the documentation of `cxxrtl_outline` for details. When creating a `cxxrtl_object`, set + // this field to NULL. + struct _cxxrtl_outline *outline; + + // Opaque reference to an attribute set. + // + // See the documentation of `cxxrtl_attr_set` for details. When creating a `cxxrtl_object`, set + // this field to NULL. + // + // The lifetime of the pointers returned by `cxxrtl_attr_*` family of functions is the same as + // the lifetime of this structure. + struct _cxxrtl_attr_set *attrs; + // More description fields may be added in the future, but the existing ones will never change. }; @@ -240,7 +286,7 @@ struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, s // This function is a shortcut for the most common use of `cxxrtl_get_parts`. It asserts that, // if the object exists, it consists of a single part. If assertions are disabled, it returns NULL // for multi-part objects. -inline struct cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name) { +static inline struct cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name) { size_t parts = 0; struct cxxrtl_object *object = cxxrtl_get_parts(handle, name, &parts); assert(object == NULL || parts == 1); @@ -258,6 +304,76 @@ void cxxrtl_enum(cxxrtl_handle handle, void *data, void (*callback)(void *data, const char *name, struct cxxrtl_object *object, size_t parts)); +// Opaque reference to an outline. +// +// An outline is a group of outline objects that are evaluated simultaneously. The identity of +// an outline can be compared to determine whether any two objects belong to the same outline. +typedef struct _cxxrtl_outline *cxxrtl_outline; + +// Evaluate an outline. +// +// After evaluating an outline, the bits of every outline object contained in it are consistent +// with the current state of the netlist. In general, any further modification to the netlist +// causes every outline object to become stale, after which the corresponding outline must be +// re-evaluated, otherwise the bits read from that object are meaningless. +void cxxrtl_outline_eval(cxxrtl_outline outline); + +// Opaque reference to an attribute set. +// +// An attribute set is a map between attribute names (always strings) and values (which may have +// several different types). To find out the type of an attribute, use `cxxrtl_attr_type`, and +// to retrieve the value of an attribute, use `cxxrtl_attr_as_string`. +typedef struct _cxxrtl_attr_set *cxxrtl_attr_set; + +// Type of an attribute. +enum cxxrtl_attr_type { + // Attribute is not present. + CXXRTL_ATTR_NONE = 0, + + // Attribute has an unsigned integer value. + CXXRTL_ATTR_UNSIGNED_INT = 1, + + // Attribute has an unsigned integer value. + CXXRTL_ATTR_SIGNED_INT = 2, + + // Attribute has a string value. + CXXRTL_ATTR_STRING = 3, + + // Attribute has a double precision floating point value. + CXXRTL_ATTR_DOUBLE = 4, + + // More attribute types may be defined in the future, but the existing values will never change. +}; + +// Determine the presence and type of an attribute in an attribute set. +// +// This function returns one of the possible `cxxrtl_attr_type` values. +int cxxrtl_attr_type(cxxrtl_attr_set attrs, const char *name); + +// Retrieve an unsigned integer valued attribute from an attribute set. +// +// This function asserts that `cxxrtl_attr_type(attrs, name) == CXXRTL_ATTR_UNSIGNED_INT`. +// If assertions are disabled, returns 0 if the attribute is missing or has an incorrect type. +uint64_t cxxrtl_attr_get_unsigned_int(cxxrtl_attr_set attrs, const char *name); + +// Retrieve a signed integer valued attribute from an attribute set. +// +// This function asserts that `cxxrtl_attr_type(attrs, name) == CXXRTL_ATTR_SIGNED_INT`. +// If assertions are disabled, returns 0 if the attribute is missing or has an incorrect type. +int64_t cxxrtl_attr_get_signed_int(cxxrtl_attr_set attrs, const char *name); + +// Retrieve a string valued attribute from an attribute set. The returned string is zero-terminated. +// +// This function asserts that `cxxrtl_attr_type(attrs, name) == CXXRTL_ATTR_STRING`. If assertions +// are disabled, returns NULL if the attribute is missing or has an incorrect type. +const char *cxxrtl_attr_get_string(cxxrtl_attr_set attrs, const char *name); + +// Retrieve a double precision floating point valued attribute from an attribute set. +// +// This function asserts that `cxxrtl_attr_type(attrs, name) == CXXRTL_ATTR_DOUBLE`. If assertions +// are disabled, returns NULL if the attribute is missing or has an incorrect type. +double cxxrtl_attr_get_double(cxxrtl_attr_set attrs, const char *name); + #ifdef __cplusplus } #endif diff --git a/backends/cxxrtl/cxxrtl_vcd_capi.cc b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc similarity index 95% rename from backends/cxxrtl/cxxrtl_vcd_capi.cc rename to backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc index 52a9198b869..0949a9363eb 100644 --- a/backends/cxxrtl/cxxrtl_vcd_capi.cc +++ b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc @@ -16,10 +16,10 @@ * */ -// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.h`. +// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl/capi/cxxrtl_capi_vcd.h`. -#include -#include +#include +#include extern const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle); diff --git a/backends/cxxrtl/cxxrtl_vcd_capi.h b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h similarity index 97% rename from backends/cxxrtl/cxxrtl_vcd_capi.h rename to backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h index d55afe2230e..844f3cb8c83 100644 --- a/backends/cxxrtl/cxxrtl_vcd_capi.h +++ b/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h @@ -16,8 +16,8 @@ * */ -#ifndef CXXRTL_VCD_CAPI_H -#define CXXRTL_VCD_CAPI_H +#ifndef CXXRTL_CAPI_VCD_H +#define CXXRTL_CAPI_VCD_H // This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.cc`. // @@ -27,7 +27,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h similarity index 64% rename from backends/cxxrtl/cxxrtl.h rename to backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index 41089a153d5..b834cd12019 100644 --- a/backends/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -36,22 +37,50 @@ #include #include #include +#include #include +#include -#include +// `cxxrtl::debug_item` has to inherit from `cxxrtl_object` to satisfy strict aliasing requirements. +#include + +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif // CXXRTL essentially uses the C++ compiler as a hygienic macro engine that feeds an instruction selector. // It generates a lot of specialized template functions with relatively large bodies that, when inlined // into the caller and (for those with loops) unrolled, often expose many new optimization opportunities. // Because of this, most of the CXXRTL runtime must be always inlined for best performance. -#ifndef __has_attribute -# define __has_attribute(x) 0 -#endif #if __has_attribute(always_inline) #define CXXRTL_ALWAYS_INLINE inline __attribute__((__always_inline__)) #else #define CXXRTL_ALWAYS_INLINE inline #endif +// Conversely, some functions in the generated code are extremely large yet very cold, with both of these +// properties being extreme enough to confuse C++ compilers into spending pathological amounts of time +// on a futile (the code becomes worse) attempt to optimize the least important parts of code. +#if __has_attribute(optnone) +#define CXXRTL_EXTREMELY_COLD __attribute__((__optnone__)) +#elif __has_attribute(optimize) +#define CXXRTL_EXTREMELY_COLD __attribute__((__optimize__(0))) +#else +#define CXXRTL_EXTREMELY_COLD +#endif + +// CXXRTL uses assert() to check for C++ contract violations (which may result in e.g. undefined behavior +// of the simulation code itself), and CXXRTL_ASSERT to check for RTL contract violations (which may at +// most result in undefined simulation results). +// +// Though by default, CXXRTL_ASSERT() expands to assert(), it may be overridden e.g. when integrating +// the simulation into another process that should survive violating RTL contracts. +#ifndef CXXRTL_ASSERT +#ifndef CXXRTL_NDEBUG +#define CXXRTL_ASSERT(x) assert(x) +#else +#define CXXRTL_ASSERT(x) +#endif +#endif namespace cxxrtl { @@ -96,9 +125,11 @@ struct value : public expr_base> { explicit constexpr value(Init ...init) : data{init...} {} value(const value &) = default; - value(value &&) = default; value &operator=(const value &) = default; + value(value &&) = default; + value &operator=(value &&) = default; + // A (no-op) helper that forces the cast to value<>. CXXRTL_ALWAYS_INLINE const value &val() const { @@ -116,7 +147,7 @@ struct value : public expr_base> { // These functions ensure that a conversion is never out of range, and should be always used, if at all // possible, instead of direct manipulation of the `data` member. For very large types, .slice() and // .concat() can be used to split them into more manageable parts. - template + template::value, int>::type = 0> CXXRTL_ALWAYS_INLINE IntegerT get() const { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, @@ -129,15 +160,32 @@ struct value : public expr_base> { return result; } - template + template::value, int>::type = 0> CXXRTL_ALWAYS_INLINE - void set(IntegerT other) { + IntegerT get() const { + auto unsigned_result = get::type>(); + IntegerT result; + memcpy(&result, &unsigned_result, sizeof(IntegerT)); + return result; + } + + template::value, int>::type = 0> + CXXRTL_ALWAYS_INLINE + void set(IntegerT value) { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "set() requires T to be an unsigned integral type"); static_assert(std::numeric_limits::digits >= Bits, "set() requires the value to be at least as wide as T is"); for (size_t n = 0; n < chunks; n++) - data[n] = (other >> (n * chunk::bits)) & chunk::mask; + data[n] = (value >> (n * chunk::bits)) & chunk::mask; + } + + template::value, int>::type = 0> + CXXRTL_ALWAYS_INLINE + void set(IntegerT value) { + typename std::make_unsigned::type unsigned_value; + memcpy(&unsigned_value, &value, sizeof(IntegerT)); + set(unsigned_value); } // Operations with compile-time parameters. @@ -289,6 +337,14 @@ struct value : public expr_base> { return sext_cast()(*this); } + // Bit replication is far more efficient than the equivalent concatenation. + template + CXXRTL_ALWAYS_INLINE + value repeat() const { + static_assert(Bits == 1, "repeat() is implemented only for 1-bit values"); + return *this ? value().bit_not() : value(); + } + // Operations with run-time parameters (offsets, amounts, etc). // // These operations are used for computations. @@ -382,6 +438,7 @@ struct value : public expr_base> { carry = (shift_bits == 0) ? 0 : data[n] >> (chunk::bits - shift_bits); } + result.data[result.chunks - 1] &= result.msb_mask; return result; } @@ -392,12 +449,12 @@ struct value : public expr_base> { // Detect shifts definitely large than Bits early. for (size_t n = 1; n < amount.chunks; n++) if (amount.data[n] != 0) - return {}; + return (Signed && is_neg()) ? value().bit_not() : value(); // Past this point we can use the least significant chunk as the shift size. size_t shift_chunks = amount.data[0] / chunk::bits; size_t shift_bits = amount.data[0] % chunk::bits; if (shift_chunks >= chunks) - return {}; + return (Signed && is_neg()) ? value().bit_not() : value(); value result; chunk::type carry = 0; for (size_t n = 0; n < chunks - shift_chunks; n++) { @@ -406,12 +463,13 @@ struct value : public expr_base> { : data[chunks - 1 - n] << (chunk::bits - shift_bits); } if (Signed && is_neg()) { - size_t top_chunk_idx = (Bits - shift_bits) / chunk::bits; - size_t top_chunk_bits = (Bits - shift_bits) % chunk::bits; + size_t top_chunk_idx = amount.data[0] > Bits ? 0 : (Bits - amount.data[0]) / chunk::bits; + size_t top_chunk_bits = amount.data[0] > Bits ? 0 : (Bits - amount.data[0]) % chunk::bits; for (size_t n = top_chunk_idx + 1; n < chunks; n++) result.data[n] = chunk::mask; - if (shift_bits != 0) + if (amount.data[0] != 0) result.data[top_chunk_idx] |= chunk::mask << top_chunk_bits; + result.data[result.chunks - 1] &= result.msb_mask; } return result; } @@ -421,6 +479,43 @@ struct value : public expr_base> { return shr(amount); } + template + value bmux(const value &sel) const { + static_assert(ResultBits << SelBits == Bits, "invalid sizes used in bmux()"); + size_t amount = sel.data[0] * ResultBits; + size_t shift_chunks = amount / chunk::bits; + size_t shift_bits = amount % chunk::bits; + value result; + chunk::type carry = 0; + if (ResultBits % chunk::bits + shift_bits > chunk::bits) + carry = data[result.chunks + shift_chunks] << (chunk::bits - shift_bits); + for (size_t n = 0; n < result.chunks; n++) { + result.data[result.chunks - 1 - n] = carry | (data[result.chunks + shift_chunks - 1 - n] >> shift_bits); + carry = (shift_bits == 0) ? 0 + : data[result.chunks + shift_chunks - 1 - n] << (chunk::bits - shift_bits); + } + result.data[result.chunks - 1] &= result.msb_mask; + return result; + } + + template + value demux(const value &sel) const { + static_assert(Bits << SelBits == ResultBits, "invalid sizes used in demux()"); + size_t amount = sel.data[0] * Bits; + size_t shift_chunks = amount / chunk::bits; + size_t shift_bits = amount % chunk::bits; + value result; + chunk::type carry = 0; + for (size_t n = 0; n < chunks; n++) { + result.data[shift_chunks + n] = (data[n] << shift_bits) | carry; + carry = (shift_bits == 0) ? 0 + : data[n] >> (chunk::bits - shift_bits); + } + if (Bits % chunk::bits + shift_bits > chunk::bits) + result.data[shift_chunks + chunks] = carry; + return result; + } + size_t ctpop() const { size_t count = 0; for (size_t n = 0; n < chunks; n++) { @@ -435,12 +530,14 @@ struct value : public expr_base> { size_t count = 0; for (size_t n = 0; n < chunks; n++) { chunk::type x = data[chunks - 1 - n]; - if (x == 0) { - count += (n == 0 ? Bits % chunk::bits : chunk::bits); - } else { - // This loop implements the find first set idiom as recognized by LLVM. - for (; x != 0; count++) + // First add to `count` as if the chunk is zero + constexpr size_t msb_chunk_bits = Bits % chunk::bits != 0 ? Bits % chunk::bits : chunk::bits; + count += (n == 0 ? msb_chunk_bits : chunk::bits); + // If the chunk isn't zero, correct the `count` value and return + if (x != 0) { + for (; x != 0; count--) x >>= 1; + break; } } return count; @@ -469,7 +566,7 @@ struct value : public expr_base> { } value neg() const { - return value { 0u }.sub(*this); + return value().sub(*this); } bool ucmp(const value &other) const { @@ -503,6 +600,38 @@ struct value : public expr_base> { result.data[result.chunks - 1] &= result.msb_mask; return result; } + + std::pair, value> udivmod(value divisor) const { + value quotient; + value dividend = *this; + if (dividend.ucmp(divisor)) + return {/*quotient=*/value{0u}, /*remainder=*/dividend}; + int64_t divisor_shift = divisor.ctlz() - dividend.ctlz(); + assert(divisor_shift >= 0); + divisor = divisor.shl(value{(chunk::type) divisor_shift}); + for (size_t step = 0; step <= divisor_shift; step++) { + quotient = quotient.shl(value{1u}); + if (!dividend.ucmp(divisor)) { + dividend = dividend.sub(divisor); + quotient.set_bit(0, true); + } + divisor = divisor.shr(value{1u}); + } + return {quotient, /*remainder=*/dividend}; + } + + std::pair, value> sdivmod(const value &other) const { + value quotient; + value remainder; + value dividend = sext(); + value divisor = other.template sext(); + if (dividend.is_neg()) dividend = dividend.neg(); + if (divisor.is_neg()) divisor = divisor.neg(); + std::tie(quotient, remainder) = dividend.udivmod(divisor); + if (dividend.is_neg() != divisor.is_neg()) quotient = quotient.neg(); + if (dividend.is_neg()) remainder = remainder.neg(); + return {quotient.template trunc(), remainder.template trunc()}; + } }; // Expression template for a slice, usable as lvalue or rvalue, and composable with other expression templates here. @@ -643,14 +772,20 @@ struct wire { value next; wire() = default; - constexpr wire(const value &init) : curr(init), next(init) {} + explicit constexpr wire(const value &init) : curr(init), next(init) {} template explicit constexpr wire(Init ...init) : curr{init...}, next{init...} {} + // Copying and copy-assigning values is natural. If, however, a value is replaced with a wire, + // e.g. because a module is built with a different optimization level, then existing code could + // unintentionally copy a wire instead, which would create a subtle but serious bug. To make sure + // this doesn't happen, prohibit copying and copy-assigning wires. wire(const wire &) = delete; - wire(wire &&) = default; wire &operator=(const wire &) = delete; + wire(wire &&) = default; + wire &operator=(wire &&) = default; + template CXXRTL_ALWAYS_INLINE IntegerT get() const { @@ -663,8 +798,13 @@ struct wire { next.template set(other); } - bool commit() { + // This method intentionally takes a mandatory argument (to make it more difficult to misuse in + // black box implementations, leading to missed observer events). It is generic over its argument + // to allow the `on_update` method to be non-virtual. + template + bool commit(ObserverT &observer) { if (curr != next) { + observer.on_update(curr.chunks, curr.data, next.data); curr = next; return true; } @@ -680,47 +820,32 @@ std::ostream &operator<<(std::ostream &os, const wire &val) { template struct memory { - std::vector> data; - - size_t depth() const { - return data.size(); - } + const size_t depth; + std::unique_ptr[]> data; - memory() = delete; - explicit memory(size_t depth) : data(depth) {} + explicit memory(size_t depth) : depth(depth), data(new value[depth]) {} memory(const memory &) = delete; memory &operator=(const memory &) = delete; - // The only way to get the compiler to put the initializer in .rodata and do not copy it on stack is to stuff it - // into a plain array. You'd think an std::initializer_list would work here, but it doesn't, because you can't - // construct an initializer_list in a constexpr (or something) and so if you try to do that the whole thing is - // first copied on the stack (probably overflowing it) and then again into `data`. - template - struct init { - size_t offset; - value data[Size]; - }; - - template - explicit memory(size_t depth, const init &...init) : data(depth) { - data.resize(depth); - // This utterly reprehensible construct is the most reasonable way to apply a function to every element - // of a parameter pack, if the elements all have different types and so cannot be cast to an initializer list. - auto _ = {std::move(std::begin(init.data), std::end(init.data), data.begin() + init.offset)...}; - (void)_; + memory(memory &&) = default; + memory &operator=(memory &&other) { + assert(depth == other.depth); + data = std::move(other.data); + write_queue = std::move(other.write_queue); + return *this; } // An operator for direct memory reads. May be used at any time during the simulation. const value &operator [](size_t index) const { - assert(index < data.size()); + assert(index < depth); return data[index]; } // An operator for direct memory writes. May only be used before the simulation is started. If used // after the simulation is started, the design may malfunction. value &operator [](size_t index) { - assert(index < data.size()); + assert(index < depth); return data[index]; } @@ -745,7 +870,7 @@ struct memory { std::vector write_queue; void update(size_t index, const value &val, const value &mask, int priority = 0) { - assert(index < data.size()); + assert(index < depth); // Queue up the write while keeping the queue sorted by priority. write_queue.insert( std::upper_bound(write_queue.begin(), write_queue.end(), priority, @@ -753,12 +878,17 @@ struct memory { write { index, val, mask, priority }); } - bool commit() { + // See the note for `wire::commit()`. + template + bool commit(ObserverT &observer) { bool changed = false; for (const write &entry : write_queue) { value elem = data[entry.index]; elem = elem.update(entry.val, entry.mask); - changed |= (data[entry.index] != elem); + if (data[entry.index] != elem) { + observer.on_update(value::chunks, data[0].data, elem.data, entry.index); + changed |= true; + } data[entry.index] = elem; } write_queue.clear(); @@ -777,14 +907,14 @@ struct metadata { // In debug mode, using the wrong .as_*() function will assert. // In release mode, using the wrong .as_*() function will safely return a default value. - const unsigned uint_value = 0; - const signed sint_value = 0; + const uint64_t uint_value = 0; + const int64_t sint_value = 0; const std::string string_value = ""; const double double_value = 0.0; metadata() : value_type(MISSING) {} - metadata(unsigned value) : value_type(UINT), uint_value(value) {} - metadata(signed value) : value_type(SINT), sint_value(value) {} + metadata(uint64_t value) : value_type(UINT), uint_value(value) {} + metadata(int64_t value) : value_type(SINT), sint_value(value) {} metadata(const std::string &value) : value_type(STRING), string_value(value) {} metadata(const char *value) : value_type(STRING), string_value(value) {} metadata(double value) : value_type(DOUBLE), double_value(value) {} @@ -792,12 +922,12 @@ struct metadata { metadata(const metadata &) = default; metadata &operator=(const metadata &) = delete; - unsigned as_uint() const { + uint64_t as_uint() const { assert(value_type == UINT); return uint_value; } - signed as_sint() const { + int64_t as_sint() const { assert(value_type == SINT); return sint_value; } @@ -815,21 +945,221 @@ struct metadata { typedef std::map metadata_map; -// Helper class to disambiguate values/wires and their aliases. +struct performer; + +// An object that allows formatting a string lazily. +struct lazy_fmt { + virtual std::string operator() () const = 0; +}; + +// Flavor of a `$check` cell. +enum class flavor { + // Corresponds to a `$assert` cell in other flows, and a Verilog `assert ()` statement. + ASSERT, + // Corresponds to a `$assume` cell in other flows, and a Verilog `assume ()` statement. + ASSUME, + // Corresponds to a `$live` cell in other flows, and a Verilog `assert (eventually)` statement. + ASSERT_EVENTUALLY, + // Corresponds to a `$fair` cell in other flows, and a Verilog `assume (eventually)` statement. + ASSUME_EVENTUALLY, + // Corresponds to a `$cover` cell in other flows, and a Verilog `cover ()` statement. + COVER, +}; + +// An object that can be passed to a `eval()` method in order to act on side effects. The default behavior implemented +// below is the same as the behavior of `eval(nullptr)`, except that `-print-output` option of `write_cxxrtl` is not +// taken into account. +struct performer { + // Called by generated formatting code to evaluate a Verilog `$time` expression. + virtual int64_t vlog_time() const { return 0; } + + // Called by generated formatting code to evaluate a Verilog `$realtime` expression. + virtual double vlog_realtime() const { return vlog_time(); } + + // Called when a `$print` cell is triggered. + virtual void on_print(const lazy_fmt &formatter, const metadata_map &attributes) { + std::cout << formatter(); + } + + // Called when a `$check` cell is triggered. + virtual void on_check(flavor type, bool condition, const lazy_fmt &formatter, const metadata_map &attributes) { + if (type == flavor::ASSERT || type == flavor::ASSUME) { + if (!condition) + std::cerr << formatter(); + CXXRTL_ASSERT(condition && "Check failed"); + } + } +}; + +// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in +// the simulation. Unlike `performer`, `observer` does not use virtual calls as their overhead is unacceptable, and +// a comparatively heavyweight template-based solution is justified. +struct observer { + // Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks + // at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and + // `base` points to the first chunk. + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) {} + + // Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]` + // with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to + // the memory element chunk count and `base` points to the first chunk of the first element of the memory. + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) {} +}; + +// Must be kept in sync with `struct FmtPart` in kernel/fmt.h! +// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. +struct fmt_part { + enum { + STRING = 0, + INTEGER = 1, + CHARACTER = 2, + VLOG_TIME = 3, + } type; + + // STRING type + std::string str; + + // INTEGER/CHARACTER types + // + value val; + + // INTEGER/CHARACTER/VLOG_TIME types + enum { + RIGHT = 0, + LEFT = 1, + } justify; // = RIGHT; + char padding; // = '\0'; + size_t width; // = 0; + + // INTEGER type + unsigned base; // = 10; + bool signed_; // = false; + bool plus; // = false; + + // VLOG_TIME type + bool realtime; // = false; + // + int64_t itime; + // + double ftime; + + // Format the part as a string. + // + // The values of `vlog_time` and `vlog_realtime` are used for Verilog `$time` and `$realtime`, correspondingly. + template + std::string render(value val, performer *performer = nullptr) + { + // We might want to replace some of these bit() calls with direct + // chunk access if it turns out to be slow enough to matter. + std::string buf; + switch (type) { + case STRING: + return str; + + case CHARACTER: { + buf.reserve(Bits/8); + for (int i = 0; i < Bits; i += 8) { + char ch = 0; + for (int j = 0; j < 8 && i + j < int(Bits); j++) + if (val.bit(i + j)) + ch |= 1 << j; + if (ch != 0) + buf.append({ch}); + } + std::reverse(buf.begin(), buf.end()); + break; + } + + case INTEGER: { + size_t width = Bits; + if (base != 10) { + width = 0; + for (size_t index = 0; index < Bits; index++) + if (val.bit(index)) + width = index + 1; + } + + if (base == 2) { + for (size_t i = width; i > 0; i--) + buf += (val.bit(i - 1) ? '1' : '0'); + } else if (base == 8 || base == 16) { + size_t step = (base == 16) ? 4 : 3; + for (size_t index = 0; index < width; index += step) { + uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); + if (step == 4) + value |= val.bit(index + 3) << 3; + buf += "0123456789abcdef"[value]; + } + std::reverse(buf.begin(), buf.end()); + } else if (base == 10) { + bool negative = signed_ && val.is_neg(); + if (negative) + val = val.neg(); + if (val.is_zero()) + buf += '0'; + value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); + while (!xval.is_zero()) { + value<(Bits > 4 ? Bits : 4)> quotient, remainder; + if (Bits >= 4) + std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); + else + std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); + buf += '0' + remainder.template trunc<4>().template get(); + xval = quotient; + } + if (negative || plus) + buf += negative ? '-' : '+'; + std::reverse(buf.begin(), buf.end()); + } else assert(false && "Unsupported base for fmt_part"); + break; + } + + case VLOG_TIME: { + if (performer) { + buf = realtime ? std::to_string(performer->vlog_realtime()) : std::to_string(performer->vlog_time()); + } else { + buf = realtime ? std::to_string(0.0) : std::to_string(0); + } + break; + } + } + + std::string str; + assert(width == 0 || padding != '\0'); + if (justify == RIGHT && buf.size() < width) { + size_t pad_width = width - buf.size(); + if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { + str += buf.front(); + buf.erase(0, 1); + } + str += std::string(pad_width, padding); + } + str += buf; + if (justify == LEFT && buf.size() < width) + str += std::string(width - buf.size(), padding); + return str; + } +}; + +// Tag class to disambiguate values/wires and their aliases. struct debug_alias {}; +// Tag declaration to disambiguate values and debug outlines. +using debug_outline = ::_cxxrtl_outline; + // This structure is intended for consumption via foreign function interfaces, like Python's ctypes. // Because of this it uses a C-style layout that is easy to parse rather than more idiomatic C++. // // To avoid violating strict aliasing rules, this structure has to be a subclass of the one used // in the C API, or it would not be possible to cast between the pointers to these. +// +// The `attrs` member cannot be owned by this structure because a `cxxrtl_object` can be created +// from external C code. struct debug_item : ::cxxrtl_object { // Object types. enum : uint32_t { - VALUE = CXXRTL_VALUE, - WIRE = CXXRTL_WIRE, - MEMORY = CXXRTL_MEMORY, - ALIAS = CXXRTL_ALIAS, + VALUE = CXXRTL_VALUE, + WIRE = CXXRTL_WIRE, + MEMORY = CXXRTL_MEMORY, + ALIAS = CXXRTL_ALIAS, + OUTLINE = CXXRTL_OUTLINE, }; // Object flags. @@ -846,7 +1176,7 @@ struct debug_item : ::cxxrtl_object { template debug_item(value &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { - static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = VALUE; flags = flags_; @@ -856,11 +1186,13 @@ struct debug_item : ::cxxrtl_object { zero_at = 0; curr = item.data; next = item.data; + outline = nullptr; + attrs = nullptr; } template debug_item(const value &item, size_t lsb_offset = 0) { - static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = VALUE; flags = DRIVEN_COMB; @@ -870,12 +1202,15 @@ struct debug_item : ::cxxrtl_object { zero_at = 0; curr = const_cast(item.data); next = nullptr; + outline = nullptr; + attrs = nullptr; } template debug_item(wire &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { - static_assert(sizeof(item.curr) == value::chunks * sizeof(chunk_t) && - sizeof(item.next) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || + (sizeof(item.curr) == value::chunks * sizeof(chunk_t) && + sizeof(item.next) == value::chunks * sizeof(chunk_t)), "wire is not compatible with C layout"); type = WIRE; flags = flags_; @@ -885,25 +1220,29 @@ struct debug_item : ::cxxrtl_object { zero_at = 0; curr = item.curr.data; next = item.next.data; + outline = nullptr; + attrs = nullptr; } template debug_item(memory &item, size_t zero_offset = 0) { - static_assert(sizeof(item.data[0]) == value::chunks * sizeof(chunk_t), + static_assert(Width == 0 || sizeof(item.data[0]) == value::chunks * sizeof(chunk_t), "memory is not compatible with C layout"); type = MEMORY; flags = 0; width = Width; lsb_at = 0; - depth = item.data.size(); + depth = item.depth; zero_at = zero_offset; - curr = item.data.empty() ? nullptr : item.data[0].data; + curr = item.data ? item.data[0].data : nullptr; next = nullptr; + outline = nullptr; + attrs = nullptr; } template debug_item(debug_alias, const value &item, size_t lsb_offset = 0) { - static_assert(sizeof(item) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = ALIAS; flags = DRIVEN_COMB; @@ -913,12 +1252,15 @@ struct debug_item : ::cxxrtl_object { zero_at = 0; curr = const_cast(item.data); next = nullptr; + outline = nullptr; + attrs = nullptr; } template debug_item(debug_alias, const wire &item, size_t lsb_offset = 0) { - static_assert(sizeof(item.curr) == value::chunks * sizeof(chunk_t) && - sizeof(item.next) == value::chunks * sizeof(chunk_t), + static_assert(Bits == 0 || + (sizeof(item.curr) == value::chunks * sizeof(chunk_t) && + sizeof(item.next) == value::chunks * sizeof(chunk_t)), "wire is not compatible with C layout"); type = ALIAS; flags = DRIVEN_COMB; @@ -928,15 +1270,76 @@ struct debug_item : ::cxxrtl_object { zero_at = 0; curr = const_cast(item.curr.data); next = nullptr; + outline = nullptr; + attrs = nullptr; + } + + template + debug_item(debug_outline &group, const value &item, size_t lsb_offset = 0) { + static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), + "value is not compatible with C layout"); + type = OUTLINE; + flags = DRIVEN_COMB; + width = Bits; + lsb_at = lsb_offset; + depth = 1; + zero_at = 0; + curr = const_cast(item.data); + next = nullptr; + outline = &group; + attrs = nullptr; + } + + template + IntegerT get() const { + assert(width == Bits && depth == 1); + value item; + std::copy(curr, curr + value::chunks, item.data); + return item.template get(); + } + + template + void set(IntegerT other) const { + assert(width == Bits && depth == 1); + value item; + item.template set(other); + std::copy(item.data, item.data + value::chunks, next); } }; static_assert(std::is_standard_layout::value, "debug_item is not compatible with C layout"); +} // namespace cxxrtl + +typedef struct _cxxrtl_attr_set { + cxxrtl::metadata_map map; +} *cxxrtl_attr_set; + +namespace cxxrtl { + +// Representation of an attribute set in the C++ interface. +using debug_attrs = ::_cxxrtl_attr_set; + struct debug_items { + // Debug items may be composed of multiple parts, but the attributes are shared between all of them. + // There are additional invariants, not all of which are not checked by this code: + // - Memories and non-memories cannot be mixed together. + // - Bit indices (considering `lsb_at` and `width`) must not overlap. + // - Row indices (considering `depth` and `zero_at`) must be the same. + // - The `INPUT` and `OUTPUT` flags must be the same for all parts. + // Other than that, the parts can be quite different, e.g. it is OK to mix a value, a wire, an alias, + // and an outline, in the debug information for a single name in four parts. std::map> table; - - void add(const std::string &name, debug_item &&item) { - std::vector &parts = table[name]; + std::map> attrs_table; + + void add(const std::string &path, debug_item &&item, metadata_map &&item_attrs = {}) { + assert((path.empty() || path[path.size() - 1] != ' ') && path.find(" ") == std::string::npos); + std::unique_ptr &attrs = attrs_table[path]; + if (attrs.get() == nullptr) + attrs = std::unique_ptr(new debug_attrs); + for (auto attr : item_attrs) + attrs->map.insert(attr); + item.attrs = attrs.get(); + std::vector &parts = table[path]; parts.emplace_back(item); std::sort(parts.begin(), parts.end(), [](const debug_item &a, const debug_item &b) { @@ -944,59 +1347,130 @@ struct debug_items { }); } - size_t count(const std::string &name) const { - if (table.count(name) == 0) + size_t count(const std::string &path) const { + if (table.count(path) == 0) return 0; - return table.at(name).size(); + return table.at(path).size(); } - const std::vector &parts_at(const std::string &name) const { - return table.at(name); + const std::vector &at(const std::string &path) const { + return table.at(path); } - const debug_item &at(const std::string &name) const { - const std::vector &parts = table.at(name); + // Like `at()`, but operates only on single-part debug items. + const debug_item &operator [](const std::string &path) const { + const std::vector &parts = table.at(path); assert(parts.size() == 1); return parts.at(0); } - const debug_item &operator [](const std::string &name) const { - return at(name); + bool is_memory(const std::string &path) const { + return at(path).at(0).type == debug_item::MEMORY; + } + + const metadata_map &attrs(const std::string &path) const { + return attrs_table.at(path)->map; } }; +// Only `module` scopes are defined. The type is implicit, since Yosys does not currently support +// any other scope types. +struct debug_scope { + std::string module_name; + std::unique_ptr module_attrs; + std::unique_ptr cell_attrs; +}; + +struct debug_scopes { + std::map table; + + void add(const std::string &path, const std::string &module_name, metadata_map &&module_attrs, metadata_map &&cell_attrs) { + assert((path.empty() || path[path.size() - 1] != ' ') && path.find(" ") == std::string::npos); + assert(table.count(path) == 0); + debug_scope &scope = table[path]; + scope.module_name = module_name; + scope.module_attrs = std::unique_ptr(new debug_attrs { module_attrs }); + scope.cell_attrs = std::unique_ptr(new debug_attrs { cell_attrs }); + } + + size_t contains(const std::string &path) const { + return table.count(path); + } + + const debug_scope &operator [](const std::string &path) const { + return table.at(path); + } +}; + +// Tag class to disambiguate the default constructor used by the toplevel module that calls `reset()`, +// and the constructor of interior modules that should not call it. +struct interior {}; + +// The core API of the `module` class consists of only four virtual methods: `reset()`, `eval()`, +// `commit`, and `debug_info()`. (The virtual destructor is made necessary by C++.) Every other method +// is a convenience method, and exists solely to simplify some common pattern for C++ API consumers. +// No behavior may be added to such convenience methods that other parts of CXXRTL can rely on, since +// there is no guarantee they will be called (and, for example, other CXXRTL libraries will often call +// the `eval()` and `commit()` directly instead, as well as being exposed in the C API). struct module { module() {} virtual ~module() {} + // Modules with black boxes cannot be copied. Although not all designs include black boxes, + // delete the copy constructor and copy assignment operator to make sure that any downstream + // code that manipulates modules doesn't accidentally depend on their availability. module(const module &) = delete; module &operator=(const module &) = delete; - virtual bool eval() = 0; + module(module &&) = default; + module &operator=(module &&) = default; + + virtual void reset() = 0; + + // The `eval()` callback object, `performer`, is included in the virtual call signature since + // the generated code has broadly identical performance properties. + virtual bool eval(performer *performer = nullptr) = 0; + + // The `commit()` callback object, `observer`, is not included in the virtual call signature since + // the generated code is severely pessimized by it. To observe commit events, the non-virtual + // `commit(observer *)` overload must be called directly on a `module` subclass. virtual bool commit() = 0; - size_t step() { + size_t step(performer *performer = nullptr) { size_t deltas = 0; bool converged = false; do { - converged = eval(); + converged = eval(performer); deltas++; } while (commit() && !converged); return deltas; } - virtual void debug_info(debug_items &items, std::string path = "") { - (void)items, (void)path; + virtual void debug_info(debug_items *items, debug_scopes *scopes, std::string path, metadata_map &&cell_attrs = {}) { + (void)items, (void)scopes, (void)path, (void)cell_attrs; + } + + // Compatibility method. +#if __has_attribute(deprecated) + __attribute__((deprecated("Use `debug_info(path, &items, /*scopes=*/nullptr);` instead. (`path` could be \"top \".)"))) +#endif + void debug_info(debug_items &items, std::string path) { + debug_info(&items, /*scopes=*/nullptr, path); } }; } // namespace cxxrtl -// Internal structure used to communicate with the implementation of the C interface. +// Internal structures used to communicate with the implementation of the C interface. + typedef struct _cxxrtl_toplevel { std::unique_ptr module; } *cxxrtl_toplevel; +typedef struct _cxxrtl_outline { + std::function eval; +} *cxxrtl_outline; + // Definitions of internal Yosys cells. Other than the functions in this namespace, CXXRTL is fully generic // and indepenent of Yosys implementation details. // @@ -1130,49 +1604,49 @@ value xnor_ss(const value &a, const value &b) { template CXXRTL_ALWAYS_INLINE value shl_uu(const value &a, const value &b) { - return a.template zcast().template shl(b); + return a.template zcast().shl(b); } template CXXRTL_ALWAYS_INLINE value shl_su(const value &a, const value &b) { - return a.template scast().template shl(b); + return a.template scast().shl(b); } template CXXRTL_ALWAYS_INLINE value sshl_uu(const value &a, const value &b) { - return a.template zcast().template shl(b); + return a.template zcast().shl(b); } template CXXRTL_ALWAYS_INLINE value sshl_su(const value &a, const value &b) { - return a.template scast().template shl(b); + return a.template scast().shl(b); } template CXXRTL_ALWAYS_INLINE value shr_uu(const value &a, const value &b) { - return a.template shr(b).template zcast(); + return a.shr(b).template zcast(); } template CXXRTL_ALWAYS_INLINE value shr_su(const value &a, const value &b) { - return a.template shr(b).template scast(); + return a.shr(b).template scast(); } template CXXRTL_ALWAYS_INLINE value sshr_uu(const value &a, const value &b) { - return a.template shr(b).template zcast(); + return a.shr(b).template zcast(); } template CXXRTL_ALWAYS_INLINE value sshr_su(const value &a, const value &b) { - return a.template sshr(b).template scast(); + return a.sshr(b).template scast(); } template @@ -1399,35 +1873,23 @@ CXXRTL_ALWAYS_INLINE std::pair, value> divmod_uu(const value &a, const value &b) { constexpr size_t Bits = max(BitsY, max(BitsA, BitsB)); value quotient; + value remainder; value dividend = a.template zext(); value divisor = b.template zext(); - if (dividend.ucmp(divisor)) - return {/*quotient=*/value { 0u }, /*remainder=*/dividend.template trunc()}; - uint32_t divisor_shift = dividend.ctlz() - divisor.ctlz(); - divisor = divisor.shl(value<32> { divisor_shift }); - for (size_t step = 0; step <= divisor_shift; step++) { - quotient = quotient.shl(value<1> { 1u }); - if (!dividend.ucmp(divisor)) { - dividend = dividend.sub(divisor); - quotient.set_bit(0, true); - } - divisor = divisor.shr(value<1> { 1u }); - } - return {quotient.template trunc(), /*remainder=*/dividend.template trunc()}; + std::tie(quotient, remainder) = dividend.udivmod(divisor); + return {quotient.template trunc(), remainder.template trunc()}; } template CXXRTL_ALWAYS_INLINE std::pair, value> divmod_ss(const value &a, const value &b) { - value ua = a.template sext(); - value ub = b.template sext(); - if (ua.is_neg()) ua = ua.neg(); - if (ub.is_neg()) ub = ub.neg(); - value y, r; - std::tie(y, r) = divmod_uu(ua, ub); - if (a.is_neg() != b.is_neg()) y = y.neg(); - if (a.is_neg()) r = r.neg(); - return {y, r}; + constexpr size_t Bits = max(BitsY, max(BitsA, BitsB)); + value quotient; + value remainder; + value dividend = a.template sext(); + value divisor = b.template sext(); + std::tie(quotient, remainder) = dividend.sdivmod(divisor); + return {quotient.template trunc(), remainder.template trunc()}; } template @@ -1454,6 +1916,46 @@ value mod_ss(const value &a, const value &b) { return divmod_ss(a, b).second; } +template +CXXRTL_ALWAYS_INLINE +value modfloor_uu(const value &a, const value &b) { + return divmod_uu(a, b).second; +} + +// GHDL Modfloor operator. Returns r=a mod b, such that r has the same sign as b and +// a=b*N+r where N is some integer +// In practical terms, when a and b have different signs and the remainder returned by divmod_ss is not 0 +// then return the remainder + b +template +CXXRTL_ALWAYS_INLINE +value modfloor_ss(const value &a, const value &b) { + value r; + r = divmod_ss(a, b).second; + if((b.is_neg() != a.is_neg()) && !r.is_zero()) + return add_ss(b, r); + return r; +} + +template +CXXRTL_ALWAYS_INLINE +value divfloor_uu(const value &a, const value &b) { + return divmod_uu(a, b).first; +} + +// Divfloor. Similar to above: returns q=a//b, where q has the sign of a*b and a=b*q+N. +// In other words, returns (truncating) a/b, except if a and b have different signs +// and there's non-zero remainder, subtract one more towards floor. +template +CXXRTL_ALWAYS_INLINE +value divfloor_ss(const value &a, const value &b) { + value q, r; + std::tie(q, r) = divmod_ss(a, b); + if ((b.is_neg() != a.is_neg()) && !r.is_zero()) + return sub_uu(q, value<1> { 1u }); + return q; + +} + // Memory helper struct memory_index { bool valid; diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h new file mode 100644 index 00000000000..b8233b007c6 --- /dev/null +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h @@ -0,0 +1,872 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2023 Catherine + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef CXXRTL_REPLAY_H +#define CXXRTL_REPLAY_H + +#if !defined(WIN32) +#include +#define O_BINARY 0 +#else +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +// Theory of operation +// =================== +// +// Log format +// ---------- +// +// The replay log is a simple data format based on a sequence of 32-bit words. The following BNF-like grammar describes +// enough detail to understand the overall structure of the log data and be able to read hex dumps. For a greater +// degree of detail see the source code. The format is considered fully internal to CXXRTL and is subject to change +// without notice. +// +// ::= + +// ::= 0x52585843 0x00004c54 +// ::= * +// ::= ( | )* +// ::= 0xc0000000 ... +// ::= 0xc0000001 ... +// ::= 0x0??????? + | 0x1??????? + | 0x2??????? | 0x3??????? +// , ::= 0x???????? +// ::= | | | +// ::= 0xc0000010 +// ::= 0xc0000011 +// ::= 0xc0000012 +// ::= 0xc0000013 +// ::= 0xFFFFFFFF +// +// The replay log contains sample data, however, it does not cover the entire design. Rather, it only contains sample +// data for the subset of debug items containing _design state_: inputs and registers/latches. This keeps its size to +// a minimum, and recording speed to a maximum. The player samples any missing data by setting the design state items +// to the same values they had during recording, and re-evaluating the design. +// +// Packets for diagnostics (prints, breakpoints, assertions, and assumptions) are used solely for diagnostics emitted +// by the C++ testbench driving the simulation, and are not recorded while evaluating the design. (Diagnostics emitted +// by the RTL can be reconstructed at replay time, so recording them would be a waste of space.) +// +// Limits +// ------ +// +// The log may contain: +// +// * Up to 2**28-1 debug items containing design state. +// * Up to 2**32 chunks per debug item. +// * Up to 2**32 rows per memory. +// * Up to 2**32 samples. +// +// Of these limits, the last two are most likely to be eventually exceeded by practical recordings. However, other +// performance considerations will likely limit the size of such practical recordings first, so the log data format +// will undergo a breaking change at that point. +// +// Operations +// ---------- +// +// As suggested by the name "replay log", this format is designed for recording (writing) once and playing (reading) +// many times afterwards, such that reading the format can be done linearly and quickly. The log format is designed to +// support three primary read operations: +// +// 1. Initialization +// 2. Rewinding (to time T) +// 3. Replaying (for N samples) +// +// During initialization, the player establishes the mapping between debug item names and their 28-bit identifiers in +// the log. It is done once. +// +// During rewinding, the player begins reading at the latest non-incremental sample that still lies before the requested +// sample time. It continues reading incremental samples after that point until it reaches the requested sample time. +// This process is very cheap as the design is not evaluated; it is essentially a (convoluted) memory copy operation. +// +// During replaying, the player evaluates the design at the current time, which causes all debug items to assume +// the values they had before recording. This process is expensive. Once done, the player advances to the next state +// by reading the next (complete or incremental) sample, as above. Since a range of samples is replayed, this process +// is repeated several times in a row. +// +// In principle, when replaying, the player could only read the state of the inputs and the time delta and use a normal +// eval/commit loop to progress the simulation, which is fully deterministic so its calculated design state should be +// exactly the same as the recorded design state. In practice, it is both faster and more reliable (in presence of e.g. +// user-defined black boxes) to read the recorded values instead of calculating them. +// +// Note: The operations described above are conceptual and do not correspond exactly to methods on `cxxrtl::player`. +// The `cxxrtl::player::replay()` method does not evaluate the design. This is so that delta cycles could be ignored +// if they are not of interest while replaying. + +namespace cxxrtl { + +// A single diagnostic that can be manipulated as an object (including being written to and read from a file). +// This differs from the base CXXRTL interface, where diagnostics can only be emitted via a procedure call, and are +// not materialized as objects. +struct diagnostic { + // The `BREAK` flavor corresponds to a breakpoint, which is a diagnostic type that can currently only be emitted + // by the C++ testbench code. + enum flavor { + BREAK = 0, + PRINT = 1, + ASSERT = 2, + ASSUME = 3, + }; + + flavor type; + std::string message; + std::string location; // same format as the `src` attribute of `$print` or `$check` cell + + diagnostic() + : type(BREAK) {} + + diagnostic(flavor type, const std::string &message, const std::string &location) + : type(type), message(message), location(location) {} + + diagnostic(flavor type, const std::string &message, const char *file, unsigned line) + : type(type), message(message), location(std::string(file) + ':' + std::to_string(line)) {} +}; + +// A spool stores CXXRTL design state changes in a file. +class spool { +public: + // Unique pointer to a specific sample within a replay log. (Timestamps are not unique.) + typedef uint32_t pointer_t; + + // Numeric identifier assigned to a debug item within a replay log. Range limited to [1, MAXIMUM_IDENT]. + typedef uint32_t ident_t; + + static constexpr uint16_t VERSION = 0x0400; + + static constexpr uint64_t HEADER_MAGIC = 0x00004c5452585843; + static constexpr uint64_t VERSION_MASK = 0xffff000000000000; + + static constexpr uint32_t PACKET_DEFINE = 0xc0000000; + + static constexpr uint32_t PACKET_SAMPLE = 0xc0000001; + enum sample_flag : uint32_t { + EMPTY = 0, + INCREMENTAL = 1, + }; + + static constexpr uint32_t MAXIMUM_IDENT = 0x0fffffff; + static constexpr uint32_t CHANGE_MASK = 0x30000000; + + static constexpr uint32_t PACKET_CHANGE = 0x00000000/* | ident */; + static constexpr uint32_t PACKET_CHANGEI = 0x10000000/* | ident */; + static constexpr uint32_t PACKET_CHANGEL = 0x20000000/* | ident */; + static constexpr uint32_t PACKET_CHANGEH = 0x30000000/* | ident */; + + static constexpr uint32_t PACKET_DIAGNOSTIC = 0xc0000010/* | diagnostic::flavor */; + static constexpr uint32_t DIAGNOSTIC_MASK = 0x0000000f; + + static constexpr uint32_t PACKET_END = 0xffffffff; + + // Writing spools. + + class writer { + int fd; + size_t position; + std::vector buffer; + + // These functions aren't overloaded because of implicit numeric conversions. + + void emit_word(uint32_t word) { + if (position + 1 == buffer.size()) + flush(); + buffer[position++] = word; + } + + void emit_dword(uint64_t dword) { + emit_word(dword >> 0); + emit_word(dword >> 32); + } + + void emit_ident(ident_t ident) { + assert(ident <= MAXIMUM_IDENT); + emit_word(ident); + } + + void emit_size(size_t size) { + assert(size <= std::numeric_limits::max()); + emit_word(size); + } + + // Same implementation as `emit_size()`, different declared intent. + void emit_index(size_t index) { + assert(index <= std::numeric_limits::max()); + emit_word(index); + } + + void emit_string(std::string str) { + // Align to a word boundary, and add at least one terminating \0. + str.resize(str.size() + (sizeof(uint32_t) - (str.size() + sizeof(uint32_t)) % sizeof(uint32_t))); + for (size_t index = 0; index < str.size(); index += sizeof(uint32_t)) { + uint32_t word; + memcpy(&word, &str[index], sizeof(uint32_t)); + emit_word(word); + } + } + + void emit_time(const time ×tamp) { + const value &raw_timestamp(timestamp); + emit_word(raw_timestamp.data[0]); + emit_word(raw_timestamp.data[1]); + emit_word(raw_timestamp.data[2]); + } + + public: + // Creates a writer, and transfers ownership of `fd`, which must be open for appending. + // + // The buffer size is currently fixed to a "reasonably large" size, determined empirically by measuring writer + // performance on a representative design; large but not so large it would e.g. cause address space exhaustion + // on 32-bit platforms. + writer(spool &spool) : fd(spool.take_write()), position(0), buffer(32 * 1024 * 1024) { + assert(fd != -1); +#if !defined(WIN32) + int result = ftruncate(fd, 0); +#else + int result = _chsize_s(fd, 0); +#endif + assert(result == 0); + } + + writer(writer &&moved) : fd(moved.fd), position(moved.position), buffer(moved.buffer) { + moved.fd = -1; + moved.position = 0; + } + + writer(const writer &) = delete; + writer &operator=(const writer &) = delete; + + // Both write() calls and fwrite() calls are too expensive to perform implicitly. The API consumer must determine + // the optimal time to flush the writer and do that explicitly for best performance. + void flush() { + assert(fd != -1); + size_t data_size = position * sizeof(uint32_t); + size_t data_written = write(fd, buffer.data(), data_size); + assert(data_size == data_written); + position = 0; + } + + ~writer() { + if (fd != -1) { + flush(); + close(fd); + } + } + + void write_magic() { + // `CXXRTL` followed by version in binary. This header will read backwards on big-endian machines, which allows + // detection of this case, both visually and programmatically. + emit_dword(((uint64_t)VERSION << 48) | HEADER_MAGIC); + } + + void write_define(ident_t ident, const std::string &name, size_t part_index, size_t chunks, size_t depth) { + emit_word(PACKET_DEFINE); + emit_ident(ident); + emit_string(name); + emit_index(part_index); + emit_size(chunks); + emit_size(depth); + } + + void write_sample(bool incremental, pointer_t pointer, const time ×tamp) { + uint32_t flags = (incremental ? sample_flag::INCREMENTAL : 0); + emit_word(PACKET_SAMPLE); + emit_word(flags); + emit_word(pointer); + emit_time(timestamp); + } + + void write_change(ident_t ident, size_t chunks, const chunk_t *data) { + assert(ident <= MAXIMUM_IDENT); + + if (chunks == 1 && *data == 0) { + emit_word(PACKET_CHANGEL | ident); + } else if (chunks == 1 && *data == 1) { + emit_word(PACKET_CHANGEH | ident); + } else { + emit_word(PACKET_CHANGE | ident); + for (size_t offset = 0; offset < chunks; offset++) + emit_word(data[offset]); + } + } + + void write_change(ident_t ident, size_t chunks, const chunk_t *data, size_t index) { + assert(ident <= MAXIMUM_IDENT); + + emit_word(PACKET_CHANGEI | ident); + emit_index(index); + for (size_t offset = 0; offset < chunks; offset++) + emit_word(data[offset]); + } + + void write_diagnostic(const diagnostic &diagnostic) { + emit_word(PACKET_DIAGNOSTIC | diagnostic.type); + emit_string(diagnostic.message); + emit_string(diagnostic.location); + } + + void write_end() { + emit_word(PACKET_END); + } + }; + + // Reading spools. + + class reader { + FILE *f; + + uint32_t absorb_word() { + // If we're at end of file, `fread` will not write to `word`, and `PACKET_END` will be returned. + uint32_t word = PACKET_END; + fread(&word, sizeof(word), 1, f); + return word; + } + + uint64_t absorb_dword() { + uint32_t lo = absorb_word(); + uint32_t hi = absorb_word(); + return ((uint64_t)hi << 32) | lo; + } + + ident_t absorb_ident() { + ident_t ident = absorb_word(); + assert(ident <= MAXIMUM_IDENT); + return ident; + } + + size_t absorb_size() { + return absorb_word(); + } + + size_t absorb_index() { + return absorb_word(); + } + + std::string absorb_string() { + std::string str; + do { + size_t end = str.size(); + str.resize(end + 4); + uint32_t word = absorb_word(); + memcpy(&str[end], &word, sizeof(uint32_t)); + } while (str.back() != '\0'); + // Strings have no embedded zeroes besides the terminating one(s). + return str.substr(0, str.find('\0')); + } + + time absorb_time() { + value raw_timestamp; + raw_timestamp.data[0] = absorb_word(); + raw_timestamp.data[1] = absorb_word(); + raw_timestamp.data[2] = absorb_word(); + return time(raw_timestamp); + } + + public: + typedef uint64_t pos_t; + + // Creates a reader, and transfers ownership of `fd`, which must be open for reading. + reader(spool &spool) : f(fdopen(spool.take_read(), "r")) { + assert(f != nullptr); + } + + reader(reader &&moved) : f(moved.f) { + moved.f = nullptr; + } + + reader(const reader &) = delete; + reader &operator=(const reader &) = delete; + + ~reader() { + if (f != nullptr) + fclose(f); + } + + pos_t position() { + return ftell(f); + } + + void rewind(pos_t position) { + fseek(f, position, SEEK_SET); + } + + void read_magic() { + uint64_t magic = absorb_dword(); + assert((magic & ~VERSION_MASK) == HEADER_MAGIC); + assert((magic >> 48) == VERSION); + } + + bool read_define(ident_t &ident, std::string &name, size_t &part_index, size_t &chunks, size_t &depth) { + uint32_t header = absorb_word(); + if (header == PACKET_END) + return false; + assert(header == PACKET_DEFINE); + ident = absorb_ident(); + name = absorb_string(); + part_index = absorb_index(); + chunks = absorb_size(); + depth = absorb_size(); + return true; + } + + bool read_sample(bool &incremental, pointer_t &pointer, time ×tamp) { + uint32_t header = absorb_word(); + if (header == PACKET_END) + return false; + assert(header == PACKET_SAMPLE); + uint32_t flags = absorb_word(); + incremental = (flags & sample_flag::INCREMENTAL); + pointer = absorb_word(); + timestamp = absorb_time(); + return true; + } + + bool read_header(uint32_t &header) { + header = absorb_word(); + return header != PACKET_END; + } + + // This method must be separate from `read_change_data` because `chunks` and `depth` can only be looked up + // if `ident` is known. + bool read_change_ident(uint32_t header, ident_t &ident) { + if ((header & ~(CHANGE_MASK | MAXIMUM_IDENT)) != 0) + return false; // some other packet + ident = header & MAXIMUM_IDENT; + return true; + } + + void read_change_data(uint32_t header, size_t chunks, size_t depth, chunk_t *data) { + uint32_t index = 0; + switch (header & CHANGE_MASK) { + case PACKET_CHANGEL: + *data = 0; + return; + case PACKET_CHANGEH: + *data = 1; + return; + case PACKET_CHANGE: + break; + case PACKET_CHANGEI: + index = absorb_word(); + assert(index < depth); + break; + default: + assert(false && "Unrecognized change packet"); + } + for (size_t offset = 0; offset < chunks; offset++) + data[chunks * index + offset] = absorb_word(); + } + + bool read_diagnostic(uint32_t header, diagnostic &diagnostic) { + if ((header & ~DIAGNOSTIC_MASK) != PACKET_DIAGNOSTIC) + return false; // some other packet + uint32_t type = header & DIAGNOSTIC_MASK; + assert(type == diagnostic::BREAK || type == diagnostic::PRINT || + type == diagnostic::ASSERT || type == diagnostic::ASSUME); + diagnostic.type = (diagnostic::flavor)type; + diagnostic.message = absorb_string(); + diagnostic.location = absorb_string(); + return true; + } + }; + + // Opening spools. For certain uses of the record/replay mechanism, two distinct open files (two open files, i.e. + // two distinct file pointers, and not just file descriptors, which share the file pointer if duplicated) are used, + // for a reader and writer thread. This class manages the lifetime of the descriptors for these files. When only + // one of them is used, the other is closed harmlessly when the spool is destroyed. +private: + std::atomic writefd; + std::atomic readfd; + +public: + spool(const std::string &filename) + : writefd(open(filename.c_str(), O_CREAT|O_BINARY|O_WRONLY|O_APPEND, 0644)), + readfd(open(filename.c_str(), O_BINARY|O_RDONLY)) { + assert(writefd.load() != -1 && readfd.load() != -1); + } + + spool(spool &&moved) : writefd(moved.writefd.exchange(-1)), readfd(moved.readfd.exchange(-1)) {} + + spool(const spool &) = delete; + spool &operator=(const spool &) = delete; + + ~spool() { + if (int fd = writefd.exchange(-1)) + close(fd); + if (int fd = readfd.exchange(-1)) + close(fd); + } + + // Atomically acquire a write file descriptor for the spool. Can be called once, and will return -1 the next time + // it is called. Thread-safe. + int take_write() { + return writefd.exchange(-1); + } + + // Atomically acquire a read file descriptor for the spool. Can be called once, and will return -1 the next time + // it is called. Thread-safe. + int take_read() { + return readfd.exchange(-1); + } +}; + +// A CXXRTL recorder samples design state, producing complete or incremental updates, and writes them to a spool. +class recorder { + struct variable { + spool::ident_t ident; /* <= spool::MAXIMUM_IDENT */ + size_t chunks; + size_t depth; /* == 1 for wires */ + chunk_t *curr; + bool memory; + }; + + spool::writer writer; + std::vector variables; + std::vector inputs; // values of inputs must be recorded explicitly, as their changes are not observed + std::unordered_map ident_lookup; + bool streaming = false; // whether variable definitions have been written + spool::pointer_t pointer = 0; + time timestamp; + +public: + template + recorder(Args &&...args) : writer(std::forward(args)...) {} + + void start(module &module, std::string top_path = "") { + debug_items items; + module.debug_info(&items, /*scopes=*/nullptr, top_path); + start(items); + } + + void start(const debug_items &items) { + assert(!streaming); + + writer.write_magic(); + for (auto item : items.table) + for (size_t part_index = 0; part_index < item.second.size(); part_index++) { + auto &part = item.second[part_index]; + if ((part.flags & debug_item::INPUT) || (part.flags & debug_item::DRIVEN_SYNC) || + (part.type == debug_item::MEMORY)) { + variable var; + var.ident = variables.size() + 1; + var.chunks = (part.width + sizeof(chunk_t) * 8 - 1) / (sizeof(chunk_t) * 8); + var.depth = part.depth; + var.curr = part.curr; + var.memory = (part.type == debug_item::MEMORY); + ident_lookup[var.curr] = var.ident; + + assert(variables.size() < spool::MAXIMUM_IDENT); + if (part.flags & debug_item::INPUT) + inputs.push_back(variables.size()); + variables.push_back(var); + + writer.write_define(var.ident, item.first, part_index, var.chunks, var.depth); + } + } + writer.write_end(); + streaming = true; + } + + const time &latest_time() { + return timestamp; + } + + const time &advance_time(const time &delta) { + assert(!delta.is_negative()); + timestamp += delta; + return timestamp; + } + + void record_complete() { + assert(streaming); + + writer.write_sample(/*incremental=*/false, pointer++, timestamp); + for (auto var : variables) { + assert(var.ident != 0); + if (!var.memory) + writer.write_change(var.ident, var.chunks, var.curr); + else + for (size_t index = 0; index < var.depth; index++) + writer.write_change(var.ident, var.chunks, &var.curr[var.chunks * index], index); + } + writer.write_end(); + } + + // This function is generic over ModuleT to encourage observer callbacks to be inlined into the commit function. + template + bool record_incremental(ModuleT &module) { + assert(streaming); + + struct : observer { + std::unordered_map *ident_lookup; + spool::writer *writer; + + CXXRTL_ALWAYS_INLINE + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value) { + writer->write_change(ident_lookup->at(base), chunks, value); + } + + CXXRTL_ALWAYS_INLINE + void on_update(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) { + writer->write_change(ident_lookup->at(base), chunks, value, index); + } + } record_observer; + record_observer.ident_lookup = &ident_lookup; + record_observer.writer = &writer; + + writer.write_sample(/*incremental=*/true, pointer++, timestamp); + for (auto input_index : inputs) { + variable &var = variables.at(input_index); + assert(!var.memory); + writer.write_change(var.ident, var.chunks, var.curr); + } + bool changed = module.commit(record_observer); + writer.write_end(); + return changed; + } + + void record_diagnostic(const diagnostic &diagnostic) { + assert(streaming); + + // Emit an incremental delta cycle per diagnostic to simplify the logic of the recorder. This is inefficient, but + // diagnostics should be rare enough that this inefficiency does not matter. If it turns out to be an issue, this + // code should be changed to accumulate diagnostics to a buffer that is flushed in `record_{complete,incremental}` + // and also in `advance_time` before the timestamp is changed. (Right now `advance_time` never writes to the spool.) + writer.write_sample(/*incremental=*/true, pointer++, timestamp); + writer.write_diagnostic(diagnostic); + writer.write_end(); + } + + void flush() { + writer.flush(); + } +}; + +// A CXXRTL player reads samples from a spool, and changes the design state accordingly. To start reading samples, +// a spool must have been initialized: the recorder must have been started and an initial complete sample must have +// been written. +class player { + struct variable { + size_t chunks; + size_t depth; /* == 1 for wires */ + chunk_t *curr; + }; + + spool::reader reader; + std::unordered_map variables; + bool streaming = false; // whether variable definitions have been read + bool initialized = false; // whether a sample has ever been read + spool::pointer_t pointer = 0; + time timestamp; + + std::map> index_by_pointer; + std::map> index_by_timestamp; + + bool peek_sample(spool::pointer_t &pointer, time ×tamp) { + bool incremental; + auto position = reader.position(); + bool success = reader.read_sample(incremental, pointer, timestamp); + reader.rewind(position); + return success; + } + +public: + template + player(Args &&...args) : reader(std::forward(args)...) {} + + // The `top_path` must match the one given to the recorder. + void start(module &module, std::string top_path = "") { + debug_items items; + module.debug_info(&items, /*scopes=*/nullptr, top_path); + start(items); + } + + void start(const debug_items &items) { + assert(!streaming); + + reader.read_magic(); + while (true) { + spool::ident_t ident; + std::string name; + size_t part_index; + size_t chunks; + size_t depth; + if (!reader.read_define(ident, name, part_index, chunks, depth)) + break; + assert(variables.count(ident) == 0); + assert(items.count(name) != 0); + assert(part_index < items.count(name)); + + const debug_item &part = items.at(name).at(part_index); + assert(chunks == (part.width + sizeof(chunk_t) * 8 - 1) / (sizeof(chunk_t) * 8)); + assert(depth == part.depth); + + variable &var = variables[ident]; + var.chunks = chunks; + var.depth = depth; + var.curr = part.curr; + } + assert(variables.size() > 0); + streaming = true; + + // Establish the initial state of the design. + std::vector diagnostics; + initialized = replay(&diagnostics); + assert(initialized && diagnostics.empty()); + } + + // Returns the pointer of the current sample. + spool::pointer_t current_pointer() { + assert(initialized); + return pointer; + } + + // Returns the time of the current sample. + const time ¤t_time() { + assert(initialized); + return timestamp; + } + + // Returns `true` if there is a next sample to read, and sets `pointer` to its pointer if there is. + bool get_next_pointer(spool::pointer_t &pointer) { + assert(streaming); + time timestamp; + return peek_sample(pointer, timestamp); + } + + // Returns `true` if there is a next sample to read, and sets `timestamp` to its time if there is. + bool get_next_time(time ×tamp) { + assert(streaming); + uint32_t pointer; + return peek_sample(pointer, timestamp); + } + + // If this function returns `true`, then `current_pointer() == at_pointer`, and the module contains values that + // correspond to this pointer in the replay log. To obtain a valid pointer, call `current_pointer()`; while pointers + // are monotonically increasing for each consecutive sample, using arithmetic operations to create a new pointer is + // not allowed. The `diagnostics` argument, if not `nullptr`, receives the diagnostics recorded in this sample. + bool rewind_to(spool::pointer_t at_pointer, std::vector *diagnostics) { + assert(initialized); + + // The pointers in the replay log start from one that is greater than `at_pointer`. In this case the pointer will + // never be reached. + assert(index_by_pointer.size() > 0); + if (at_pointer < index_by_pointer.rbegin()->first) + return false; + + // Find the last complete sample whose pointer is less than or equal to `at_pointer`. Note that the comparison + // function used here is `std::greater`, inverting the direction of `lower_bound`. + auto position_it = index_by_pointer.lower_bound(at_pointer); + assert(position_it != index_by_pointer.end()); + reader.rewind(position_it->second); + + // Replay samples until eventually arriving to `at_pointer` or encountering end of file. + while(replay(diagnostics)) { + if (pointer == at_pointer) + return true; + + if (diagnostics) + diagnostics->clear(); + } + return false; + } + + // If this function returns `true`, then `current_time() <= at_or_before_timestamp`, and the module contains values + // that correspond to `current_time()` in the replay log. If `current_time() == at_or_before_timestamp` and there + // are several consecutive samples with the same time, the module contains values that correspond to the first of + // these samples. The `diagnostics` argument, if not `nullptr`, receives the diagnostics recorded in this sample. + bool rewind_to_or_before(const time &at_or_before_timestamp, std::vector *diagnostics) { + assert(initialized); + + // The timestamps in the replay log start from one that is greater than `at_or_before_timestamp`. In this case + // the timestamp will never be reached. Otherwise, this function will always succeed. + assert(index_by_timestamp.size() > 0); + if (at_or_before_timestamp < index_by_timestamp.rbegin()->first) + return false; + + // Find the last complete sample whose timestamp is less than or equal to `at_or_before_timestamp`. Note that + // the comparison function used here is `std::greater`, inverting the direction of `lower_bound`. + auto position_it = index_by_timestamp.lower_bound(at_or_before_timestamp); + assert(position_it != index_by_timestamp.end()); + reader.rewind(position_it->second); + + // Replay samples until eventually arriving to or past `at_or_before_timestamp` or encountering end of file. + while (replay(diagnostics)) { + if (timestamp == at_or_before_timestamp) + break; + + time next_timestamp; + if (!get_next_time(next_timestamp)) + break; + if (next_timestamp > at_or_before_timestamp) + break; + + if (diagnostics) + diagnostics->clear(); + } + return true; + } + + // If this function returns `true`, then `current_pointer()` and `current_time()` are updated for the next sample + // and the module now contains values that correspond to that sample. If it returns `false`, there was no next sample + // to read. The `diagnostics` argument, if not `nullptr`, receives the diagnostics recorded in the next sample. + bool replay(std::vector *diagnostics) { + assert(streaming); + + bool incremental; + auto position = reader.position(); + if (!reader.read_sample(incremental, pointer, timestamp)) + return false; + + // The very first sample that is read must be a complete sample. This is required for the rewind functions to work. + assert(initialized || !incremental); + + // It is possible (though not very useful) to have several complete samples with the same timestamp in a row. + // Ensure that we associate the timestamp with the position of the first such complete sample. (This condition + // works because the player never jumps over a sample.) + if (!incremental && !index_by_pointer.count(pointer)) { + assert(!index_by_timestamp.count(timestamp)); + index_by_pointer[pointer] = position; + index_by_timestamp[timestamp] = position; + } + + uint32_t header; + while (reader.read_header(header)) { + spool::ident_t ident; + diagnostic diag; + if (reader.read_change_ident(header, ident)) { + variable &var = variables.at(ident); + reader.read_change_data(header, var.chunks, var.depth, var.curr); + } else if (reader.read_diagnostic(header, diag)) { + if (diagnostics) + diagnostics->push_back(diag); + } else assert(false && "Unrecognized packet header"); + } + return true; + } +}; + +} + +#endif diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h new file mode 100644 index 00000000000..f37c2b65640 --- /dev/null +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h @@ -0,0 +1,231 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2023 Catherine + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef CXXRTL_TIME_H +#define CXXRTL_TIME_H + +#include +#include + +#include + +namespace cxxrtl { + +// A timestamp or a difference in time, stored as a 96-bit number of femtoseconds (10e-15 s). The range and resolution +// of this format can represent any VCD timestamp within approx. ±1255321.2 years, without the need for a timescale. +class time { +public: + static constexpr size_t bits = 96; // 3 chunks + +private: + static constexpr size_t resolution_digits = 15; + + static_assert(sizeof(chunk_t) == 4, "a chunk is expected to be 32-bit"); + static constexpr value resolution = value { + chunk_t(1000000000000000ull & 0xffffffffull), chunk_t(1000000000000000ull >> 32), 0u + }; + + // Signed number of femtoseconds from the beginning of time. + value raw; + +public: + constexpr time() {} + + explicit constexpr time(const value &raw) : raw(raw) {} + explicit operator const value &() const { return raw; } + + static constexpr time maximum() { + return time(value { 0xffffffffu, 0xffffffffu, 0x7fffffffu }); + } + + time(int64_t secs, int64_t femtos) { + value<64> secs_val; + secs_val.set(secs); + value<64> femtos_val; + femtos_val.set(femtos); + raw = secs_val.sext().mul(resolution).add(femtos_val.sext()); + } + + bool is_zero() const { + return raw.is_zero(); + } + + // Extracts the sign of the value. + bool is_negative() const { + return raw.is_neg(); + } + + // Extracts the number of whole seconds. Negative if the value is negative. + int64_t secs() const { + return raw.sdivmod(resolution).first.trunc<64>().get(); + } + + // Extracts the number of femtoseconds in the fractional second. Negative if the value is negative. + int64_t femtos() const { + return raw.sdivmod(resolution).second.trunc<64>().get(); + } + + bool operator==(const time &other) const { + return raw == other.raw; + } + + bool operator!=(const time &other) const { + return raw != other.raw; + } + + bool operator>(const time &other) const { + return other.raw.scmp(raw); + } + + bool operator>=(const time &other) const { + return !raw.scmp(other.raw); + } + + bool operator<(const time &other) const { + return raw.scmp(other.raw); + } + + bool operator<=(const time &other) const { + return !other.raw.scmp(raw); + } + + time operator+(const time &other) const { + return time(raw.add(other.raw)); + } + + time &operator+=(const time &other) { + *this = *this + other; + return *this; + } + + time operator-() const { + return time(raw.neg()); + } + + time operator-(const time &other) const { + return *this + (-other); + } + + time &operator-=(const time &other) { + *this = *this - other; + return *this; + } + + operator std::string() const { + char buf[48]; // x=2**95; len(f"-{x/1_000_000_000_000_000}.{x^1_000_000_000_000_000}") == 48 + int64_t secs = this->secs(); + int64_t femtos = this->femtos(); + snprintf(buf, sizeof(buf), "%s%" PRIi64 ".%015" PRIi64, + is_negative() ? "-" : "", secs >= 0 ? secs : -secs, femtos >= 0 ? femtos : -femtos); + return buf; + } + +#if __cplusplus >= 201603L + [[nodiscard("ignoring parse errors")]] +#endif + bool parse(const std::string &str) { + enum { + parse_sign_opt, + parse_integral, + parse_fractional, + } state = parse_sign_opt; + bool negative = false; + int64_t integral = 0; + int64_t fractional = 0; + size_t frac_digits = 0; + for (auto chr : str) { + switch (state) { + case parse_sign_opt: + state = parse_integral; + if (chr == '+' || chr == '-') { + negative = (chr == '-'); + break; + } + /* fallthrough */ + case parse_integral: + if (chr >= '0' && chr <= '9') { + integral *= 10; + integral += chr - '0'; + } else if (chr == '.') { + state = parse_fractional; + } else { + return false; + } + break; + case parse_fractional: + if (chr >= '0' && chr <= '9' && frac_digits < resolution_digits) { + fractional *= 10; + fractional += chr - '0'; + frac_digits++; + } else { + return false; + } + break; + } + } + if (frac_digits == 0) + return false; + while (frac_digits++ < resolution_digits) + fractional *= 10; + *this = negative ? -time { integral, fractional} : time { integral, fractional }; + return true; + } +}; + +// Out-of-line definition required until C++17. +constexpr value time::resolution; + +std::ostream &operator<<(std::ostream &os, const time &val) { + os << (std::string)val; + return os; +} + +// These literals are (confusingly) compatible with the ones from `std::chrono`: the `std::chrono` literals do not +// have an underscore (e.g. 1ms) and the `cxxrtl::time` literals do (e.g. 1_ms). This syntactic difference is +// a requirement of the C++ standard. Despite being compatible the literals should not be mixed in the same namespace. +namespace time_literals { + +time operator""_s(unsigned long long seconds) { + return time { (int64_t)seconds, 0 }; +} + +time operator""_ms(unsigned long long milliseconds) { + return time { 0, (int64_t)milliseconds * 1000000000000 }; +} + +time operator""_us(unsigned long long microseconds) { + return time { 0, (int64_t)microseconds * 1000000000 }; +} + +time operator""_ns(unsigned long long nanoseconds) { + return time { 0, (int64_t)nanoseconds * 1000000 }; +} + +time operator""_ps(unsigned long long picoseconds) { + return time { 0, (int64_t)picoseconds * 1000 }; +} + +time operator""_fs(unsigned long long femtoseconds) { + return time { 0, (int64_t)femtoseconds }; +} + +}; + +}; + +#endif diff --git a/backends/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h similarity index 81% rename from backends/cxxrtl/cxxrtl_vcd.h rename to backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h index dbeabbaf2d8..cb2ccf5fc26 100644 --- a/backends/cxxrtl/cxxrtl_vcd.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h @@ -19,7 +19,7 @@ #ifndef CXXRTL_VCD_H #define CXXRTL_VCD_H -#include +#include namespace cxxrtl { @@ -28,10 +28,13 @@ class vcd_writer { size_t ident; size_t width; chunk_t *curr; - size_t prev_off; + size_t cache_offset; + debug_outline *outline; + bool *outline_warm; }; std::vector current_scope; + std::map outlines; std::vector variables; std::vector cache; std::map aliases; @@ -66,12 +69,25 @@ class vcd_writer { } while (ident != 0); } + void emit_name(const std::string &name) { + for (char c : name) { + if (c == ':') { + // Due to a bug, GTKWave cannot parse a colon in the variable name, causing the VCD file + // to be unreadable. It cannot be escaped either, so replace it with the sideways colon. + buffer += ".."; + } else { + buffer += c; + } + } + } + void emit_var(const variable &var, const std::string &type, const std::string &name, size_t lsb_at, bool multipart) { assert(!streaming); buffer += "$var " + type + " " + std::to_string(var.width) + " "; emit_ident(var.ident); - buffer += " " + name; + buffer += " "; + emit_name(name); if (multipart || name.back() == ']' || lsb_at != 0) { if (var.width == 1) buffer += " [" + std::to_string(lsb_at) + "]"; @@ -112,16 +128,22 @@ class vcd_writer { buffer += '\n'; } - const variable ®ister_variable(size_t width, chunk_t *curr, bool constant = false) { + void reset_outlines() { + for (auto &outline_it : outlines) + outline_it.second = /*warm=*/(outline_it.first == nullptr); + } + + variable ®ister_variable(size_t width, chunk_t *curr, bool constant = false, debug_outline *outline = nullptr) { if (aliases.count(curr)) { return variables[aliases[curr]]; } else { + auto outline_it = outlines.emplace(outline, /*warm=*/(outline == nullptr)).first; const size_t chunks = (width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8); aliases[curr] = variables.size(); if (constant) { - variables.emplace_back(variable { variables.size(), width, curr, (size_t)-1 }); + variables.emplace_back(variable { variables.size(), width, curr, (size_t)-1, outline_it->first, &outline_it->second }); } else { - variables.emplace_back(variable { variables.size(), width, curr, cache.size() }); + variables.emplace_back(variable { variables.size(), width, curr, cache.size(), outline_it->first, &outline_it->second }); cache.insert(cache.end(), &curr[0], &curr[chunks]); } return variables.back(); @@ -129,13 +151,17 @@ class vcd_writer { } bool test_variable(const variable &var) { - if (var.prev_off == (size_t)-1) + if (var.cache_offset == (size_t)-1) return false; // constant + if (!*var.outline_warm) { + var.outline->eval(); + *var.outline_warm = true; + } const size_t chunks = (var.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8); - if (std::equal(&var.curr[0], &var.curr[chunks], &cache[var.prev_off])) { + if (std::equal(&var.curr[0], &var.curr[chunks], &cache[var.cache_offset])) { return false; } else { - std::copy(&var.curr[0], &var.curr[chunks], &cache[var.prev_off]); + std::copy(&var.curr[0], &var.curr[chunks], &cache[var.cache_offset]); return true; } } @@ -197,6 +223,10 @@ class vcd_writer { emit_var(register_variable(item.width, item.curr), "wire", name, item.lsb_at, multipart); break; + case debug_item::OUTLINE: + emit_var(register_variable(item.width, item.curr, /*constant=*/false, item.outline), + "wire", name, item.lsb_at, multipart); + break; } } @@ -211,13 +241,13 @@ class vcd_writer { } void add(const debug_items &items) { - this->template add(items, [](const std::string &, const debug_item &) { + this->add(items, [](const std::string &, const debug_item &) { return true; }); } void add_without_memories(const debug_items &items) { - this->template add(items, [](const std::string &, const debug_item &item) { + this->add(items, [](const std::string &, const debug_item &item) { return item.type != debug_item::MEMORY; }); } @@ -228,6 +258,7 @@ class vcd_writer { emit_scope({}); emit_enddefinitions(); } + reset_outlines(); emit_time(timestamp); for (auto var : variables) if (test_variable(var) || first_sample) { diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index e0013238c83..553eb23d645 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -107,8 +107,8 @@ struct EdifBackend : public Backend { log(" constant drivers first)\n"); log("\n"); log(" -gndvccy\n"); - log(" create \"GND\" and \"VCC\" cells with \"Y\" outputs. (the default is \"G\"\n"); - log(" for \"GND\" and \"P\" for \"VCC\".)\n"); + log(" create \"GND\" and \"VCC\" cells with \"Y\" outputs. (the default is\n"); + log(" \"G\" for \"GND\" and \"P\" for \"VCC\".)\n"); log("\n"); log(" -attrprop\n"); log(" create EDIF properties for cell attributes\n"); @@ -120,6 +120,9 @@ struct EdifBackend : public Backend { log(" sets the delimiting character for module port rename clauses to\n"); log(" parentheses, square brackets, or angle brackets.\n"); log("\n"); + log(" -lsbidx\n"); + log(" use index 0 for the LSB bit of a net or port instead of MSB.\n"); + log("\n"); log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n"); log("command generates EDIF files for the Xilinx place&route tools. It might be\n"); log("necessary to make small modifications to this command when a different tool\n"); @@ -132,6 +135,7 @@ struct EdifBackend : public Backend { std::string top_module_name; bool port_rename = false; bool attr_properties = false; + bool lsbidx = false; std::map> lib_cell_ports; bool nogndvcc = false, gndvccy = false, keepmode = false; CellTypes ct(design); @@ -173,6 +177,10 @@ struct EdifBackend : public Backend { } continue; } + if (args[argidx] == "-lsbidx") { + lsbidx = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -184,6 +192,14 @@ struct EdifBackend : public Backend { for (auto module : design->modules()) { + lib_cell_ports[module->name]; + + for (auto port : module->ports) + { + Wire *wire = module->wire(port); + lib_cell_ports[module->name][port] = std::max(lib_cell_ports[module->name][port], GetSize(wire)); + } + if (module->get_blackbox_attribute()) continue; @@ -197,10 +213,13 @@ struct EdifBackend : public Backend { for (auto cell : module->cells()) { + if (cell->type == ID($scopeinfo)) + continue; + if (design->module(cell->type) == nullptr || design->module(cell->type)->get_blackbox_attribute()) { lib_cell_ports[cell->type]; for (auto p : cell->connections()) - lib_cell_ports[cell->type][p.first] = GetSize(p.second); + lib_cell_ports[cell->type][p.first] = std::max(lib_cell_ports[cell->type][p.first], GetSize(p.second)); } } } @@ -437,7 +456,7 @@ struct EdifBackend : public Backend { *f << ")\n"; for (int i = 0; i < wire->width; i++) { RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i)); - net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input)); + net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), lsbidx ? i : GetSize(wire)-i-1), wire->port_input)); } } } @@ -468,13 +487,13 @@ struct EdifBackend : public Backend { log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n", i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i])); else { - int member_idx = GetSize(sig)-i-1; + int member_idx = lsbidx ? i : GetSize(sig)-i-1; auto m = design->module(cell->type); int width = sig.size(); if (m) { auto w = m->wire(p.first); if (w) { - member_idx = GetSize(w)-i-1; + member_idx = lsbidx ? i : GetSize(w)-i-1; width = GetSize(w); } } diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 9739a7a9ff5..dc76dbeecf5 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,8 +21,8 @@ #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" -#include "kernel/cellaigs.h" #include "kernel/log.h" +#include "kernel/mem.h" #include #include #include @@ -102,136 +102,276 @@ const char *make_id(IdString id) return namecache.at(id).c_str(); } -struct FirrtlWorker +std::string dump_const_string(const RTLIL::Const &data) { - Module *module; - std::ostream &f; + std::string res_str; - dict> reverse_wire_map; - string unconn_id; - RTLIL::Design *design; - std::string indent; + std::string str = data.decode_string(); + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] == '\n') + res_str += "\\n"; + else if (str[i] == '\t') + res_str += "\\t"; + else if (str[i] < 32) + res_str += stringf("\\%03o", str[i]); + else if (str[i] == '"') + res_str += "\\\""; + else if (str[i] == '\\') + res_str += "\\\\"; + else + res_str += str[i]; + } - // Define read/write ports and memories. - // We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction. - // For the moment, we don't handle $readmemh or $readmemb. - // These will be part of a subsequent PR. - struct read_port { - string name; - bool clk_enable; - bool clk_parity; - bool transparent; - RTLIL::SigSpec clk; - RTLIL::SigSpec ena; - RTLIL::SigSpec addr; - read_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr) : name(name), clk_enable(clk_enable), clk_parity(clk_parity), transparent(transparent), clk(clk), ena(ena), addr(addr) { - // Current (3/13/2019) conventions: - // generate a constant 0 for clock and a constant 1 for enable if they are undefined. - if (!clk.is_fully_def()) - this->clk = SigSpec(State::S0); - if (!ena.is_fully_def()) - this->ena = SigSpec(State::S1); - } - string gen_read(const char * indent) { - string addr_expr = make_expr(addr); - string ena_expr = make_expr(ena); - string clk_expr = make_expr(clk); - string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str()); - string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str()); - string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str()); - return addr_str + ena_str + clk_str; - } - }; - struct write_port : read_port { - RTLIL::SigSpec mask; - write_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr, RTLIL::SigSpec mask) : read_port(name, clk_enable, clk_parity, transparent, clk, ena, addr), mask(mask) { - if (!clk.is_fully_def()) - this->clk = SigSpec(RTLIL::Const(0)); - if (!ena.is_fully_def()) - this->ena = SigSpec(RTLIL::Const(0)); - if (!mask.is_fully_def()) - this->ena = SigSpec(RTLIL::Const(1)); - } - string gen_read(const char * /* indent */) { - log_error("gen_read called on write_port: %s\n", name.c_str()); - return stringf("gen_read called on write_port: %s\n", name.c_str()); - } - string gen_write(const char * indent) { - string addr_expr = make_expr(addr); - string ena_expr = make_expr(ena); - string clk_expr = make_expr(clk); - string mask_expr = make_expr(mask); - string mask_str = stringf("%s%s.mask <= %s\n", indent, name.c_str(), mask_expr.c_str()); - string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str()); - string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str()); - string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str()); - return addr_str + ena_str + clk_str + mask_str; - } - }; - /* Memories defined within this module. */ - struct memory { - Cell *pCell; // for error reporting - string name; // memory name - int abits; // number of address bits - int size; // size (in units) of the memory - int width; // size (in bits) of each element - int read_latency; - int write_latency; - vector read_ports; - vector write_ports; - std::string init_file; - std::string init_file_srcFileSpec; - string srcLine; - memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") { - // Provide defaults for abits or size if one (but not the other) is specified. - if (this->abits == 0 && this->size != 0) { - this->abits = ceil_log2(this->size); - } else if (this->abits != 0 && this->size == 0) { - this->size = 1 << this->abits; - } - // Sanity-check this construction. - if (this->name == "") { - log_error("Nameless memory%s\n", this->atLine()); - } - if (this->abits == 0 && this->size == 0) { - log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine()); - } - if (this->width == 0) { - log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine()); - } - } + return res_str; +} - // We need a default constructor for the dict insert. - memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} +std::string dump_const(const RTLIL::Const &data) +{ + std::string res_str; - const char *atLine() { - if (srcLine == "") { - if (pCell) { - auto p = pCell->attributes.find(ID::src); - srcLine = " at " + p->second.decode_string(); + // // For debugging purposes to find out how Yosys encodes flags. + // res_str += stringf("flags_%x --> ", data.flags); + + // Real-valued parameter. + if (data.flags & RTLIL::CONST_FLAG_REAL) + { + // Yosys stores real values as strings, so we call the string dumping code. + res_str += dump_const_string(data); + } + // String parameter. + else if (data.flags & RTLIL::CONST_FLAG_STRING) + { + res_str += "\""; + res_str += dump_const_string(data); + res_str += "\""; + } + // Numeric (non-real) parameter. + else + { + int width = data.bits.size(); + + // If a standard 32-bit int, then emit standard int value like "56" or + // "-56". Firrtl supports negative-valued int literals. + // + // SignedInt + // : ( '+' | '-' ) PosInt + // ; + if (width <= 32) + { + int32_t int_val = 0; + + for (int i = 0; i < width; i++) + { + switch (data.bits[i]) + { + case State::S0: break; + case State::S1: int_val |= (1 << i); break; + default: + log_error("Unexpected int value\n"); + break; } } - return srcLine.c_str(); - } - void add_memory_read_port(read_port &rp) { - read_ports.push_back(rp); + + res_str += stringf("%d", int_val); } - void add_memory_write_port(write_port &wp) { - write_ports.push_back(wp); + else + { + // If value is larger than 32 bits, then emit a binary representation of + // the number as integers are not large enough to contain the result. + // There is a caveat to this approach though: + // + // Note that parameter may be defined as having a fixed width as follows: + // + // parameter signed [26:0] test_signed; + // parameter [26:0] test_unsigned; + // parameter signed [40:0] test_signed_large; + // + // However, if you assign a value on the RHS without specifying the + // precision, then yosys considers the value you used as an int and + // assigns it a width of 32 bits regardless of the type of the parameter. + // + // defparam .test_signed = 49; (width = 32, though should be 27 based on definition) + // defparam .test_unsigned = 40'd35; (width = 40, though should be 27 based on definition) + // defparam .test_signed_large = 40'd12; (width = 40) + // + // We therefore may lose the precision of the original verilog literal if + // it was written without its bitwidth specifier. + + // Emit binary prefix for string. + res_str += "\"b"; + + // Emit bits. + for (int i = width - 1; i >= 0; i--) + { + log_assert(i < width); + switch (data.bits[i]) + { + case State::S0: res_str += "0"; break; + case State::S1: res_str += "1"; break; + case State::Sx: res_str += "x"; break; + case State::Sz: res_str += "z"; break; + case State::Sa: res_str += "-"; break; + case State::Sm: res_str += "m"; break; + } + } + + res_str += "\""; } - void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) { - this->init_file = init_file; - this->init_file_srcFileSpec = init_file_srcFileSpec; + } + + return res_str; +} + +std::string extmodule_name(RTLIL::Cell *cell, RTLIL::Module *mod_instance) +{ + // Since we are creating a custom extmodule for every cell that instantiates + // this blackbox, we need to create a custom name for it. We just use the + // name of the blackbox itself followed by the name of the cell. + const std::string cell_name = std::string(make_id(cell->name)); + const std::string blackbox_name = std::string(make_id(mod_instance->name)); + const std::string extmodule_name = blackbox_name + "_" + cell_name; + return extmodule_name; +} + +/** + * Emits a parameterized extmodule. Instance parameters are obtained from + * ''cell'' as it represents the instantiation of the blackbox defined by + * ''mod_instance'' and therefore contains all its instance parameters. + */ +void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream &f) +{ + const std::string indent = " "; + + const std::string blackbox_name = std::string(make_id(mod_instance->name)); + const std::string exported_name = extmodule_name(cell, mod_instance); + + // We use the cell's fileinfo for this extmodule as its parameters come from + // the cell and not from the module itself (the module contains default + // parameters, not the instance-specific ones we're using to emit the + // extmodule). + const std::string extmoduleFileinfo = getFileinfo(cell); + + // Emit extmodule header. + f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str()); + + // Emit extmodule ports. + for (auto wire : mod_instance->wires()) + { + const auto wireName = make_id(wire->name); + const std::string wireFileinfo = getFileinfo(wire); + + if (wire->port_input && wire->port_output) + { + log_error("Module port %s.%s is inout!\n", log_id(mod_instance), log_id(wire)); } - }; - dict memories; + const std::string portDecl = stringf("%s%s %s: UInt<%d> %s\n", + indent.c_str(), + wire->port_input ? "input" : "output", + wireName, + wire->width, + wireFileinfo.c_str() + ); - void register_memory(memory &m) + f << portDecl; + } + + // Emit extmodule "defname" field. This is the name of the verilog blackbox + // that is used when verilog is emitted, so we use the name of mod_instance + // here. + f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str()); + + // Emit extmodule generic parameters. + for (const auto &p : cell->parameters) { - memories[m.name] = m; + const RTLIL::IdString p_id = p.first; + const RTLIL::Const p_value = p.second; + + std::string param_name(p_id.c_str()); + const std::string param_value = dump_const(p_value); + + // Remove backslashes from parameters as these come from the internal RTLIL + // naming scheme, but should not exist in the emitted firrtl blackboxes. + // When firrtl is converted to verilog and given to downstream synthesis + // tools, these tools expect to find blackbox names and parameters as they + // were originally defined, i.e. without the extra RTLIL naming conventions. + param_name.erase( + std::remove(param_name.begin(), param_name.end(), '\\'), + param_name.end() + ); + + f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str()); } + f << "\n"; +} + +/** + * Emits extmodules for every instantiated blackbox in the design. + * + * RTLIL stores instance parameters at the cell's instantiation location. + * However, firrtl does not support module parameterization (everything is + * already elaborated). Firrtl instead supports external modules (extmodule), + * i.e. blackboxes that are defined by verilog and which have no body in + * firrtl itself other than the declaration of the blackboxes ports and + * parameters. + * + * Furthermore, firrtl does not support parameterization (even of extmodules) + * at a module's instantiation location and users must instead declare + * different extmodules with different instance parameters in the extmodule + * definition itself. + * + * This function goes through the design to identify all RTLIL blackboxes + * and emit parameterized extmodules with a unique name for each of them. The + * name that's given to the extmodule is + * + * _ + * + * Beware that it is therefore necessary for users to replace "parameterized" + * instances in the RTLIL sense with these custom extmodules for the firrtl to + * be valid. + */ +void emit_elaborated_extmodules(RTLIL::Design *design, std::ostream &f) +{ + for (auto module : design->modules()) + { + for (auto cell : module->cells()) + { + // Is this cell a module instance? + bool cellIsModuleInstance = cell->type[0] != '$'; + + if (cellIsModuleInstance) + { + // Find the module corresponding to this instance. + auto modInstance = design->module(cell->type); + // Ensure that we actually have a module instance + if (modInstance == nullptr) { + log_error("Unknown cell type %s\n", cell->type.c_str()); + return; + } + + bool modIsBlackbox = modInstance->get_blackbox_attribute(); + + if (modIsBlackbox) + { + emit_extmodule(cell, modInstance, f); + } + } + } + } +} + +struct FirrtlWorker +{ + Module *module; + std::ostream &f; + + dict> reverse_wire_map; + string unconn_id; + RTLIL::Design *design; + std::string indent; + void register_reverse_wire_map(string id, SigSpec sig) { for (int i = 0; i < GetSize(sig); i++) @@ -328,8 +468,16 @@ struct FirrtlWorker log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str()); return; } + + // If the instance is that of a blackbox, use the modified extmodule name + // that contains per-instance parameterizations. These instances were + // emitted earlier in the firrtl backend. + const std::string instanceName = instModule->get_blackbox_attribute() ? + extmodule_name(cell, instModule) : + instanceOf; + std::string cellFileinfo = getFileinfo(cell); - wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str(), cellFileinfo.c_str())); + wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str())); for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->second.size() > 0) { @@ -392,38 +540,15 @@ struct FirrtlWorker return result; } - void emit_extmodule() - { - std::string moduleFileinfo = getFileinfo(module); - f << stringf(" extmodule %s: %s\n", make_id(module->name), moduleFileinfo.c_str()); - vector port_decls; - - for (auto wire : module->wires()) - { - const auto wireName = make_id(wire->name); - std::string wireFileinfo = getFileinfo(wire); - - if (wire->port_input && wire->port_output) - { - log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire)); - } - port_decls.push_back(stringf(" %s %s: UInt<%d> %s\n", wire->port_input ? "input" : "output", - wireName, wire->width, wireFileinfo.c_str())); - } - - for (auto &str : port_decls) - { - f << str; - } - - f << stringf("\n"); - } - void emit_module() { std::string moduleFileinfo = getFileinfo(module); f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo.c_str()); - vector port_decls, wire_decls, cell_exprs, wire_exprs; + vector port_decls, wire_decls, mem_exprs, cell_exprs, wire_exprs; + + std::vector memories = Mem::get_all_memories(module); + for (auto &mem : memories) + mem.narrow(); for (auto wire : module->wires()) { @@ -440,25 +565,26 @@ struct FirrtlWorker { if (wire->port_input && wire->port_output) log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire)); - port_decls.push_back(stringf(" %s %s: UInt<%d> %s\n", wire->port_input ? "input" : "output", + port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent.c_str(), wire->port_input ? "input" : "output", wireName, wire->width, wireFileinfo.c_str())); } else { - wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", wireName, wire->width, wireFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), wireName, wire->width, wireFileinfo.c_str())); } } for (auto cell : module->cells()) { - static Const ndef(0, 0); + Const ndef(0, 0); // Is this cell is a module instance? - if (cell->type[0] != '$') + if (module->design->module(cell->type)) { process_instance(cell, wire_exprs); continue; } + // Not a module instance. Set up cell properties bool extract_y_bits = false; // Assume no extraction of final bits will be required. int a_width = cell->parameters.at(ID::A_WIDTH, ndef).as_int(); // The width of "A" @@ -476,7 +602,7 @@ struct FirrtlWorker if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor))) { string a_expr = make_expr(cell->getPort(ID::A)); - wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -516,7 +642,7 @@ struct FirrtlWorker if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -528,7 +654,7 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); std::string cellFileinfo = getFileinfo(cell); - wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str())); if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; @@ -608,12 +734,12 @@ struct FirrtlWorker always_uint = true; firrtl_width = max(a_width, b_width); } - else if ((cell->type == ID($eq)) | (cell->type == ID($eqx))) { + else if ((cell->type == ID($eq)) || (cell->type == ID($eqx))) { primop = "eq"; always_uint = true; firrtl_width = 1; } - else if ((cell->type == ID($ne)) | (cell->type == ID($nex))) { + else if ((cell->type == ID($ne)) || (cell->type == ID($nex))) { primop = "neq"; always_uint = true; firrtl_width = 1; @@ -638,7 +764,7 @@ struct FirrtlWorker always_uint = true; firrtl_width = 1; } - else if ((cell->type == ID($shl)) | (cell->type == ID($sshl))) { + else if ((cell->type == ID($shl)) || (cell->type == ID($sshl))) { // FIRRTL will widen the result (y) by the amount of the shift. // We'll need to offset this by extracting the un-widened portion as Verilog would do. extract_y_bits = true; @@ -656,7 +782,7 @@ struct FirrtlWorker firrtl_width = a_width + (1 << b_width) - 1; } } - else if ((cell->type == ID($shr)) | (cell->type == ID($sshr))) { + else if ((cell->type == ID($shr)) || (cell->type == ID($sshr))) { // We don't need to extract a specific range of bits. extract_y_bits = false; // Is the shift amount constant? @@ -746,7 +872,7 @@ struct FirrtlWorker if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; @@ -759,136 +885,19 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); string s_expr = make_expr(cell->getPort(ID::S)); - wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), width, cellFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str())); string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } - if (cell->type.in(ID($mem))) + if (cell->is_mem_cell()) { - string mem_id = make_id(cell->name); - int abits = cell->parameters.at(ID::ABITS).as_int(); - int width = cell->parameters.at(ID::WIDTH).as_int(); - int size = cell->parameters.at(ID::SIZE).as_int(); - memory m(cell, mem_id, abits, size, width); - int rd_ports = cell->parameters.at(ID::RD_PORTS).as_int(); - int wr_ports = cell->parameters.at(ID::WR_PORTS).as_int(); - - Const initdata = cell->parameters.at(ID::INIT); - for (State bit : initdata.bits) - if (bit != State::Sx) - log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(cell)); - - Const rd_clk_enable = cell->parameters.at(ID::RD_CLK_ENABLE); - Const wr_clk_enable = cell->parameters.at(ID::WR_CLK_ENABLE); - Const wr_clk_polarity = cell->parameters.at(ID::WR_CLK_POLARITY); - - int offset = cell->parameters.at(ID::OFFSET).as_int(); - if (offset != 0) - log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell)); - - for (int i = 0; i < rd_ports; i++) - { - if (rd_clk_enable[i] != State::S0) - log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); - - SigSpec addr_sig = cell->getPort(ID::RD_ADDR).extract(i*abits, abits); - SigSpec data_sig = cell->getPort(ID::RD_DATA).extract(i*width, width); - string addr_expr = make_expr(addr_sig); - string name(stringf("%s.r%d", m.name.c_str(), i)); - bool clk_enable = false; - bool clk_parity = true; - bool transparency = false; - SigSpec ena_sig = RTLIL::SigSpec(RTLIL::State::S1, 1); - SigSpec clk_sig = RTLIL::SigSpec(RTLIL::State::S0, 1); - read_port rp(name, clk_enable, clk_parity, transparency, clk_sig, ena_sig, addr_sig); - m.add_memory_read_port(rp); - cell_exprs.push_back(rp.gen_read(indent.c_str())); - register_reverse_wire_map(stringf("%s.data", name.c_str()), data_sig); - } - - for (int i = 0; i < wr_ports; i++) - { - if (wr_clk_enable[i] != State::S1) - log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); - - if (wr_clk_polarity[i] != State::S1) - log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); - - string name(stringf("%s.w%d", m.name.c_str(), i)); - bool clk_enable = true; - bool clk_parity = true; - bool transparency = false; - SigSpec addr_sig =cell->getPort(ID::WR_ADDR).extract(i*abits, abits); - string addr_expr = make_expr(addr_sig); - SigSpec data_sig =cell->getPort(ID::WR_DATA).extract(i*width, width); - string data_expr = make_expr(data_sig); - SigSpec clk_sig = cell->getPort(ID::WR_CLK).extract(i); - string clk_expr = make_expr(clk_sig); - - SigSpec wen_sig = cell->getPort(ID::WR_EN).extract(i*width, width); - string wen_expr = make_expr(wen_sig[0]); - - for (int i = 1; i < GetSize(wen_sig); i++) - if (wen_sig[0] != wen_sig[i]) - log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); - - SigSpec mask_sig = RTLIL::SigSpec(RTLIL::State::S1, 1); - write_port wp(name, clk_enable, clk_parity, transparency, clk_sig, wen_sig[0], addr_sig, mask_sig); - m.add_memory_write_port(wp); - cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), name.c_str(), data_expr.c_str())); - cell_exprs.push_back(wp.gen_write(indent.c_str())); - } - register_memory(m); - continue; - } - - if (cell->type.in(ID($memwr), ID($memrd), ID($meminit))) - { - std::string cell_type = fid(cell->type); - std::string mem_id = make_id(cell->parameters[ID::MEMID].decode_string()); - int abits = cell->parameters.at(ID::ABITS).as_int(); - int width = cell->parameters.at(ID::WIDTH).as_int(); - memory *mp = nullptr; - if (cell->type == ID($meminit) ) { - log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str()); - } else { - // It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition. - auto addrSig = cell->getPort(ID::ADDR); - auto dataSig = cell->getPort(ID::DATA); - auto enableSig = cell->getPort(ID::EN); - auto clockSig = cell->getPort(ID::CLK); - Const clk_enable = cell->parameters.at(ID::CLK_ENABLE); - Const clk_polarity = cell->parameters.at(ID::CLK_POLARITY); - - // Do we already have an entry for this memory? - if (memories.count(mem_id) == 0) { - memory m(cell, mem_id, abits, 0, width); - register_memory(m); - } - mp = &memories.at(mem_id); - int portNum = 0; - bool transparency = false; - string data_expr = make_expr(dataSig); - if (cell->type.in(ID($memwr))) { - portNum = (int) mp->write_ports.size(); - write_port wp(stringf("%s.w%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig, dataSig); - mp->add_memory_write_port(wp); - cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), wp.name.c_str(), data_expr.c_str())); - cell_exprs.push_back(wp.gen_write(indent.c_str())); - } else if (cell->type.in(ID($memrd))) { - portNum = (int) mp->read_ports.size(); - read_port rp(stringf("%s.r%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig); - mp->add_memory_read_port(rp); - cell_exprs.push_back(rp.gen_read(indent.c_str())); - register_reverse_wire_map(stringf("%s.data", rp.name.c_str()), dataSig); - } - } + // Will be handled below, as part of a Mem. continue; } @@ -902,20 +911,14 @@ struct FirrtlWorker string expr = make_expr(cell->getPort(ID::D)); string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")"; - wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s %s\n", y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); + wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str())); - cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Q)); continue; } - // This may be a parameterized module - paramod. - if (cell->type.begins_with("$paramod")) - { - process_instance(cell, wire_exprs); - continue; - } if (cell->type == ID($shiftx)) { // assign y = a[b +: y_width]; // We'll extract the correct bits as part of the primop. @@ -923,7 +926,7 @@ struct FirrtlWorker string a_expr = make_expr(cell->getPort(ID::A)); // Get the initial bit selector string b_expr = make_expr(cell->getPort(ID::B)); - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); if (cell->getParam(ID::B_SIGNED).as_bool()) { // Use validif to constrain the selection (test the sign bit) @@ -933,7 +936,7 @@ struct FirrtlWorker } string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str()); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } @@ -945,7 +948,7 @@ struct FirrtlWorker string b_expr = make_expr(cell->getPort(ID::B)); auto b_string = b_expr.c_str(); string expr; - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); if (cell->getParam(ID::B_SIGNED).as_bool()) { // We generate a left or right shift based on the sign of b. @@ -959,7 +962,7 @@ struct FirrtlWorker } else { expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string); } - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } @@ -972,22 +975,101 @@ struct FirrtlWorker if (a_width < y_width) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); } - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), a_expr.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str())); register_reverse_wire_map(y_id, cell->getPort(ID::Y)); continue; } + + if (cell->type == ID($scopeinfo)) + continue; log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); } + for (auto &mem : memories) { + string mem_id = make_id(mem.memid); + + Const init_data = mem.get_init_data(); + if (!init_data.is_fully_undef()) + log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(mem.memid)); + + if (mem.start_offset != 0) + log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(mem.memid)); + + for (int i = 0; i < GetSize(mem.rd_ports); i++) + { + auto &port = mem.rd_ports[i]; + string port_name(stringf("%s.r%d", mem_id.c_str(), i)); + + if (port.clk_enable) + log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); + + std::ostringstream rpe; + + string addr_expr = make_expr(port.addr); + string ena_expr = make_expr(State::S1); + string clk_expr = make_expr(State::S0); + + rpe << stringf("%s%s.addr <= %s\n", indent.c_str(), port_name.c_str(), addr_expr.c_str()); + rpe << stringf("%s%s.en <= %s\n", indent.c_str(), port_name.c_str(), ena_expr.c_str()); + rpe << stringf("%s%s.clk <= asClock(%s)\n", indent.c_str(), port_name.c_str(), clk_expr.c_str()); + cell_exprs.push_back(rpe.str()); + register_reverse_wire_map(stringf("%s.data", port_name.c_str()), port.data); + } + + for (int i = 0; i < GetSize(mem.wr_ports); i++) + { + auto &port = mem.wr_ports[i]; + string port_name(stringf("%s.w%d", mem_id.c_str(), i)); + + if (!port.clk_enable) + log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); + if (!port.clk_polarity) + log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); + for (int i = 1; i < GetSize(port.en); i++) + if (port.en[0] != port.en[i]) + log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(mem.memid)); + + std::ostringstream wpe; + + string data_expr = make_expr(port.data); + string addr_expr = make_expr(port.addr); + string ena_expr = make_expr(port.en[0]); + string clk_expr = make_expr(port.clk); + string mask_expr = make_expr(State::S1); + wpe << stringf("%s%s.data <= %s\n", indent.c_str(), port_name.c_str(), data_expr.c_str()); + wpe << stringf("%s%s.addr <= %s\n", indent.c_str(), port_name.c_str(), addr_expr.c_str()); + wpe << stringf("%s%s.en <= %s\n", indent.c_str(), port_name.c_str(), ena_expr.c_str()); + wpe << stringf("%s%s.clk <= asClock(%s)\n", indent.c_str(), port_name.c_str(), clk_expr.c_str()); + wpe << stringf("%s%s.mask <= %s\n", indent.c_str(), port_name.c_str(), mask_expr.c_str()); + + cell_exprs.push_back(wpe.str()); + } + + std::ostringstream me; + + me << stringf(" mem %s:\n", mem_id.c_str()); + me << stringf(" data-type => UInt<%d>\n", mem.width); + me << stringf(" depth => %d\n", mem.size); + for (int i = 0; i < GetSize(mem.rd_ports); i++) + me << stringf(" reader => r%d\n", i); + for (int i = 0; i < GetSize(mem.wr_ports); i++) + me << stringf(" writer => w%d\n", i); + me << stringf(" read-latency => %d\n", 0); + me << stringf(" write-latency => %d\n", 1); + me << stringf(" read-under-write => undefined\n"); + + mem_exprs.push_back(me.str()); + } + for (auto conn : module->connections()) { string y_id = next_id(); int y_width = GetSize(conn.first); string expr = make_expr(conn.second); - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); - cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width)); + cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str())); register_reverse_wire_map(y_id, conn.first); } @@ -1053,13 +1135,13 @@ struct FirrtlWorker if (is_valid) { if (make_unconn_id) { - wire_decls.push_back(stringf(" wire %s: UInt<1> %s\n", unconn_id.c_str(), wireFileinfo.c_str())); + wire_decls.push_back(stringf("%swire %s: UInt<1> %s\n", indent.c_str(), unconn_id.c_str(), wireFileinfo.c_str())); // `invalid` is a firrtl construction for simulation so we will not // tag it with a @[fileinfo] tag as it doesn't directly correspond to // a specific line of verilog code. - wire_decls.push_back(stringf(" %s is invalid\n", unconn_id.c_str())); + wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), unconn_id.c_str())); } - wire_exprs.push_back(stringf(" %s <= %s %s\n", make_id(wire->name), expr.c_str(), wireFileinfo.c_str())); + wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), make_id(wire->name), expr.c_str(), wireFileinfo.c_str())); } else { if (make_unconn_id) { unconn_id.clear(); @@ -1067,7 +1149,7 @@ struct FirrtlWorker // `invalid` is a firrtl construction for simulation so we will not // tag it with a @[fileinfo] tag as it doesn't directly correspond to // a specific line of verilog code. - wire_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name))); + wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), make_id(wire->name))); } } @@ -1081,22 +1163,9 @@ struct FirrtlWorker f << stringf("\n"); - // If we have any memory definitions, output them. - for (auto kv : memories) { - memory &m = kv.second; - f << stringf(" mem %s:\n", m.name.c_str()); - f << stringf(" data-type => UInt<%d>\n", m.width); - f << stringf(" depth => %d\n", m.size); - for (int i = 0; i < (int) m.read_ports.size(); i += 1) { - f << stringf(" reader => r%d\n", i); - } - for (int i = 0; i < (int) m.write_ports.size(); i += 1) { - f << stringf(" writer => w%d\n", i); - } - f << stringf(" read-latency => %d\n", m.read_latency); - f << stringf(" write-latency => %d\n", m.write_latency); - f << stringf(" read-under-write => undefined\n"); - } + for (auto str : mem_exprs) + f << str; + f << stringf("\n"); for (auto str : cell_exprs) @@ -1112,12 +1181,7 @@ struct FirrtlWorker void run() { - // Blackboxes should be emitted as `extmodule`s in firrtl. Only ports are - // emitted in such a case. - if (module->get_blackbox_attribute()) - emit_extmodule(); - else - emit_module(); + emit_module(); } }; @@ -1132,6 +1196,9 @@ struct FirrtlBackend : public Backend { log("Write a FIRRTL netlist of the current design.\n"); log("The following commands are executed by this command:\n"); log(" pmuxtree\n"); + log(" bmuxmap\n"); + log(" demuxmap\n"); + log(" bwmuxmap\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override @@ -1154,7 +1221,10 @@ struct FirrtlBackend : public Backend { log_header(design, "Executing FIRRTL backend.\n"); log_push(); - Pass::call(design, stringf("pmuxtree")); + Pass::call(design, "pmuxtree"); + Pass::call(design, "bmuxmap"); + Pass::call(design, "demuxmap"); + Pass::call(design, "bwmuxmap"); namecache.clear(); autoid_counter = 0; @@ -1177,13 +1247,22 @@ struct FirrtlBackend : public Backend { if (top == nullptr) top = last; + if (!top) + log_cmd_error("There is no top module in this design!\n"); + std::string circuitFileinfo = getFileinfo(top); *f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo.c_str()); + emit_elaborated_extmodules(design, *f); + + // Emit non-blackbox modules. for (auto module : design->modules()) { - FirrtlWorker worker(module, *f, design); - worker.run(); + if (!module->get_blackbox_attribute()) + { + FirrtlWorker worker(module, *f, design); + worker.run(); + } } namecache.clear(); diff --git a/backends/firrtl/test.sh b/backends/firrtl/test.sh index fe7e3a32912..dd675e91712 100644 --- a/backends/firrtl/test.sh +++ b/backends/firrtl/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex cd ../../ diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index a6b36de6cba..59173c4a2d4 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -68,7 +68,7 @@ struct IntersynthBackend : public Backend { log(" only write selected modules. modules must be selected entirely or\n"); log(" not at all.\n"); log("\n"); - log("http://www.clifford.at/intersynth/\n"); + log("http://bygone.clairexen.net/intersynth/\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override diff --git a/backends/jny/Makefile.inc b/backends/jny/Makefile.inc new file mode 100644 index 00000000000..5e417128ed9 --- /dev/null +++ b/backends/jny/Makefile.inc @@ -0,0 +1,2 @@ + +OBJS += backends/jny/jny.o diff --git a/backends/jny/jny.cc b/backends/jny/jny.cc new file mode 100644 index 00000000000..9989feed599 --- /dev/null +++ b/backends/jny/jny.cc @@ -0,0 +1,577 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Aki "lethalbit" Van Ness + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/rtlil.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/log.h" +#include +#include +#include +#include +#include +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + + +struct JnyWriter +{ + private: + std::ostream &f; + bool _use_selection; + + // XXX(aki): TODO: this needs to be updated to us + // dict and then coalesce_cells needs to be updated + // but for now for the PoC this looks to be sufficient + std::unordered_map> _cells{}; + + bool _include_connections; + bool _include_attributes; + bool _include_properties; + + string escape_string(string str) { + std::string newstr; + + auto itr = str.begin(); + + for(; itr != str.end(); ++itr) { + switch (*itr) { + case '\\': { + newstr += "\\\\"; + break; + } case '\n': { + newstr += "\\n"; + break; + } case '\f': { + newstr += "\\f"; + break; + } case '\t': { + newstr += "\\t"; + break; + } case '\r': { + newstr += "\\r"; + break; + } case '\"': { + newstr += "\\\""; + break; + } case '\b': { + newstr += "\\b"; + break; + } default: { + newstr += *itr; + } + } + } + + return newstr; + } + + // XXX(aki): I know this is far from ideal but i'm out of spoons and cant focus so + // it'll have to do for now, + void coalesce_cells(Module* mod) + { + _cells.clear(); + for (auto cell : mod->cells()) { + const auto cell_type = escape_string(RTLIL::unescape_id(cell->type)); + + if (_cells.find(cell_type) == _cells.end()) + _cells.emplace(cell_type, std::vector()); + + _cells.at(cell_type).push_back(cell); + } + } + + // XXX(aki): this is a lazy way to do this i know,,, + std::string gen_indent(const uint16_t level) + { + std::stringstream s; + for (uint16_t i = 0; i <= level; ++i) + { + s << " "; + } + return s.str(); + } + + public: + JnyWriter(std::ostream &f, bool use_selection, bool connections, bool attributes, bool properties) noexcept: + f(f), _use_selection(use_selection), + _include_connections(connections), _include_attributes(attributes), _include_properties(properties) + { } + + void write_metadata(Design *design, uint16_t indent_level = 0, std::string invk = "") + { + log_assert(design != nullptr); + + design->sort(); + + f << "{\n"; + f << " \"$schema\": \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\",\n"; + f << stringf(" \"generator\": \"%s\",\n", escape_string(yosys_version_str).c_str()); + f << " \"version\": \"0.0.1\",\n"; + f << " \"invocation\": \"" << escape_string(invk) << "\",\n"; + f << " \"features\": ["; + + size_t fnum{0}; + if (_include_connections) { + ++fnum; + f << "\"connections\""; + } + + if (_include_attributes) { + if (fnum > 0) + f << ", "; + ++fnum; + f << "\"attributes\""; + } + + if (_include_properties) { + if (fnum > 0) + f << ", "; + ++fnum; + f << "\"properties\""; + } + + f << "],\n"; + + f << " \"modules\": [\n"; + + bool first{true}; + for (auto mod : _use_selection ? design->selected_modules() : design->modules()) { + if (!first) + f << ",\n"; + write_module(mod, indent_level + 2); + first = false; + } + + f << "\n"; + f << " ]\n"; + f << "}\n"; + } + + void write_sigspec(const RTLIL::SigSpec& sig, uint16_t indent_level = 0) { + const auto _indent = gen_indent(indent_level); + + f << _indent << " {\n"; + f << _indent << " \"width\": \"" << sig.size() << "\",\n"; + f << _indent << " \"type\": \""; + + if (sig.is_wire()) { + f << "wire"; + } else if (sig.is_chunk()) { + f << "chunk"; + } else if (sig.is_bit()) { + f << "bit"; + } else { + f << "unknown"; + } + f << "\",\n"; + + f << _indent << " \"const\": "; + if (sig.has_const()) { + f << "true"; + } else { + f << "false"; + } + + f << "\n"; + + f << _indent << " }"; + } + + void write_mod_conn(const std::pair& conn, uint16_t indent_level = 0) { + const auto _indent = gen_indent(indent_level); + f << _indent << " {\n"; + f << _indent << " \"signals\": [\n"; + + write_sigspec(conn.first, indent_level + 2); + f << ",\n"; + write_sigspec(conn.second, indent_level + 2); + f << "\n"; + + f << _indent << " ]\n"; + f << _indent << " }"; + } + + void write_cell_conn(const std::pair& sig, uint16_t indent_level = 0) { + const auto _indent = gen_indent(indent_level); + f << _indent << " {\n"; + f << _indent << " \"name\": \"" << escape_string(RTLIL::unescape_id(sig.first)) << "\",\n"; + f << _indent << " \"signals\": [\n"; + + write_sigspec(sig.second, indent_level + 2); + f << "\n"; + + f << _indent << " ]\n"; + f << _indent << " }"; + } + + void write_module(Module* mod, uint16_t indent_level = 0) { + log_assert(mod != nullptr); + + coalesce_cells(mod); + + const auto _indent = gen_indent(indent_level); + + f << _indent << "{\n"; + f << stringf(" %s\"name\": \"%s\",\n", _indent.c_str(), escape_string(RTLIL::unescape_id(mod->name)).c_str()); + f << _indent << " \"cell_sorts\": [\n"; + + bool first_sort{true}; + for (auto& sort : _cells) { + if (!first_sort) + f << ",\n"; + write_cell_sort(sort, indent_level + 2); + first_sort = false; + } + f << "\n"; + + f << _indent << " ]"; + if (_include_connections) { + f << ",\n" << _indent << " \"connections\": [\n"; + + bool first_conn{true}; + for (const auto& conn : mod->connections()) { + if (!first_conn) + f << ",\n"; + + write_mod_conn(conn, indent_level + 2); + + first_conn = false; + } + + f << _indent << " ]"; + } + if (_include_attributes) { + f << ",\n" << _indent << " \"attributes\": {\n"; + + write_prams(mod->attributes, indent_level + 2); + + f << "\n"; + f << _indent << " }"; + } + f << "\n" << _indent << "}"; + } + + void write_cell_ports(RTLIL::Cell* port_cell, uint64_t indent_level = 0) { + const auto _indent = gen_indent(indent_level); + + bool first_port{true}; + for (auto con : port_cell->connections()) { + if (!first_port) + f << ",\n"; + + f << _indent << " {\n"; + f << stringf(" %s\"name\": \"%s\",\n", _indent.c_str(), escape_string(RTLIL::unescape_id(con.first)).c_str()); + f << _indent << " \"direction\": \""; + if (port_cell->input(con.first)) + f << "i"; + if (port_cell->input(con.first)) + f << "o"; + f << "\",\n"; + if (con.second.size() == 1) + f << _indent << " \"range\": [0, 0]\n"; + else + f << stringf(" %s\"range\": [%d, %d]\n", _indent.c_str(), con.second.size(), 0); + f << _indent << " }"; + + first_port = false; + } + f << "\n"; + } + + + void write_cell_sort(std::pair>& sort, uint16_t indent_level = 0) { + const auto port_cell = sort.second.front(); + const auto _indent = gen_indent(indent_level); + + f << _indent << "{\n"; + f << stringf(" %s\"type\": \"%s\",\n", _indent.c_str(), sort.first.c_str()); + f << _indent << " \"ports\": [\n"; + + write_cell_ports(port_cell, indent_level + 2); + + f << _indent << " ],\n" << _indent << " \"cells\": [\n"; + + bool first_cell{true}; + for (auto& cell : sort.second) { + if (!first_cell) + f << ",\n"; + + write_cell(cell, indent_level + 2); + + first_cell = false; + } + + f << "\n"; + f << _indent << " ]\n"; + f << _indent << "}"; + } + + void write_param_val(const Const& v) { + if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) == RTLIL::ConstFlags::CONST_FLAG_STRING) { + const auto str = v.decode_string(); + + // XXX(aki): TODO, uh, yeah + + f << "\"" << escape_string(str) << "\""; + } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) == RTLIL::ConstFlags::CONST_FLAG_SIGNED) { + f << stringf("\"%dsd %d\"", v.size(), v.as_int(true)); + } else if ((v.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) == RTLIL::ConstFlags::CONST_FLAG_REAL) { + + } else { + f << "\"" << escape_string(v.as_string()) << "\""; + } + } + + void write_prams(dict& params, uint16_t indent_level = 0) { + const auto _indent = gen_indent(indent_level); + + bool first_param{true}; + for (auto& param : params) { + if (!first_param) + f << stringf(",\n"); + const auto param_val = param.second; + if (!param_val.empty()) { + f << stringf(" %s\"%s\": ", _indent.c_str(), escape_string(RTLIL::unescape_id(param.first)).c_str()); + write_param_val(param_val); + } else { + f << stringf(" %s\"%s\": true", _indent.c_str(), escape_string(RTLIL::unescape_id(param.first)).c_str()); + } + + first_param = false; + } + } + + void write_cell(Cell* cell, uint16_t indent_level = 0) { + const auto _indent = gen_indent(indent_level); + log_assert(cell != nullptr); + + f << _indent << " {\n"; + f << stringf(" %s\"name\": \"%s\"", _indent.c_str(), escape_string(RTLIL::unescape_id(cell->name)).c_str()); + + if (_include_connections) { + f << ",\n" << _indent << " \"connections\": [\n"; + + bool first_conn{true}; + for (const auto& conn : cell->connections()) { + if (!first_conn) + f << ",\n"; + + write_cell_conn(conn, indent_level + 2); + + first_conn = false; + } + + f << "\n"; + f << _indent << " ]"; + } + + if (_include_attributes) { + f << ",\n" << _indent << " \"attributes\": {\n"; + + write_prams(cell->attributes, indent_level + 2); + + f << "\n"; + f << _indent << " }"; + } + + if (_include_properties) { + f << ",\n" << _indent << " \"parameters\": {\n"; + + write_prams(cell->parameters, indent_level + 2); + + f << "\n"; + f << _indent << " }"; + } + + f << "\n" << _indent << " }"; + } +}; + +struct JnyBackend : public Backend { + JnyBackend() : Backend("jny", "generate design metadata") { } + void help() override { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" jny [options] [selection]\n"); + log("\n"); + log("Write JSON netlist metadata for the current design\n"); + log("\n"); + log(" -no-connections\n"); + log(" Don't include connection information in the netlist output.\n"); + log("\n"); + log(" -no-attributes\n"); + log(" Don't include attributed information in the netlist output.\n"); + log("\n"); + log(" -no-properties\n"); + log(" Don't include property information in the netlist output.\n"); + log("\n"); + log("The JSON schema for JNY output files is located in the \"jny.schema.json\" file\n"); + log("which is located at \"https://raw.githubusercontent.com/YosysHQ/yosys/master/misc/jny.schema.json\"\n"); + log("\n"); + } + + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { + + bool connections{true}; + bool attributes{true}; + bool properties{true}; + + size_t argidx{1}; + for (; argidx < args.size(); argidx++) { + if (args[argidx] == "-no-connections") { + connections = false; + continue; + } + + if (args[argidx] == "-no-attributes") { + attributes = false; + continue; + } + + if (args[argidx] == "-no-properties") { + properties = false; + continue; + } + + break; + } + + // Compose invocation line + std::ostringstream invk; + if (!args.empty()) { + std::copy(args.begin(), args.end(), + std::ostream_iterator(invk, " ") + ); + } + invk << filename; + + extra_args(f, filename, args, argidx); + + log_header(design, "Executing jny backend.\n"); + + JnyWriter jny_writer(*f, false, connections, attributes, properties); + jny_writer.write_metadata(design, 0, invk.str()); + } + +} JnyBackend; + + +struct JnyPass : public Pass { + JnyPass() : Pass("jny", "write design and metadata") { } + + void help() override { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" jny [options] [selection]\n"); + log("\n"); + log("Write JSON netlist metadata for the current design\n"); + log("\n"); + log(" -o \n"); + log(" write to the specified file.\n"); + log("\n"); + log(" -no-connections\n"); + log(" Don't include connection information in the netlist output.\n"); + log("\n"); + log(" -no-attributes\n"); + log(" Don't include attributed information in the netlist output.\n"); + log("\n"); + log(" -no-properties\n"); + log(" Don't include property information in the netlist output.\n"); + log("\n"); + log("See 'help write_jny' for a description of the JSON format used.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override { + std::string filename{}; + + bool connections{true}; + bool attributes{true}; + bool properties{true}; + + size_t argidx{1}; + for (; argidx < args.size(); argidx++) { + if (args[argidx] == "-o" && argidx+1 < args.size()) { + filename = args[++argidx]; + continue; + } + + if (args[argidx] == "-no-connections") { + connections = false; + continue; + } + + if (args[argidx] == "-no-attributes") { + attributes = false; + continue; + } + + if (args[argidx] == "-no-properties") { + properties = false; + continue; + } + + break; + } + + // Compose invocation line + std::ostringstream invk; + if (!args.empty()) { + std::copy(args.begin(), args.end(), + std::ostream_iterator(invk, " ") + ); + } + + extra_args(args, argidx, design); + + std::ostream *f; + std::stringstream buf; + bool empty = filename.empty(); + + if (!empty) { + rewrite_filename(filename); + std::ofstream *ff = new std::ofstream; + ff->open(filename.c_str(), std::ofstream::trunc); + if (ff->fail()) { + delete ff; + log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); + } + f = ff; + invk << filename; + } else { + f = &buf; + } + + + JnyWriter jny_writer(*f, false, connections, attributes, properties); + jny_writer.write_metadata(design, 0, invk.str()); + + if (!empty) { + delete f; + } else { + log("%s", buf.str().c_str()); + } + } + +} JnyPass; + +PRIVATE_NAMESPACE_END diff --git a/backends/json/json.cc b/backends/json/json.cc index eeadc1b8903..2f442c494f7 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -52,8 +52,23 @@ struct JsonWriter string newstr = "\""; for (char c : str) { if (c == '\\') + newstr += "\\\\"; + else if (c == '"') + newstr += "\\\""; + else if (c == '\b') + newstr += "\\b"; + else if (c == '\f') + newstr += "\\f"; + else if (c == '\n') + newstr += "\\n"; + else if (c == '\r') + newstr += "\\r"; + else if (c == '\t') + newstr += "\\t"; + else if (c < 0x20) + newstr += stringf("\\u%04X", c); + else newstr += c; - newstr += c; } return newstr + "\""; } @@ -135,6 +150,10 @@ struct JsonWriter // reserve 0 and 1 to avoid confusion with "0" and "1" sigidcounter = 2; + if (module->has_processes()) { + log_error("Module %s contains processes, which are not supported by JSON backend (run `proc` first).\n", log_id(module)); + } + f << stringf(" %s: {\n", get_name(module->name).c_str()); f << stringf(" \"attributes\": {"); @@ -173,6 +192,10 @@ struct JsonWriter for (auto c : module->cells()) { if (use_selection && !module->selected(c)) continue; + // Eventually we will want to emit $scopeinfo, but currently this + // will break JSON netlist consumers like nextpnr + if (c->type == ID($scopeinfo)) + continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(c->name).c_str()); f << stringf(" \"hide_name\": %s,\n", c->name[0] == '$' ? "1" : "0"); @@ -216,6 +239,27 @@ struct JsonWriter } f << stringf("\n },\n"); + if (!module->memories.empty()) { + f << stringf(" \"memories\": {"); + first = true; + for (auto &it : module->memories) { + if (use_selection && !module->selected(it.second)) + continue; + f << stringf("%s\n", first ? "" : ","); + f << stringf(" %s: {\n", get_name(it.second->name).c_str()); + f << stringf(" \"hide_name\": %s,\n", it.second->name[0] == '$' ? "1" : "0"); + f << stringf(" \"attributes\": {"); + write_parameters(it.second->attributes); + f << stringf("\n },\n"); + f << stringf(" \"width\": %d,\n", it.second->width); + f << stringf(" \"start_offset\": %d,\n", it.second->start_offset); + f << stringf(" \"size\": %d\n", it.second->size); + f << stringf(" }"); + first = false; + } + f << stringf("\n },\n"); + } + f << stringf(" \"netnames\": {"); first = true; for (auto w : module->wires()) { @@ -332,6 +376,10 @@ struct JsonBackend : public Backend { log(" : ,\n"); log(" ...\n"); log(" },\n"); + log(" \"memories\": {\n"); + log(" : ,\n"); + log(" ...\n"); + log(" },\n"); log(" \"netnames\": {\n"); log(" : ,\n"); log(" ...\n"); @@ -350,10 +398,11 @@ struct JsonBackend : public Backend { log(" \"bits\": \n"); log(" \"offset\": \n"); log(" \"upto\": <1 if the port bit indexing is MSB-first>\n"); + log(" \"signed\": <1 if the port is signed>\n"); log(" }\n"); log("\n"); - log("The \"offset\" and \"upto\" fields are skipped if their value would be 0."); - log("They don't affect connection semantics, and are only used to preserve original"); + log("The \"offset\" and \"upto\" fields are skipped if their value would be 0.\n"); + log("They don't affect connection semantics, and are only used to preserve original\n"); log("HDL bit indexing."); log("And is:\n"); log("\n"); @@ -379,6 +428,19 @@ struct JsonBackend : public Backend { log(" },\n"); log(" }\n"); log("\n"); + log("And is:\n"); + log("\n"); + log(" {\n"); + log(" \"hide_name\": <1 | 0>,\n"); + log(" \"attributes\": {\n"); + log(" : ,\n"); + log(" ...\n"); + log(" },\n"); + log(" \"width\": \n"); + log(" \"start_offset\": \n"); + log(" \"size\": \n"); + log(" }\n"); + log("\n"); log("And is:\n"); log("\n"); log(" {\n"); @@ -386,6 +448,7 @@ struct JsonBackend : public Backend { log(" \"bits\": \n"); log(" \"offset\": \n"); log(" \"upto\": <1 if the port bit indexing is MSB-first>\n"); + log(" \"signed\": <1 if the port is signed>\n"); log(" }\n"); log("\n"); log("The \"hide_name\" fields are set to 1 when the name of this cell or net is\n"); @@ -400,8 +463,8 @@ struct JsonBackend : public Backend { log("connected to a constant driver are denoted as string \"0\", \"1\", \"x\", or\n"); log("\"z\" instead of a number.\n"); log("\n"); - log("Bit vectors (including integers) are written as string holding the binary"); - log("representation of the value. Strings are written as strings, with an appended"); + log("Bit vectors (including integers) are written as string holding the binary\n"); + log("representation of the value. Strings are written as strings, with an appended\n"); log("blank in cases of strings of the form /[01xz]* */.\n"); log("\n"); log("For example the following Verilog code:\n"); @@ -607,8 +670,9 @@ struct JsonPass : public Pass { std::ostream *f; std::stringstream buf; + bool empty = filename.empty(); - if (!filename.empty()) { + if (!empty) { rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), std::ofstream::trunc); @@ -624,7 +688,7 @@ struct JsonPass : public Pass { JsonWriter json_writer(*f, true, aig_mode, compat_int_mode); json_writer.write_design(design); - if (!filename.empty()) { + if (!empty) { delete f; } else { log("%s", buf.str().c_str()); diff --git a/backends/protobuf/.gitignore b/backends/protobuf/.gitignore deleted file mode 100644 index 849b38d45aa..00000000000 --- a/backends/protobuf/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -yosys.pb.cc -yosys.pb.h diff --git a/backends/protobuf/Makefile.inc b/backends/protobuf/Makefile.inc deleted file mode 100644 index 834cad42cde..00000000000 --- a/backends/protobuf/Makefile.inc +++ /dev/null @@ -1,8 +0,0 @@ -ifeq ($(ENABLE_PROTOBUF),1) - -backends/protobuf/yosys.pb.cc backends/protobuf/yosys.pb.h: misc/yosys.proto - $(Q) cd misc && protoc --cpp_out "../backends/protobuf" yosys.proto - -OBJS += backends/protobuf/protobuf.o backends/protobuf/yosys.pb.o - -endif diff --git a/backends/protobuf/protobuf.cc b/backends/protobuf/protobuf.cc deleted file mode 100644 index f6623a38268..00000000000 --- a/backends/protobuf/protobuf.cc +++ /dev/null @@ -1,371 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Clifford Wolf - * Copyright (C) 2018 Serge Bazanski - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -#include - -#include "kernel/rtlil.h" -#include "kernel/register.h" -#include "kernel/sigtools.h" -#include "kernel/celltypes.h" -#include "kernel/cellaigs.h" -#include "kernel/log.h" -#include "yosys.pb.h" - -USING_YOSYS_NAMESPACE -PRIVATE_NAMESPACE_BEGIN - -struct ProtobufDesignSerializer -{ - bool aig_mode_; - bool use_selection_; - yosys::pb::Design *pb_; - - Design *design_; - Module *module_; - - SigMap sigmap_; - int sigidcounter_; - dict sigids_; - pool aig_models_; - - - ProtobufDesignSerializer(bool use_selection, bool aig_mode) : - aig_mode_(aig_mode), use_selection_(use_selection) { } - - string get_name(IdString name) - { - return RTLIL::unescape_id(name); - } - - - void serialize_parameters(google::protobuf::Map *out, - const dict ¶meters) - { - for (auto ¶m : parameters) { - std::string key = get_name(param.first); - - - yosys::pb::Parameter pb_param; - - if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) { - pb_param.set_str(param.second.decode_string()); - } else if (GetSize(param.second.bits) > 64) { - pb_param.set_str(param.second.as_string()); - } else { - pb_param.set_int_(param.second.as_int()); - } - - (*out)[key] = pb_param; - } - } - - void get_bits(yosys::pb::BitVector *out, SigSpec sig) - { - for (auto bit : sigmap_(sig)) { - auto sig = out->add_signal(); - - // Constant driver. - if (bit.wire == nullptr) { - if (bit == State::S0) sig->set_constant(sig->CONSTANT_DRIVER_LOW); - else if (bit == State::S1) sig->set_constant(sig->CONSTANT_DRIVER_HIGH); - else if (bit == State::Sz) sig->set_constant(sig->CONSTANT_DRIVER_Z); - else sig->set_constant(sig->CONSTANT_DRIVER_X); - continue; - } - - // Signal - give it a unique identifier. - if (sigids_.count(bit) == 0) { - sigids_[bit] = sigidcounter_++; - } - sig->set_id(sigids_[bit]); - } - } - - void serialize_module(yosys::pb::Module* out, Module *module) - { - module_ = module; - log_assert(module_->design == design_); - sigmap_.set(module_); - sigids_.clear(); - sigidcounter_ = 0; - - serialize_parameters(out->mutable_attribute(), module_->attributes); - - for (auto n : module_->ports) { - Wire *w = module->wire(n); - if (use_selection_ && !module_->selected(w)) - continue; - - yosys::pb::Module::Port pb_port; - pb_port.set_direction(w->port_input ? w->port_output ? - yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT : yosys::pb::DIRECTION_OUTPUT); - get_bits(pb_port.mutable_bits(), w); - (*out->mutable_port())[get_name(n)] = pb_port; - } - - for (auto c : module_->cells()) { - if (use_selection_ && !module_->selected(c)) - continue; - - yosys::pb::Module::Cell pb_cell; - pb_cell.set_hide_name(c->name[0] == '$'); - pb_cell.set_type(get_name(c->type)); - - if (aig_mode_) { - Aig aig(c); - if (aig.name.empty()) - continue; - pb_cell.set_model(aig.name); - aig_models_.insert(aig); - } - serialize_parameters(pb_cell.mutable_parameter(), c->parameters); - serialize_parameters(pb_cell.mutable_attribute(), c->attributes); - - if (c->known()) { - for (auto &conn : c->connections()) { - yosys::pb::Direction direction = yosys::pb::DIRECTION_OUTPUT; - if (c->input(conn.first)) - direction = c->output(conn.first) ? yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT; - (*pb_cell.mutable_port_direction())[get_name(conn.first)] = direction; - } - } - for (auto &conn : c->connections()) { - yosys::pb::BitVector vec; - get_bits(&vec, conn.second); - (*pb_cell.mutable_connection())[get_name(conn.first)] = vec; - } - - (*out->mutable_cell())[get_name(c->name)] = pb_cell; - } - - for (auto w : module_->wires()) { - if (use_selection_ && !module_->selected(w)) - continue; - - auto netname = out->add_netname(); - netname->set_hide_name(w->name[0] == '$'); - get_bits(netname->mutable_bits(), w); - serialize_parameters(netname->mutable_attributes(), w->attributes); - } - } - - - void serialize_models(google::protobuf::Map *models) - { - for (auto &aig : aig_models_) { - yosys::pb::Model pb_model; - for (auto &node : aig.nodes) { - auto pb_node = pb_model.add_node(); - if (node.portbit >= 0) { - if (node.inverter) { - pb_node->set_type(pb_node->TYPE_NPORT); - } else { - pb_node->set_type(pb_node->TYPE_PORT); - } - auto port = pb_node->mutable_port(); - port->set_portname(log_id(node.portname)); - port->set_bitindex(node.portbit); - } else if (node.left_parent < 0 && node.right_parent < 0) { - if (node.inverter) { - pb_node->set_type(pb_node->TYPE_TRUE); - } else { - pb_node->set_type(pb_node->TYPE_FALSE); - } - } else { - if (node.inverter) { - pb_node->set_type(pb_node->TYPE_NAND); - } else { - pb_node->set_type(pb_node->TYPE_AND); - } - auto gate = pb_node->mutable_gate(); - gate->set_left(node.left_parent); - gate->set_right(node.right_parent); - } - for (auto &op : node.outports) { - auto pb_op = pb_node->add_out_port(); - pb_op->set_name(log_id(op.first)); - pb_op->set_bit_index(op.second); - } - } - (*models)[aig.name] = pb_model; - } - } - - void serialize_design(yosys::pb::Design *pb, Design *design) - { - GOOGLE_PROTOBUF_VERIFY_VERSION; - pb_ = pb; - pb_->Clear(); - pb_->set_creator(yosys_version_str); - - design_ = design; - design_->sort(); - - auto modules = use_selection_ ? design_->selected_modules() : design_->modules(); - for (auto mod : modules) { - yosys::pb::Module pb_mod; - serialize_module(&pb_mod, mod); - (*pb->mutable_modules())[mod->name.str()] = pb_mod; - } - - serialize_models(pb_->mutable_models()); - } -}; - -struct ProtobufBackend : public Backend { - ProtobufBackend(): Backend("protobuf", "write design to a Protocol Buffer file") { } - void help() override - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" write_protobuf [options] [filename]\n"); - log("\n"); - log("Write a JSON netlist of the current design.\n"); - log("\n"); - log(" -aig\n"); - log(" include AIG models for the different gate types\n"); - log("\n"); - log(" -text\n"); - log(" output protobuf in Text/ASCII representation\n"); - log("\n"); - log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n"); - log("Yosys source code distribution.\n"); - log("\n"); - } - void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override - { - bool aig_mode = false; - bool text_mode = false; - - size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-aig") { - aig_mode = true; - continue; - } - if (args[argidx] == "-text") { - text_mode = true; - continue; - } - break; - } - extra_args(f, filename, args, argidx, !text_mode); - - log_header(design, "Executing Protobuf backend.\n"); - - yosys::pb::Design pb; - ProtobufDesignSerializer serializer(false, aig_mode); - serializer.serialize_design(&pb, design); - - if (text_mode) { - string out; - google::protobuf::TextFormat::PrintToString(pb, &out); - *f << out; - } else { - pb.SerializeToOstream(f); - } - } -} ProtobufBackend; - -struct ProtobufPass : public Pass { - ProtobufPass() : Pass("protobuf", "write design in Protobuf format") { } - void help() override - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" protobuf [options] [selection]\n"); - log("\n"); - log("Write a JSON netlist of all selected objects.\n"); - log("\n"); - log(" -o \n"); - log(" write to the specified file.\n"); - log("\n"); - log(" -aig\n"); - log(" include AIG models for the different gate types\n"); - log("\n"); - log(" -text\n"); - log(" output protobuf in Text/ASCII representation\n"); - log("\n"); - log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n"); - log("Yosys source code distribution.\n"); - log("\n"); - } - void execute(std::vector args, RTLIL::Design *design) override - { - std::string filename; - bool aig_mode = false; - bool text_mode = false; - - size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) - { - if (args[argidx] == "-o" && argidx+1 < args.size()) { - filename = args[++argidx]; - continue; - } - if (args[argidx] == "-aig") { - aig_mode = true; - continue; - } - if (args[argidx] == "-text") { - text_mode = true; - continue; - } - break; - } - extra_args(args, argidx, design); - - std::ostream *f; - std::stringstream buf; - - if (!filename.empty()) { - rewrite_filename(filename); - std::ofstream *ff = new std::ofstream; - ff->open(filename.c_str(), text_mode ? std::ofstream::trunc : (std::ofstream::trunc | std::ofstream::binary)); - if (ff->fail()) { - delete ff; - log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); - } - f = ff; - } else { - f = &buf; - } - - yosys::pb::Design pb; - ProtobufDesignSerializer serializer(true, aig_mode); - serializer.serialize_design(&pb, design); - - if (text_mode) { - string out; - google::protobuf::TextFormat::PrintToString(pb, &out); - *f << out; - } else { - pb.SerializeToOstream(f); - } - - if (!filename.empty()) { - delete f; - } else { - log("%s", buf.str().c_str()); - } - } -} ProtobufPass; - -PRIVATE_NAMESPACE_END; diff --git a/backends/rtlil/rtlil_backend.cc b/backends/rtlil/rtlil_backend.cc index 01b4bde5330..574eb3aaad1 100644 --- a/backends/rtlil/rtlil_backend.cc +++ b/backends/rtlil/rtlil_backend.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -51,15 +51,19 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi } } f << stringf("%d'", width); - for (int i = offset+width-1; i >= offset; i--) { - log_assert(i < (int)data.bits.size()); - switch (data.bits[i]) { - case State::S0: f << stringf("0"); break; - case State::S1: f << stringf("1"); break; - case RTLIL::Sx: f << stringf("x"); break; - case RTLIL::Sz: f << stringf("z"); break; - case RTLIL::Sa: f << stringf("-"); break; - case RTLIL::Sm: f << stringf("m"); break; + if (data.is_fully_undef_x_only()) { + f << "x"; + } else { + for (int i = offset+width-1; i >= offset; i--) { + log_assert(i < (int)data.bits.size()); + switch (data.bits[i]) { + case State::S0: f << stringf("0"); break; + case State::S1: f << stringf("1"); break; + case RTLIL::Sx: f << stringf("x"); break; + case RTLIL::Sz: f << stringf("z"); break; + case RTLIL::Sa: f << stringf("-"); break; + case RTLIL::Sm: f << stringf("m"); break; + } } } } else { @@ -71,7 +75,7 @@ void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi else if (str[i] == '\t') f << stringf("\\t"); else if (str[i] < 32) - f << stringf("\\%03o", str[i]); + f << stringf("\\%03o", (unsigned char)str[i]); else if (str[i] == '"') f << stringf("\\\""); else if (str[i] == '\\') @@ -242,11 +246,28 @@ void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT case RTLIL::STi: f << stringf("init\n"); break; } - for (auto it = sy->actions.begin(); it != sy->actions.end(); ++it) { + for (auto &it: sy->actions) { f << stringf("%s update ", indent.c_str()); - dump_sigspec(f, it->first); + dump_sigspec(f, it.first); f << stringf(" "); - dump_sigspec(f, it->second); + dump_sigspec(f, it.second); + f << stringf("\n"); + } + + for (auto &it: sy->mem_write_actions) { + for (auto it2 = it.attributes.begin(); it2 != it.attributes.end(); ++it2) { + f << stringf("%s attribute %s ", indent.c_str(), it2->first.c_str()); + dump_const(f, it2->second); + f << stringf("\n"); + } + f << stringf("%s memwr %s ", indent.c_str(), it.memid.c_str()); + dump_sigspec(f, it.address); + f << stringf(" "); + dump_sigspec(f, it.data); + f << stringf(" "); + dump_sigspec(f, it.enable); + f << stringf(" "); + dump_const(f, it.priority_mask); f << stringf("\n"); } } @@ -337,8 +358,8 @@ void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu bool first_conn_line = true; for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { - bool show_conn = !only_selected; - if (only_selected) { + bool show_conn = !only_selected || design->selected_whole_module(module->name); + if (!show_conn) { RTLIL::SigSpec sigs = it->first; sigs.append(it->second); for (auto &c : sigs.chunks()) { @@ -509,8 +530,9 @@ struct DumpPass : public Pass { std::ostream *f; std::stringstream buf; + bool empty = filename.empty(); - if (!filename.empty()) { + if (!empty) { rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc); @@ -525,7 +547,7 @@ struct DumpPass : public Pass { RTLIL_BACKEND::dump_design(*f, design, true, flag_m, flag_n); - if (!filename.empty()) { + if (!empty) { delete f; } else { log("%s", buf.str().c_str()); diff --git a/backends/rtlil/rtlil_backend.h b/backends/rtlil/rtlil_backend.h index 77eea353ca9..35829729c56 100644 --- a/backends/rtlil/rtlil_backend.h +++ b/backends/rtlil/rtlil_backend.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc index 3adeaa6c02b..e283dcf7c17 100644 --- a/backends/simplec/simplec.cc +++ b/backends/simplec/simplec.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/backends/simplec/test00.sh b/backends/simplec/test00.sh index ede75727378..9895a97e46b 100644 --- a/backends/simplec/test00.sh +++ b/backends/simplec/test00.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex ../../yosys -p 'synth -top test; write_simplec -verbose -i8 test00_uut.c' test00_uut.v clang -o test00_tb test00_tb.c diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc index fb01308bd8a..3afe990e769 100644 --- a/backends/smt2/Makefile.inc +++ b/backends/smt2/Makefile.inc @@ -7,6 +7,7 @@ ifneq ($(CONFIG),emcc) # MSYS targets support yosys-smtbmc, but require a launcher script ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64)) TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc.exe $(PROGRAM_PREFIX)yosys-smtbmc-script.py +TARGETS += $(PROGRAM_PREFIX)yosys-witness.exe $(PROGRAM_PREFIX)yosys-witness-script.py # Needed to find the Python interpreter for yosys-smtbmc scripts. # Override if necessary, it is only used for msys2 targets. PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3) @@ -15,18 +16,31 @@ $(PROGRAM_PREFIX)yosys-smtbmc-script.py: backends/smt2/smtbmc.py $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' \ -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ +$(PROGRAM_PREFIX)yosys-witness-script.py: backends/smt2/witness.py + $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' \ + -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ + $(PROGRAM_PREFIX)yosys-smtbmc.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-smtbmc-script.py $(P) $(CXX) -DGUI=0 -O -s -o $@ $< + +$(PROGRAM_PREFIX)yosys-witness.exe: misc/launcher.c $(PROGRAM_PREFIX)yosys-witness-script.py + $(P) $(CXX) -DGUI=0 -O -s -o $@ $< # Other targets else -TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc +TARGETS += $(PROGRAM_PREFIX)yosys-smtbmc $(PROGRAM_PREFIX)yosys-witness $(PROGRAM_PREFIX)yosys-smtbmc: backends/smt2/smtbmc.py $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' < $< > $@.new $(Q) chmod +x $@.new $(Q) mv $@.new $@ + +$(PROGRAM_PREFIX)yosys-witness: backends/smt2/witness.py + $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/$(PROGRAM_PREFIX)yosys/python3"]]|;' < $< > $@.new + $(Q) chmod +x $@.new + $(Q) mv $@.new $@ endif $(eval $(call add_share_file,share/python3,backends/smt2/smtio.py)) +$(eval $(call add_share_file,share/python3,backends/smt2/ywio.py)) endif endif diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index a185fdd747d..c702d5e7e54 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,6 +23,7 @@ #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" +#include "libs/json11/json11.hpp" #include USING_YOSYS_NAMESPACE @@ -53,6 +54,8 @@ struct Smt2Worker std::map bvsizes; dict ids; + bool is_smtlib2_module; + const char *get_id(IdString n) { if (ids.count(n) == 0) { @@ -112,9 +115,10 @@ struct Smt2Worker } Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, bool forallmode, - dict &mod_stbv_width, dict>> &mod_clk_cache) : - ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), wiresmode(wiresmode), - verbose(verbose), statebv(statebv), statedt(statedt), forallmode(forallmode), mod_stbv_width(mod_stbv_width) + dict &mod_stbv_width, dict>> &mod_clk_cache) + : ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), wiresmode(wiresmode), verbose(verbose), + statebv(statebv), statedt(statedt), forallmode(forallmode), mod_stbv_width(mod_stbv_width), + is_smtlib2_module(module->has_attribute(ID::smtlib2_module)) { pool noclock; @@ -124,6 +128,10 @@ struct Smt2Worker memories = Mem::get_all_memories(module); for (auto &mem : memories) { + if (is_smtlib2_module) + log_error("Memory %s.%s not allowed in module with smtlib2_module attribute", get_id(module), mem.memid.c_str()); + + mem.narrow(); mem_dict[mem.memid] = &mem; for (auto &port : mem.wr_ports) { @@ -182,7 +190,7 @@ struct Smt2Worker continue; // Handled above. - if (cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit))) { + if (cell->is_mem_cell()) { mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()]; continue; } @@ -232,6 +240,17 @@ struct Smt2Worker clock_negedge.erase(bit); } + for (auto wire : module->wires()) + { + auto gclk_attr = wire->attributes.find(ID::replaced_by_gclk); + if (gclk_attr != wire->attributes.end()) { + if (gclk_attr->second == State::S1) + clock_posedge.insert(sigmap(wire)); + else if (gclk_attr->second == State::S0) + clock_negedge.insert(sigmap(wire)); + } + } + for (auto wire : module->wires()) { if (!wire->port_input || GetSize(wire) != 1) @@ -439,11 +458,14 @@ struct Smt2Worker { RTLIL::SigSpec sig_a, sig_b; RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y)); - bool is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + bool is_signed = type == 'U' ? false : cell->getParam(ID::A_SIGNED).as_bool(); int width = GetSize(sig_y); - if (type == 's' || type == 'd' || type == 'b') { - width = max(width, GetSize(cell->getPort(ID::A))); + if (type == 's' || type == 'S' || type == 'd' || type == 'b') { + if (type == 'b') + width = GetSize(cell->getPort(ID::A)); + else + width = max(width, GetSize(cell->getPort(ID::A))); if (cell->hasPort(ID::B)) width = max(width, GetSize(cell->getPort(ID::B))); } @@ -455,7 +477,7 @@ struct Smt2Worker if (cell->hasPort(ID::B)) { sig_b = cell->getPort(ID::B); - sig_b.extend_u0(width, is_signed && !(type == 's')); + sig_b.extend_u0(width, (type == 'S') || (is_signed && !(type == 's'))); } std::string processed_expr; @@ -464,6 +486,7 @@ struct Smt2Worker if (ch == 'A') processed_expr += get_bv(sig_a); else if (ch == 'B') processed_expr += get_bv(sig_b); else if (ch == 'P') processed_expr += get_bv(cell->getPort(ID::B)); + else if (ch == 'S') processed_expr += get_bv(cell->getPort(ID::S)); else if (ch == 'L') processed_expr += is_signed ? "a" : "l"; else if (ch == 'U') processed_expr += is_signed ? "s" : "u"; else processed_expr += ch; @@ -540,6 +563,9 @@ struct Smt2Worker if (cell->type.in(ID($_FF_), ID($_DFF_P_), ID($_DFF_N_))) { registers.insert(cell); + SigBit q_bit = cell->getPort(ID::Q); + if (q_bit.is_wire()) + decls.push_back(witness_signal("reg", 1, 0, "", idcounter, q_bit.wire)); makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(cell->getPort(ID::Q))); register_bool(cell->getPort(ID::Q), idcounter++); recursive_cells.erase(cell); @@ -570,31 +596,48 @@ struct Smt2Worker if (cell->type.in(ID($ff), ID($dff))) { registers.insert(cell); + int smtoffset = 0; + for (auto chunk : cell->getPort(ID::Q).chunks()) { + if (chunk.is_wire()) + decls.push_back(witness_signal("reg", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset)); + smtoffset += chunk.width; + } makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(ID::Q)), log_signal(cell->getPort(ID::Q))); register_bv(cell->getPort(ID::Q), idcounter++); recursive_cells.erase(cell); return; } - if (cell->type.in(ID($anyconst), ID($anyseq), ID($allconst), ID($allseq))) + if (cell->type.in(ID($anyconst), ID($anyseq), ID($anyinit), ID($allconst), ID($allseq))) { + auto QY = cell->type == ID($anyinit) ? ID::Q : ID::Y; registers.insert(cell); string infostr = cell->attributes.count(ID::src) ? cell->attributes.at(ID::src).decode_string().c_str() : get_id(cell); if (cell->attributes.count(ID::reg)) infostr += " " + cell->attributes.at(ID::reg).decode_string(); - decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort(ID::Y)), infostr.c_str())); - if (cell->getPort(ID::Y).is_wire() && cell->getPort(ID::Y).as_wire()->get_bool_attribute(ID::maximize)){ + decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort(QY)), infostr.c_str())); + if (cell->getPort(QY).is_wire() && cell->getPort(QY).as_wire()->get_bool_attribute(ID::maximize)){ decls.push_back(stringf("; yosys-smt2-maximize %s#%d\n", get_id(module), idcounter)); - log("Wire %s is maximized\n", cell->getPort(ID::Y).as_wire()->name.str().c_str()); + log("Wire %s is maximized\n", cell->getPort(QY).as_wire()->name.str().c_str()); } - else if (cell->getPort(ID::Y).is_wire() && cell->getPort(ID::Y).as_wire()->get_bool_attribute(ID::minimize)){ + else if (cell->getPort(QY).is_wire() && cell->getPort(QY).as_wire()->get_bool_attribute(ID::minimize)){ decls.push_back(stringf("; yosys-smt2-minimize %s#%d\n", get_id(module), idcounter)); - log("Wire %s is minimized\n", cell->getPort(ID::Y).as_wire()->name.str().c_str()); + log("Wire %s is minimized\n", cell->getPort(QY).as_wire()->name.str().c_str()); + } + + bool init_only = cell->type.in(ID($anyconst), ID($anyinit), ID($allconst)); + bool clk2fflogic = cell->type == ID($anyinit) && cell->get_bool_attribute(ID(clk2fflogic)); + int smtoffset = 0; + for (auto chunk : cell->getPort(clk2fflogic ? ID::D : QY).chunks()) { + if (chunk.is_wire()) + decls.push_back(witness_signal(init_only ? "init" : "seq", chunk.width, chunk.offset, "", idcounter, chunk.wire, smtoffset)); + smtoffset += chunk.width; } - makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(ID::Y)), log_signal(cell->getPort(ID::Y))); + + makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort(QY)), log_signal(cell->getPort(QY))); if (cell->type == ID($anyseq)) ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter)); - register_bv(cell->getPort(ID::Y), idcounter++); + register_bv(cell->getPort(QY), idcounter++); recursive_cells.erase(cell); return; } @@ -604,6 +647,9 @@ struct Smt2Worker if (cell->type == ID($xor)) return export_bvop(cell, "(bvxor A B)"); if (cell->type == ID($xnor)) return export_bvop(cell, "(bvxnor A B)"); + if (cell->type == ID($bweqx)) return export_bvop(cell, "(bvxnor A B)", 'U'); + if (cell->type == ID($bwmux)) return export_bvop(cell, "(bvor (bvand A (bvnot S)) (bvand B S))", 'U'); + if (cell->type == ID($shl)) return export_bvop(cell, "(bvshl A B)", 's'); if (cell->type == ID($shr)) return export_bvop(cell, "(bvlshr A B)", 's'); if (cell->type == ID($sshl)) return export_bvop(cell, "(bvshl A B)", 's'); @@ -612,8 +658,8 @@ struct Smt2Worker if (cell->type.in(ID($shift), ID($shiftx))) { if (cell->getParam(ID::B_SIGNED).as_bool()) { return export_bvop(cell, stringf("(ite (bvsge P #b%0*d) " - "(bvlshr A B) (bvlshr A (bvneg B)))", - GetSize(cell->getPort(ID::B)), 0), 's'); + "(bvlshr A B) (bvshl A (bvneg B)))", + GetSize(cell->getPort(ID::B)), 0), 'S'); // type 'S' sign extends B } else { return export_bvop(cell, "(bvlshr A B)", 's'); } @@ -648,6 +694,27 @@ struct Smt2Worker return export_bvop(cell, "(bvurem A B)", 'd'); } } + // "div" = flooring division + if (cell->type == ID($divfloor)) { + if (cell->getParam(ID::A_SIGNED).as_bool()) { + // bvsdiv is truncating division, so we can't use it here. + int width = max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::B))); + width = max(width, GetSize(cell->getPort(ID::Y))); + auto expr = stringf("(let (" + "(a_neg (bvslt A #b%0*d)) " + "(b_neg (bvslt B #b%0*d))) " + "(let ((abs_a (ite a_neg (bvneg A) A)) " + "(abs_b (ite b_neg (bvneg B) B))) " + "(let ((u (bvudiv abs_a abs_b)) " + "(adj (ite (= #b%0*d (bvurem abs_a abs_b)) #b%0*d #b%0*d))) " + "(ite (= a_neg b_neg) u " + "(bvneg (bvadd u adj))))))", + width, 0, width, 0, width, 0, width, 0, width, 1); + return export_bvop(cell, expr, 'd'); + } else { + return export_bvop(cell, "(bvudiv A B)", 'd'); + } + } if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool)) && 2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) { @@ -694,7 +761,7 @@ struct Smt2Worker // FIXME: $slice $concat } - if (memmode && cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit))) + if (memmode && cell->is_mem_cell()) { Mem *mem = mem_cells[cell]; @@ -706,7 +773,7 @@ struct Smt2Worker int arrayid = idcounter++; memarrays[mem] = arrayid; - int abits = ceil_log2(mem->size); + int abits = max(1, ceil_log2(mem->size)); bool has_sync_wr = false; bool has_async_wr = false; @@ -720,6 +787,7 @@ struct Smt2Worker log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module)); decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(mem->memid), abits, mem->width, GetSize(mem->rd_ports), GetSize(mem->wr_ports), has_async_wr ? "async" : "sync")); + decls.push_back(witness_memory(get_id(mem->memid), cell, mem)); string memstate; if (has_async_wr) { @@ -812,6 +880,7 @@ struct Smt2Worker if (m != nullptr) { decls.push_back(stringf("; yosys-smt2-cell %s %s\n", get_id(cell->type), get_id(cell->name))); + decls.push_back(witness_cell(get_id(cell->name), cell)); string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name)); for (auto &conn : cell->connections()) @@ -855,28 +924,74 @@ struct Smt2Worker return; } + if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { + log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smt2`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smt2`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_smt2`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } log_error("Unsupported cell type %s for cell %s.%s.\n", log_id(cell->type), log_id(module), log_id(cell)); } + void verify_smtlib2_module() + { + if (!module->get_blackbox_attribute()) + log_error("Module %s with smtlib2_module attribute must also have blackbox attribute.\n", log_id(module)); + if (module->cells().size() > 0) + log_error("Module %s with smtlib2_module attribute must not have any cells inside it.\n", log_id(module)); + for (auto wire : module->wires()) + if (!wire->port_id) + log_error("Wire %s.%s must be input or output since module has smtlib2_module attribute.\n", log_id(module), + log_id(wire)); + } + void run() { if (verbose) log("=> export logic driving outputs\n"); + if (is_smtlib2_module) + verify_smtlib2_module(); + pool reg_bits; for (auto cell : module->cells()) - if (cell->type.in(ID($ff), ID($dff), ID($_FF_), ID($_DFF_P_), ID($_DFF_N_))) { + if (cell->type.in(ID($ff), ID($dff), ID($_FF_), ID($_DFF_P_), ID($_DFF_N_), ID($anyinit))) { // not using sigmap -- we want the net directly at the dff output for (auto bit : cell->getPort(ID::Q)) reg_bits.insert(bit); } + std::string smtlib2_inputs; + std::vector smtlib2_decls; + if (is_smtlib2_module) { + for (auto wire : module->wires()) { + if (!wire->port_input) + continue; + smtlib2_inputs += stringf("(|%s| (|%s_n %s| state))\n", get_id(wire), get_id(module), get_id(wire)); + } + } + for (auto wire : module->wires()) { bool is_register = false; - for (auto bit : SigSpec(wire)) + bool contains_clock = false; + for (auto bit : SigSpec(wire)) { if (reg_bits.count(bit)) is_register = true; - if (wire->port_id || is_register || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) { + auto sig_bit = sigmap(bit); + if (clock_posedge.count(sig_bit) || clock_negedge.count(sig_bit)) + contains_clock = true; + } + bool is_smtlib2_comb_expr = wire->has_attribute(ID::smtlib2_comb_expr); + if (is_smtlib2_comb_expr && !is_smtlib2_module) + log_error("smtlib2_comb_expr is only valid in a module with the smtlib2_module attribute: wire %s.%s", log_id(module), + log_id(wire)); + if (wire->port_id || is_register || contains_clock || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) { RTLIL::SigSpec sig = sigmap(wire); std::vector comments; if (wire->port_input) @@ -887,14 +1002,38 @@ struct Smt2Worker comments.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width)); if (wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) comments.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width)); - if (GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig))) + if (contains_clock && GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig))) comments.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire), clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : "")); + if (wire->port_input && contains_clock) { + for (int i = 0; i < GetSize(sig); i++) { + bool is_posedge = clock_posedge.count(sig[i]); + bool is_negedge = clock_negedge.count(sig[i]); + if (is_posedge != is_negedge) + comments.push_back(witness_signal( + is_posedge ? "posedge" : "negedge", 1, i, get_id(wire), -1, wire)); + } + } + if (wire->port_input) + comments.push_back(witness_signal("input", wire->width, 0, get_id(wire), -1, wire)); + std::string smtlib2_comb_expr; + if (is_smtlib2_comb_expr) { + smtlib2_comb_expr = + "(let (\n" + smtlib2_inputs + ")\n" + wire->get_string_attribute(ID::smtlib2_comb_expr) + "\n)"; + if (wire->port_input || !wire->port_output) + log_error("smtlib2_comb_expr is only valid on output: wire %s.%s", log_id(module), log_id(wire)); + if (!bvmode && GetSize(sig) > 1) + log_error("smtlib2_comb_expr is unsupported on multi-bit wires when -nobv is specified: wire %s.%s", + log_id(module), log_id(wire)); + + comments.push_back(witness_signal("blackbox", wire->width, 0, get_id(wire), -1, wire)); + } + auto &out_decls = is_smtlib2_comb_expr ? smtlib2_decls : decls; if (bvmode && GetSize(sig) > 1) { - std::string sig_bv = get_bv(sig); + std::string sig_bv = is_smtlib2_comb_expr ? smtlib2_comb_expr : get_bv(sig); if (!comments.empty()) - decls.insert(decls.end(), comments.begin(), comments.end()); - decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n", + out_decls.insert(out_decls.end(), comments.begin(), comments.end()); + out_decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n", get_id(module), get_id(wire), get_id(module), GetSize(sig), sig_bv.c_str())); if (wire->port_input) ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))", @@ -902,19 +1041,19 @@ struct Smt2Worker } else { std::vector sig_bool; for (int i = 0; i < GetSize(sig); i++) { - sig_bool.push_back(get_bool(sig[i])); + sig_bool.push_back(is_smtlib2_comb_expr ? smtlib2_comb_expr : get_bool(sig[i])); } if (!comments.empty()) - decls.insert(decls.end(), comments.begin(), comments.end()); + out_decls.insert(out_decls.end(), comments.begin(), comments.end()); for (int i = 0; i < GetSize(sig); i++) { if (GetSize(sig) > 1) { - decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n", + out_decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(wire), i, get_id(module), sig_bool[i].c_str())); if (wire->port_input) ex_input_eq.push_back(stringf(" (= (|%s_n %s %d| state) (|%s_n %s %d| other_state))", get_id(module), get_id(wire), i, get_id(module), get_id(wire), i)); } else { - decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n", + out_decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(wire), get_id(module), sig_bool[i].c_str())); if (wire->port_input) ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))", @@ -925,11 +1064,17 @@ struct Smt2Worker } } + decls.insert(decls.end(), smtlib2_decls.begin(), smtlib2_decls.end()); + if (verbose) log("=> export logic associated with the initial state\n"); vector init_list; for (auto wire : module->wires()) if (wire->attributes.count(ID::init)) { + if (is_smtlib2_module) + log_error("init attribute not allowed on wires in module with smtlib2_module attribute: wire %s.%s", + log_id(module), log_id(wire)); + RTLIL::SigSpec sig = sigmap(wire); Const val = wire->attributes.at(ID::init); val.bits.resize(GetSize(sig), State::Sx); @@ -972,8 +1117,21 @@ struct Smt2Worker string name_a = get_bool(cell->getPort(ID::A)); string name_en = get_bool(cell->getPort(ID::EN)); - string infostr = (cell->name[0] == '$' && cell->attributes.count(ID::src)) ? cell->attributes.at(ID::src).decode_string() : get_id(cell); - decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, infostr.c_str())); + bool private_name = cell->name[0] == '$'; + + if (!private_name && cell->has_attribute(ID::hdlname)) { + for (auto const &part : cell->get_hdlname_attribute()) { + if (part == "_witness_") { + private_name = true; + break; + } + } + } + + if (private_name && cell->attributes.count(ID::src)) + decls.push_back(stringf("; yosys-smt2-%s %d %s %s\n", cell->type.c_str() + 1, id, get_id(cell), cell->attributes.at(ID::src).decode_string().c_str())); + else + decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, get_id(cell))); if (cell->type == ID($cover)) decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n", @@ -1048,7 +1206,7 @@ struct Smt2Worker ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort(ID::Q)).c_str(), get_bool(cell->getPort(ID::Q), "other_state").c_str())); } - if (cell->type.in(ID($ff), ID($dff))) + if (cell->type.in(ID($ff), ID($dff), ID($anyinit))) { std::string expr_d = get_bv(cell->getPort(ID::D)); std::string expr_q = get_bv(cell->getPort(ID::Q), "next_state"); @@ -1073,7 +1231,7 @@ struct Smt2Worker { int arrayid = memarrays.at(mem); - int abits = ceil_log2(mem->size);; + int abits = max(1, ceil_log2(mem->size)); bool has_sync_wr = false; bool has_async_wr = false; @@ -1170,10 +1328,12 @@ struct Smt2Worker data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))", data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str()); + string empty_mask(mem->width, '0'); + decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) " - "(store (|%s#%d#%d| state) %s %s)) ; %s\n", + "(ite (= %s #b%s) (|%s#%d#%d| state) (store (|%s#%d#%d| state) %s %s))) ; %s\n", get_id(module), arrayid, i+1, get_id(module), abits, mem->width, - get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(mem->memid))); + mask.c_str(), empty_mask.c_str(), get_id(module), arrayid, i, get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(mem->memid))); } } @@ -1345,6 +1505,91 @@ struct Smt2Worker f << "true)"; f << stringf(" ; end of module %s\n", get_id(module)); } + + template static std::vector witness_path(T *obj) { + std::vector path; + if (obj->name.isPublic()) { + auto hdlname = obj->get_string_attribute(ID::hdlname); + for (auto token : split_tokens(hdlname)) + path.push_back("\\" + token); + } + if (path.empty()) + path.push_back(obj->name.str()); + return path; + } + + std::string witness_signal(const char *type, int width, int offset, const std::string &smtname, int smtid, RTLIL::Wire *wire, int smtoffset = 0) + { + std::vector hiername; + const char *wire_name = wire->name.c_str(); + if (wire_name[0] == '\\') { + auto hdlname = wire->get_string_attribute(ID::hdlname); + for (auto token : split_tokens(hdlname)) + hiername.push_back("\\" + token); + } + if (hiername.empty()) + hiername.push_back(wire->name.str()); + + std::string line = "; yosys-smt2-witness "; + (json11::Json { json11::Json::object { + { "type", type }, + { "offset", offset }, + { "width", width }, + { "smtname", smtname.empty() ? json11::Json(smtid) : json11::Json(smtname) }, + { "smtoffset", smtoffset }, + { "path", witness_path(wire) }, + }}).dump(line); + line += "\n"; + return line; + } + + std::string witness_cell(const char *smtname, RTLIL::Cell *cell) + { + std::string line = "; yosys-smt2-witness "; + (json11::Json {json11::Json::object { + { "type", "cell" }, + { "smtname", smtname }, + { "path", witness_path(cell) }, + }}).dump(line); + line += "\n"; + return line; + } + + std::string witness_memory(const char *smtname, RTLIL::Cell *cell, Mem *mem) + { + json11::Json::array uninitialized; + auto init_data = mem->get_init_data(); + + int cursor = 0; + + while (cursor < init_data.size()) { + while (cursor < init_data.size() && init_data[cursor] != State::Sx) + cursor++; + int offset = cursor; + while (cursor < init_data.size() && init_data[cursor] == State::Sx) + cursor++; + int width = cursor - offset; + if (width) + uninitialized.push_back(json11::Json::object { + {"width", width}, + {"offset", offset}, + }); + } + + std::string line = "; yosys-smt2-witness "; + (json11::Json { json11::Json::object { + { "type", "mem" }, + { "width", mem->width }, + { "size", mem->size }, + { "rom", mem->wr_ports.empty() }, + { "statebv", statebv }, + { "smtname", smtname }, + { "uninitialized", uninitialized }, + { "path", witness_path(cell) }, + }}).dump(line); + line += "\n"; + return line; + } }; struct Smt2Backend : public Backend { @@ -1518,6 +1763,11 @@ struct Smt2Backend : public Backend { log_header(design, "Executing SMT2 backend.\n"); + log_push(); + Pass::call(design, "bmuxmap"); + Pass::call(design, "demuxmap"); + log_pop(); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -1644,7 +1894,10 @@ struct Smt2Backend : public Backend { for (auto module : sorted_modules) { - if (module->get_blackbox_attribute() || module->has_processes_warn()) + if (module->get_blackbox_attribute() && !module->has_attribute(ID::smtlib2_module)) + continue; + + if (module->has_processes_warn()) continue; log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module)); diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index da5a7f57ed8..e6b4088dbd7 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -2,7 +2,7 @@ # # yosys -- Yosys Open SYnthesis Suite # -# Copyright (C) 2012 Clifford Wolf +# Copyright (C) 2012 Claire Xenia Wolf # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -17,9 +17,10 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -import os, sys, getopt, re +import os, sys, getopt, re, bisect, json ##yosys-sys-path## from smtio import SmtIo, SmtOpts, MkVcd +from ywio import ReadWitness, WriteWitness, WitnessValues from collections import defaultdict got_topt = False @@ -28,6 +29,8 @@ num_steps = 20 append_steps = 0 vcdfile = None +inywfile = None +outywfile = None cexfile = None aimfile = None aiwfile = None @@ -50,12 +53,21 @@ smtctop = None noinit = False binarymode = False +keep_going = False +check_witness = False +detect_loops = False +incremental = None +track_assumes = False +minimize_assumes = False so = SmtOpts() -def usage(): +def help(): print(os.path.basename(sys.argv[0]) + """ [options] + -h, --help + show this message + -t -t : -t :: @@ -93,6 +105,9 @@ def usage(): the AIGER witness file does not include the status and properties lines. + --yw + read a Yosys witness. + --btorwit read a BTOR witness. @@ -120,6 +135,9 @@ def usage(): (hint: use 'write_smt2 -wires' for maximum coverage of signals in generated VCD file) + --dump-yw + write trace as a Yosys witness trace + --dump-vlogtb write trace as Verilog test bench @@ -143,7 +161,7 @@ def usage(): --dump-all when using -g or -i, create a dump file for each - step. The character '%' is replaces in all dump + step. The character '%' is replaced in all dump filenames with the step number. --append @@ -153,20 +171,56 @@ def usage(): --binary dump anyconst values as raw bit strings + + --keep-going + continue BMC after the first failed assertion and report + further failed assertions. To output multiple traces + covering all found failed assertions, the character '%' is + replaced in all dump filenames with an increasing number. + In cover mode, don't stop when a cover trace contains a failed + assertion. + + --check-witness + check that the used witness file contains sufficient + constraints to force an assertion failure. + + --detect-loops + check if states are unique in temporal induction counter examples + (this feature is experimental and incomplete) + + --incremental + run in incremental mode (experimental) + + --track-assumes + track individual assumptions and report a subset of used + assumptions that are sufficient for the reported outcome. This + can be used to debug PREUNSAT failures as well as to find a + smaller set of sufficient assumptions. + + --minimize-assumes + when using --track-assumes, solve for a minimal set of sufficient assumptions. + """ + so.helpmsg()) + +def usage(): + help() sys.exit(1) try: - opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts + - ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat", - "dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=", - "smtc-init", "smtc-top=", "noinit", "binary"]) + opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:higcm:", so.longopts + + ["help", "final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "yw=", "btorwit=", "presat", + "dump-vcd=", "dump-yw=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=", + "smtc-init", "smtc-top=", "noinit", "binary", "keep-going", "check-witness", "detect-loops", "incremental", + "track-assumes", "minimize-assumes"]) except: usage() for o, a in opts: - if o == "-t": + if o in ("-h", "--help"): + help() + sys.exit(0) + elif o == "-t": got_topt = True a = a.split(":") if len(a) == 1: @@ -196,10 +250,14 @@ def usage(): aiwfile = a + ".aiw" elif o == "--aig-noheader": aigheader = False + elif o == "--yw": + inywfile = a elif o == "--btorwit": btorwitfile = a elif o == "--dump-vcd": vcdfile = a + elif o == "--dump-yw": + outywfile = a elif o == "--dump-vlogtb": vlogtbfile = a elif o == "--vlogtb-top": @@ -234,6 +292,19 @@ def usage(): topmod = a elif o == "--binary": binarymode = True + elif o == "--keep-going": + keep_going = True + elif o == "--check-witness": + check_witness = True + elif o == "--detect-loops": + detect_loops = True + elif o == "--incremental": + from smtbmc_incremental import Incremental + incremental = Incremental() + elif o == "--track-assumes": + track_assumes = True + elif o == "--minimize-assumes": + minimize_assumes = True elif so.handle(o, a): pass else: @@ -242,7 +313,7 @@ def usage(): if len(args) != 1: usage() -if sum([tempind, gentrace, covermode]) > 1: +if sum([tempind, gentrace, covermode, incremental is not None]) > 1: usage() constr_final_start = None @@ -341,13 +412,13 @@ def usage(): assert False -def get_constr_expr(db, state, final=False, getvalues=False): +def get_constr_expr(db, state, final=False, getvalues=False, individual=False): if final: if ("final-%d" % state) not in db: - return ([], [], []) if getvalues else "true" + return ([], [], []) if getvalues or individual else "true" else: if state not in db: - return ([], [], []) if getvalues else "true" + return ([], [], []) if getvalues or individual else "true" netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*|\S*)\](?=[ )]|$)') @@ -368,15 +439,18 @@ def replace_netref(match): expr_list = list() for loc, expr in db[("final-%d" % state) if final else state]: actual_expr = netref_regex.sub(replace_netref, expr) - if getvalues: + if getvalues or individual: expr_list.append((loc, expr, actual_expr)) else: expr_list.append(actual_expr) - if getvalues: - loc_list, expr_list, acual_expr_list = zip(*expr_list) - value_list = smt.get_list(acual_expr_list) - return loc_list, expr_list, value_list + if getvalues or individual: + loc_list, expr_list, actual_expr_list = zip(*expr_list) + if individual: + return loc_list, expr_list, actual_expr_list + else: + value_list = smt.get_list(actual_expr_list) + return loc_list, expr_list, value_list if len(expr_list) == 0: return "true" @@ -389,12 +463,17 @@ def replace_netref(match): smt = SmtIo(opts=so) +if track_assumes: + smt.smt2_options[':produce-unsat-assumptions'] = 'true' + if noinfo and vcdfile is None and vlogtbfile is None and outconstr is None: smt.produce_models = False def print_msg(msg): - print("%s %s" % (smt.timestamp(), msg)) - sys.stdout.flush() + if incremental: + incremental.print_msg(msg) + else: + print("%s %s" % (smt.timestamp(), msg), flush=True) print_msg("Solver: %s" % (so.solver)) @@ -413,7 +492,6 @@ def print_msg(msg): if cexfile is not None: if not got_topt: - assume_skipped = 0 skip_steps = 0 num_steps = 0 @@ -449,7 +527,8 @@ def print_msg(msg): constr_assumes[step].append((cexfile, smtexpr)) if not got_topt: - skip_steps = max(skip_steps, step) + if not check_witness: + skip_steps = max(skip_steps, step) num_steps = max(num_steps, step+1) if aimfile is not None: @@ -458,7 +537,6 @@ def print_msg(msg): latch_map = dict() if not got_topt: - assume_skipped = 0 skip_steps = 0 num_steps = 0 @@ -492,7 +570,7 @@ def print_msg(msg): got_state = True for entry in f.read().splitlines(): - if len(entry) == 0 or entry[0] in "bcjfu.": + if len(entry) == 0 or entry[0] in "bcjfu.#": continue if not got_state: @@ -582,10 +660,137 @@ def print_msg(msg): constr_assumes[step].append((cexfile, smtexpr)) if not got_topt: - skip_steps = max(skip_steps, step) - num_steps = max(num_steps, step+1) + if not check_witness: + skip_steps = max(skip_steps, step) + # some solvers optimize the properties so that they fail one cycle early, + # thus we check the properties in the cycle the aiger witness ends, and + # if that doesn't work, we check the cycle after that as well. + num_steps = max(num_steps, step+2) step += 1 +ywfile_hierwitness_cache = None + +def ywfile_constraints(inywfile, constr_assumes, map_steps=None, skip_x=False): + global ywfile_hierwitness_cache + if map_steps is None: + map_steps = {} + + with open(inywfile, "r") as f: + inyw = ReadWitness(f) + + if ywfile_hierwitness_cache is None: + ywfile_hierwitness_cache = smt.hierwitness(topmod, allregs=True, blackbox=True) + + inits, seqs, clocks, mems = ywfile_hierwitness_cache + + smt_wires = defaultdict(list) + smt_mems = defaultdict(list) + + for wire in inits + seqs: + smt_wires[wire["path"]].append(wire) + + for mem in mems: + smt_mems[mem["path"]].append(mem) + + addr_re = re.compile(r'\\\[[0-9]+\]$') + bits_re = re.compile(r'[01?]*$') + + max_t = -1 + + for t, step in inyw.steps(): + present_signals, missing = step.present_signals(inyw.sigmap) + for sig in present_signals: + bits = step[sig] + if skip_x: + bits = bits.replace('x', '?') + if not bits_re.match(bits): + raise ValueError("unsupported bit value in Yosys witness file") + + sig_end = sig.offset + len(bits) + if sig.path in smt_wires: + for wire in smt_wires[sig.path]: + width, offset = wire["width"], wire["offset"] + + smt_bool = smt.net_width(topmod, wire["smtpath"]) == 1 + + offset = max(offset, 0) + + end = width + offset + common_offset = max(sig.offset, offset) + common_end = min(sig_end, end) + if common_end <= common_offset: + continue + + smt_expr = smt.witness_net_expr(topmod, f"s{map_steps.get(t, t)}", wire) + + if not smt_bool: + slice_high = common_end - offset - 1 + slice_low = common_offset - offset + smt_expr = "((_ extract %d %d) %s)" % (slice_high, slice_low, smt_expr) + + bit_slice = bits[len(bits) - (common_end - sig.offset):len(bits) - (common_offset - sig.offset)] + + if bit_slice.count("?") == len(bit_slice): + continue + + if smt_bool: + assert width == 1 + smt_constr = "(= %s %s)" % (smt_expr, "true" if bit_slice == "1" else "false") + else: + if "?" in bit_slice: + mask = bit_slice.replace("0", "1").replace("?", "0") + bit_slice = bit_slice.replace("?", "0") + smt_expr = "(bvand %s #b%s)" % (smt_expr, mask) + + smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice) + + constr_assumes[t].append((inywfile, smt_constr)) + + if sig.memory_path: + if sig.memory_path in smt_mems: + for mem in smt_mems[sig.memory_path]: + width, size, bv = mem["width"], mem["size"], mem["statebv"] + + smt_expr = smt.net_expr(topmod, f"s{map_steps.get(t, t)}", mem["smtpath"]) + + if bv: + word_low = sig.memory_addr * width + word_high = word_low + width - 1 + smt_expr = "((_ extract %d %d) %s)" % (word_high, word_low, smt_expr) + else: + addr_width = (size - 1).bit_length() + addr_bits = f"{sig.memory_addr:0{addr_width}b}" + smt_expr = "(select %s #b%s )" % (smt_expr, addr_bits) + + if len(bits) < width: + slice_high = sig.offset + len(bits) - 1 + smt_expr = "((_ extract %d %d) %s)" % (slice_high, sig.offset, smt_expr) + + bit_slice = bits + + if "?" in bit_slice: + mask = bit_slice.replace("0", "1").replace("?", "0") + bit_slice = bit_slice.replace("?", "0") + smt_expr = "(bvand %s #b%s)" % (smt_expr, mask) + + smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice) + constr_assumes[t].append((inywfile, smt_constr)) + max_t = t + + return max_t + +if inywfile is not None: + if not got_topt: + skip_steps = 0 + num_steps = 0 + + max_t = ywfile_constraints(inywfile, constr_assumes) + + if not got_topt: + if not check_witness: + skip_steps = max(skip_steps, max_t) + num_steps = max(num_steps, max_t+1) + if btorwitfile is not None: with open(btorwitfile, "r") as f: step = None @@ -683,7 +888,116 @@ def print_msg(msg): skip_steps = step num_steps = step+1 -def write_vcd_trace(steps_start, steps_stop, index): +def collect_mem_trace_data(steps, vcd=None): + mem_trace_data = dict() + + for mempath in sorted(smt.hiermems(topmod)): + abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath) + + expr_id = list() + expr_list = list() + for seq, i in enumerate(steps): + for j in range(rports): + expr_id.append(('R', seq, j, 'A')) + expr_id.append(('R', seq, j, 'D')) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j)) + for j in range(wports): + expr_id.append(('W', seq, j, 'A')) + expr_id.append(('W', seq, j, 'D')) + expr_id.append(('W', seq, j, 'M')) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j)) + + rdata = list() + wdata = list() + addrs = set() + + for eid, edat in zip(expr_id, smt.get_list(expr_list)): + t, i, j, f = eid + + if t == 'R': + c = rdata + elif t == 'W': + c = wdata + else: + assert False + + while len(c) <= i: + c.append(list()) + c = c[i] + + while len(c) <= j: + c.append(dict()) + c = c[j] + + c[f] = smt.bv2bin(edat) + + if f == 'A': + addrs.add(c[f]) + + for addr in addrs: + tdata = list() + data = ["x"] * width + gotread = False + + if len(wdata) == 0 and len(rdata) != 0: + wdata = [[]] * len(rdata) + + assert len(rdata) == len(wdata) + + for i in range(len(wdata)): + if not gotread: + for j_data in rdata[i]: + if j_data["A"] == addr: + data = list(j_data["D"]) + gotread = True + break + + if gotread: + buf = data[:] + for ii in reversed(range(len(tdata))): + for k in range(width): + if tdata[ii][k] == "x": + tdata[ii][k] = buf[k] + else: + buf[k] = tdata[ii][k] + + if not asyncwr: + tdata.append(data[:]) + + for j_data in wdata[i]: + if j_data["A"] != addr: + continue + + D = j_data["D"] + M = j_data["M"] + + for k in range(width): + if M[k] == "1": + data[k] = D[k] + + if asyncwr: + tdata.append(data[:]) + + assert len(tdata) == len(rdata) + + int_addr = int(addr, 2) + + netpath = mempath[:] + if vcd: + netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int_addr) + vcd.add_net([topmod] + netpath, width) + + for seq, i in enumerate(steps): + if i not in mem_trace_data: + mem_trace_data[i] = list() + mem_trace_data[i].append((netpath, int_addr, "".join(tdata[seq]))) + + return mem_trace_data + +def write_vcd_trace(steps, index, seq_time=False): filename = vcdfile.replace("%", index) print_msg("Writing trace to VCD file: %s" % (filename)) @@ -704,118 +1018,49 @@ def write_vcd_trace(steps_start, steps_stop, index): vcd.add_clock([topmod] + netpath, edge) path_list.append(netpath) - mem_trace_data = dict() - for mempath in sorted(smt.hiermems(topmod)): - abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath) - - expr_id = list() - expr_list = list() - for i in range(steps_start, steps_stop): - for j in range(rports): - expr_id.append(('R', i-steps_start, j, 'A')) - expr_id.append(('R', i-steps_start, j, 'D')) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j)) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j)) - for j in range(wports): - expr_id.append(('W', i-steps_start, j, 'A')) - expr_id.append(('W', i-steps_start, j, 'D')) - expr_id.append(('W', i-steps_start, j, 'M')) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j)) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j)) - expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j)) - - rdata = list() - wdata = list() - addrs = set() - - for eid, edat in zip(expr_id, smt.get_list(expr_list)): - t, i, j, f = eid - - if t == 'R': - c = rdata - elif t == 'W': - c = wdata - else: - assert False - - while len(c) <= i: - c.append(list()) - c = c[i] - - while len(c) <= j: - c.append(dict()) - c = c[j] - - c[f] = smt.bv2bin(edat) - - if f == 'A': - addrs.add(c[f]) + mem_trace_data = collect_mem_trace_data(steps, vcd) - for addr in addrs: - tdata = list() - data = ["x"] * width - gotread = False - - if len(wdata) == 0 and len(rdata) != 0: - wdata = [[]] * len(rdata) - - assert len(rdata) == len(wdata) - - for i in range(len(wdata)): - if not gotread: - for j_data in rdata[i]: - if j_data["A"] == addr: - data = list(j_data["D"]) - gotread = True - break - - if gotread: - buf = data[:] - for i in reversed(range(len(tdata))): - for k in range(width): - if tdata[i][k] == "x": - tdata[i][k] = buf[k] - else: - buf[k] = tdata[i][k] - - if not asyncwr: - tdata.append(data[:]) - - for j_data in wdata[i]: - if j_data["A"] != addr: - continue + for seq, i in enumerate(steps): + vcd.set_time(seq if seq_time else i) + value_list = smt.get_net_bin_list(topmod, path_list, "s%d" % i) + for path, value in zip(path_list, value_list): + vcd.set_net([topmod] + path, value) + if i in mem_trace_data: + for path, addr, value in mem_trace_data[i]: + vcd.set_net([topmod] + path, value) - D = j_data["D"] - M = j_data["M"] + if seq_time: + end_time = len(steps) + elif steps: + end_time = steps[-1] + 1 + else: + end_time = 0 - for k in range(width): - if M[k] == "1": - data[k] = D[k] + vcd.set_time(end_time) - if asyncwr: - tdata.append(data[:]) +def detect_state_loop(steps_start, steps_stop): + print_msg(f"Checking for loops in found induction counter example") + print_msg(f"This feature is experimental and incomplete") - assert len(tdata) == len(rdata) + path_list = sorted(smt.hiernets(topmod, regs_only=True)) - netpath = mempath[:] - netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int(addr, 2)) - vcd.add_net([topmod] + netpath, width) + mem_trace_data = collect_mem_trace_data(steps_start, steps_stop) - for i in range(steps_start, steps_stop): - if i not in mem_trace_data: - mem_trace_data[i] = list() - mem_trace_data[i].append((netpath, "".join(tdata[i-steps_start]))) + # Map state to index of step when it occurred + states = dict() - for i in range(steps_start, steps_stop): - vcd.set_time(i) - value_list = smt.get_net_bin_list(topmod, path_list, "s%d" % i) - for path, value in zip(path_list, value_list): - vcd.set_net([topmod] + path, value) - if i in mem_trace_data: - for path, value in mem_trace_data[i]: - vcd.set_net([topmod] + path, value) + for i in range(steps_start, steps_stop): + value_list = smt.get_net_bin_list(topmod, path_list, "s%d" % i) + mem_state = sorted( + [(tuple(path), addr, data) + for path, addr, data in mem_trace_data.get(i, [])]) + state = tuple(value_list), tuple(mem_state) + if state in states: + return (i, states[state]) + else: + states[state] = i - vcd.set_time(steps_stop) + return None def char_ok_in_verilog(c,i): if ('A' <= c <= 'Z'): return True @@ -826,7 +1071,7 @@ def char_ok_in_verilog(c,i): return False def escape_identifier(identifier): - if type(identifier) is list: + if type(identifier) is list: return map(escape_identifier, identifier) if "." in identifier: return ".".join(escape_identifier(identifier.split("."))) @@ -836,7 +1081,7 @@ def escape_identifier(identifier): -def write_vlogtb_trace(steps_start, steps_stop, index): +def write_vlogtb_trace(steps, index): filename = vlogtbfile.replace("%", index) print_msg("Writing trace to Verilog testbench: %s" % (filename)) @@ -901,7 +1146,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index): print(" initial begin", file=f) regs = sorted(smt.hiernets(vlogtb_topmod, regs_only=True)) - regvals = smt.get_net_bin_list(vlogtb_topmod, regs, vlogtb_state.replace("@@step_idx@@", str(steps_start))) + regvals = smt.get_net_bin_list(vlogtb_topmod, regs, vlogtb_state.replace("@@step_idx@@", str(steps[0]))) print("`ifndef VERILATOR", file=f) print(" #1;", file=f) @@ -916,7 +1161,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index): anyconsts = sorted(smt.hieranyconsts(vlogtb_topmod)) for info in anyconsts: if info[3] is not None: - modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(steps_start)), info[0]) + modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(steps[0])), info[0]) value = smt.bv2bin(smt.get("(|%s| %s)" % (info[1], modstate))) print(" UUT.%s = %d'b%s;" % (".".join(escape_identifier(info[0] + [info[3]])), len(value), value), file=f); @@ -926,7 +1171,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index): addr_expr_list = list() data_expr_list = list() - for i in range(steps_start, steps_stop): + for i in steps: for j in range(rports): addr_expr_list.append(smt.mem_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), mempath, "R%dA" % j)) data_expr_list.append(smt.mem_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), mempath, "R%dD" % j)) @@ -947,7 +1192,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index): print("", file=f) anyseqs = sorted(smt.hieranyseqs(vlogtb_topmod)) - for i in range(steps_start, steps_stop): + for i in steps: pi_names = [[name] for name, _ in primary_inputs if name not in clock_inputs] pi_values = smt.get_net_bin_list(vlogtb_topmod, pi_names, vlogtb_state.replace("@@step_idx@@", str(i))) @@ -979,14 +1224,14 @@ def write_vlogtb_trace(steps_start, steps_stop, index): print(" end", file=f) print(" always @(posedge clock) begin", file=f) - print(" genclock <= cycle < %d;" % (steps_stop-1), file=f) + print(" genclock <= cycle < %d;" % (steps[-1]), file=f) print(" cycle <= cycle + 1;", file=f) print(" end", file=f) print("endmodule", file=f) -def write_constr_trace(steps_start, steps_stop, index): +def write_constr_trace(steps, index): filename = outconstr.replace("%", index) print_msg("Writing trace to constraints file: %s" % (filename)) @@ -1003,7 +1248,7 @@ def write_constr_trace(steps_start, steps_stop, index): constr_prefix = smtctop[1] + "." if smtcinit: - steps_start = steps_stop - 1 + steps = [steps[-1]] with open(filename, "w") as f: primary_inputs = list() @@ -1012,13 +1257,13 @@ def write_constr_trace(steps_start, steps_stop, index): width = smt.modinfo[constr_topmod].wsize[name] primary_inputs.append((name, width)) - if steps_start == 0 or smtcinit: + if steps[0] == 0 or smtcinit: print("initial", file=f) else: - print("state %d" % steps_start, file=f) + print("state %d" % steps[0], file=f) regnames = sorted(smt.hiernets(constr_topmod, regs_only=True)) - regvals = smt.get_net_list(constr_topmod, regnames, constr_state.replace("@@step_idx@@", str(steps_start))) + regvals = smt.get_net_list(constr_topmod, regnames, constr_state.replace("@@step_idx@@", str(steps[0]))) for name, val in zip(regnames, regvals): print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f) @@ -1029,7 +1274,7 @@ def write_constr_trace(steps_start, steps_stop, index): addr_expr_list = list() data_expr_list = list() - for i in range(steps_start, steps_stop): + for i in steps: for j in range(rports): addr_expr_list.append(smt.mem_expr(constr_topmod, constr_state.replace("@@step_idx@@", str(i)), mempath, "R%dA" % j)) data_expr_list.append(smt.mem_expr(constr_topmod, constr_state.replace("@@step_idx@@", str(i)), mempath, "R%dD" % j)) @@ -1045,7 +1290,7 @@ def write_constr_trace(steps_start, steps_stop, index): for addr, data in addr_data.items(): print("assume (= (select [%s%s] %s) %s)" % (constr_prefix, ".".join(mempath), addr, data), file=f) - for k in range(steps_start, steps_stop): + for k in steps: if not smtcinit: print("", file=f) print("state %d" % k, file=f) @@ -1056,19 +1301,107 @@ def write_constr_trace(steps_start, steps_stop, index): for name, val in zip(pi_names, pi_values): print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f) +def write_yw_trace(steps, index, allregs=False, filename=None): + if filename is None: + if outywfile is None: + return + filename = outywfile.replace("%", index) + print_msg("Writing trace to Yosys witness file: %s" % (filename)) + + mem_trace_data = collect_mem_trace_data(steps) + + with open(filename, "w") as f: + inits, seqs, clocks, mems = smt.hierwitness(topmod, allregs) + + yw = WriteWitness(f, "smtbmc") + + for clock in clocks: + yw.add_clock(clock["path"], clock["offset"], clock["type"]) + + for seq in seqs: + seq["sig"] = yw.add_sig(seq["path"], seq["offset"], seq["width"]) + + for init in inits: + init["sig"] = yw.add_sig(init["path"], init["offset"], init["width"], True) + + inits = seqs + inits + + mem_dict = {tuple(mem["smtpath"]): mem for mem in mems} + mem_init_values = [] + + for path, addr, value in mem_trace_data.get(0, ()): + json_mem = mem_dict.get(tuple(path)) + if not json_mem: + continue + + bit_addr = addr * json_mem["width"] + uninit_chunks = [(chunk["width"] + chunk["offset"], chunk["offset"]) for chunk in json_mem["uninitialized"]] + first_chunk_nr = bisect.bisect_left(uninit_chunks, (bit_addr + 1,)) + + for uninit_end, uninit_offset in uninit_chunks[first_chunk_nr:]: + assert uninit_end > bit_addr + if uninit_offset > bit_addr + json_mem["width"]: + break + + word_path = (*json_mem["path"], f"\\[{addr}]") + + overlap_start = max(uninit_offset - bit_addr, 0) + overlap_end = min(uninit_end - bit_addr, json_mem["width"]) + overlap_bits = value[len(value)-overlap_end:len(value)-overlap_start] + + sig = yw.add_sig(word_path, overlap_start, overlap_end - overlap_start, True) + mem_init_values.append((sig, overlap_bits.replace("x", "?"))) + + exprs = [] + all_sigs = [] + + for i, k in enumerate(steps): + step_values = WitnessValues() + + if not i: + for sig, value in mem_init_values: + step_values[sig] = value + sigs = inits + seqs + else: + sigs = seqs + + exprs.extend(smt.witness_net_expr(topmod, f"s{k}", sig) for sig in sigs) + + all_sigs.append(sigs) + + bvs = iter(smt.get_list(exprs)) + + for sigs in all_sigs: + for sig in sigs: + value = smt.bv2bin(next(bvs)) + step_values[sig["sig"]] = value + yw.step(step_values) + + yw.end_trace() + + +def write_trace(steps_start, steps_stop, index, allregs=False): + if steps_stop is None: + steps = steps_start + seq_time = True + else: + steps = list(range(steps_start, steps_stop)) + seq_time = False -def write_trace(steps_start, steps_stop, index): if vcdfile is not None: - write_vcd_trace(steps_start, steps_stop, index) + write_vcd_trace(steps, index, seq_time=seq_time) if vlogtbfile is not None: - write_vlogtb_trace(steps_start, steps_stop, index) + write_vlogtb_trace(steps, index) if outconstr is not None: - write_constr_trace(steps_start, steps_stop, index) + write_constr_trace(steps, index) + + if outywfile is not None: + write_yw_trace(steps, index, allregs) -def print_failed_asserts_worker(mod, state, path, extrainfo): +def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()): assert mod in smt.modinfo found_failed_assert = False @@ -1076,29 +1409,31 @@ def print_failed_asserts_worker(mod, state, path, extrainfo): return for cellname, celltype in smt.modinfo[mod].cells.items(): - if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname, extrainfo): + cell_infokey = (mod, cellname, infokey) + if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname, extrainfo, infomap, cell_infokey): found_failed_assert = True for assertfun, assertinfo in smt.modinfo[mod].asserts.items(): if smt.get("(|%s| %s)" % (assertfun, state)) in ["false", "#b0"]: - print_msg("Assert failed in %s: %s%s" % (path, assertinfo, extrainfo)) + assert_key = (assertfun, infokey) + print_msg("Assert failed in %s: %s%s%s" % (path, assertinfo, extrainfo, infomap.get(assert_key, ''))) found_failed_assert = True return found_failed_assert -def print_failed_asserts(state, final=False, extrainfo=""): +def print_failed_asserts(state, final=False, extrainfo="", infomap={}): if noinfo: return loc_list, expr_list, value_list = get_constr_expr(constr_asserts, state, final=final, getvalues=True) found_failed_assert = False for loc, expr, value in zip(loc_list, expr_list, value_list): if smt.bv2int(value) == 0: - print_msg("Assert %s failed: %s%s" % (loc, expr, extrainfo)) + print_msg("Assert %s failed: %s%s%s" % (loc, expr, extrainfo, infomap.get(loc, ''))) found_failed_assert = True if not final: - if print_failed_asserts_worker(topmod, "s%d" % state, topmod, extrainfo): + if print_failed_asserts_worker(topmod, "s%d" % state, topmod, extrainfo, infomap): found_failed_assert = True return found_failed_assert @@ -1145,6 +1480,81 @@ def get_cover_list(mod, base): return cover_expr, cover_desc + +def get_assert_map(mod, base, path, key_base=()): + assert mod in smt.modinfo + + assert_map = dict() + + for expr, desc in smt.modinfo[mod].asserts.items(): + assert_map[(expr, key_base)] = ("(|%s| %s)" % (expr, base), path, desc) + + for cell, submod in smt.modinfo[mod].cells.items(): + assert_map.update(get_assert_map(submod, "(|%s_h %s| %s)" % (mod, cell, base), path + "." + cell, (mod, cell, key_base))) + + return assert_map + + +def get_assert_keys(): + keys = set() + keys.update(get_assert_map(topmod, 'state', topmod).keys()) + for step_constr_asserts in constr_asserts.values(): + keys.update(loc for loc, expr in step_constr_asserts) + + return keys + + +def get_active_assert_map(step, active): + assert_map = dict() + for key, assert_data in get_assert_map(topmod, "s%s" % step, topmod).items(): + if key in active: + assert_map[key] = assert_data + + for loc, expr, actual_expr in zip(*get_constr_expr(constr_asserts, step, individual=True)): + if loc in active: + assert_map[loc] = (actual_expr, None, (expr, loc)) + + return assert_map + +assume_enables = {} + +def declare_assume_enables(): + def recurse(mod, path, key_base=()): + for expr, desc in smt.modinfo[mod].assumes.items(): + enable = f"|assume_enable {len(assume_enables)}|" + smt.smt2_assumptions[(expr, key_base)] = enable + smt.write(f"(declare-const {enable} Bool)") + assume_enables[(expr, key_base)] = (enable, path, desc) + + for cell, submod in smt.modinfo[mod].cells.items(): + recurse(submod, f"{path}.{cell}", (mod, cell, key_base)) + + recurse(topmod, topmod) + +if track_assumes: + declare_assume_enables() + +def smt_assert_design_assumes(step): + if not track_assumes: + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + return + + if not assume_enables: + return + + def expr_for_assume(assume_key, base=None): + expr, key_base = assume_key + expr_prefix = f"(|{expr}| " + expr_suffix = ")" + while key_base: + mod, cell, key_base = key_base + expr_prefix += f"(|{mod}_h {cell}| " + expr_suffix += ")" + return f"{expr_prefix} s{step}{expr_suffix}" + + for assume_key, (enable, path, desc) in assume_enables.items(): + smt_assert_consequent(f"(=> {enable} {expr_for_assume(assume_key)})") + states = list() asserts_antecedent_cache = [list()] asserts_consequent_cache = [list()] @@ -1298,7 +1708,18 @@ def smt_check_sat(expected=["sat", "unsat"]): smt_forall_assert() return smt.check_sat(expected=expected) -if tempind: +def report_tracked_assumptions(msg): + if track_assumes: + print_msg(msg) + for key in smt.get_unsat_assumptions(minimize=minimize_assumes): + enable, path, descr = assume_enables[key] + print_msg(f" In {path}: {descr}") + + +if incremental: + incremental.mainloop() + +elif tempind: retstatus = "FAILED" skip_counter = step_size for step in range(num_steps, -1, -1): @@ -1307,7 +1728,7 @@ def smt_check_sat(expected=["sat", "unsat"]): break smt_state(step) - smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_design_assumes(step) smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) smt_assert_consequent(get_constr_expr(constr_assumes, step)) @@ -1337,15 +1758,20 @@ def smt_check_sat(expected=["sat", "unsat"]): print_msg("Temporal induction failed!") print_anyconsts(num_steps) print_failed_asserts(num_steps) - write_trace(step, num_steps+1, '%') + write_trace(step, num_steps+1, '%', allregs=True) + if detect_loops: + loop = detect_state_loop(step, num_steps+1) + if loop: + print_msg(f"Loop detected, increasing induction depth will not help. Step {loop[0]} = step {loop[1]}") elif dumpall: print_anyconsts(num_steps) print_failed_asserts(num_steps) - write_trace(step, num_steps+1, "%d" % step) + write_trace(step, num_steps+1, "%d" % step, allregs=True) else: print_msg("Temporal induction successful.") + report_tracked_assumptions("Used assumptions:") retstatus = "PASSED" break @@ -1371,7 +1797,7 @@ def smt_check_sat(expected=["sat", "unsat"]): while step < num_steps: smt_state(step) - smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_design_assumes(step) smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) smt_assert_consequent(get_constr_expr(constr_assumes, step)) @@ -1392,6 +1818,7 @@ def smt_check_sat(expected=["sat", "unsat"]): smt_assert("(distinct (covers_%d s%d) #b%s)" % (coveridx, step, "0" * len(cover_desc))) if smt_check_sat() == "unsat": + report_tracked_assumptions("Used assumptions:") smt_pop() break @@ -1400,13 +1827,14 @@ def smt_check_sat(expected=["sat", "unsat"]): print_msg("Appending additional step %d." % i) smt_state(i) smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i)) - smt_assert_consequent("(|%s_u| s%d)" % (topmod, i)) + smt_assert_design_assumes(i) smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i)) smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i)) smt_assert_consequent(get_constr_expr(constr_assumes, i)) print_msg("Re-solving with appended steps..") if smt_check_sat() == "unsat": print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) + report_tracked_assumptions("Conflicting assumptions:") found_failed_assert = True retstatus = "FAILED" break @@ -1439,7 +1867,7 @@ def smt_check_sat(expected=["sat", "unsat"]): smt_pop() smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask)) - if found_failed_assert: + if found_failed_assert and not keep_going: break if "1" not in cover_mask: @@ -1454,11 +1882,15 @@ def smt_check_sat(expected=["sat", "unsat"]): print_msg("Unreached cover statement at %s." % cover_desc[i]) else: # not tempind, covermode + active_assert_keys = get_assert_keys() + failed_assert_infomap = dict() + traceidx = 0 + step = 0 retstatus = "PASSED" while step < num_steps: smt_state(step) - smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_design_assumes(step) smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) smt_assert_consequent(get_constr_expr(constr_assumes, step)) @@ -1488,7 +1920,7 @@ def smt_check_sat(expected=["sat", "unsat"]): if step+i < num_steps: smt_state(step+i) smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step+i)) - smt_assert_consequent("(|%s_u| s%d)" % (topmod, step+i)) + smt_assert_design_assumes(step + i) smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step+i)) smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step+i-1, step+i)) smt_assert_consequent(get_constr_expr(constr_assumes, step+i)) @@ -1502,49 +1934,102 @@ def smt_check_sat(expected=["sat", "unsat"]): print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step)) if smt_check_sat() == "unsat": - print("%s Assumptions are unsatisfiable!" % smt.timestamp()) + print_msg("Assumptions are unsatisfiable!") + report_tracked_assumptions("Conficting assumptions:") retstatus = "PREUNSAT" break if not final_only: - if last_check_step == step: - print_msg("Checking assertions in step %d.." % (step)) - else: - print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step)) - smt_push() - - smt_assert("(not (and %s))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] + - [get_constr_expr(constr_asserts, i) for i in range(step, last_check_step+1)])) - - if smt_check_sat() == "sat": - print("%s BMC failed!" % smt.timestamp()) - if append_steps > 0: - for i in range(last_check_step+1, last_check_step+1+append_steps): - print_msg("Appending additional step %d." % i) - smt_state(i) - smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i)) - smt_assert_consequent("(|%s_u| s%d)" % (topmod, i)) - smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i)) - smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i)) - smt_assert_consequent(get_constr_expr(constr_assumes, i)) - print_msg("Re-solving with appended steps..") - if smt_check_sat() == "unsat": - print("%s Cannot append steps without violating assumptions!" % smt.timestamp()) - retstatus = "FAILED" - break - print_anyconsts(step) + recheck_current_step = True + while recheck_current_step: + recheck_current_step = False + if last_check_step == step: + print_msg("Checking assertions in step %d.." % (step)) + else: + print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step)) + smt_push() + + active_assert_maps = dict() + active_assert_exprs = list() for i in range(step, last_check_step+1): - print_failed_asserts(i) - write_trace(0, last_check_step+1+append_steps, '%') - retstatus = "FAILED" - break + assert_expr_map = get_active_assert_map(i, active_assert_keys) + active_assert_maps[i] = assert_expr_map + active_assert_exprs.extend(assert_data[0] for assert_data in assert_expr_map.values()) - smt_pop() + if active_assert_exprs: + if len(active_assert_exprs) == 1: + active_assert_expr = active_assert_exprs[0] + else: + active_assert_expr = "(and %s)" % " ".join(active_assert_exprs) + + smt_assert("(not %s)" % active_assert_expr) + else: + active_assert_expr = "true" + smt_assert("false") + + + if smt_check_sat() == "sat": + if retstatus != "FAILED": + print("%s BMC failed!" % smt.timestamp()) + + if check_witness: + print_msg("Checking witness constraints...") + smt_pop() + smt_push() + smt_assert(active_assert_expr) + if smt_check_sat() != "sat": + retstatus = "PASSED" + check_witness = False + num_steps = -1 + break + + if append_steps > 0: + for i in range(last_check_step+1, last_check_step+1+append_steps): + print_msg("Appending additional step %d." % i) + smt_state(i) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i)) + smt_assert_design_assumes(i) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i)) + smt_assert_consequent(get_constr_expr(constr_assumes, i)) + print_msg("Re-solving with appended steps..") + if smt_check_sat() == "unsat": + print_msg("Cannot append steps without violating assumptions!") + report_tracked_assumptions("Conflicting assumptions:") + retstatus = "FAILED" + break + print_anyconsts(step) + + for i in range(step, last_check_step+1): + print_failed_asserts(i, infomap=failed_assert_infomap) + + if keep_going: + for i in range(step, last_check_step+1): + for key, (expr, path, desc) in active_assert_maps[i].items(): + if key in active_assert_keys and not smt.bv2int(smt.get(expr)): + failed_assert_infomap[key] = " [failed before]" + + active_assert_keys.remove(key) + + if active_assert_keys: + recheck_current_step = True + + write_trace(0, last_check_step+1+append_steps, "%d" % traceidx if keep_going else '%') + traceidx += 1 + retstatus = "FAILED" + + smt_pop() + if recheck_current_step: + print_msg("Checking remaining assertions..") + + if retstatus == "FAILED" and not (keep_going and active_assert_keys): + break if (constr_final_start is not None) or (last_check_step+1 != num_steps): for i in range(step, last_check_step+1): - smt_assert("(|%s_a| s%d)" % (topmod, i)) - smt_assert(get_constr_expr(constr_asserts, i)) + assert_expr_map = get_active_assert_map(i, active_assert_keys) + for assert_data in assert_expr_map.values(): + smt_assert(assert_data[0]) if constr_final_start is not None: for i in range(step, last_check_step+1): @@ -1591,9 +2076,12 @@ def smt_check_sat(expected=["sat", "unsat"]): print_anyconsts(0) write_trace(0, num_steps, '%') + if check_witness: + retstatus = "FAILED" smt.write("(exit)") smt.wait() -print_msg("Status: %s" % retstatus) -sys.exit(0 if retstatus == "PASSED" else 1) +if not incremental: + print_msg("Status: %s" % retstatus) + sys.exit(0 if retstatus == "PASSED" else 1) diff --git a/backends/smt2/smtbmc_incremental.py b/backends/smt2/smtbmc_incremental.py new file mode 100644 index 00000000000..f43e878f31c --- /dev/null +++ b/backends/smt2/smtbmc_incremental.py @@ -0,0 +1,497 @@ +from collections import defaultdict +import json +import typing +from functools import partial + +if typing.TYPE_CHECKING: + import smtbmc +else: + import sys + + smtbmc = sys.modules["__main__"] + + +class InteractiveError(Exception): + pass + + +def mkkey(data): + if isinstance(data, list): + return tuple(map(mkkey, data)) + elif isinstance(data, dict): + raise InteractiveError(f"JSON objects found in assumption key: {data!r}") + return data + + +class Incremental: + def __init__(self): + self.traceidx = 0 + + self.state_set = set() + self.map_cache = {} + + self._cached_hierwitness = {} + self._witness_index = None + + self._yw_constraints = {} + + def setup(self): + generic_assert_map = smtbmc.get_assert_map( + smtbmc.topmod, "state", smtbmc.topmod + ) + self.inv_generic_assert_map = { + tuple(data[1:]): key for key, data in generic_assert_map.items() + } + assert len(self.inv_generic_assert_map) == len(generic_assert_map) + + def print_json(self, **kwargs): + print(json.dumps(kwargs), flush=True) + + def print_msg(self, msg): + self.print_json(msg=msg) + + def get_cached_assert(self, step, name): + try: + assert_map = self.map_cache[step] + except KeyError: + assert_map = self.map_cache[step] = smtbmc.get_assert_map( + smtbmc.topmod, f"s{step}", smtbmc.topmod + ) + return assert_map[self.inv_generic_assert_map[name]][0] + + def arg_step(self, cmd, declare=False, name="step", optional=False): + step = cmd.get(name, None) + if step is None and optional: + return None + if not isinstance(step, int): + if optional: + raise InteractiveError(f"{name} must be an integer") + else: + raise InteractiveError(f"integer {name} argument required") + if declare and step in self.state_set: + raise InteractiveError(f"step {step} already declared") + if not declare and step not in self.state_set: + raise InteractiveError(f"step {step} not declared") + return step + + def expr_arg_len(self, expr, min_len, max_len=-1): + if max_len == -1: + max_len = min_len + arg_len = len(expr) - 1 + + if min_len is not None and arg_len < min_len: + if min_len == max_len: + raise InteractiveError( + f"{json.dumps(expr[0])} expression must have " + f"{min_len} argument{'s' if min_len != 1 else ''}" + ) + else: + raise InteractiveError( + f"{json.dumps(expr[0])} expression must have at least " + f"{min_len} argument{'s' if min_len != 1 else ''}" + ) + if max_len is not None and arg_len > max_len: + raise InteractiveError( + f"{json.dumps(expr[0])} expression can have at most " + f"{min_len} argument{'s' if max_len != 1 else ''}" + ) + + def expr_step(self, expr, smt_out): + self.expr_arg_len(expr, 1) + step = expr[1] + if step not in self.state_set: + raise InteractiveError(f"step {step} not declared") + smt_out.append(f"s{step}") + return "module", smtbmc.topmod + + def expr_cell(self, expr, smt_out): + self.expr_arg_len(expr, 2) + position = len(smt_out) + smt_out.append(None) + arg_sort = self.expr(expr[2], smt_out, required_sort=["module", None]) + smt_out.append(")") + module = arg_sort[1] + cell = expr[1] + submod = smtbmc.smt.modinfo[module].cells.get(cell) + if submod is None: + raise InteractiveError(f"module {module!r} has no cell {cell!r}") + smt_out[position] = f"(|{module}_h {cell}| " + return ("module", submod) + + def expr_mod_constraint(self, expr, smt_out): + suffix = expr[0][3:] + self.expr_arg_len(expr, 1, 2 if suffix in ["_a", "_u", "_c"] else 1) + position = len(smt_out) + smt_out.append(None) + arg_sort = self.expr(expr[-1], smt_out, required_sort=["module", None]) + module = arg_sort[1] + if len(expr) == 3: + smt_out[position] = f"(|{module}{suffix} {expr[1]}| " + else: + smt_out[position] = f"(|{module}{suffix}| " + smt_out.append(")") + return "Bool" + + def expr_mod_constraint2(self, expr, smt_out): + self.expr_arg_len(expr, 2) + + position = len(smt_out) + smt_out.append(None) + arg_sort = self.expr(expr[1], smt_out, required_sort=["module", None]) + smt_out.append(" ") + self.expr(expr[2], smt_out, required_sort=arg_sort) + module = arg_sort[1] + suffix = expr[0][3:] + smt_out[position] = f"(|{module}{suffix}| " + smt_out.append(")") + return "Bool" + + def expr_not(self, expr, smt_out): + self.expr_arg_len(expr, 1) + + smt_out.append("(not ") + self.expr(expr[1], smt_out, required_sort="Bool") + smt_out.append(")") + return "Bool" + + def expr_eq(self, expr, smt_out): + self.expr_arg_len(expr, 2) + + smt_out.append("(= ") + arg_sort = self.expr(expr[1], smt_out) + if ( + smtbmc.smt.unroll + and isinstance(arg_sort, (list, tuple)) + and arg_sort[0] == "module" + ): + raise InteractiveError("state equality not supported in unroll mode") + + smt_out.append(" ") + self.expr(expr[2], smt_out, required_sort=arg_sort) + smt_out.append(")") + return "Bool" + + def expr_andor(self, expr, smt_out): + if len(expr) == 1: + smt_out.push({"and": "true", "or": "false"}[expr[0]]) + elif len(expr) == 2: + arg_sort = self.expr(expr[1], smt_out) + if arg_sort != "Bool": + raise InteractiveError( + f"arguments of {json.dumps(expr[0])} must have sort Bool" + ) + else: + sep = f"({expr[0]} " + for arg in expr[1:]: + smt_out.append(sep) + sep = " " + self.expr(arg, smt_out, required_sort="Bool") + smt_out.append(")") + return "Bool" + + def expr_yw(self, expr, smt_out): + if len(expr) == 2: + name = None + step = expr[1] + elif len(expr) == 3: + name = expr[1] + step = expr[2] + + if step not in self.state_set: + raise InteractiveError(f"step {step} not declared") + + if name not in self._yw_constraints: + raise InteractiveError(f"no yw file loaded as name {name!r}") + + constraints = self._yw_constraints[name].get(step, []) + + if len(constraints) == 0: + smt_out.append("true") + elif len(constraints) == 1: + smt_out.append(constraints[0]) + else: + sep = "(and " + for constraint in constraints: + smt_out.append(sep) + sep = " " + smt_out.append(constraint) + smt_out.append(")") + + return "Bool" + + def expr_smtlib(self, expr, smt_out): + self.expr_arg_len(expr, 2) + + smtlib_expr = expr[1] + sort = expr[2] + + if not isinstance(smtlib_expr, str): + raise InteractiveError( + "raw SMT-LIB expression has to be a string, " + f"got {json.dumps(smtlib_expr)}" + ) + + if not isinstance(sort, str): + raise InteractiveError( + f"raw SMT-LIB sort has to be a string, got {json.dumps(sort)}" + ) + + smt_out.append(smtlib_expr) + return sort + + def expr_label(self, expr, smt_out): + if len(expr) != 3: + raise InteractiveError( + f'expected ["!", label, sub_expr], got {json.dumps(expr)}' + ) + label = expr[1] + subexpr = expr[2] + + if not isinstance(label, str): + raise InteractiveError("expression label has to be a string") + + smt_out.append("(! ") + sort = self.expr(subexpr, smt_out) + smt_out.append(" :named ") + smt_out.append(label) + smt_out.append(")") + + return sort + + expr_handlers = { + "step": expr_step, + "cell": expr_cell, + "mod_h": expr_mod_constraint, + "mod_is": expr_mod_constraint, + "mod_i": expr_mod_constraint, + "mod_a": expr_mod_constraint, + "mod_u": expr_mod_constraint, + "mod_t": expr_mod_constraint2, + "not": expr_not, + "and": expr_andor, + "or": expr_andor, + "=": expr_eq, + "yw": expr_yw, + "smtlib": expr_smtlib, + "!": expr_label, + } + + def expr(self, expr, smt_out, required_sort=None): + if not isinstance(expr, (list, tuple)) or not expr: + raise InteractiveError( + f"expression must be a non-empty JSON array, found: {json.dumps(expr)}" + ) + name = expr[0] + + handler = self.expr_handlers.get(name) + if handler: + sort = handler(self, expr, smt_out) + + if required_sort is not None: + if isinstance(required_sort, (list, tuple)): + if ( + not isinstance(sort, (list, tuple)) + or len(sort) != len(required_sort) + or any( + r is not None and r != s + for r, s in zip(required_sort, sort) + ) + ): + raise InteractiveError( + f"required sort {json.dumps(required_sort)} " + f"found sort {json.dumps(sort)}" + ) + return sort + raise InteractiveError(f"unknown expression {json.dumps(expr[0])}") + + def expr_smt(self, expr, required_sort): + smt_out = [] + self.expr(expr, smt_out, required_sort=required_sort) + out = "".join(smt_out) + return out + + def cmd_new_step(self, cmd): + step = self.arg_step(cmd, declare=True) + self.state_set.add(step) + smtbmc.smt_state(step) + + def cmd_assert(self, cmd): + name = cmd.get("cmd") + + assert_fn = { + "assert_antecedent": smtbmc.smt_assert_antecedent, + "assert_consequent": smtbmc.smt_assert_consequent, + "assert": smtbmc.smt_assert, + }[name] + + assert_fn(self.expr_smt(cmd.get("expr"), "Bool")) + + def cmd_assert_design_assumes(self, cmd): + step = self.arg_step(cmd) + smtbmc.smt_assert_design_assumes(step) + + def cmd_get_design_assume(self, cmd): + key = mkkey(cmd.get("key")) + return smtbmc.assume_enables.get(key) + + def cmd_update_assumptions(self, cmd): + expr = cmd.get("expr") + key = cmd.get("key") + + + key = mkkey(key) + + result = smtbmc.smt.smt2_assumptions.pop(key, None) + if expr is not None: + expr = self.expr_smt(expr, "Bool") + smtbmc.smt.smt2_assumptions[key] = expr + return result + + def cmd_get_unsat_assumptions(self, cmd): + return smtbmc.smt.get_unsat_assumptions(minimize=bool(cmd.get('minimize'))) + + def cmd_push(self, cmd): + smtbmc.smt_push() + + def cmd_pop(self, cmd): + smtbmc.smt_pop() + + def cmd_check(self, cmd): + return smtbmc.smt_check_sat() + + def cmd_smtlib(self, cmd): + command = cmd.get("command") + response = cmd.get("response", False) + if not isinstance(command, str): + raise InteractiveError( + f"raw SMT-LIB command must be a string, found {json.dumps(command)}" + ) + smtbmc.smt.write(command) + if response: + return smtbmc.smt.read() + + def cmd_design_hierwitness(self, cmd=None): + allregs = (cmd is None) or bool(cmd.get("allreges", False)) + if self._cached_hierwitness[allregs] is not None: + return self._cached_hierwitness[allregs] + inits, seqs, clocks, mems = smtbmc.smt.hierwitness(smtbmc.topmod, allregs) + self._cached_hierwitness[allregs] = result = dict( + inits=inits, seqs=seqs, clocks=clocks, mems=mems + ) + return result + + def cmd_write_yw_trace(self, cmd): + steps = cmd.get("steps") + allregs = bool(cmd.get("allregs", False)) + + if steps is None: + steps = sorted(self.state_set) + + path = cmd.get("path") + + smtbmc.write_yw_trace(steps, self.traceidx, allregs=allregs, filename=path) + + if path is None: + self.traceidx += 1 + + def cmd_read_yw_trace(self, cmd): + steps = cmd.get("steps") + path = cmd.get("path") + name = cmd.get("name") + skip_x = cmd.get("skip_x", False) + if path is None: + raise InteractiveError("path required") + + constraints = defaultdict(list) + + if steps is None: + steps = sorted(self.state_set) + + map_steps = {i: int(j) for i, j in enumerate(steps)} + + last_step = smtbmc.ywfile_constraints( + path, constraints, map_steps=map_steps, skip_x=skip_x + ) + + self._yw_constraints[name] = { + map_steps.get(i, i): [smtexpr for cexfile, smtexpr in constraint_list] + for i, constraint_list in constraints.items() + } + + return dict(last_step=last_step) + + def cmd_modinfo(self, cmd): + fields = cmd.get("fields", []) + + mod = cmd.get("mod") + if mod is None: + mod = smtbmc.topmod + modinfo = smtbmc.smt.modinfo.get(mod) + if modinfo is None: + return None + + result = dict(name=mod) + for field in fields: + result[field] = getattr(modinfo, field, None) + return result + + def cmd_ping(self, cmd): + return cmd + + cmd_handlers = { + "new_step": cmd_new_step, + "assert": cmd_assert, + "assert_antecedent": cmd_assert, + "assert_consequent": cmd_assert, + "assert_design_assumes": cmd_assert_design_assumes, + "get_design_assume": cmd_get_design_assume, + "update_assumptions": cmd_update_assumptions, + "get_unsat_assumptions": cmd_get_unsat_assumptions, + "push": cmd_push, + "pop": cmd_pop, + "check": cmd_check, + "smtlib": cmd_smtlib, + "design_hierwitness": cmd_design_hierwitness, + "write_yw_trace": cmd_write_yw_trace, + "read_yw_trace": cmd_read_yw_trace, + "modinfo": cmd_modinfo, + "ping": cmd_ping, + } + + def handle_command(self, cmd): + if not isinstance(cmd, dict) or "cmd" not in cmd: + raise InteractiveError('object with "cmd" key required') + + name = cmd.get("cmd", None) + + handler = self.cmd_handlers.get(name) + if handler: + return handler(self, cmd) + else: + raise InteractiveError(f"unknown command: {name}") + + def mainloop(self): + self.setup() + while True: + try: + cmd = input().strip() + if not cmd or cmd.startswith("#") or cmd.startswith("//"): + continue + try: + cmd = json.loads(cmd) + except json.decoder.JSONDecodeError as e: + self.print_json(err=f"invalid JSON: {e}") + continue + except EOFError: + break + + try: + result = self.handle_command(cmd) + except InteractiveError as e: + self.print_json(err=str(e)) + continue + except Exception as e: + self.print_json(err=f"internal error: {e}") + raise + else: + self.print_json(ok=result) diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index 516091011d7..e32f43c60a0 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -1,7 +1,7 @@ # # yosys -- Yosys Open SYnthesis Suite # -# Copyright (C) 2012 Clifford Wolf +# Copyright (C) 2012 Claire Xenia Wolf # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -16,11 +16,11 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -import sys, re, os, signal +import sys, re, os, signal, json import subprocess if os.name == "posix": import resource -from copy import deepcopy +from copy import copy from select import select from time import time from queue import Queue, Empty @@ -79,6 +79,20 @@ def except_hook(exctype, value, traceback): sys.excepthook = except_hook +def recursion_helper(iteration, *request): + stack = [iteration(*request)] + + while stack: + top = stack.pop() + try: + request = next(top) + except StopIteration: + continue + + stack.append(top) + stack.append(iteration(*request)) + + hex_dict = { "0": "0000", "1": "0001", "2": "0010", "3": "0011", "4": "0100", "5": "0101", "6": "0110", "7": "0111", @@ -100,6 +114,7 @@ def __init__(self): self.clocks = dict() self.cells = dict() self.asserts = dict() + self.assumes = dict() self.covers = dict() self.maximize = set() self.minimize = set() @@ -108,6 +123,7 @@ def __init__(self): self.allconsts = dict() self.allseqs = dict() self.asize = dict() + self.witness = [] class SmtIo: @@ -123,8 +139,10 @@ def __init__(self, opts=None): self.forall = False self.timeout = 0 self.produce_models = True + self.recheck = False self.smt2cache = [list()] self.smt2_options = dict() + self.smt2_assumptions = dict() self.p = None self.p_index = solvers_index solvers_index += 1 @@ -176,7 +194,10 @@ def setup(self): self.unroll = False if self.solver == "yices": - if self.noincr or self.forall: + if self.forall: + self.noincr = True + + if self.noincr: self.popen_vargs = ['yices-smt2'] + self.solver_opts else: self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts @@ -189,11 +210,12 @@ def setup(self): if self.timeout != 0: self.popen_vargs.append('-T:%d' % self.timeout); - if self.solver == "cvc4": + if self.solver in ["cvc4", "cvc5"]: + self.recheck = True if self.noincr: - self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts + self.popen_vargs = [self.solver, '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts else: - self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts + self.popen_vargs = [self.solver, '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts if self.timeout != 0: self.popen_vargs.append('--tlimit=%d000' % self.timeout); @@ -203,14 +225,14 @@ def setup(self): print('timeout option is not supported for mathsat.') sys.exit(1) - if self.solver == "boolector": + if self.solver in ["boolector", "bitwuzla"]: if self.noincr: - self.popen_vargs = ['boolector', '--smt2'] + self.solver_opts + self.popen_vargs = [self.solver, '--smt2'] + self.solver_opts else: - self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts + self.popen_vargs = [self.solver, '--smt2', '-i'] + self.solver_opts self.unroll = True if self.timeout != 0: - print('timeout option is not supported for boolector.') + print('timeout option is not supported for %s.' % self.solver) sys.exit(1) if self.solver == "abc": @@ -239,6 +261,7 @@ def setup(self): self.logic_uf = False self.unroll_idcnt = 0 self.unroll_buffer = "" + self.unroll_level = 0 self.unroll_sorts = set() self.unroll_objs = set() self.unroll_decls = dict() @@ -291,17 +314,29 @@ def replace_in_stmt(self, stmt, pat, repl): return stmt def unroll_stmt(self, stmt): + result = [] + recursion_helper(self._unroll_stmt_into, stmt, result) + return result.pop() + + def _unroll_stmt_into(self, stmt, output, depth=128): if not isinstance(stmt, list): - return stmt + output.append(stmt) + return - stmt = [self.unroll_stmt(s) for s in stmt] + new_stmt = [] + for s in stmt: + if depth: + yield from self._unroll_stmt_into(s, new_stmt, depth - 1) + else: + yield s, new_stmt + stmt = new_stmt if len(stmt) >= 2 and not isinstance(stmt[0], list) and stmt[0] in self.unroll_decls: assert stmt[1] in self.unroll_objs key = tuple(stmt) if key not in self.unroll_cache: - decl = deepcopy(self.unroll_decls[key[0]]) + decl = copy(self.unroll_decls[key[0]]) self.unroll_cache[key] = "|UNROLL#%d|" % self.unroll_idcnt decl[1] = self.unroll_cache[key] @@ -323,16 +358,23 @@ def unroll_stmt(self, stmt): decl[2] = list() if len(decl) > 0: - decl = self.unroll_stmt(decl) + tmp = [] + if depth: + yield from self._unroll_stmt_into(decl, tmp, depth - 1) + else: + yield decl, tmp + + decl = tmp.pop() self.write(self.unparse(decl), unroll=False) - return self.unroll_cache[key] + output.append(self.unroll_cache[key]) + return - return stmt + output.append(stmt) def p_thread_main(self): while True: - data = self.p.stdout.readline().decode("ascii") + data = self.p.stdout.readline().decode("utf-8") if data == "": break self.p_queue.put(data) self.p_queue.put("") @@ -354,7 +396,7 @@ def p_open(self): def p_write(self, data, flush): assert self.p is not None - self.p.stdin.write(bytes(data, "ascii")) + self.p.stdin.write(bytes(data, "utf-8")) if flush: self.p.stdin.flush() def p_read(self): @@ -404,17 +446,31 @@ def write(self, stmt, unroll=True): stmt = re.sub(r" *;.*", "", stmt) if stmt == "": return - if unroll and self.unroll: - stmt = self.unroll_buffer + stmt - self.unroll_buffer = "" + recheck = None + if self.solver != "dummy": + if self.noincr: + # Don't close the solver yet, if we're just unrolling definitions + # required for a (get-...) statement + if self.p is not None and not stmt.startswith("(get-") and unroll: + self.p_close() + + if unroll and self.unroll: s = re.sub(r"\|[^|]*\|", "", stmt) - if s.count("(") != s.count(")"): - self.unroll_buffer = stmt + " " + self.unroll_level += s.count("(") - s.count(")") + if self.unroll_level > 0: + self.unroll_buffer += stmt + self.unroll_buffer += " " return + else: + stmt = self.unroll_buffer + stmt + self.unroll_buffer = "" s = self.parse(stmt) + if self.recheck and s and s[0].startswith("get-"): + recheck = self.unroll_idcnt + if self.debug_print: print("-> %s" % s) @@ -440,12 +496,15 @@ def write(self, stmt, unroll=True): stmt = self.unparse(self.unroll_stmt(s)) + if recheck is not None and recheck != self.unroll_idcnt: + self.check_sat(["sat"]) + if stmt == "(push 1)": self.unroll_stack.append(( - deepcopy(self.unroll_sorts), - deepcopy(self.unroll_objs), - deepcopy(self.unroll_decls), - deepcopy(self.unroll_cache), + copy(self.unroll_sorts), + copy(self.unroll_objs), + copy(self.unroll_decls), + copy(self.unroll_cache), )) if stmt == "(pop 1)": @@ -460,8 +519,6 @@ def write(self, stmt, unroll=True): if self.solver != "dummy": if self.noincr: - if self.p is not None and not stmt.startswith("(get-"): - self.p_close() if stmt == "(push 1)": self.smt2cache.append(list()) elif stmt == "(pop 1)": @@ -536,10 +593,22 @@ def info(self, stmt): self.modinfo[self.curmod].clocks[fields[2]] = "event" if fields[1] == "yosys-smt2-assert": - self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3] + if len(fields) > 4: + self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = f'{fields[4]} ({fields[3]})' + else: + self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3] if fields[1] == "yosys-smt2-cover": - self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3] + if len(fields) > 4: + self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = f'{fields[4]} ({fields[3]})' + else: + self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3] + + if fields[1] == "yosys-smt2-assume": + if len(fields) > 4: + self.modinfo[self.curmod].assumes["%s_u %s" % (self.curmod, fields[2])] = f'{fields[4]} ({fields[3]})' + else: + self.modinfo[self.curmod].assumes["%s_u %s" % (self.curmod, fields[2])] = fields[3] if fields[1] == "yosys-smt2-maximize": self.modinfo[self.curmod].maximize.add(fields[2]) @@ -563,6 +632,11 @@ def info(self, stmt): self.modinfo[self.curmod].allseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) + if fields[1] == "yosys-smt2-witness": + data = json.loads(stmt.split(None, 2)[2]) + if data.get("type") in ["cell", "mem", "posedge", "negedge", "input", "reg", "init", "seq", "blackbox"]: + self.modinfo[self.curmod].witness.append(data) + def hiernets(self, top, regs_only=False): def hiernets_worker(nets, mod, cursor): for netname in sorted(self.modinfo[mod].wsize.keys()): @@ -634,6 +708,57 @@ def hiermems_worker(mems, mod, cursor): hiermems_worker(mems, top, []) return mems + def hierwitness(self, top, allregs=False, blackbox=True): + init_witnesses = [] + seq_witnesses = [] + clk_witnesses = [] + mem_witnesses = [] + + def absolute(path, cursor, witness): + return { + **witness, + "path": path + tuple(witness["path"]), + "smtpath": cursor + [witness["smtname"]], + } + + for witness in self.modinfo[top].witness: + if witness["type"] == "input": + seq_witnesses.append(absolute((), [], witness)) + if witness["type"] in ("posedge", "negedge"): + clk_witnesses.append(absolute((), [], witness)) + + init_types = ["init"] + if allregs: + init_types.append("reg") + + seq_types = ["seq"] + if blackbox: + seq_types.append("blackbox") + + def worker(mod, path, cursor): + cell_paths = {} + for witness in self.modinfo[mod].witness: + if witness["type"] in init_types: + init_witnesses.append(absolute(path, cursor, witness)) + if witness["type"] in seq_types: + seq_witnesses.append(absolute(path, cursor, witness)) + if witness["type"] == "mem": + if allregs and not witness["rom"]: + width, size = witness["width"], witness["size"] + witness = {**witness, "uninitialized": [{"width": width * size, "offset": 0}]} + if not witness["uninitialized"]: + continue + + mem_witnesses.append(absolute(path, cursor, witness)) + if witness["type"] == "cell": + cell_paths[witness["smtname"]] = tuple(witness["path"]) + + for cellname, celltype in sorted(self.modinfo[mod].cells.items()): + worker(celltype, path + cell_paths.get(cellname, ("?" + cellname,)), cursor + [cellname]) + + worker(top, (), []) + return init_witnesses, seq_witnesses, clk_witnesses, mem_witnesses + def read(self): stmt = [] count_brackets = 0 @@ -668,8 +793,13 @@ def read(self): return stmt def check_sat(self, expected=["sat", "unsat", "unknown", "timeout", "interrupted"]): + if self.smt2_assumptions: + assume_exprs = " ".join(self.smt2_assumptions.values()) + check_stmt = f"(check-sat-assuming ({assume_exprs}))" + else: + check_stmt = "(check-sat)" if self.debug_print: - print("> (check-sat)") + print(f"> {check_stmt}") if self.debug_file and not self.nocomments: print("; running check-sat..", file=self.debug_file) self.debug_file.flush() @@ -683,11 +813,11 @@ def check_sat(self, expected=["sat", "unsat", "unknown", "timeout", "interrupted for cache_stmt in cache_ctx: self.p_write(cache_stmt + "\n", False) - self.p_write("(check-sat)\n", True) + self.p_write(f"{check_stmt}\n", True) if self.timeinfo: i = 0 - s = "/-\|" + s = r"/-\|" count = 0 num_bs = 0 @@ -751,7 +881,7 @@ def check_sat(self, expected=["sat", "unsat", "unknown", "timeout", "interrupted if self.debug_file: print("(set-info :status %s)" % result, file=self.debug_file) - print("(check-sat)", file=self.debug_file) + print(check_stmt, file=self.debug_file) self.debug_file.flush() if result not in expected: @@ -766,31 +896,28 @@ def check_sat(self, expected=["sat", "unsat", "unknown", "timeout", "interrupted return result def parse(self, stmt): - def worker(stmt): - if stmt[0] == '(': + def worker(stmt, cursor=0): + while stmt[cursor] in [" ", "\t", "\r", "\n"]: + cursor += 1 + + if stmt[cursor] == '(': expr = [] - cursor = 1 + cursor += 1 while stmt[cursor] != ')': - el, le = worker(stmt[cursor:]) + el, cursor = worker(stmt, cursor) expr.append(el) - cursor += le return expr, cursor+1 - if stmt[0] == '|': + if stmt[cursor] == '|': expr = "|" - cursor = 1 + cursor += 1 while stmt[cursor] != '|': expr += stmt[cursor] cursor += 1 expr += "|" return expr, cursor+1 - if stmt[0] in [" ", "\t", "\r", "\n"]: - el, le = worker(stmt[1:]) - return el, le+1 - expr = "" - cursor = 0 while stmt[cursor] not in ["(", ")", "|", " ", "\t", "\r", "\n"]: expr += stmt[cursor] cursor += 1 @@ -831,6 +958,48 @@ def bv2bin(self, v): def bv2int(self, v): return int(self.bv2bin(v), 2) + def get_raw_unsat_assumptions(self): + self.write("(get-unsat-assumptions)") + exprs = set(self.unparse(part) for part in self.parse(self.read())) + unsat_assumptions = [] + for key, value in self.smt2_assumptions.items(): + # normalize expression + value = self.unparse(self.parse(value)) + if value in exprs: + exprs.remove(value) + unsat_assumptions.append(key) + return unsat_assumptions + + def get_unsat_assumptions(self, minimize=False): + if not minimize: + return self.get_raw_unsat_assumptions() + required_assumptions = {} + + while True: + candidate_assumptions = {} + for key in self.get_raw_unsat_assumptions(): + if key not in required_assumptions: + candidate_assumptions[key] = self.smt2_assumptions[key] + + while candidate_assumptions: + + candidate_key, candidate_assume = candidate_assumptions.popitem() + + self.smt2_assumptions = {} + for key, assume in candidate_assumptions.items(): + self.smt2_assumptions[key] = assume + for key, assume in required_assumptions.items(): + self.smt2_assumptions[key] = assume + result = self.check_sat() + + if result == 'unsat': + candidate_assumptions = None + else: + required_assumptions[candidate_key] = candidate_assume + + if candidate_assumptions is not None: + return list(required_assumptions) + def get(self, expr): self.write("(get-value (%s))" % (expr)) return self.parse(self.read())[0][1] @@ -839,7 +1008,7 @@ def get_list(self, expr_list): if len(expr_list) == 0: return [] self.write("(get-value (%s))" % " ".join(expr_list)) - return [n[1] for n in self.parse(self.read())] + return [n[1] for n in self.parse(self.read()) if n] def get_path(self, mod, path): assert mod in self.modinfo @@ -863,6 +1032,8 @@ def net_expr(self, mod, base, path): assert mod in self.modinfo if path[0] == "": return base + if isinstance(path[0], int): + return "(|%s#%d| %s)" % (mod, path[0], base) if path[0] in self.modinfo[mod].cells: return "(|%s_h %s| %s)" % (mod, path[0], base) if path[0] in self.modinfo[mod].wsize: @@ -878,6 +1049,15 @@ def net_expr(self, mod, base, path): nextbase = "(|%s_h %s| %s)" % (mod, path[0], base) return self.net_expr(nextmod, nextbase, path[1:]) + def witness_net_expr(self, mod, base, witness): + net = self.net_expr(mod, base, witness["smtpath"]) + is_bool = self.net_width(mod, witness["smtpath"]) == 1 + if is_bool: + assert witness["width"] == 1 + assert witness["smtoffset"] == 0 + return net + return "((_ extract %d %d) %s)" % (witness["smtoffset"] + witness["width"] - 1, witness["smtoffset"], net) + def net_width(self, mod, net_path): for i in range(len(net_path)-1): assert mod in self.modinfo @@ -885,6 +1065,8 @@ def net_width(self, mod, net_path): mod = self.modinfo[mod].cells[net_path[i]] assert mod in self.modinfo + if isinstance(net_path[-1], int): + return None assert net_path[-1] in self.modinfo[mod].wsize return self.modinfo[mod].wsize[net_path[-1]] @@ -1010,7 +1192,7 @@ def handle(self, o, a): def helpmsg(self): return """ -s - set SMT solver: z3, yices, boolector, cvc4, mathsat, dummy + set SMT solver: z3, yices, boolector, bitwuzla, cvc4, mathsat, dummy default: yices -S @@ -1080,7 +1262,7 @@ def set_net(self, path, bits): def escape_name(self, name): name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name) - if re.match("[\[\]]", name) and name[0] != "\\": + if re.match(r"[\[\]]", name) and name[0] != "\\": name = "\\" + name return name diff --git a/backends/smt2/test_cells.sh b/backends/smt2/test_cells.sh index 34adb7af3c1..3f84d65a21a 100644 --- a/backends/smt2/test_cells.sh +++ b/backends/smt2/test_cells.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex diff --git a/backends/smt2/witness.py b/backends/smt2/witness.py new file mode 100644 index 00000000000..b7e25851cbb --- /dev/null +++ b/backends/smt2/witness.py @@ -0,0 +1,487 @@ +#!/usr/bin/env python3 +# +# yosys -- Yosys Open SYnthesis Suite +# +# Copyright (C) 2022 Jannis Harder +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +import os, sys, itertools, re +##yosys-sys-path## +import json +import click + +from ywio import ReadWitness, WriteWitness, WitnessSig, WitnessSigMap, WitnessValues, coalesce_signals + +@click.group() +def cli(): + pass + + +@cli.command(help=""" +Display a Yosys witness trace in a human readable format. +""") +@click.argument("input", type=click.File("r")) +@click.option("--skip-x", help="Treat x bits as unassigned.", is_flag=True) +def display(input, skip_x): + click.echo(f"Reading Yosys witness trace {input.name!r}...") + inyw = ReadWitness(input) + + if skip_x: + inyw.skip_x() + + def output(): + + yield click.style("*** RTLIL bit-order below may differ from source level declarations ***", fg="red") + if inyw.clocks: + yield click.style("=== Clock Signals ===", fg="blue") + for clock in inyw.clocks: + yield f" {clock['edge']} {WitnessSig(clock['path'], clock['offset']).pretty()}" + + for t, values in inyw.steps(): + if t: + yield click.style(f"=== Step {t} ===", fg="blue") + else: + yield click.style("=== Initial State ===", fg="blue") + + step_prefix = click.style(f"#{t}", fg="bright_black") + + signals, missing = values.present_signals(inyw.sigmap) + + assert not missing + + for sig in signals: + display_bits = values[sig].replace("?", click.style("?", fg="bright_black")) + yield f" {step_prefix} {sig.pretty()} = {display_bits}" + click.echo_via_pager([line + "\n" for line in output()]) + + +@cli.command(help=""" +Display statistics of a Yosys witness trace. +""") +@click.argument("input", type=click.File("r")) +def stats(input): + click.echo(f"Reading Yosys witness trace {input.name!r}...") + inyw = ReadWitness(input) + + total = 0 + + for t, values in inyw.steps(): + click.echo(f"{t:5}: {len(values.values):8} bits") + total += len(values.values) + + click.echo(f"total: {total:8} bits") + + +@cli.command(help=""" +Transform a Yosys witness trace. + +Currently no transformations are implemented, so it is only useful for testing. +If two or more inputs are provided they will be concatenated together into the output. +""") +@click.argument("inputs", type=click.File("r"), nargs=-1) +@click.argument("output", type=click.File("w")) +@click.option("--append", "-p", type=int, multiple=True, + help="Number of steps (+ve or -ve) to append to end of input trace. " + +"Can be defined multiple times, following the same order as input traces. ") +@click.option("--skip-x", help="Leave input x bits unassigned.", is_flag=True) +def yw2yw(inputs, output, append, skip_x): + if len(inputs) == 0: + raise click.ClickException(f"no inputs specified") + + outyw = WriteWitness(output, "yosys-witness yw2yw") + join_inputs = len(inputs) > 1 + inyws = {} + + if not append: + # default to 0 + append = [0] * len(inputs) + if len(append) != len(inputs): + print(f"Mismatch in number of --append values ({len(append)}) and input traces ({len(inputs)}).") + sys.exit(1) + + for (input, p) in zip(inputs, append): + if (join_inputs): + click.echo(f"Loading signals from yosys witness trace {input.name!r}...") + inyw = ReadWitness(input) + if p: + click.echo(f" appending {p} steps") + if (p + len(inyw) <= 0): + click.echo(f" skipping {input.name!r} (only {len(inyw)} steps to skip)") + continue + inyw.append_steps(p) + inyws[input] = inyw + for clock in inyw.clocks: + if clock not in outyw.clocks: + outyw.add_clock(clock["path"], clock["offset"], clock["edge"]) + + for sig in inyw.signals: + if sig not in outyw.signals: + outyw.add_sig(sig.path, sig.offset, sig.width, sig.init_only) + + init_values = sum([inyw.init_step() for inyw in inyws.values()], start=WitnessValues()) + + first_witness = True + for (input, inyw) in inyws.items(): + click.echo(f"Copying yosys witness trace from {input.name!r} to {output.name!r}...") + + if first_witness: + outyw.step(init_values, skip_x=skip_x) + else: + outyw.step(inyw.first_step(), skip_x=skip_x) + + for t, values in inyw.steps(1): + outyw.step(values, skip_x=skip_x) + + click.echo(f" copied {t + 1} time steps.") + first_witness = False + + outyw.end_trace() + + if join_inputs: + click.echo(f"Copied {outyw.t} total time steps.") + + +class AigerMap: + def __init__(self, mapfile): + data = json.load(mapfile) + + version = data.get("version") if isinstance(data, dict) else {} + if version != "Yosys Witness Aiger map": + raise click.ClickException(f"{mapfile.name}: unexpected file format version {version!r}") + + self.latch_count = data["latch_count"] + self.input_count = data["input_count"] + + self.clocks = data["clocks"] + + self.sigmap = WitnessSigMap() + self.init_inputs = set(init["input"] for init in data["inits"]) + + for bit in data["inputs"] + data["seqs"] + data["inits"]: + self.sigmap.add_bit((tuple(bit["path"]), bit["offset"]), bit["input"]) + + + +@cli.command(help=""" +Convert an AIGER witness trace into a Yosys witness trace. + +This requires a Yosys witness AIGER map file as generated by 'write_aiger -ywmap'. +""") +@click.argument("input", type=click.File("r")) +@click.argument("mapfile", type=click.File("r")) +@click.argument("output", type=click.File("w")) +@click.option("--skip-x", help="Leave input x bits unassigned.", is_flag=True) +@click.option("--present-only", help="Only include bits present in at least one time step.", is_flag=True) +def aiw2yw(input, mapfile, output, skip_x, present_only): + input_name = input.name + click.echo(f"Converting AIGER witness trace {input_name!r} to Yosys witness trace {output.name!r}...") + click.echo(f"Using Yosys witness AIGER map file {mapfile.name!r}") + aiger_map = AigerMap(mapfile) + + header_lines = list(itertools.islice(input, 0, 2)) + + if len(header_lines) == 2 and header_lines[1][0] in ".bcjf": + status = header_lines[0].strip() + if status == "0": + raise click.ClickException(f"{input_name}: file contains no trace, the AIGER status is unsat") + elif status == "2": + raise click.ClickException(f"{input_name}: file contains no trace, the AIGER status is sat") + elif status != "1": + raise click.ClickException(f"{input_name}: unexpected data in AIGER witness file") + else: + input = itertools.chain(header_lines, input) + + ffline = next(input, None) + if ffline is None: + raise click.ClickException(f"{input_name}: unexpected end of file") + ffline = ffline.strip() + if not re.match(r'[01x]*$', ffline): + raise click.ClickException(f"{input_name}: unexpected data in AIGER witness file") + if not re.match(r'[0]*$', ffline): + raise click.ClickException(f"{input_name}: non-default initial state not supported") + + if not present_only: + outyw = WriteWitness(output, "yosys-witness aiw2yw") + + for clock in aiger_map.clocks: + outyw.add_clock(clock["path"], clock["offset"], clock["edge"]) + + for (path, offset), id in aiger_map.sigmap.bit_to_id.items(): + outyw.add_sig(path, offset, init_only=id in aiger_map.init_inputs) + + missing = set() + seen = set() + + buffered_steps = [] + + skip = "x?" if skip_x else "?" + + t = -1 + while True: + inline = next(input, None) + if inline is None: + click.echo(f"Warning: {input_name}: file may be incomplete") + break + inline = inline.strip() + if inline in [".", "# DONE"]: + break + if inline.startswith("#"): + continue + + t += 1 + + if not re.match(r"[01x]*$", inline): + raise click.ClickException(f"{input_name}: unexpected data in AIGER witness file") + + if len(inline) != aiger_map.input_count: + raise click.ClickException( + f"{input_name}: {mapfile.name}: number of inputs does not match, " + f"{len(inline)} in witness, {aiger_map.input_count} in map file" + ) + + values = WitnessValues() + for i, v in enumerate(inline): + if v in skip or (t > 0 and i in aiger_map.init_inputs): + continue + + try: + bit = aiger_map.sigmap.id_to_bit[i] + except IndexError: + bit = None + if bit is None: + missing.add(i) + elif present_only: + seen.add(i) + + values[bit] = v + + if present_only: + buffered_steps.append(values) + else: + outyw.step(values) + + if present_only: + outyw = WriteWitness(output, "yosys-witness aiw2yw") + + for clock in aiger_map.clocks: + outyw.add_clock(clock["path"], clock["offset"], clock["edge"]) + + for (path, offset), id in aiger_map.sigmap.bit_to_id.items(): + if id in seen: + outyw.add_sig(path, offset, init_only=id in aiger_map.init_inputs) + + for values in buffered_steps: + outyw.step(values) + + outyw.end_trace() + + if missing: + click.echo("The following AIGER inputs belong to unknown signals:") + click.echo(" " + " ".join(str(id) for id in sorted(missing))) + + click.echo(f"Converted {outyw.t} time steps.") + +@cli.command(help=""" +Convert a Yosys witness trace into an AIGER witness trace. + +This requires a Yosys witness AIGER map file as generated by 'write_aiger -ywmap'. +""") +@click.argument("input", type=click.File("r")) +@click.argument("mapfile", type=click.File("r")) +@click.argument("output", type=click.File("w")) +def yw2aiw(input, mapfile, output): + click.echo(f"Converting Yosys witness trace {input.name!r} to AIGER witness trace {output.name!r}...") + click.echo(f"Using Yosys witness AIGER map file {mapfile.name!r}") + aiger_map = AigerMap(mapfile) + inyw = ReadWitness(input) + + print("1", file=output) + print("b0", file=output) + # TODO the b0 status isn't really accurate, but we don't have any better info here + print("0" * aiger_map.latch_count, file=output) + + all_missing = set() + + for t, step in inyw.steps(): + bits, missing = step.pack_present(aiger_map.sigmap) + bits = bits[::-1].replace('?', 'x') + all_missing.update(missing) + bits += 'x' * (aiger_map.input_count - len(bits)) + print(bits, file=output) + + print(".", file=output) + + if all_missing: + click.echo("The following signals are missing in the AIGER map file and will be dropped:") + for sig in coalesce_signals(WitnessSig(*bit) for bit in all_missing): + click.echo(" " + sig.pretty()) + + + click.echo(f"Converted {len(inyw)} time steps.") + +class BtorMap: + def __init__(self, mapfile): + self.data = data = json.load(mapfile) + + version = data.get("version") if isinstance(data, dict) else {} + if version != "Yosys Witness BTOR map": + raise click.ClickException(f"{mapfile.name}: unexpected file format version {version!r}") + + self.sigmap = WitnessSigMap() + + for mode in ("states", "inputs"): + for btor_signal_def in data[mode]: + if btor_signal_def is None: + continue + if isinstance(btor_signal_def, dict): + btor_signal_def["path"] = tuple(btor_signal_def["path"]) + else: + for chunk in btor_signal_def: + chunk["path"] = tuple(chunk["path"]) + + +@cli.command(help=""" +Convert a BTOR witness trace into a Yosys witness trace. + +This requires a Yosys witness BTOR map file as generated by 'write_btor -ywmap'. +""") +@click.argument("input", type=click.File("r")) +@click.argument("mapfile", type=click.File("r")) +@click.argument("output", type=click.File("w")) +def wit2yw(input, mapfile, output): + input_name = input.name + click.echo(f"Converting BTOR witness trace {input_name!r} to Yosys witness trace {output.name!r}...") + click.echo(f"Using Yosys witness BTOR map file {mapfile.name!r}") + btor_map = BtorMap(mapfile) + + input = filter(None, (line.split(';', 1)[0].strip() for line in input)) + + sat = next(input, None) + if sat != "sat": + raise click.ClickException(f"{input_name}: not a BTOR witness file") + + props = next(input, None) + + t = -1 + + line = next(input, None) + + frames = [] + bits = {} + + while line and not line.startswith('.'): + current_t = int(line[1:].strip()) + if line[0] == '#': + mode = "states" + elif line[0] == '@': + mode = "inputs" + else: + raise click.ClickException(f"{input_name}: unexpected data in BTOR witness file") + + if current_t > t: + t = current_t + values = WitnessValues() + array_inits = set() + frames.append(values) + + line = next(input, None) + while line and line[0] not in "#@.": + if current_t > 0 and mode == "states": + line = next(input, None) + continue + tokens = line.split() + line = next(input, None) + + btor_sig = btor_map.data[mode][int(tokens[0])] + btor_sigs = [btor_sig] + + if btor_sig is None: + continue + + if isinstance(btor_sig, dict): + addr = tokens[1] + if not addr.startswith('['): + addr = '[*]' + value = tokens[1] + else: + value = tokens[2] + if not addr.endswith(']'): + raise click.ClickException(f"{input_name}: expected address in BTOR witness file") + path = btor_sig["path"] + width = btor_sig["width"] + size = btor_sig["size"] + if addr == '[*]': + btor_sigs = [ + [{ + "path": (*path, f"\\[{addr}]"), + "width": width, + "offset": 0, + }] + for addr in range(size) + if (path, addr) not in array_inits + ] + array_inits.update((path, addr) for addr in range(size)) + else: + addr = int(addr[1:-1], 2) + + if addr < 0 or addr >= size: + raise click.ClickException(f"{input_name}: out of bounds address in BTOR witness file") + + array_inits.add((path, addr)) + btor_sig = [{ + "path": (*path, f"\\[{addr}]"), + "width": width, + "offset": 0, + }] + btor_sigs = [btor_sig] + else: + value = tokens[1] + + for btor_sig in btor_sigs: + value_bits = iter(reversed(value)) + + for chunk in btor_sig: + offset = chunk["offset"] + path = chunk["path"] + for i in range(offset, offset + chunk["width"]): + key = (path, i) + bits[key] = mode == "inputs" + values[key] = next(value_bits) + + if next(value_bits, None) is not None: + raise click.ClickException(f"{input_name}: excess bits in BTOR witness file") + + + if line is None: + raise click.ClickException(f"{input_name}: unexpected end of BTOR witness file") + if line.strip() != '.': + raise click.ClickException(f"{input_name}: unexpected data in BTOR witness file") + if next(input, None) is not None: + raise click.ClickException(f"{input_name}: unexpected trailing data in BTOR witness file") + + outyw = WriteWitness(output, 'yosys-witness wit2yw') + + outyw.signals = coalesce_signals((), bits) + for clock in btor_map.data["clocks"]: + outyw.add_clock(clock["path"], clock["offset"], clock["edge"]) + + for values in frames: + outyw.step(values) + + outyw.end_trace() + click.echo(f"Converted {outyw.t} time steps.") + +if __name__ == "__main__": + cli() diff --git a/backends/smt2/ywio.py b/backends/smt2/ywio.py new file mode 100644 index 00000000000..023a2d351b3 --- /dev/null +++ b/backends/smt2/ywio.py @@ -0,0 +1,432 @@ +# +# yosys -- Yosys Open SYnthesis Suite +# +# Copyright (C) 2022 Jannis Harder +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +import json, re + +from functools import total_ordering + + +class PrettyJson: + def __init__(self, f): + self.f = f + self.indent = 0 + self.state = ["value"] + + def line(self): + indent = len(self.state) - bool(self.state and self.state[-1] == "value") + print("\n", end=" " * (2 * indent), file=self.f) + + def raw(self, str): + print(end=str, file=self.f) + + def begin_object(self): + self.begin_value() + self.raw("{") + self.state.append("object_first") + + def begin_array(self): + self.begin_value() + self.raw("[") + self.state.append("array_first") + + def end_object(self): + state = self.state.pop() + if state == "object": + self.line() + else: + assert state == "object_first" + self.raw("}") + self.end_value() + + def end_array(self): + state = self.state.pop() + if state == "array": + self.line() + else: + assert state == "array_first" + self.raw("]") + self.end_value() + + def name(self, name): + if self.state[-1] == "object_first": + self.state[-1] = "object" + else: + self.raw(",") + self.line() + json.dump(str(name), self.f) + self.raw(": ") + self.state.append("value") + + def begin_value(self): + if self.state[-1] == "array_first": + self.line() + self.state[-1] = "array" + elif self.state[-1] == "array": + self.raw(",") + self.line() + else: + assert self.state.pop() == "value" + + def end_value(self): + if not self.state: + print(file=self.f, flush=True) + + def value(self, value): + self.begin_value() + json.dump(value, self.f) + self.end_value() + + def entry(self, name, value): + self.name(name) + self.value(value) + + def object(self, entries=None): + if isinstance(entries, dict): + entries = dict.items() + self.begin_object() + for name, value in entries: + self.entry(name, value) + self.end_object() + + def array(self, values=None): + self.begin_array() + for value in values: + self.value(value) + self.end_array() + + +addr_re = re.compile(r'\\\[[0-9]+\]$') +public_name_re = re.compile(r"\\([a-zA-Z_][a-zA-Z0-9_]*(\[[0-9]+\])?|\[[0-9]+\])$") + +def pretty_name(id): + if public_name_re.match(id): + return id.lstrip("\\") + else: + return id + +def pretty_path(path): + out = "" + for name in path: + name = pretty_name(name) + if name.startswith("["): + out += name + continue + if out: + out += "." + if name.startswith("\\") or name.startswith("$"): + out += name + " " + else: + out += name + + return out + +@total_ordering +class WitnessSig: + def __init__(self, path, offset, width=1, init_only=False): + path = tuple(path) + self.path, self.width, self.offset, self.init_only = path, width, offset, init_only + + self.memory_path = None + self.memory_addr = None + + sort_path = path + sort_id = -1 + if path and addr_re.match(path[-1]): + self.memory_path = sort_path = path[:-1] + self.memory_addr = sort_id = int(path[-1][2:-1]) + + self.sort_key = (init_only, sort_path, sort_id, offset, width) + + def bits(self): + return ((self.path, i) for i in range(self.offset, self.offset + self.width)) + + def rev_bits(self): + return ((self.path, i) for i in reversed(range(self.offset, self.offset + self.width))) + + def pretty(self): + if self.width > 1: + last_offset = self.offset + self.width - 1 + return f"{pretty_path(self.path)}[{last_offset}:{self.offset}]" + else: + return f"{pretty_path(self.path)}[{self.offset}]" + + def __eq__(self, other): + return self.sort_key == other.sort_key + + def __hash__(self): + return hash(self.sort_key) + + def __lt__(self, other): + return self.sort_key < other.sort_key + + +def coalesce_signals(signals, bits=None): + if bits is None: + bits = {} + for sig in signals: + for bit in sig.bits(): + if sig.init_only: + bits.setdefault(bit, False) + else: + bits[bit] = True + + active = None + + out = [] + + for bit, not_init in sorted(bits.items()): + if active: + if active[0] == bit[0] and active[2] == bit[1] and active[3] == not_init: + active[2] += 1 + else: + out.append(WitnessSig(active[0], active[1], active[2] - active[1], not active[3])) + active = None + + if active is None: + active = [bit[0], bit[1], bit[1] + 1, not_init] + + if active: + out.append(WitnessSig(active[0], active[1], active[2] - active[1], not active[3])) + + return sorted(out) + + +class WitnessSigMap: + def __init__(self, signals=[]): + self.signals = [] + + self.id_to_bit = [] + self.bit_to_id = {} + self.bit_to_sig = {} + + for sig in signals: + self.add_signal(sig) + + def add_signal(self, sig): + self.signals.append(sig) + for bit in sig.bits(): + self.add_bit(bit) + self.bit_to_sig[bit] = sig + + def add_bit(self, bit, id=None): + if id is None: + id = len(self.id_to_bit) + self.id_to_bit.append(bit) + else: + if len(self.id_to_bit) <= id: + self.id_to_bit += [None] * (id - len(self.id_to_bit) + 1) + self.id_to_bit[id] = bit + self.bit_to_id[bit] = id + + +class WitnessValues: + def __init__(self): + self.values = {} + + def __setitem__(self, key, value): + if isinstance(key, tuple) and len(key) == 2: + if value != "?": + assert isinstance(value, str) + assert len(value) == 1 + self.values[key] = value + else: + assert isinstance(key, WitnessSig) + assert key.width == len(value) + if isinstance(value, str): + value = reversed(value) + for bit, bit_value in zip(key.bits(), value): + if bit_value != "?": + self.values[bit] = bit_value + + def __getitem__(self, key): + if isinstance(key, tuple) and len(key) == 2: + return self.values.get(key, "?") + else: + assert isinstance(key, WitnessSig) + return "".join([self.values.get(bit, "?") for bit in key.rev_bits()]) + + def pack_present(self, sigmap): + missing = [] + + max_id = max((sigmap.bit_to_id.get(bit, -1) for bit in self.values), default=-1) + + vector = ["?"] * (max_id + 1) + for bit, bit_value in self.values.items(): + id = sigmap.bit_to_id.get(bit, - 1) + if id < 0: + missing.append(bit) + else: + vector[max_id - sigmap.bit_to_id[bit]] = bit_value + + return "".join(vector), missing + + def pack(self, sigmap): + packed, missing = self.pack_present(sigmap) + if missing: + raise RuntimeError(f"Cannot pack bits {missing!r}") + return packed + + def unpack(self, sigmap, bits): + for i, bit_value in enumerate(reversed(bits)): + if bit_value != "?": + self.values[sigmap.id_to_bit[i]] = bit_value + + def present_signals(self, sigmap): + signals = set(sigmap.bit_to_sig.get(bit) for bit in self.values) + missing_signals = None in signals + if missing_signals: + signals.discard(None) + + return sorted(signals), missing_signals + + def __add__(self, other: "WitnessValues"): + new = WitnessValues() + new += self + new += other + return new + + def __iadd__(self, other: "WitnessValues"): + for key, value in other.values.items(): + self.values.setdefault(key, value) + return self + +class WriteWitness: + def __init__(self, f, generator): + self.out = PrettyJson(f) + self.t = 0 + self.header_written = False + self.clocks = [] + self.signals = [] + + self.out.begin_object() + self.out.entry("format", "Yosys Witness Trace") + self.out.entry("generator", generator) + + def add_clock(self, path, offset, edge): + assert not self.header_written + self.clocks.append({ + "path": path, + "edge": edge, + "offset": offset, + }) + + def add_sig(self, path, offset, width=1, init_only=False): + assert not self.header_written + sig = WitnessSig(path, offset, width, init_only) + self.signals.append(sig) + return sig + + def write_header(self): + assert not self.header_written + self.header_written = True + self.out.name("clocks") + self.out.array(self.clocks) + + self.signals = coalesce_signals(self.signals) + self.sigmap = WitnessSigMap(self.signals) + + self.out.name("signals") + self.out.array({ + "path": sig.path, + "width": sig.width, + "offset": sig.offset, + "init_only": sig.init_only, + } for sig in self.signals) + + self.out.name("steps") + self.out.begin_array() + + def step(self, values, skip_x=False): + if not self.header_written: + self.write_header() + + packed = values.pack(self.sigmap) + if skip_x: + packed = packed.replace('x', '?') + self.out.value({"bits": packed}) + + self.t += 1 + + def end_trace(self): + if not self.header_written: + self.write_header() + self.out.end_array() + self.out.end_object() + + +class ReadWitness: + def __init__(self, f): + data = json.load(f) + if not isinstance(data, dict): + data = {} + + data_format = data.get("format", "Unknown Format") + + if data_format != "Yosys Witness Trace": + raise ValueError(f"unsupported format {data_format!r}") + + self.clocks = data["clocks"] + for clock in self.clocks: + clock["path"] = tuple(clock["path"]) + + self.signals = [ + WitnessSig(sig["path"], sig["offset"], sig["width"], sig["init_only"]) + for sig in data["signals"] + ] + + self.sigmap = WitnessSigMap(self.signals) + + self.bits = [step["bits"] for step in data["steps"]] + + def skip_x(self): + self.bits = [step.replace('x', '?') for step in self.bits] + + def init_step(self): + return self.step(0) + + def non_init_bits(self): + if len(self) > 1: + return len(self.bits[1]) + else: + return sum([sig.width for sig in self.signals if not sig.init_only]) + + def first_step(self): + values = WitnessValues() + # may have issues when non_init_bits is 0 + values.unpack(WitnessSigMap([sig for sig in self.signals if not sig.init_only]), self.bits[0][-self.non_init_bits():]) + return values + + def step(self, t): + values = WitnessValues() + values.unpack(self.sigmap, self.bits[t]) + return values + + def steps(self, start=0): + for i in range(start, len(self.bits)): + yield i, self.step(i) + + def append_steps(self, t): + if not t: + pass + elif t < 0: + self.bits = self.bits[:t] + else: + self.bits.extend(["0"*self.non_init_bits()]*t) + + def __len__(self): + return len(self.bits) diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index 4e5c6050db6..44e20038441 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -573,8 +573,25 @@ struct SmvWorker continue; } - if (cell->type[0] == '$') - log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell)); + if (cell->type == ID($scopeinfo)) + continue; + + if (cell->type[0] == '$') { + if (cell->type.in(ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)) || cell->type.str().substr(0, 6) == "$_SDFF" || (cell->type.str().substr(0, 6) == "$_DFFE" && cell->type.str().size() == 10)) { + log_error("Unsupported cell type %s for cell %s.%s -- please run `dffunmap` before `write_smv`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($dffsr), ID($dffsre)) || cell->type.str().substr(0, 5) == "$_DFF" || cell->type.str().substr(0, 7) == "$_ALDFF") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `async2sync; dffunmap` or `clk2fflogic` before `write_smv`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + if (cell->type.in(ID($sr), ID($dlatch), ID($adlatch), ID($dlatchsr)) || cell->type.str().substr(0, 8) == "$_DLATCH" || cell->type.str().substr(0, 5) == "$_SR_") { + log_error("Unsupported cell type %s for cell %s.%s -- please run `clk2fflogic` before `write_smv`.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } + log_error("Unsupported cell type %s for cell %s.%s.\n", + log_id(cell->type), log_id(module), log_id(cell)); + } // f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type)); @@ -727,6 +744,12 @@ struct SmvBackend : public Backend { log_header(design, "Executing SMV backend.\n"); + log_push(); + Pass::call(design, "bmuxmap"); + Pass::call(design, "demuxmap"); + Pass::call(design, "bwmuxmap"); + log_pop(); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/backends/smv/test_cells.sh b/backends/smv/test_cells.sh index 145b9c33b92..1347b70446b 100644 --- a/backends/smv/test_cells.sh +++ b/backends/smv/test_cells.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -ex diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index aa20f106aee..1160a01a188 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -64,7 +64,7 @@ static void print_spice_net(std::ostream &f, RTLIL::SigBit s, std::string &neg, } } -static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, std::string &neg, std::string &pos, std::string &ncpf, bool big_endian, bool use_inames) +static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, std::string &neg, std::string &pos, std::string &buf, std::string &ncpf, bool big_endian, bool use_inames) { SigMap sigmap(module); idict inums; @@ -72,6 +72,9 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De for (auto cell : module->cells()) { + if (cell->type == ID($scopeinfo)) + continue; + f << stringf("X%d", cell_counter++); std::vector port_sigs; @@ -121,10 +124,10 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De for (auto &conn : module->connections()) for (int i = 0; i < conn.first.size(); i++) { - f << stringf("V%d", conn_counter++); - print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); + f << (buf == "DC" ? stringf("V%d", conn_counter++) : stringf("X%d", cell_counter++)); print_spice_net(f, conn.second.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); - f << stringf(" DC 0\n"); + print_spice_net(f, conn.first.extract(i, 1), neg, pos, ncpf, nc_counter, use_inames, inums); + f << (buf == "DC" ? " DC 0\n" : stringf(" %s\n", buf.c_str())); } } @@ -148,6 +151,10 @@ struct SpiceBackend : public Backend { log(" -pos net_name\n"); log(" set the net name for constant 1 (default: Vdd)\n"); log("\n"); + log(" -buf DC|subckt_name\n"); + log(" set the name for jumper element (default: DC)\n"); + log(" (used to connect different nets)\n"); + log("\n"); log(" -nc_prefix\n"); log(" prefix for not-connected nets (default: _NC)\n"); log("\n"); @@ -164,7 +171,7 @@ struct SpiceBackend : public Backend { std::string top_module_name; RTLIL::Module *top_module = NULL; bool big_endian = false, use_inames = false; - std::string neg = "Vss", pos = "Vdd", ncpf = "_NC"; + std::string neg = "Vss", pos = "Vdd", ncpf = "_NC", buf = "DC"; log_header(design, "Executing SPICE backend.\n"); @@ -187,6 +194,10 @@ struct SpiceBackend : public Backend { pos = args[++argidx]; continue; } + if (args[argidx] == "-buf" && argidx+1 < args.size()) { + buf = args[++argidx]; + continue; + } if (args[argidx] == "-nc_prefix" && argidx+1 < args.size()) { ncpf = args[++argidx]; continue; @@ -241,14 +252,14 @@ struct SpiceBackend : public Backend { *f << stringf(" %s", spice_id2str(wire->name).c_str()); } *f << stringf("\n"); - print_spice_module(*f, module, design, neg, pos, ncpf, big_endian, use_inames); + print_spice_module(*f, module, design, neg, pos, buf, ncpf, big_endian, use_inames); *f << stringf(".ENDS %s\n\n", spice_id2str(module->name).c_str()); } if (!top_module_name.empty()) { if (top_module == NULL) log_error("Can't find top module `%s'!\n", top_module_name.c_str()); - print_spice_module(*f, top_module, design, neg, pos, ncpf, big_endian, use_inames); + print_spice_module(*f, top_module, design, neg, pos, buf, ncpf, big_endian, use_inames); *f << stringf("\n"); } diff --git a/backends/table/table.cc b/backends/table/table.cc index 77642ccbdf2..2bf64e7b1c6 100644 --- a/backends/table/table.cc +++ b/backends/table/table.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 9523f4a526c..05b7c6c4008 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -27,6 +27,7 @@ #include "kernel/sigtools.h" #include "kernel/ff.h" #include "kernel/mem.h" +#include "kernel/fmt.h" #include #include #include @@ -35,15 +36,16 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog; +bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog, simple_lhs, noparallelcase; int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; -std::map auto_name_map; +dict auto_name_map; std::set reg_wires; std::string auto_prefix, extmem_prefix; RTLIL::Module *active_module; dict active_initdata; SigMap active_sigmap; +IdString initial_id; void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) { @@ -128,7 +130,7 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) break; } - const pool keywords = { + static const pool keywords = { // IEEE 1800-2017 Annex B "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", @@ -357,7 +359,8 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) { if (GetSize(sig) == 0) { - f << "\"\""; + // See IEEE 1364-2005 Clause 5.1.14. + f << "{0{1'b0}}"; return; } if (sig.is_chunk()) { @@ -373,7 +376,7 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } } -void dump_attributes(std::ostream &f, std::string indent, dict &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false) +void dump_attributes(std::ostream &f, std::string indent, dict &attributes, std::string term = "\n", bool modattr = false, bool regattr = false, bool as_comment = false) { if (noattr) return; @@ -389,13 +392,13 @@ void dump_attributes(std::ostream &f, std::string indent, dictsecond, -1, 0, false, as_comment); - f << stringf(" %s%c", as_comment ? "*/" : "*)", term); + f << stringf(" %s%s", as_comment ? "*/" : "*)", term.c_str()); } } void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) { - dump_attributes(f, indent, wire->attributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); + dump_attributes(f, indent, wire->attributes, "\n", /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); #if 0 if (wire->port_input && !wire->port_output) f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); @@ -430,7 +433,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) dump_const(f, wire->attributes.at(ID::init)); } f << stringf(";\n"); - } else if (!wire->port_input && !wire->port_output) + } else f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); #endif } @@ -504,9 +507,24 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) int start = init.addr.as_int(); for (int i=0; i expressions within that clock domain dict> clk_to_lof_body; + dict clk_to_arst_cond; + dict> clk_to_arst_body; clk_to_lof_body[""] = std::vector(); std::string clk_domain_str; // create a list of reg declarations @@ -529,10 +549,27 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) std::ostringstream os; dump_sigspec(os, port.clk); clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str()); - if( clk_to_lof_body.count(clk_domain_str) == 0 ) - clk_to_lof_body[clk_domain_str] = std::vector(); + if (port.arst != State::S0) { + std::ostringstream os2; + dump_sigspec(os2, port.arst); + clk_domain_str += stringf(", posedge %s", os2.str().c_str()); + clk_to_arst_cond[clk_domain_str] = os2.str(); + } } - if (!port.transparent) + + // Decide how to represent the transparency; same idea as Mem::extract_rdff. + bool trans_use_addr = true; + for (auto bit : port.transparency_mask) + if (!bit) + trans_use_addr = false; + + if (GetSize(mem.wr_ports) == 0) + trans_use_addr = false; + + if (port.en != State::S1 || port.srst != State::S0 || port.arst != State::S0 || !port.init_value.is_fully_undef()) + trans_use_addr = false; + + if (!trans_use_addr) { // for clocked read ports make something like: // reg [..] temp_id; @@ -541,19 +578,148 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) // assign r_data = temp_id; std::string temp_id = next_auto_id(); lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.data.size() - 1, temp_id.c_str()) ); - { + + bool has_indent = false; + + if (port.arst != State::S0) { std::ostringstream os; - if (port.en != RTLIL::SigBit(true)) - { - os << stringf("if ("); - dump_sigspec(os, port.en); - os << stringf(") "); + os << stringf("%s <= ", temp_id.c_str()); + dump_sigspec(os, port.arst_value); + os << ";\n"; + clk_to_arst_body[clk_domain_str].push_back(os.str()); + } + + if (port.srst != State::S0 && !port.ce_over_srst) { + std::ostringstream os; + os << stringf("if ("); + dump_sigspec(os, port.srst); + os << stringf(")\n"); + clk_to_lof_body[clk_domain_str].push_back(os.str()); + std::ostringstream os2; + os2 << stringf("%s" "%s <= ", indent.c_str(), temp_id.c_str()); + dump_sigspec(os2, port.srst_value); + os2 << ";\n"; + clk_to_lof_body[clk_domain_str].push_back(os2.str()); + std::ostringstream os3; + if (port.en == State::S1) { + os3 << "else begin\n"; + } else { + os3 << "else if ("; + dump_sigspec(os3, port.en); + os3 << ") begin\n"; } - os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str()); - dump_sigspec(os, port.addr); + clk_to_lof_body[clk_domain_str].push_back(os3.str()); + has_indent = true; + } else if (port.en != State::S1) { + std::ostringstream os; + os << stringf("if ("); + dump_sigspec(os, port.en); + os << stringf(") begin\n"); + clk_to_lof_body[clk_domain_str].push_back(os.str()); + has_indent = true; + } + + for (int sub = 0; sub < (1 << port.wide_log2); sub++) + { + SigSpec addr = port.sub_addr(sub); + std::ostringstream os; + if (has_indent) + os << indent; + os << temp_id; + if (port.wide_log2) + os << stringf("[%d:%d]", (sub + 1) * mem.width - 1, sub * mem.width); + os << stringf(" <= %s[", mem_id.c_str()); + dump_sigspec(os, addr); os << stringf("];\n"); clk_to_lof_body[clk_domain_str].push_back(os.str()); } + + for (int i = 0; i < GetSize(mem.wr_ports); i++) { + auto &wport = mem.wr_ports[i]; + if (!port.transparency_mask[i] && !port.collision_x_mask[i]) + continue; + int min_wide_log2 = std::min(port.wide_log2, wport.wide_log2); + int max_wide_log2 = std::max(port.wide_log2, wport.wide_log2); + bool wide_write = wport.wide_log2 > port.wide_log2; + for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) { + SigSpec raddr = port.addr; + SigSpec waddr = wport.addr; + if (wide_write) + waddr = wport.sub_addr(sub); + else + raddr = port.sub_addr(sub); + int pos = 0; + int ewidth = mem.width << min_wide_log2; + int wsub = wide_write ? sub : 0; + int rsub = wide_write ? 0 : sub; + while (pos < ewidth) { + int epos = pos; + while (epos < ewidth && wport.en[epos + wsub * mem.width] == wport.en[pos + wsub * mem.width]) + epos++; + + std::ostringstream os; + if (has_indent) + os << indent; + os << "if ("; + dump_sigspec(os, wport.en[pos + wsub * mem.width]); + if (raddr != waddr) { + os << " && "; + dump_sigspec(os, raddr); + os << " == "; + dump_sigspec(os, waddr); + } + os << ")\n"; + clk_to_lof_body[clk_domain_str].push_back(os.str()); + + std::ostringstream os2; + if (has_indent) + os2 << indent; + os2 << indent; + os2 << temp_id; + if (epos-pos != GetSize(port.data)) + os2 << stringf("[%d:%d]", rsub * mem.width + epos-1, rsub * mem.width + pos); + os2 << " <= "; + if (port.transparency_mask[i]) + dump_sigspec(os2, wport.data.extract(wsub * mem.width + pos, epos-pos)); + else + dump_sigspec(os2, Const(State::Sx, epos - pos)); + os2 << ";\n"; + clk_to_lof_body[clk_domain_str].push_back(os2.str()); + + pos = epos; + } + } + } + + if (port.srst != State::S0 && port.ce_over_srst) + { + std::ostringstream os; + if (has_indent) + os << indent; + os << stringf("if ("); + dump_sigspec(os, port.srst); + os << stringf(")\n"); + clk_to_lof_body[clk_domain_str].push_back(os.str()); + std::ostringstream os2; + if (has_indent) + os2 << indent; + os2 << stringf("%s" "%s <= ", indent.c_str(), temp_id.c_str()); + dump_sigspec(os2, port.srst_value); + os2 << ";\n"; + clk_to_lof_body[clk_domain_str].push_back(os2.str()); + } + + if (has_indent) + clk_to_lof_body[clk_domain_str].push_back("end\n"); + + if (!port.init_value.is_fully_undef()) + { + std::ostringstream os; + dump_sigspec(os, port.init_value); + std::string line = stringf("initial %s = %s;\n", temp_id.c_str(), os.str().c_str()); + clk_to_lof_body[""].push_back(line); + } + { std::ostringstream os; dump_sigspec(os, port.data); @@ -569,73 +735,153 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) // temp_id <= r_addr; // assign r_data = array_reg[temp_id]; std::string temp_id = next_auto_id(); - lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1, temp_id.c_str()) ); + lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1 - port.wide_log2, temp_id.c_str()) ); { std::ostringstream os; - dump_sigspec(os, port.addr); + dump_sigspec(os, port.addr.extract_end(port.wide_log2)); std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str()); clk_to_lof_body[clk_domain_str].push_back(line); } + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { std::ostringstream os; - dump_sigspec(os, port.data); - std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str()); - clk_to_lof_body[""].push_back(line); + os << "assign "; + dump_sigspec(os, port.data.extract(sub * mem.width, mem.width)); + os << stringf(" = %s[", mem_id.c_str());; + if (port.wide_log2) { + Const addr_lo; + for (int i = 0; i < port.wide_log2; i++) + addr_lo.bits.push_back(State(sub >> i & 1)); + os << "{"; + os << temp_id; + os << ", "; + dump_const(os, addr_lo); + os << "}"; + } else { + os << temp_id; + } + os << "];\n"; + clk_to_lof_body[""].push_back(os.str()); } } } else { // for non-clocked read-ports make something like: // assign r_data = array_reg[r_addr]; - std::ostringstream os, os2; - dump_sigspec(os, port.data); - dump_sigspec(os2, port.addr); - std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str()); - clk_to_lof_body[""].push_back(line); + for (int sub = 0; sub < (1 << port.wide_log2); sub++) + { + SigSpec addr = port.sub_addr(sub); + + std::ostringstream os, os2; + dump_sigspec(os, port.data.extract(sub * mem.width, mem.width)); + dump_sigspec(os2, addr); + std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str()); + clk_to_lof_body[""].push_back(line); + } } } - // write ports - for (auto &port : mem.wr_ports) + // Write ports. Those are messy because we try to preserve priority, as much as we can: + // + // 1. We split all ports into several disjoint processes. + // 2. If a port has priority over another port, the two ports need to share + // a process, so that priority can be reconstructed on the other end. + // 3. We want each process to be as small as possible, to avoid extra + // priorities inferred on the other end. + pool wr_ports_done; + for (int ridx = 0; ridx < GetSize(mem.wr_ports); ridx++) { + if (wr_ports_done.count(ridx)) + continue; + + auto &root = mem.wr_ports[ridx]; + + // Start from a root. + pool wr_ports_now; + wr_ports_now.insert(ridx); + + // Transitively fill list of ports in this process by following priority edges. + while (true) { - std::ostringstream os; - dump_sigspec(os, port.clk); - clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "pos" : "neg", os.str().c_str()); - if( clk_to_lof_body.count(clk_domain_str) == 0 ) - clk_to_lof_body[clk_domain_str] = std::vector(); + bool changed = false; + + for (int i = 0; i < GetSize(mem.wr_ports); i++) + for (int j = 0; j < i; j++) + if (mem.wr_ports[i].priority_mask[j]) + { + if (wr_ports_now.count(i) && !wr_ports_now.count(j)) { + wr_ports_now.insert(j); + changed = true; + } + if (!wr_ports_now.count(i) && wr_ports_now.count(j)) { + wr_ports_now.insert(i); + changed = true; + } + } + + if (!changed) + break; } - // make something like: - // always @(posedge clk) - // if (wr_en_bit) memid[w_addr][??] <= w_data[??]; - // ... - for (int i = 0; i < GetSize(port.en); i++) - { - int start_i = i, width = 1; - SigBit wen_bit = port.en[i]; - while (i+1 < GetSize(port.en) && active_sigmap(port.en[i+1]) == active_sigmap(wen_bit)) - i++, width++; + if (root.clk_enable) { + f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", root.clk_polarity ? "pos" : "neg"); + dump_sigspec(f, root.clk); + f << ") begin\n"; + } else { + f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_latch" : " @*"); + } - if (wen_bit == State::S0) + for (int pidx = 0; pidx < GetSize(mem.wr_ports); pidx++) + { + if (!wr_ports_now.count(pidx)) continue; + wr_ports_done.insert(pidx); + + auto &port = mem.wr_ports[pidx]; + log_assert(port.clk_enable == root.clk_enable); + if (port.clk_enable) { + log_assert(port.clk == root.clk); + log_assert(port.clk_polarity == root.clk_polarity); + } - std::ostringstream os; - if (wen_bit != State::S1) + // make something like: + // always @(posedge clk) + // if (wr_en_bit) memid[w_addr][??] <= w_data[??]; + // ... + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { - os << stringf("if ("); - dump_sigspec(os, wen_bit); - os << stringf(") "); + SigSpec addr = port.sub_addr(sub); + for (int i = 0; i < mem.width; i++) + { + int start_i = i, width = 1; + SigBit wen_bit = port.en[sub * mem.width + i]; + + while (i+1 < mem.width && active_sigmap(port.en[sub * mem.width + i+1]) == active_sigmap(wen_bit)) + i++, width++; + + if (wen_bit == State::S0) + continue; + + f << stringf("%s%s", indent.c_str(), indent.c_str()); + if (wen_bit != State::S1) + { + f << stringf("if ("); + dump_sigspec(f, wen_bit); + f << stringf(")\n"); + f << stringf("%s%s%s", indent.c_str(), indent.c_str(), indent.c_str()); + } + f << stringf("%s[", mem_id.c_str()); + dump_sigspec(f, addr); + if (width == GetSize(port.en)) + f << stringf("] <= "); + else + f << stringf("][%d:%d] <= ", i, start_i); + dump_sigspec(f, port.data.extract(sub * mem.width + start_i, width)); + f << stringf(";\n"); + } } - os << stringf("%s[", mem_id.c_str()); - dump_sigspec(os, port.addr); - if (width == GetSize(port.en)) - os << stringf("] <= "); - else - os << stringf("][%d:%d] <= ", i, start_i); - dump_sigspec(os, port.data.extract(start_i, width)); - os << stringf(";\n"); - clk_to_lof_body[clk_domain_str].push_back(os.str()); } + + f << stringf("%s" "end\n", indent.c_str()); } // Output Verilog that looks something like this: // reg [..] _3_; @@ -668,8 +914,19 @@ void dump_memory(std::ostream &f, std::string indent, Mem &mem) if( clk_domain != "") { f << stringf("%s" "always%s @(%s) begin\n", indent.c_str(), systemverilog ? "_ff" : "", clk_domain.c_str()); - for(auto &line : lof_lines) - f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str()); + bool has_arst = clk_to_arst_cond.count(clk_domain) != 0; + if (has_arst) { + f << stringf("%s%s" "if (%s) begin\n", indent.c_str(), indent.c_str(), clk_to_arst_cond[clk_domain].c_str()); + for(auto &line : clk_to_arst_body[clk_domain]) + f << stringf("%s%s%s" "%s", indent.c_str(), indent.c_str(), indent.c_str(), line.c_str()); + f << stringf("%s%s" "end else begin\n", indent.c_str(), indent.c_str()); + for(auto &line : lof_lines) + f << stringf("%s%s%s" "%s", indent.c_str(), indent.c_str(), indent.c_str(), line.c_str()); + f << stringf("%s%s" "end\n", indent.c_str(), indent.c_str()); + } else { + for(auto &line : lof_lines) + f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str()); + } f << stringf("%s" "end\n", indent.c_str()); } else @@ -732,7 +989,7 @@ void dump_cell_expr_uniop(std::ostream &f, std::string indent, RTLIL::Cell *cell f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = %s ", op.c_str()); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "A", true); f << stringf(";\n"); } @@ -744,11 +1001,63 @@ void dump_cell_expr_binop(std::ostream &f, std::string indent, RTLIL::Cell *cell f << stringf(" = "); dump_cell_expr_port(f, cell, "A", true); f << stringf(" %s ", op.c_str()); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "B", true); f << stringf(";\n"); } +void dump_cell_expr_print(std::ostream &f, std::string indent, const RTLIL::Cell *cell) +{ + Fmt fmt; + fmt.parse_rtlil(cell); + std::vector args = fmt.emit_verilog(); + + f << stringf("%s" "$write(", indent.c_str()); + bool first = true; + for (auto &arg : args) { + if (first) { + first = false; + } else { + f << ", "; + } + switch (arg.type) { + case VerilogFmtArg::STRING: + dump_const(f, RTLIL::Const(arg.str)); + break; + case VerilogFmtArg::INTEGER: + f << (arg.signed_ ? "$signed(" : "$unsigned("); + dump_sigspec(f, arg.sig); + f << ")"; + break; + case VerilogFmtArg::TIME: + if (arg.realtime) + f << "$realtime"; + else + f << "$time"; + break; + default: log_abort(); + } + } + f << stringf(");\n"); +} + +void dump_cell_expr_check(std::ostream &f, std::string indent, const RTLIL::Cell *cell) +{ + std::string flavor = cell->getParam(ID(FLAVOR)).decode_string(); + if (flavor == "assert") + f << stringf("%s" "assert (", indent.c_str()); + else if (flavor == "assume") + f << stringf("%s" "assume (", indent.c_str()); + else if (flavor == "live") + f << stringf("%s" "assert (eventually ", indent.c_str()); + else if (flavor == "fair") + f << stringf("%s" "assume (eventually ", indent.c_str()); + else if (flavor == "cover") + f << stringf("%s" "cover (", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(");\n"); +} + bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) { if (cell->type == ID($_NOT_)) { @@ -756,7 +1065,16 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = "); f << stringf("~"); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); + dump_cell_expr_port(f, cell, "A", false); + f << stringf(";\n"); + return true; + } + + if (cell->type == ID($_BUF_)) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = "); dump_cell_expr_port(f, cell, "A", false); f << stringf(";\n"); return true; @@ -776,7 +1094,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("|"); if (cell->type.in(ID($_XOR_), ID($_XNOR_))) f << stringf("^"); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); f << stringf(" "); if (cell->type.in(ID($_ANDNOT_), ID($_ORNOT_))) f << stringf("~("); @@ -793,7 +1111,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(" = "); dump_cell_expr_port(f, cell, "S", false); f << stringf(" ? "); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "B", false); f << stringf(" : "); dump_cell_expr_port(f, cell, "A", false); @@ -807,7 +1125,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(" = !("); dump_cell_expr_port(f, cell, "S", false); f << stringf(" ? "); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "B", false); f << stringf(" : "); dump_cell_expr_port(f, cell, "A", false); @@ -823,7 +1141,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(cell->type == ID($_AOI3_) ? " & " : " | "); dump_cell_expr_port(f, cell, "B", false); f << stringf(cell->type == ID($_AOI3_) ? ") |" : ") &"); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); f << stringf(" "); dump_cell_expr_port(f, cell, "C", false); f << stringf(");\n"); @@ -838,7 +1156,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); dump_cell_expr_port(f, cell, "B", false); f << stringf(cell->type == ID($_AOI4_) ? ") |" : ") &"); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); f << stringf(" ("); dump_cell_expr_port(f, cell, "C", false); f << stringf(cell->type == ID($_AOI4_) ? " & " : " | "); @@ -940,7 +1258,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort(ID::Y)); f << stringf(" = $signed(%s) / ", buf_num.c_str()); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); f << stringf("$signed(%s);\n", buf_b.c_str()); return true; } else { @@ -953,7 +1271,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (cell->type == ID($modfloor)) { // wire truncated = $signed(A) % $signed(B); - // assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated); + // assign Y = (A[-1] == B[-1]) || truncated == 0 ? $signed(truncated) : $signed(B) + $signed(truncated); if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) { SigSpec sig_a = cell->getPort(ID::A); @@ -963,7 +1281,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str()); dump_cell_expr_port(f, cell, "A", true); f << stringf(" %% "); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); dump_cell_expr_port(f, cell, "B", true); f << stringf(";\n"); @@ -973,7 +1291,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, sig_a.extract(sig_a.size()-1)); f << stringf(" == "); dump_sigspec(f, sig_b.extract(sig_b.size()-1)); - f << stringf(") || %s == 0 ? %s : ", temp_id.c_str(), temp_id.c_str()); + f << stringf(") || %s == 0 ? $signed(%s) : ", temp_id.c_str(), temp_id.c_str()); dump_cell_expr_port(f, cell, "B", true); f << stringf(" + $signed(%s);\n", temp_id.c_str()); return true; @@ -1038,7 +1356,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(" = "); dump_sigspec(f, cell->getPort(ID::S)); f << stringf(" ? "); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); dump_sigspec(f, cell->getPort(ID::B)); f << stringf(" : "); dump_sigspec(f, cell->getPort(ID::A)); @@ -1058,24 +1376,38 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1); dump_attributes(f, indent + " ", cell->attributes); - if (!noattr) - f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); - f << stringf("%s" " casez (s)", indent.c_str()); - f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); + if (noparallelcase) + f << stringf("%s" " case (s)\n", indent.c_str()); + else { + if (!noattr) + f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); + f << stringf("%s" " casez (s)", indent.c_str()); + f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); + } for (int i = 0; i < s_width; i++) { f << stringf("%s" " %d'b", indent.c_str(), s_width); for (int j = s_width-1; j >= 0; j--) - f << stringf("%c", j == i ? '1' : '?'); + f << stringf("%c", j == i ? '1' : noparallelcase ? '0' : '?'); f << stringf(":\n"); f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width); } - f << stringf("%s" " default:\n", indent.c_str()); + if (noparallelcase) { + f << stringf("%s" " %d'b", indent.c_str(), s_width); + for (int j = s_width-1; j >= 0; j--) + f << '0'; + f << stringf(":\n"); + } else + f << stringf("%s" " default:\n", indent.c_str()); f << stringf("%s" " %s = a;\n", indent.c_str(), func_name.c_str()); + if (noparallelcase) { + f << stringf("%s" " default:\n", indent.c_str()); + f << stringf("%s" " %s = %d'bx;\n", indent.c_str(), func_name.c_str(), width); + } f << stringf("%s" " endcase\n", indent.c_str()); f << stringf("%s" "endfunction\n", indent.c_str()); @@ -1133,7 +1465,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(" = "); dump_const(f, cell->parameters.at(ID::LUT)); f << stringf(" >> "); - dump_attributes(f, "", cell->attributes, ' '); + dump_attributes(f, "", cell->attributes, " "); dump_sigspec(f, cell->getPort(ID::A)); f << stringf(";\n"); return true; @@ -1144,7 +1476,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) FfData ff(nullptr, cell); // $ff / $_FF_ cell: not supported. - if (ff.has_d && !ff.has_clk && !ff.has_en) + if (ff.has_gclk) return false; std::string reg_name = cellname(cell); @@ -1165,17 +1497,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) for (int i = 0; i < chunks; i++) { - SigSpec sig_d; + SigSpec sig_d, sig_ad; Const val_arst, val_srst; - std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name; + std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name, sig_aload_name; if (chunky) { reg_bit_name = stringf("%s[%d]", reg_name.c_str(), i); - if (ff.has_d) + if (ff.has_gclk || ff.has_clk) sig_d = ff.sig_d[i]; + if (ff.has_aload) + sig_ad = ff.sig_ad[i]; } else { reg_bit_name = reg_name; - if (ff.has_d) - sig_d = ff.sig_d; + sig_d = ff.sig_d; + sig_ad = ff.sig_ad; } if (ff.has_arst) val_arst = chunky ? ff.val_arst[i] : ff.val_arst; @@ -1183,28 +1517,38 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) val_srst = chunky ? ff.val_srst[i] : ff.val_srst; // If there are constants in the sensitivity list, replace them with an intermediate wire - if (ff.has_sr) { - if (ff.sig_set[i].wire == NULL) - { - sig_set_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str()); - dump_const(f, ff.sig_set[i].data); - f << stringf(";\n"); - } - if (ff.sig_clr[i].wire == NULL) - { - sig_clr_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str()); - dump_const(f, ff.sig_clr[i].data); - f << stringf(";\n"); - } - } else if (ff.has_arst) { - if (ff.sig_arst[i].wire == NULL) - { - sig_arst_name = next_auto_id(); - f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str()); - dump_const(f, ff.sig_arst[i].data); - f << stringf(";\n"); + if (ff.has_clk) { + if (ff.has_sr) { + if (ff.sig_set[i].wire == NULL) + { + sig_set_name = next_auto_id(); + f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str()); + dump_const(f, ff.sig_set[i].data); + f << stringf(";\n"); + } + if (ff.sig_clr[i].wire == NULL) + { + sig_clr_name = next_auto_id(); + f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str()); + dump_const(f, ff.sig_clr[i].data); + f << stringf(";\n"); + } + } else if (ff.has_arst) { + if (ff.sig_arst[0].wire == NULL) + { + sig_arst_name = next_auto_id(); + f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str()); + dump_const(f, ff.sig_arst[0].data); + f << stringf(";\n"); + } + } else if (ff.has_aload) { + if (ff.sig_aload[0].wire == NULL) + { + sig_aload_name = next_auto_id(); + f << stringf("%s" "wire %s = ", indent.c_str(), sig_aload_name.c_str()); + dump_const(f, ff.sig_aload[0].data); + f << stringf(";\n"); + } } } @@ -1226,13 +1570,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s", sig_clr_name.c_str()); else dump_sigspec(f, ff.sig_clr[i]); - } else if (ff.has_arst) { f << stringf(", %sedge ", ff.pol_arst ? "pos" : "neg"); - if (ff.sig_arst[i].wire == NULL) + if (ff.sig_arst[0].wire == NULL) f << stringf("%s", sig_arst_name.c_str()); else dump_sigspec(f, ff.sig_arst); + } else if (ff.has_aload) { + f << stringf(", %sedge ", ff.pol_aload ? "pos" : "neg"); + if (ff.sig_aload[0].wire == NULL) + f << stringf("%s", sig_aload_name.c_str()); + else + dump_sigspec(f, ff.sig_aload); } f << stringf(")\n"); @@ -1253,7 +1602,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " else ", indent.c_str()); } else if (ff.has_arst) { f << stringf("if (%s", ff.pol_arst ? "" : "!"); - if (ff.sig_arst[i].wire == NULL) + if (ff.sig_arst[0].wire == NULL) f << stringf("%s", sig_arst_name.c_str()); else dump_sigspec(f, ff.sig_arst); @@ -1261,11 +1610,21 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, val_arst); f << stringf(";\n"); f << stringf("%s" " else ", indent.c_str()); + } else if (ff.has_aload) { + f << stringf("if (%s", ff.pol_aload ? "" : "!"); + if (ff.sig_aload[0].wire == NULL) + f << stringf("%s", sig_aload_name.c_str()); + else + dump_sigspec(f, ff.sig_aload); + f << stringf(") %s <= ", reg_bit_name.c_str()); + dump_sigspec(f, sig_ad); + f << stringf(";\n"); + f << stringf("%s" " else ", indent.c_str()); } - if (ff.has_srst && ff.has_en && ff.ce_over_srst) { - f << stringf("if (%s", ff.pol_en ? "" : "!"); - dump_sigspec(f, ff.sig_en); + if (ff.has_srst && ff.has_ce && ff.ce_over_srst) { + f << stringf("if (%s", ff.pol_ce ? "" : "!"); + dump_sigspec(f, ff.sig_ce); f << stringf(")\n"); f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!"); dump_sigspec(f, ff.sig_srst); @@ -1282,9 +1641,9 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(";\n"); f << stringf("%s" " else ", indent.c_str()); } - if (ff.has_en) { - f << stringf("if (%s", ff.pol_en ? "" : "!"); - dump_sigspec(f, ff.sig_en); + if (ff.has_ce) { + f << stringf("if (%s", ff.pol_ce ? "" : "!"); + dump_sigspec(f, ff.sig_ce); f << stringf(") "); } } @@ -1306,7 +1665,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!"); dump_sigspec(f, ff.sig_set[i]); f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str()); - if (ff.has_d) + if (ff.has_aload) f << stringf("%s" " else ", indent.c_str()); } else if (ff.has_arst) { f << stringf("if (%s", ff.pol_arst ? "" : "!"); @@ -1314,14 +1673,14 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf(") %s = ", reg_bit_name.c_str()); dump_sigspec(f, val_arst); f << stringf(";\n"); - if (ff.has_d) + if (ff.has_aload) f << stringf("%s" " else ", indent.c_str()); } - if (ff.has_d) { - f << stringf("if (%s", ff.pol_en ? "" : "!"); - dump_sigspec(f, ff.sig_en); + if (ff.has_aload) { + f << stringf("if (%s", ff.pol_aload ? "" : "!"); + dump_sigspec(f, ff.sig_aload); f << stringf(") %s = ", reg_bit_name.c_str()); - dump_sigspec(f, sig_d); + dump_sigspec(f, sig_ad); f << stringf(";\n"); } } @@ -1456,6 +1815,55 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == ID($print)) + { + // Sync $print cells are accumulated and handled in dump_module. + if (cell->getParam(ID::TRG_ENABLE).as_bool()) + return true; + + f << stringf("%s" "always @*\n", indent.c_str()); + + f << stringf("%s" " if (", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::EN)); + f << stringf(")\n"); + + dump_cell_expr_print(f, indent + " ", cell); + return true; + } + + if (cell->type == ID($check)) + { + // Sync $check cells are accumulated and handled in dump_module. + if (cell->getParam(ID::TRG_ENABLE).as_bool()) + return true; + + f << stringf("%s" "always @*\n", indent.c_str()); + + f << stringf("%s" " if (", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::EN)); + f << stringf(") begin\n"); + + std::string flavor = cell->getParam(ID::FLAVOR).decode_string(); + if (flavor == "assert" || flavor == "assume") { + Fmt fmt; + fmt.parse_rtlil(cell); + if (!fmt.parts.empty()) { + f << stringf("%s" " if (!", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(")\n"); + dump_cell_expr_print(f, indent + " ", cell); + } + } else { + f << stringf("%s" " /* message omitted */\n", indent.c_str()); + } + + dump_cell_expr_check(f, indent + " ", cell); + + f << stringf("%s" " end\n", indent.c_str()); + + return true; + } + // FIXME: $fsm return false; @@ -1463,8 +1871,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) { + // To keep the output compatible with other tools we ignore $scopeinfo + // cells that exist only to hold metadata. If in the future that metadata + // should be exposed as part of the write_verilog output it should be + // opt-in and/or represented as something else than a $scopeinfo cell. + if (cell->type == ID($scopeinfo)) + return; + // Handled by dump_memory - if (cell->type.in(ID($mem), ID($memwr), ID($memrd), ID($meminit))) + if (cell->is_mem_cell()) return; if (cell->type[0] == '$' && !noexpr) { @@ -1481,7 +1896,8 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (it != cell->parameters.begin()) f << stringf(","); f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); - dump_const(f, it->second); + if (it->second.size() > 0) + dump_const(f, it->second); f << stringf(")"); } f << stringf("\n%s" ")", indent.c_str()); @@ -1544,24 +1960,83 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) } } +void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec &trg, const RTLIL::Const &polarity, std::vector &cells) +{ + if (trg.size() == 0) { + f << stringf("%s" "initial begin\n", indent.c_str()); + } else { + f << stringf("%s" "always @(", indent.c_str()); + for (int i = 0; i < trg.size(); i++) { + if (i != 0) + f << " or "; + if (polarity[i]) + f << "posedge "; + else + f << "negedge "; + dump_sigspec(f, trg[i]); + } + f << ") begin\n"; + } + + std::sort(cells.begin(), cells.end(), [](const RTLIL::Cell *a, const RTLIL::Cell *b) { + return a->getParam(ID::PRIORITY).as_int() > b->getParam(ID::PRIORITY).as_int(); + }); + for (auto cell : cells) { + f << stringf("%s" " if (", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::EN)); + f << stringf(") begin\n"); + + if (cell->type == ID($print)) { + dump_cell_expr_print(f, indent + " ", cell); + } else if (cell->type == ID($check)) { + std::string flavor = cell->getParam(ID::FLAVOR).decode_string(); + if (flavor == "assert" || flavor == "assume") { + Fmt fmt; + fmt.parse_rtlil(cell); + if (!fmt.parts.empty()) { + f << stringf("%s" " if (!", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::A)); + f << stringf(")\n"); + dump_cell_expr_print(f, indent + " ", cell); + } + } else { + f << stringf("%s" " /* message omitted */\n", indent.c_str()); + } + + dump_cell_expr_check(f, indent + " ", cell); + } + + f << stringf("%s" " end\n", indent.c_str()); + } + + f << stringf("%s" "end\n", indent.c_str()); +} + void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - f << stringf("%s" "assign ", indent.c_str()); - dump_sigspec(f, left); - f << stringf(" = "); - dump_sigspec(f, right); - f << stringf(";\n"); + if (simple_lhs) { + int offset = 0; + for (auto &chunk : left.chunks()) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, chunk); + f << stringf(" = "); + dump_sigspec(f, right.extract(offset, GetSize(chunk))); + f << stringf(";\n"); + offset += GetSize(chunk); + } + } else { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, left); + f << stringf(" = "); + dump_sigspec(f, right); + f << stringf(";\n"); + } } void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw); -void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false) +void dump_case_actions(std::ostream &f, std::string indent, RTLIL::CaseRule *cs) { - int number_of_stmts = cs->switches.size() + cs->actions.size(); - - if (!omit_trailing_begin && number_of_stmts >= 2) - f << stringf("%s" "begin\n", indent.c_str()); - for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { if (it->first.size() == 0) continue; @@ -1571,7 +2046,64 @@ void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bo dump_sigspec(f, it->second); f << stringf(";\n"); } +} + +bool dump_proc_switch_ifelse(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw) +{ + for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { + if ((*it)->compare.size() == 0) { + break; + } else if ((*it)->compare.size() == 1) { + int case_index = it - sw->cases.begin(); + SigSpec compare = (*it)->compare.at(0); + if (case_index >= compare.size()) + return false; + if (compare[case_index] != State::S1) + return false; + for (int bit_index = 0; bit_index < compare.size(); bit_index++) + if (bit_index != case_index && compare[bit_index] != State::Sa) + return false; + } else { + return false; + } + } + + dump_attributes(f, indent, sw->attributes); + f << indent; + auto sig_it = sw->signal.begin(); + for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it, ++sig_it) { + if (it != sw->cases.begin()) { + if ((*it)->compare.empty()) + f << " else begin\n"; + else + f << " else "; + } + if (!(*it)->compare.empty()) { + f << stringf("if ("); + dump_sigspec(f, *sig_it); + f << stringf(") begin\n"); + } + + dump_case_actions(f, indent, (*it)); + for (auto it2 = (*it)->switches.begin(); it2 != (*it)->switches.end(); ++it2) + dump_proc_switch(f, indent + " ", *it2); + f << indent << "end"; + if ((*it)->compare.empty()) + break; + } + f << "\n"; + return true; +} + +void dump_case_body(std::ostream &f, std::string indent, RTLIL::CaseRule *cs, bool omit_trailing_begin = false) +{ + int number_of_stmts = cs->switches.size() + cs->actions.size(); + + if (!omit_trailing_begin && number_of_stmts >= 2) + f << stringf("%s" "begin\n", indent.c_str()); + + dump_case_actions(f, indent, cs); for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) dump_proc_switch(f, indent + " ", *it); @@ -1594,17 +2126,18 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw return; } + if (dump_proc_switch_ifelse(f, indent, sw)) + return; + dump_attributes(f, indent, sw->attributes); f << stringf("%s" "casez (", indent.c_str()); dump_sigspec(f, sw->signal); f << stringf(")\n"); - bool got_default = false; for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { - dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true); + bool got_default = false; + dump_attributes(f, indent + " ", (*it)->attributes, "\n", /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true); if ((*it)->compare.size() == 0) { - if (got_default) - continue; f << stringf("%s default", indent.c_str()); got_default = true; } else { @@ -1617,6 +2150,19 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw } f << stringf(":\n"); dump_case_body(f, indent + " ", *it); + + if (got_default) { + // If we followed up the default with more cases the Verilog + // semantics would be to match those *before* the default, but + // the RTLIL semantics are to match those *after* the default + // (so they can never be selected). Exit now. + break; + } + } + + if (sw->cases.empty()) { + // Verilog does not allow empty cases. + f << stringf("%s default: ;\n", indent.c_str()); } f << stringf("%s" "endcase\n", indent.c_str()); @@ -1650,7 +2196,7 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*"); if (!systemverilog) - f << indent + " " << "if (" << id("\\initial") << ") begin end\n"; + f << indent + " " << "if (" << id(initial_id) << ") begin end\n"; dump_case_body(f, indent, &proc->root_case, true); std::string backup_indent = indent; @@ -1713,6 +2259,8 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) { + std::map, std::vector> sync_effect_cells; + reg_wires.clear(); reset_auto_counter(module); active_module = module; @@ -1743,6 +2291,11 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) std::set> reg_bits; for (auto cell : module->cells()) { + if (cell->type.in(ID($print), ID($check)) && cell->getParam(ID::TRG_ENABLE).as_bool()) { + sync_effect_cells[make_pair(cell->getPort(ID::TRG), cell->getParam(ID::TRG_POLARITY))].push_back(cell); + continue; + } + if (!RTLIL::builtin_ff_cell_types().count(cell->type) || !cell->hasPort(ID::Q) || cell->type.in(ID($ff), ID($_FF_))) continue; @@ -1766,9 +2319,10 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } } - dump_attributes(f, indent, module->attributes, '\n', /*modattr=*/true); + dump_attributes(f, indent, module->attributes, "\n", /*modattr=*/true); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); bool keep_running = true; + int cnt = 0; for (int port_id = 1; keep_running; port_id++) { keep_running = false; for (auto wire : module->wires()) { @@ -1777,14 +2331,16 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) f << stringf(", "); f << stringf("%s", id(wire->name).c_str()); keep_running = true; + if (cnt==20) { f << stringf("\n"); cnt = 0; } else cnt++; continue; } } } f << stringf(");\n"); - - if (!systemverilog && !module->processes.empty()) - f << indent + " " << "reg " << id("\\initial") << " = 0;\n"; + if (!systemverilog && !module->processes.empty()) { + initial_id = NEW_ID; + f << indent + " " << "reg " << id(initial_id) << " = 0;\n"; + } for (auto w : module->wires()) dump_wire(f, indent + " ", w); @@ -1795,6 +2351,9 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) for (auto cell : module->cells()) dump_cell(f, indent + " ", cell); + for (auto &it : sync_effect_cells) + dump_sync_effect(f, indent + " ", it.first.first, it.first.second, it.second); + for (auto it = module->processes.begin(); it != module->processes.end(); ++it) dump_process(f, indent + " ", it->second); @@ -1838,6 +2397,11 @@ struct VerilogBackend : public Backend { log(" without this option all internal cells are converted to Verilog\n"); log(" expressions.\n"); log("\n"); + log(" -noparallelcase\n"); + log(" With this option no parallel_case attributes are used. Instead, a case\n"); + log(" statement that assigns don't-care values for priority dependent inputs\n"); + log(" is generated.\n"); + log("\n"); log(" -siminit\n"); log(" add initial statements with hierarchical refs to initialize FFs when\n"); log(" in -noexpr mode.\n"); @@ -1861,6 +2425,10 @@ struct VerilogBackend : public Backend { log(" deactivates this feature and instead will write string constants\n"); log(" as binary numbers.\n"); log("\n"); + log(" -simple-lhs\n"); + log(" Connection assignments with simple left hand side without\n"); + log(" concatenations.\n"); + log("\n"); log(" -extmem\n"); log(" instead of initializing memories using assignments to individual\n"); log(" elements, use the '$readmemh' function to read initialization data\n"); @@ -1908,6 +2476,8 @@ struct VerilogBackend : public Backend { defparam = false; decimal = false; siminit = false; + simple_lhs = false; + noparallelcase = false; auto_prefix = ""; bool blackboxes = false; @@ -1943,6 +2513,10 @@ struct VerilogBackend : public Backend { noexpr = true; continue; } + if (arg == "-noparallelcase") { + noparallelcase = true; + continue; + } if (arg == "-nodec") { nodec = true; continue; @@ -1980,6 +2554,10 @@ struct VerilogBackend : public Backend { selected = true; continue; } + if (arg == "-simple-lhs") { + simple_lhs = true; + continue; + } if (arg == "-v") { verbose = true; continue; @@ -1994,6 +2572,14 @@ struct VerilogBackend : public Backend { extmem_prefix = filename.substr(0, filename.rfind('.')); } + log_push(); + if (!noexpr) { + Pass::call(design, "bmuxmap"); + Pass::call(design, "demuxmap"); + } + Pass::call(design, "clean_zerowidth"); + log_pop(); + design->sort(); *f << stringf("/* Generated by %s */\n", yosys_version_str); diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000000..d7bfd8e956a --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,9 @@ +/build/ +/source/cmd +/source/temp +/source/_images/**/*.log +/source/_images/**/*.aux +/source/_images/**/*.pdf +/source/_images/**/*.svg +/source/_images/**/*.dot +/source/_images/code_examples diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000000..a5b2fed6b7d --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,255 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " epub3 to make an epub3" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " dummy to check syntax errors of document sources" + +.PHONY: clean +clean: clean-examples + rm -rf $(BUILDDIR)/* + rm -rf source/cmd util/__pycache__ + $(MAKE) -C source/_images clean + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +# singlehtml section links are broken +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SymbiYosys.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SymbiYosys.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/SymbiYosys" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SymbiYosys" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +.PHONY: dummy +dummy: + $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy + @echo + @echo "Build finished. Dummy builder generates no files." + +PYTHON ?= python3 + +.PHONY: test test-examples test-macros examples +test: test-examples test-macros + +FORCE: +Makefile-%: FORCE + $(MAKE) -C $(@D) $(*F) + +CODE_EXAMPLES := $(wildcard source/code_examples/*/Makefile) +TEST_EXAMPLES := $(addsuffix -all,$(CODE_EXAMPLES)) +CLEAN_EXAMPLES := $(addsuffix -clean,$(CODE_EXAMPLES)) +test-examples: $(TEST_EXAMPLES) +clean-examples: $(CLEAN_EXAMPLES) +examples: $(TEST_EXAMPLES) + +test-macros: + $(PYTHON) tests/macro_commands.py + +.PHONY: images +images: + $(MAKE) -C source/_images + +.PHONY: reqs +reqs: + $(PYTHON) -m pip install -r source/requirements.txt diff --git a/docs/source/_downloads/APPNOTE_010_Verilog_to_BLIF.pdf b/docs/source/_downloads/APPNOTE_010_Verilog_to_BLIF.pdf new file mode 100644 index 00000000000..91fc1aac8e8 Binary files /dev/null and b/docs/source/_downloads/APPNOTE_010_Verilog_to_BLIF.pdf differ diff --git a/docs/source/_downloads/APPNOTE_012_Verilog_to_BTOR.pdf b/docs/source/_downloads/APPNOTE_012_Verilog_to_BTOR.pdf new file mode 100644 index 00000000000..f3ffd6f308d Binary files /dev/null and b/docs/source/_downloads/APPNOTE_012_Verilog_to_BTOR.pdf differ diff --git a/docs/source/_images/Makefile b/docs/source/_images/Makefile new file mode 100644 index 00000000000..955805f9c58 --- /dev/null +++ b/docs/source/_images/Makefile @@ -0,0 +1,46 @@ +all: examples all_tex tidy + +# set a fake time in pdf generation to prevent unnecessary differences in output +FAKETIME := TZ='Z' faketime -f '2022-01-01 00:00:00 x0,001' + +# find all code example makefiles +.PHONY: examples +CODE_EXAMPLES := ../code_examples/*/Makefile +examples: $(CODE_EXAMPLES) + +# target to convert specified dot file(s) +.PHONY: convert +TARG_DOT ?= +convert: $(TARG_DOT:.dot=.pdf) $(TARG_DOT:.dot=.svg) + +# use empty FORCE target because .PHONY ignores % expansion, using find allows +# us to generate everything in one pass, since we don't know all of the possible +# outputs until the sub-makes run +FORCE: +../%/Makefile: FORCE + @make -C $(@D) dots + @mkdir -p $* + @find $(@D) -name *.dot -exec cp -u {} -t $* \; + @find $* -name *.dot -printf "%p " | xargs -i make --no-print-directory convert TARG_DOT="{}" + +# find and build all tex files +.PHONY: all_tex +TEX_FILES := $(wildcard **/*.tex) +all_tex: $(TEX_FILES:.tex=.pdf) $(TEX_FILES:.tex=.svg) + +%.pdf: %.dot + $(FAKETIME) dot -Tpdf -o $@ $< + +%.pdf: %.tex + cd $(@D) && $(FAKETIME) pdflatex $(] (cursor) -- node[above] {Low-Level} ++(3,0); + +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/internals/overview_flow.tex b/docs/source/_images/internals/overview_flow.tex new file mode 100644 index 00000000000..ac0afde5fda --- /dev/null +++ b/docs/source/_images/internals/overview_flow.tex @@ -0,0 +1,37 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\usetikzlibrary{shapes.geometric} +\pagestyle{empty} + +\begin{document} +\begin{tikzpicture} + \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=15em] + \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=15em] + \node[process] (vlog) {Verilog Frontend}; + \node[process, dashed, fill=green!5] (vhdl) [right of=vlog] {VHDL Frontend}; + \node[process] (ilang) [right of=vhdl] {RTLIL Frontend}; + \node[data] (ast) [below of=vlog, node distance=5em, xshift=7.5em] {AST}; + \node[process] (astfe) [below of=ast, node distance=5em] {AST Frontend}; + \node[data] (rtlil) [below of=astfe, node distance=5em, xshift=7.5em] {RTLIL}; + \node[process] (pass) [right of=rtlil, node distance=5em, xshift=7.5em] {Passes}; + \node[process] (vlbe) [below of=rtlil, node distance=7em, xshift=-13em] {Verilog Backend}; + \node[process] (ilangbe) [below of=rtlil, node distance=7em, xshift=0em] {RTLIL Backend}; + \node[process, dashed, fill=green!5] (otherbe) [below of=rtlil, node distance=7em, xshift=+13em] {Other Backends}; + + \draw[-latex] (vlog) -- (ast); + \draw[-latex] (vhdl) -- (ast); + \draw[-latex] (ast) -- (astfe); + \draw[-latex] (astfe) -- (rtlil); + \draw[-latex] (ilang) -- (rtlil); + \draw[latex-latex] (rtlil) -- (pass); + \draw[-latex] (rtlil) -- (vlbe); + \draw[-latex] (rtlil) -- (ilangbe); + \draw[-latex] (rtlil) -- (otherbe); +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/internals/overview_rtlil.tex b/docs/source/_images/internals/overview_rtlil.tex new file mode 100644 index 00000000000..ddacbff00c8 --- /dev/null +++ b/docs/source/_images/internals/overview_rtlil.tex @@ -0,0 +1,27 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\pagestyle{empty} + +\begin{document} +\begin{tikzpicture} + \tikzstyle{entity} = [draw, fill=gray!10, rectangle, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}] + \node[entity] (design) {RTLIL::Design}; + \node[entity] (module) [right of=design, node distance=11em] {RTLIL::Module} edge [-latex] node[above] {\tiny 1 \hskip3em N} (design); + + \node[entity] (process) [fill=green!10, right of=module, node distance=10em] {RTLIL::Process} (process.west) edge [-latex] (module); + \node[entity] (memory) [fill=red!10, below of=process] {RTLIL::Memory} edge [-latex] (module); + \node[entity] (wire) [fill=blue!10, above of=process] {RTLIL::Wire} (wire.west) edge [-latex] (module); + \node[entity] (cell) [fill=blue!10, above of=wire] {RTLIL::Cell} (cell.west) edge [-latex] (module); + + \node[entity] (case) [fill=green!10, right of=process, node distance=10em] {RTLIL::CaseRule} edge [latex-latex] (process); + \node[entity] (sync) [fill=green!10, above of=case] {RTLIL::SyncRule} edge [-latex] (process); + \node[entity] (switch) [fill=green!10, below of=case] {RTLIL::SwitchRule} edge [-latex] (case); + \draw[latex-] (switch.east) -- ++(1em,0) |- (case.east); +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/internals/simplified_rtlil.tex b/docs/source/_images/internals/simplified_rtlil.tex new file mode 100644 index 00000000000..9dc0a7d4c7a --- /dev/null +++ b/docs/source/_images/internals/simplified_rtlil.tex @@ -0,0 +1,20 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\pagestyle{empty} + +\begin{document} +\begin{tikzpicture}[every node/.style={transform shape}] + \tikzstyle{entity} = [draw, fill=gray!10, rectangle, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}] + \node[entity] (design) {RTLIL::Design}; + \node[entity] (module) [right of=design, node distance=11em] {RTLIL::Module} edge [-latex] node[above] {\tiny 1 \hskip3em N} (design); + + \node[entity] (wire) [fill=blue!10, right of=module, node distance=10em] {RTLIL::Wire} (wire.west) edge [-latex] (module); + \node[entity] (cell) [fill=blue!10, above of=wire] {RTLIL::Cell} (cell.west) edge [-latex] (module); +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/internals/verilog_flow.tex b/docs/source/_images/internals/verilog_flow.tex new file mode 100644 index 00000000000..d3e269d0d1f --- /dev/null +++ b/docs/source/_images/internals/verilog_flow.tex @@ -0,0 +1,67 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\usetikzlibrary{shapes.geometric} +\pagestyle{empty} + +\begin{document} +\begin{tikzpicture} + \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=5em, font={\ttfamily}] + \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}] + + \node[data] (n1) {Verilog Source}; + \node[process] (n2) [below of=n1] {Verilog Frontend}; + \node[data] (n3) [below of=n2] {AST}; + \node[process] (n4) [below of=n3] {AST Frontend}; + \node[data] (n5) [below of=n4] {RTLIL}; + + \draw[-latex] (n1) -- (n2); + \draw[-latex] (n2) -- (n3); + \draw[-latex] (n3) -- (n4); + \draw[-latex] (n4) -- (n5); + + \tikzstyle{details} = [draw, fill=yellow!5, rectangle, node distance=6cm, font={\ttfamily}] + + \node[details] (d1) [right of=n2] {\begin{minipage}{5cm} + \hfil + \begin{tikzpicture} + \tikzstyle{subproc} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=10em, node distance=3em, font={\ttfamily}] + \node (s0) {}; + \node[subproc] (s1) [below of=s0] {Preprocessor}; + \node[subproc] (s2) [below of=s1] {Lexer}; + \node[subproc] (s3) [below of=s2] {Parser}; + \node[node distance=3em] (s4) [below of=s3] {}; + \draw[-latex] (s0) -- (s1); + \draw[-latex] (s1) -- (s2); + \draw[-latex] (s2) -- (s3); + \draw[-latex] (s3) -- (s4); + \end{tikzpicture} + \end{minipage}}; + + \draw[dashed] (n2.north east) -- (d1.north west); + \draw[dashed] (n2.south east) -- (d1.south west); + + \node[details] (d2) [right of=n4] {\begin{minipage}{5cm} + \hfil + \begin{tikzpicture} + \tikzstyle{subproc} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=10em, node distance=3em, font={\ttfamily}] + \node (s0) {}; + \node[subproc] (s1) [below of=s0] {Simplifier}; + \node[subproc] (s2) [below of=s1] {RTLIL Generator}; + \node[node distance=3em] (s3) [below of=s2] {}; + \draw[-latex] (s0) -- (s1); + \draw[-latex] (s1) -- (s2); + \draw[-latex] (s2) -- (s3); + \end{tikzpicture} + \end{minipage}}; + + \draw[dashed] (n4.north east) -- (d2.north west); + \draw[dashed] (n4.south east) -- (d2.south west); + +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/primer/basics_abstractions.tex b/docs/source/_images/primer/basics_abstractions.tex new file mode 100644 index 00000000000..ece06362348 --- /dev/null +++ b/docs/source/_images/primer/basics_abstractions.tex @@ -0,0 +1,41 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\pagestyle{empty} + +\begin{document} +\begin{tikzpicture} + \tikzstyle{lvl} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=15em] + \node[lvl] (sys) {System Level}; + \node[lvl] (hl) [below of=sys] {High Level}; + \node[lvl] (beh) [below of=hl] {Behavioral Level}; + \node[lvl] (rtl) [below of=beh] {Register-Transfer Level (RTL)}; + \node[lvl] (lg) [below of=rtl] {Logical Gate Level}; + \node[lvl] (pg) [below of=lg] {Physical Gate Level}; + \node[lvl] (sw) [below of=pg] {Switch Level}; + + \draw[dotted] (sys.east) -- ++(1,0) coordinate (sysx); + \draw[dotted] (hl.east) -- ++(1,0) coordinate (hlx); + \draw[dotted] (beh.east) -- ++(1,0) coordinate (behx); + \draw[dotted] (rtl.east) -- ++(1,0) coordinate (rtlx); + \draw[dotted] (lg.east) -- ++(1,0) coordinate (lgx); + \draw[dotted] (pg.east) -- ++(1,0) coordinate (pgx); + \draw[dotted] (sw.east) -- ++(1,0) coordinate (swx); + + \draw[gray,|->] (sysx) -- node[right] {System Design} (hlx); + \draw[|->|] (hlx) -- node[right] {High Level Synthesis (HLS)} (behx); + \draw[->|] (behx) -- node[right] {Behavioral Synthesis} (rtlx); + \draw[->|] (rtlx) -- node[right] {RTL Synthesis} (lgx); + \draw[->|] (lgx) -- node[right] {Logic Synthesis} (pgx); + \draw[gray,->|] (pgx) -- node[right] {Cell Library} (swx); + + \draw[dotted] (behx) -- ++(5,0) coordinate (a); + \draw[dotted] (pgx) -- ++(5,0) coordinate (b); + \draw[|->|] (a) -- node[right] {Yosys} (b); +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/primer/basics_ast.tex b/docs/source/_images/primer/basics_ast.tex new file mode 100644 index 00000000000..dac6a8d47d8 --- /dev/null +++ b/docs/source/_images/primer/basics_ast.tex @@ -0,0 +1,30 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\usetikzlibrary{shapes.geometric} +\pagestyle{empty} + +\begin{document} +\begin{tikzpicture} + \tikzstyle{node} = [draw, fill=green!10, ellipse, minimum height=2em, minimum width=8em, node distance=10em] + + \draw (+0,+0) node[node] (n1) {\tt ASSIGN}; + + \draw (-2,-2) node[node] (n11) {\tt ID: foo}; + \draw (+2,-2) node[node] (n12) {\tt PLUS}; + + \draw (+0,-4) node[node] (n121) {\tt ID: bar}; + \draw (+4,-4) node[node] (n122) {\tt CONST: 42}; + + \draw[-latex] (n1) -- (n11); + \draw[-latex] (n1) -- (n12); + + \draw[-latex] (n12) -- (n121); + \draw[-latex] (n12) -- (n122); +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/primer/basics_flow.tex b/docs/source/_images/primer/basics_flow.tex new file mode 100644 index 00000000000..53b55548768 --- /dev/null +++ b/docs/source/_images/primer/basics_flow.tex @@ -0,0 +1,44 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\pagestyle{empty} + +\begin{document} +\begin{tikzpicture} + \tikzstyle{manual} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=8em, node distance=10em] + \tikzstyle{auto} = [draw, fill=orange!10, rectangle, minimum height=2em, minimum width=8em, node distance=10em] + + \node[manual] (sys) {\begin{minipage}{8em} + \center + System Level \\ + Model + \end{minipage}}; + \node[manual] (beh) [right of=sys] {\begin{minipage}{8em} + \center + Behavioral \\ + Model + \end{minipage}}; + \node[auto] (rtl) [right of=beh] {\begin{minipage}{8em} + \center + RTL \\ + Model + \end{minipage}}; + \node[auto] (gates) [right of=rtl] {\begin{minipage}{8em} + \center + Gate-Level \\ + Model + \end{minipage}}; + + \draw[-latex] (beh) edge[double, bend left] node[above] {synthesis} (rtl); + \draw[-latex] (rtl) edge[double, bend left] node[above] {synthesis} (gates); + + \draw[latex-latex] (sys) edge[bend right] node[below] {verify} (beh); + \draw[latex-latex] (beh) edge[bend right] node[below] {verify} (rtl); + \draw[latex-latex] (rtl) edge[bend right] node[below] {verify} (gates); +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/primer/basics_parsetree.tex b/docs/source/_images/primer/basics_parsetree.tex new file mode 100644 index 00000000000..1c8392b8892 --- /dev/null +++ b/docs/source/_images/primer/basics_parsetree.tex @@ -0,0 +1,44 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\usetikzlibrary{shapes.geometric} +\pagestyle{empty} + +\begin{document} +\begin{tikzpicture} + \tikzstyle{node} = [draw, fill=green!10, ellipse, minimum height=2em, minimum width=8em, node distance=10em] + + \draw (+0,+1) node[node] (n1) {\tt assign\_stmt}; + + \draw (-6,-1) node[node] (n11) {\tt TOK\_ASSIGN}; + \draw (-3,-2) node[node] (n12) {\tt TOK\_IDENTIFIER}; + \draw (+0,-1) node[node] (n13) {\tt TOK\_EQ}; + \draw (+3,-2) node[node] (n14) {\tt expr}; + \draw (+6,-1) node[node] (n15) {\tt TOK\_SEMICOLON}; + + \draw (-1,-4) node[node] (n141) {\tt expr}; + \draw (+3,-4) node[node] (n142) {\tt TOK\_PLUS}; + \draw (+7,-4) node[node] (n143) {\tt expr}; + + \draw (-1,-5.5) node[node] (n1411) {\tt TOK\_IDENTIFIER}; + \draw (+7,-5.5) node[node] (n1431) {\tt TOK\_NUMBER}; + + \draw[-latex] (n1) -- (n11); + \draw[-latex] (n1) -- (n12); + \draw[-latex] (n1) -- (n13); + \draw[-latex] (n1) -- (n14); + \draw[-latex] (n1) -- (n15); + + \draw[-latex] (n14) -- (n141); + \draw[-latex] (n14) -- (n142); + \draw[-latex] (n14) -- (n143); + + \draw[-latex] (n141) -- (n1411); + \draw[-latex] (n143) -- (n1431); +\end{tikzpicture} +\end{document} diff --git a/docs/source/_images/primer/levels_of_abstraction.tex b/docs/source/_images/primer/levels_of_abstraction.tex new file mode 100644 index 00000000000..55725d08e12 --- /dev/null +++ b/docs/source/_images/primer/levels_of_abstraction.tex @@ -0,0 +1,42 @@ +\documentclass[12pt,tikz]{standalone} +\pdfinfoomitdate 1 +\pdfsuppressptexinfo 1 +\pdftrailerid{} +\usepackage[utf8]{inputenc} +\usepackage{amsmath} +\usepackage{pgfplots} +\usepackage{tikz} +\pagestyle{empty} +\definecolor{MyBlue}{RGB}{85,130,180} + +\begin{document} +\begin{tikzpicture}[scale=1.2, every node/.style={transform shape}] + \tikzstyle{lvl} = [draw, fill=MyBlue, rectangle, minimum height=2em, minimum width=15em] + \node[lvl] (sys) {System Level}; + \node[lvl] (hl) [below of=sys] {High Level}; + \node[lvl] (beh) [below of=hl] {Behavioral Level}; + \node[lvl] (rtl) [below of=beh] {Register-Transfer Level (RTL)}; + \node[lvl] (lg) [below of=rtl] {Logical Gate Level}; + \node[lvl] (pg) [below of=lg] {Physical Gate Level}; + \node[lvl] (sw) [below of=pg] {Switch Level}; + + \draw[dotted] (sys.east) -- ++(1,0) coordinate (sysx); + \draw[dotted] (hl.east) -- ++(1,0) coordinate (hlx); + \draw[dotted] (beh.east) -- ++(1,0) coordinate (behx); + \draw[dotted] (rtl.east) -- ++(1,0) coordinate (rtlx); + \draw[dotted] (lg.east) -- ++(1,0) coordinate (lgx); + \draw[dotted] (pg.east) -- ++(1,0) coordinate (pgx); + \draw[dotted] (sw.east) -- ++(1,0) coordinate (swx); + + \draw[gray,|->] (sysx) -- node[right] {System Design} (hlx); + \draw[|->|] (hlx) -- node[right] {High Level Synthesis (HLS)} (behx); + \draw[->|] (behx) -- node[right] {Behavioral Synthesis} (rtlx); + \draw[->|] (rtlx) -- node[right] {RTL Synthesis} (lgx); + \draw[->|] (lgx) -- node[right] {Logic Synthesis} (pgx); + \draw[gray,->|] (pgx) -- node[right] {Cell Library} (swx); + + \draw[dotted] (behx) -- ++(4,0) coordinate (a); + \draw[dotted] (pgx) -- ++(4,0) coordinate (b); + \draw[|->|] (a) -- node[right] {Yosys} (b); +\end{tikzpicture} +\end{document} diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css new file mode 100644 index 00000000000..b08194c0521 --- /dev/null +++ b/docs/source/_static/custom.css @@ -0,0 +1,20 @@ +/* Reduce whitespace in cmd def pages */ +.cmd.def .highlight-yoscrypt, .cmd.def .highlight pre { + padding: 0%; + margin: 0%; +} + +.cmd.def .highlight-none, .cmd.def .highlight pre { + padding-top: 0%; + margin-top: 0%; +} + +/* Make images full width */ +.width-helper { + max-width: 100%; +} + +/* Prevent code block caption text from bunching into the caption number */ +.literal-block-wrapper .code-block-caption .caption-number { + padding-right: 0.5em +} diff --git a/docs/source/_static/favico.png b/docs/source/_static/favico.png new file mode 100644 index 00000000000..3f5afba7656 Binary files /dev/null and b/docs/source/_static/favico.png differ diff --git a/docs/source/_static/logo.png b/docs/source/_static/logo.png new file mode 100644 index 00000000000..8e5a507c63c Binary files /dev/null and b/docs/source/_static/logo.png differ diff --git a/docs/source/_static/yosyshq.css b/docs/source/_static/yosyshq.css new file mode 100644 index 00000000000..57ae8f87a5f --- /dev/null +++ b/docs/source/_static/yosyshq.css @@ -0,0 +1,26 @@ +/* Don't hide the right sidebar as we're placing our fixed links there */ +aside.no-toc { + display: block !important; +} + +/* Colorful headings */ +h1 { + color: var(--color-brand-primary); + } + +h2, h3, h4, h5, h6 { + color: var(--color-brand-content); +} + +/* Use a different color for external links */ +a.external { + color: var(--color-brand-primary) !important; +} + +.wy-table-responsive table td { + white-space: normal; +} + +th { + text-align: left; +} diff --git a/docs/source/_templates/page.html b/docs/source/_templates/page.html new file mode 100644 index 00000000000..d830124c176 --- /dev/null +++ b/docs/source/_templates/page.html @@ -0,0 +1,44 @@ +{# + +See https://github.com/pradyunsg/furo/blob/main/src/furo/theme/furo/page.html for the original +block this is overwriting. + +The part that is customized is between the "begin of custom part" and "end of custom part" +comments below. It uses the same styles as the existing right sidebar code. + +#} +{% extends "furo/page.html" %} +{% block right_sidebar %} +
+ {# begin of custom part #} +
+ + YosysHQ + +
+ + {# end of custom part #} + {% if not furo_hide_toc %} +
+ + {{ _("On this page") }} + +
+
+
+ {{ toc }} +
+
+ {% endif %} +
+{% endblock %} + \ No newline at end of file diff --git a/docs/source/appendix.rst b/docs/source/appendix.rst new file mode 100644 index 00000000000..0b0a2d15b5d --- /dev/null +++ b/docs/source/appendix.rst @@ -0,0 +1,18 @@ +Appendix +======== + +.. toctree:: + :maxdepth: 2 + :includehidden: + + appendix/primer + appendix/auxlibs + appendix/auxprogs + + bib + +.. toctree:: + :maxdepth: 1 + :includehidden: + + cmd_ref diff --git a/docs/source/appendix/APPNOTE_010_Verilog_to_BLIF.rst b/docs/source/appendix/APPNOTE_010_Verilog_to_BLIF.rst new file mode 100644 index 00000000000..ff404cb53d1 --- /dev/null +++ b/docs/source/appendix/APPNOTE_010_Verilog_to_BLIF.rst @@ -0,0 +1,363 @@ +:orphan: + +==================================== +010: Converting Verilog to BLIF page +==================================== + +Abstract +======== + +Verilog-2005 is a powerful Hardware Description Language (HDL) that can be used +to easily create complex designs from small HDL code. It is the preferred method +of design entry for many designers. + +The Berkeley Logic Interchange Format (BLIF) is a simple file format for +exchanging sequential logic between programs. It is easy to generate and easy to +parse and is therefore the preferred method of design entry for many authors of +logic synthesis tools. + +Yosys is a feature-rich Open-Source Verilog synthesis tool that can be used to +bridge the gap between the two file formats. It implements most of Verilog-2005 +and thus can be used to import modern behavioral Verilog designs into BLIF-based +design flows without dependencies on proprietary synthesis tools. + +The scope of Yosys goes of course far beyond Verilog logic synthesis. But it is +a useful and important feature and this Application Note will focus on this +aspect of Yosys. + +Download +======== + +This document was originally published in April 2015: +:download:`Converting Verilog to BLIF PDF` + +.. + Installation + ============ + + Yosys written in C++ (using features from C++11) and is tested on modern + Linux. It should compile fine on most UNIX systems with a C++11 + compiler. The README file contains useful information on building Yosys + and its prerequisites. + + Yosys is a large and feature-rich program with a couple of dependencies. + It is, however, possible to deactivate some of the dependencies in the + Makefile, resulting in features in Yosys becoming unavailable. When + problems with building Yosys are encountered, a user who is only + interested in the features of Yosys that are discussed in this + Application Note may deactivate TCL, Qt and MiniSAT support in the + Makefile and may opt against building yosys-abc. + + This Application Note is based on `Yosys GIT`_ `Rev. e216e0e`_ from 2013-11-23. + The Verilog sources used for the examples are taken from `yosys-bigsim`_, a + collection of real-world designs used for regression testing Yosys. + + .. _Yosys GIT: https://github.com/YosysHQ/yosys + + .. _Rev. e216e0e: https://github.com/YosysHQ/yosys/tree/e216e0e + + .. _yosys-bigsim: https://github.com/YosysHQ/yosys-bigsim + + Getting started + =============== + + We start our tour with the Navré processor from yosys-bigsim. The `Navré + processor`_ is an Open Source AVR clone. It is a single module (softusb_navre) + in a single design file (softusb_navre.v). It also is using only features that + map nicely to the BLIF format, for example it only uses synchronous resets. + + .. _Navré processor: http://opencores.org/projects/navre + + Converting softusb_navre.v to softusb_navre.blif could not be easier: + + .. code:: sh + + yosys -o softusb_navre.blif -S softusb_navre.v + + Behind the scenes Yosys is controlled by synthesis scripts that execute + commands that operate on Yosys' internal state. For example, the -o + softusb_navre.blif option just adds the command write_blif + softusb_navre.blif to the end of the script. Likewise a file on the + command line – softusb_navre.v in this case – adds the command + read_verilog softusb_navre.v to the beginning of the synthesis script. + In both cases the file type is detected from the file extension. + + Finally the option -S instantiates a built-in default synthesis script. + Instead of using -S one could also specify the synthesis commands for + the script on the command line using the -p option, either using + individual options for each command or by passing one big command string + with a semicolon-separated list of commands. But in most cases it is + more convenient to use an actual script file. + + Using a synthesis script + ======================== + + With a script file we have better control over Yosys. The following + script file replicates what the command from the last section did: + + .. code:: yoscrypt + + read_verilog softusb_navre.v + hierarchy + proc; opt; memory; opt; techmap; opt + write_blif softusb_navre.blif + + The first and last line obviously read the Verilog file and write the + BLIF file. + + The 2nd line checks the design hierarchy and instantiates parametrized + versions of the modules in the design, if necessary. In the case of this + simple design this is a no-op. However, as a general rule a synthesis + script should always contain this command as first command after reading + the input files. + + The 3rd line does most of the actual work: + + - The command opt is the Yosys' built-in optimizer. It can perform some + simple optimizations such as const-folding and removing unconnected + parts of the design. It is common practice to call opt after each + major step in the synthesis procedure. In cases where too much + optimization is not appreciated (for example when analyzing a + design), it is recommended to call clean instead of opt. + + - The command proc converts processes (Yosys' internal representation + of Verilog always- and initial-blocks) to circuits of multiplexers + and storage elements (various types of flip-flops). + + - The command memory converts Yosys' internal representations of arrays + and array accesses to multi-port block memories, and then maps this + block memories to address decoders and flip-flops, unless the option + -nomap is used, in which case the multi-port block memories stay in + the design and can then be mapped to architecture-specific memory + primitives using other commands. + + - The command techmap turns a high-level circuit with coarse grain + cells such as wide adders and multipliers to a fine-grain circuit of + simple logic primitives and single-bit storage elements. The command + does that by substituting the complex cells by circuits of simpler + cells. It is possible to provide a custom set of rules for this + process in the form of a Verilog source file, as we will see in the + next section. + + Now Yosys can be run with the filename of the synthesis script as + argument: + + .. code:: sh + + yosys softusb_navre.ys + + Now that we are using a synthesis script we can easily modify how Yosys + synthesizes the design. The first thing we should customize is the call + to the hierarchy command: + + Whenever it is known that there are no implicit blackboxes in the + design, i.e. modules that are referenced but are not defined, the + hierarchy command should be called with the -check option. This will + then cause synthesis to fail when implicit blackboxes are found in the + design. + + The 2nd thing we can improve regarding the hierarchy command is that we + can tell it the name of the top level module of the design hierarchy. It + will then automatically remove all modules that are not referenced from + this top level module. + + For many designs it is also desired to optimize the encodings for the + finite state machines (FSMs) in the design. The fsm command finds FSMs, + extracts them, performs some basic optimizations and then generate a + circuit from the extracted and optimized description. It would also be + possible to tell the fsm command to leave the FSMs in their extracted + form, so they can be further processed using custom commands. But in + this case we don't want that. + + So now we have the final synthesis script for generating a BLIF file for + the Navré CPU: + + .. code:: yoscrypt + + read_verilog softusb_navre.v + hierarchy -check -top softusb_navre + proc; opt; memory; opt; fsm; opt; techmap; opt + write_blif softusb_navre.blif + + Advanced example: The Amber23 ARMv2a CPU + ======================================== + + Our 2nd example is the `Amber23 ARMv2a CPU`_. Once again we base our example on + the Verilog code that is included in `yosys-bigsim`_. + + .. _Amber23 ARMv2a CPU: http://opencores.org/projects/amber + + .. code-block:: yoscrypt + :caption: `amber23.ys` + :name: amber23.ys + + read_verilog a23_alu.v + read_verilog a23_barrel_shift_fpga.v + read_verilog a23_barrel_shift.v + read_verilog a23_cache.v + read_verilog a23_coprocessor.v + read_verilog a23_core.v + read_verilog a23_decode.v + read_verilog a23_execute.v + read_verilog a23_fetch.v + read_verilog a23_multiply.v + read_verilog a23_ram_register_bank.v + read_verilog a23_register_bank.v + read_verilog a23_wishbone.v + read_verilog generic_sram_byte_en.v + read_verilog generic_sram_line_en.v + hierarchy -check -top a23_core + add -global_input globrst 1 + proc -global_arst globrst + techmap -map adff2dff.v + opt; memory; opt; fsm; opt; techmap + write_blif amber23.blif + + The problem with this core is that it contains no dedicated reset logic. Instead + the coding techniques shown in :numref:`glob_arst` are used to define reset + values for the global asynchronous reset in an FPGA implementation. This design + can not be expressed in BLIF as it is. Instead we need to use a synthesis script + that transforms this form to synchronous resets that can be expressed in BLIF. + + (Note that there is no problem if this coding techniques are used to model ROM, + where the register is initialized using this syntax but is never updated + otherwise.) + + :numref:`amber23.ys` shows the synthesis script for the Amber23 core. In line 17 + the add command is used to add a 1-bit wide global input signal with the name + ``globrst``. That means that an input with that name is added to each module in the + design hierarchy and then all module instantiations are altered so that this new + signal is connected throughout the whole design hierarchy. + + .. code-block:: verilog + :caption: Implicit coding of global asynchronous resets + :name: glob_arst + + reg [7:0] a = 13, b; + initial b = 37; + + .. code-block:: verilog + :caption: `adff2dff.v` + :name: adff2dff.v + + (* techmap_celltype = "$adff" *) + module adff2dff (CLK, ARST, D, Q); + + parameter WIDTH = 1; + parameter CLK_POLARITY = 1; + parameter ARST_POLARITY = 1; + parameter ARST_VALUE = 0; + + input CLK, ARST; + input [WIDTH-1:0] D; + output reg [WIDTH-1:0] Q; + + wire [1023:0] _TECHMAP_DO_ = "proc"; + + wire _TECHMAP_FAIL_ = + !CLK_POLARITY || !ARST_POLARITY; + + always @(posedge CLK) + if (ARST) + Q <= ARST_VALUE; + else + Q <= D; + + endmodule + + In line 18 the :cmd:ref:`proc` command is called. But in this script the signal + name globrst is passed to the command as a global reset signal for resetting the + registers to their assigned initial values. + + Finally in line 19 the techmap command is used to replace all instances of + flip-flops with asynchronous resets with flip-flops with synchronous resets. The + map file used for this is shown in :numref:`adff2dff.v`. Note how the + ``techmap_celltype`` attribute is used in line 1 to tell the techmap command + which cells to replace in the design, how the ``_TECHMAP_FAIL_`` wire in lines + 15 and 16 (which evaluates to a constant value) determines if the parameter set + is compatible with this replacement circuit, and how the ``_TECHMAP_DO_`` wire + in line 13 provides a mini synthesis-script to be used to process this cell. + + .. code-block:: c + :caption: Test program for the Amber23 CPU (Sieve of Eratosthenes). Compiled + using GCC 4.6.3 for ARM with ``-Os -marm -march=armv2a + -mno-thumb-interwork -ffreestanding``, linked with ``--fix-v4bx`` + set and booted with a custom setup routine written in ARM assembler. + :name: sieve + + #include + #include + + #define BITMAP_SIZE 64 + #define OUTPORT 0x10000000 + + static uint32_t bitmap[BITMAP_SIZE/32]; + + static void bitmap_set(uint32_t idx) { bitmap[idx/32] |= 1 << (idx % 32); } + static bool bitmap_get(uint32_t idx) { return (bitmap[idx/32] & (1 << (idx % 32))) != 0; } + static void output(uint32_t val) { *((volatile uint32_t*)OUTPORT) = val; } + + int main() { + uint32_t i, j, k; + output(2); + for (i = 0; i < BITMAP_SIZE; i++) { + if (bitmap_get(i)) continue; + output(3+2*i); + for (j = 2*(3+2*i);; j += 3+2*i) { + if (j%2 == 0) continue; + k = (j-3)/2; + if (k >= BITMAP_SIZE) break; + bitmap_set(k); + } + } + output(0); + return 0; + } + + Verification of the Amber23 CPU + =============================== + + The BLIF file for the Amber23 core, generated using :numref:`amber23.ys` and + :numref:`adff2dff.v` and the version of the Amber23 RTL source that is bundled + with yosys-bigsim, was verified using the test-bench from yosys-bigsim. It + successfully executed the program shown in :numref:`sieve` in the test-bench. + + For simulation the BLIF file was converted back to Verilog using `ABC`_. So this + test includes the successful transformation of the BLIF file into ABC's internal + format as well. + + .. _ABC: https://github.com/berkeley-abc/abc + + The only thing left to write about the simulation itself is that it probably was + one of the most energy inefficient and time consuming ways of successfully + calculating the first 31 primes the author has ever conducted. + + Limitations + =========== + + At the time of this writing Yosys does not support multi-dimensional memories, + does not support writing to individual bits of array elements, does not support + initialization of arrays with ``$readmemb`` and ``$readmemh``, and has only + limited support for tristate logic, to name just a few limitations. + + That being said, Yosys can synthesize an overwhelming majority of real-world + Verilog RTL code. The remaining cases can usually be modified to be compatible + with Yosys quite easily. + + The various designs in yosys-bigsim are a good place to look for examples of + what is within the capabilities of Yosys. + + Conclusion + ========== + + Yosys is a feature-rich Verilog-2005 synthesis tool. It has many uses, but one + is to provide an easy gateway from high-level Verilog code to low-level logic + circuits. + + The command line option ``-S`` can be used to quickly synthesize Verilog code to + BLIF files without a hassle. + + With custom synthesis scripts it becomes possible to easily perform high-level + optimizations, such as re-encoding FSMs. In some extreme cases, such as the + Amber23 ARMv2 CPU, the more advanced Yosys features can be used to change a + design to fit a certain need without actually touching the RTL code. diff --git a/docs/source/appendix/APPNOTE_012_Verilog_to_BTOR.rst b/docs/source/appendix/APPNOTE_012_Verilog_to_BTOR.rst new file mode 100644 index 00000000000..1874b014813 --- /dev/null +++ b/docs/source/appendix/APPNOTE_012_Verilog_to_BTOR.rst @@ -0,0 +1,353 @@ +:orphan: + +==================================== +012: Converting Verilog to BTOR page +==================================== + +Abstract +======== + +Verilog-2005 is a powerful Hardware Description Language (HDL) that can be used +to easily create complex designs from small HDL code. BTOR is a bit-precise +word-level format for model checking. It is a simple format and easy to parse. +It allows to model the model checking problem over the theory of bit-vectors +with one-dimensional arrays, thus enabling to model Verilog designs with +registers and memories. Yosys is an Open-Source Verilog synthesis tool that can +be used to convert Verilog designs with simple assertions to BTOR format. + +Download +======== + +This document was originally published in November 2013: +:download:`Converting Verilog to BTOR PDF` + +.. + Installation + ============ + + Yosys written in C++ (using features from C++11) and is tested on modern Linux. + It should compile fine on most UNIX systems with a C++11 compiler. The README + file contains useful information on building Yosys and its prerequisites. + + Yosys is a large and feature-rich program with some dependencies. For this work, + we may deactivate other extra features such as TCL and ABC support in the + Makefile. + + This Application Note is based on `Yosys GIT`_ `Rev. 082550f` from 2015-04-04. + + .. _Yosys GIT: https://github.com/YosysHQ/yosys + + .. _Rev. 082550f: https://github.com/YosysHQ/yosys/tree/082550f + + Quick start + =========== + + We assume that the Verilog design is synthesizable and we also assume that the + design does not have multi-dimensional memories. As BTOR implicitly initializes + registers to zero value and memories stay uninitialized, we assume that the + Verilog design does not contain initial blocks. For more details about the BTOR + format, please refer to :cite:p:`btor`. + + We provide a shell script ``verilog2btor.sh`` which can be used to convert a + Verilog design to BTOR. The script can be found in the ``backends/btor`` + directory. The following example shows its usage: + + .. code:: sh + + verilog2btor.sh fsm.v fsm.btor test + + The script ``verilog2btor.sh`` takes three parameters. In the above example, the + first parameter ``fsm.v`` is the input design, the second parameter ``fsm.btor`` + is the file name of BTOR output, and the third parameter ``test`` is the name of + top module in the design. + + To specify the properties (that need to be checked), we have two + options: + + - We can use the Verilog ``assert`` statement in the procedural block or module + body of the Verilog design, as shown in :numref:`specifying_property_assert`. + This is the preferred option. + + - We can use a single-bit output wire, whose name starts with ``safety``. The + value of this output wire needs to be driven low when the property is met, + i.e. the solver will try to find a model that makes the safety pin go high. + This is demonstrated in :numref:`specifying_property_output`. + + .. code-block:: verilog + :caption: Specifying property in Verilog design with ``assert`` + :name: specifying_property_assert + + module test(input clk, input rst, output y); + + reg [2:0] state; + + always @(posedge clk) begin + if (rst || state == 3) begin + state <= 0; + end else begin + assert(state < 3); + state <= state + 1; + end + end + + assign y = state[2]; + + assert property (y !== 1'b1); + + endmodule + + .. code-block:: verilog + :caption: Specifying property in Verilog design with output wire + :name: specifying_property_output + + module test(input clk, input rst, + output y, output safety1); + + reg [2:0] state; + + always @(posedge clk) begin + if (rst || state == 3) + state <= 0; + else + state <= state + 1; + end + + assign y = state[2]; + + assign safety1 = !(y !== 1'b1); + + endmodule + + We can run `Boolector`_ ``1.4.1`` [1]_ on the generated BTOR file: + + .. _Boolector: http://fmv.jku.at/boolector/ + + .. code:: sh + + $ boolector fsm.btor + unsat + + We can also use `nuXmv`_, but on BTOR designs it does not support memories yet. + With the next release of nuXmv, we will be also able to verify designs with + memories. + + .. _nuXmv: https://es-static.fbk.eu/tools/nuxmv/index.php + + Detailed flow + ============= + + Yosys is able to synthesize Verilog designs up to the gate level. We are + interested in keeping registers and memories when synthesizing the design. For + this purpose, we describe a customized Yosys synthesis flow, that is also + provided by the ``verilog2btor.sh`` script. :numref:`btor_script_memory` shows + the Yosys commands that are executed by ``verilog2btor.sh``. + + .. code-block:: yoscrypt + :caption: Synthesis Flow for BTOR with memories + :name: btor_script_memory + + read_verilog -sv $1; + hierarchy -top $3; hierarchy -libdir $DIR; + hierarchy -check; + proc; opt; + opt_expr -mux_undef; opt; + rename -hide;;; + splice; opt; + memory_dff -wr_only; memory_collect;; + flatten;; + memory_unpack; + splitnets -driver; + setundef -zero -undriven; + opt;;; + write_btor $2; + + Here is short description of what is happening in the script line by + line: + + #. Reading the input file. + + #. Setting the top module in the hierarchy and trying to read automatically the + files which are given as ``include`` in the file read in first line. + + #. Checking the design hierarchy. + + #. Converting processes to multiplexers (muxs) and flip-flops. + + #. Removing undef signals from muxs. + + #. Hiding all signal names that are not used as module ports. + + #. Explicit type conversion, by introducing slice and concat cells in the + circuit. + + #. Converting write memories to synchronous memories, and collecting the + memories to multi-port memories. + + #. Flattening the design to get only one module. + + #. Separating read and write memories. + + #. Splitting the signals that are partially assigned + + #. Setting undef to zero value. + + #. Final optimization pass. + + #. Writing BTOR file. + + For detailed description of the commands mentioned above, please refer + to the Yosys documentation, or run ``yosys -h ``. + + The script presented earlier can be easily modified to have a BTOR file that + does not contain memories. This is done by removing the line number 8 and 10, + and introduces a new command :cmd:ref:`memory` at line number 8. + :numref:`btor_script_without_memory` shows the modified Yosys script file: + + .. code-block:: sh + :caption: Synthesis Flow for BTOR without memories + :name: btor_script_without_memory + + read_verilog -sv $1; + hierarchy -top $3; hierarchy -libdir $DIR; + hierarchy -check; + proc; opt; + opt_expr -mux_undef; opt; + rename -hide;;; + splice; opt; + memory;; + flatten;; + splitnets -driver; + setundef -zero -undriven; + opt;;; + write_btor $2; + + Example + ======= + + Here is an example Verilog design that we want to convert to BTOR: + + .. code-block:: verilog + :caption: Example - Verilog Design + :name: example_verilog + + module array(input clk); + + reg [7:0] counter; + reg [7:0] mem [7:0]; + + always @(posedge clk) begin + counter <= counter + 8'd1; + mem[counter] <= counter; + end + + assert property (!(counter > 8'd0) || + mem[counter - 8'd1] == counter - 8'd1); + + endmodule + + The generated BTOR file that contain memories, using the script shown in + :numref:`btor_memory`: + + .. code-block:: + :caption: Example - Converted BTOR with memory + :name: btor_memory + + 1 var 1 clk + 2 array 8 3 + 3 var 8 $auto$rename.cc:150:execute$20 + 4 const 8 00000001 + 5 sub 8 3 4 + 6 slice 3 5 2 0 + 7 read 8 2 6 + 8 slice 3 3 2 0 + 9 add 8 3 4 + 10 const 8 00000000 + 11 ugt 1 3 10 + 12 not 1 11 + 13 const 8 11111111 + 14 slice 1 13 0 0 + 15 one 1 + 16 eq 1 1 15 + 17 and 1 16 14 + 18 write 8 3 2 8 3 + 19 acond 8 3 17 18 2 + 20 anext 8 3 2 19 + 21 eq 1 7 5 + 22 or 1 12 21 + 23 const 1 1 + 24 one 1 + 25 eq 1 23 24 + 26 cond 1 25 22 24 + 27 root 1 -26 + 28 cond 8 1 9 3 + 29 next 8 3 28 + + And the BTOR file obtained by the script shown in + :numref:`btor_without_memory`, which expands the memory into individual + elements: + + .. code-block:: + :caption: Example - Converted BTOR with memory + :name: btor_without_memory + + 1 var 1 clk + 2 var 8 mem[0] + 3 var 8 $auto$rename.cc:150:execute$20 + 4 slice 3 3 2 0 + 5 slice 1 4 0 0 + 6 not 1 5 + 7 slice 1 4 1 1 + 8 not 1 7 + 9 slice 1 4 2 2 + 10 not 1 9 + 11 and 1 8 10 + 12 and 1 6 11 + 13 cond 8 12 3 2 + 14 cond 8 1 13 2 + 15 next 8 2 14 + 16 const 8 00000001 + 17 add 8 3 16 + 18 const 8 00000000 + 19 ugt 1 3 18 + 20 not 1 19 + 21 var 8 mem[2] + 22 and 1 7 10 + 23 and 1 6 22 + 24 cond 8 23 3 21 + 25 cond 8 1 24 21 + 26 next 8 21 25 + 27 sub 8 3 16 + + ... + + 54 cond 1 53 50 52 + 55 root 1 -54 + + ... + + 77 cond 8 76 3 44 + 78 cond 8 1 77 44 + 79 next 8 44 78 + + Limitations + =========== + + BTOR does not support initialization of memories and registers, i.e. they are + implicitly initialized to value zero, so the initial block for memories need to + be removed when converting to BTOR. It should also be kept in consideration that + BTOR does not support the ``x`` or ``z`` values of Verilog. + + Another thing to bear in mind is that Yosys will convert multi-dimensional + memories to one-dimensional memories and address decoders. Therefore + out-of-bounds memory accesses can yield unexpected results. + + Conclusion + ========== + + Using the described flow, we can use Yosys to generate word-level verification + benchmarks with or without memories from Verilog designs. + + .. [1] + Newer version of Boolector do not support sequential models. + Boolector 1.4.1 can be built with picosat-951. Newer versions of + picosat have an incompatible API. diff --git a/docs/source/appendix/auxlibs.rst b/docs/source/appendix/auxlibs.rst new file mode 100644 index 00000000000..321cb52c4d1 --- /dev/null +++ b/docs/source/appendix/auxlibs.rst @@ -0,0 +1,81 @@ +Auxiliary libraries +=================== + +The Yosys source distribution contains some auxiliary libraries that are +compiled into Yosys and can be used in plugins. + +BigInt +------ + +The files in ``libs/bigint/`` provide a library for performing arithmetic with +arbitrary length integers. It is written by Matt McCutchen. + +The BigInt library is used for evaluating constant expressions, e.g. using the +ConstEval class provided in kernel/consteval.h. + +See also: http://mattmccutchen.net/bigint/ + +dlfcn-win32 +----------- + +The ``dlfcn`` library enables runtime loading of plugins without requiring +recompilation of Yosys. The files in ``libs/dlfcn-win32`` provide an +implementation of ``dlfcn`` for Windows. + +See also: https://github.com/dlfcn-win32/dlfcn-win32 + +ezSAT +----- + +The files in ``libs/ezsat`` provide a library for simplifying generating CNF +formulas for SAT solvers. It also contains bindings of MiniSAT. The ezSAT +library is written by C. Wolf. It is used by the :cmd:ref:`sat` pass (see +:doc:`/cmd/sat`). + +fst +--- + +``libfst`` files from `gtkwave`_ are included in ``libs/fst`` to support +reading/writing signal traces from/to the GTKWave developed FST format. This is +primarily used in the :cmd:ref:`sim` command. + +.. _gtkwave: https://github.com/gtkwave/gtkwave + +json11 +------ + +For reading/writing designs from/to JSON, :cmd:ref:`read_json` and +:cmd:ref:`write_json` should be used. For everything else there is the `json11 +library`_: + + json11 is a tiny JSON library for C++11, providing JSON parsing and + serialization. + +This library is used for outputting machine-readable statistics (:cmd:ref:`stat` +with ``-json`` flag), using the RPC frontend (:cmd:ref:`connect_rpc`), and the +yosys-witness ``yw`` format. + +.. _json11 library: https://github.com/dropbox/json11 + +MiniSAT +------- + +The files in ``libs/minisat`` provide a high-performance SAT solver, used by the +:cmd:ref:`sat` command. + +SHA1 +---- + +The files in ``libs/sha1/`` provide a public domain SHA1 implementation written +by Steve Reid, Bruce Guenter, and Volker Grabsch. It is used for generating +unique names when specializing parameterized modules. + +.. _sec:SubCircuit: + +SubCircuit +---------- + +The files in ``libs/subcircuit`` provide a library for solving the subcircuit +isomorphism problem. It is written by C. Wolf and based on the Ullmann Subgraph +Isomorphism Algorithm :cite:p:`UllmannSubgraphIsomorphism`. It is used by the +extract pass (see :doc:`../cmd/extract`). diff --git a/docs/source/appendix/auxprogs.rst b/docs/source/appendix/auxprogs.rst new file mode 100644 index 00000000000..b6defbe802e --- /dev/null +++ b/docs/source/appendix/auxprogs.rst @@ -0,0 +1,63 @@ +Auxiliary programs +================== + +Besides the main yosys executable, the Yosys distribution contains a set of +additional helper programs. + +yosys-config +------------ + +The ``yosys-config`` tool (an auto-generated shell-script) can be used to query +compiler options and other information needed for building loadable modules for +Yosys. See :doc:`/yosys_internals/extending_yosys/extensions` for details. + +.. literalinclude:: /temp/yosys-config + :start-at: Usage + +.. _sec:filterlib: + +yosys-filterlib +--------------- + +.. todo:: how does a filterlib rules-file work? + +The ``yosys-filterlib`` tool is a small utility that can be used to strip or +extract information from a Liberty file. This can be useful for removing +sensitive or proprietary information such as timing or other trade secrets. + +.. literalinclude:: /temp/yosys-filterlib + :start-at: Usage + +yosys-abc +--------- + +This is a fork of ABC with a small set of custom modifications that have not yet +been accepted upstream. Not all versions of Yosys work with all versions of ABC. +So Yosys comes with its own yosys-abc to avoid compatibility issues between the +two. + +.. literalinclude:: /temp/yosys-abc + :start-at: usage + :end-before: UC Berkeley + +yosys-smtbmc +------------ + +The ``yosys-smtbmc`` tool is a utility used by SBY for interacting with smt +solvers. + +.. literalinclude:: /temp/yosys-smtbmc + +yosys-witness +------------- + +``yosys-witness`` is a new tool to inspect and convert yosys witness traces. +This is used in SBY and SCY for producing traces in a consistent format +independent of the solver. + +.. literalinclude:: /temp/yosys-witness + :start-at: Usage + +.. note:: ``yosys-witness`` requires `click`_ Python package for use. + +.. _click: https://pypi.org/project/click/ diff --git a/docs/source/appendix/env_vars.rst b/docs/source/appendix/env_vars.rst new file mode 100644 index 00000000000..26cc37c8133 --- /dev/null +++ b/docs/source/appendix/env_vars.rst @@ -0,0 +1,31 @@ +Yosys environment variables +=========================== + +``HOME`` + Yosys command history is stored in :file:`$HOME/.yosys_history`. Graphics + (from :cmd:ref:`show` and :cmd:ref:`viz` commands) will output to this + directory by default. This environment variable is also used in some cases + for resolving filenames with :file:`~`. + +``PATH`` + May be used in OpenBSD builds for finding the location of Yosys executable. + +``TMPDIR`` + Used for storing temporary files. + +``ABC`` + When compiling Yosys with out-of-tree ABC using :makevar:`ABCEXTERNAL`, this + variable can be used to override the external ABC executable. + +``YOSYS_NOVERIFIC`` + If Yosys was built with Verific, this environment variable can be used to + temporarily disable Verific support. + +``YOSYS_COVER_DIR`` and ``YOSYS_COVER_FILE`` + When using code coverage, these environment variables control the output file + name/location. + +``YOSYS_ABORT_ON_LOG_ERROR`` + Can be used for debugging Yosys internals. Setting it to 1 causes abort() to + be called when Yosys terminates with an error message. + diff --git a/docs/source/appendix/primer.rst b/docs/source/appendix/primer.rst new file mode 100644 index 00000000000..101723e32cb --- /dev/null +++ b/docs/source/appendix/primer.rst @@ -0,0 +1,776 @@ +.. role:: verilog(code) + :language: Verilog + +.. _chapter:basics: + +A primer on digital circuit synthesis +===================================== + +This chapter contains a short introduction to the basic principles of digital +circuit synthesis. + +Levels of abstraction +--------------------- + +Digital circuits can be represented at different levels of abstraction. During +the design process a circuit is usually first specified using a higher level +abstraction. Implementation can then be understood as finding a functionally +equivalent representation at a lower abstraction level. When this is done +automatically using software, the term synthesis is used. + +So synthesis is the automatic conversion of a high-level representation of a +circuit to a functionally equivalent low-level representation of a circuit. +:numref:`Figure %s ` lists the different levels of +abstraction and how they relate to different kinds of synthesis. + +.. figure:: /_images/primer/basics_abstractions.* + :class: width-helper + :name: fig:Basics_abstractions + + Different levels of abstraction and synthesis. + +Regardless of the way a lower level representation of a circuit is obtained +(synthesis or manual design), the lower level representation is usually verified +by comparing simulation results of the lower level and the higher level +representation [1]_. Therefore even if no synthesis is used, there must still +be a simulatable representation of the circuit in all levels to allow for +verification of the design. + +Note: The exact meaning of terminology such as "High-Level" is of course not +fixed over time. For example the HDL "ABEL" was first introduced in 1985 as "A +High-Level Design Language for Programmable Logic Devices" :cite:p:`ABEL`, but +would not be considered a "High-Level Language" today. + +System level +~~~~~~~~~~~~ + +The System Level abstraction of a system only looks at its biggest building +blocks like CPUs and computing cores. At this level the circuit is usually +described using traditional programming languages like C/C++ or Matlab. +Sometimes special software libraries are used that are aimed at simulation +circuits on the system level, such as SystemC. + +Usually no synthesis tools are used to automatically transform a system level +representation of a circuit to a lower-level representation. But system level +design tools exist that can be used to connect system level building blocks. + +The IEEE 1685-2009 standard defines the IP-XACT file format that can be used to +represent designs on the system level and building blocks that can be used in +such system level designs. :cite:p:`IP-XACT` + +High level +~~~~~~~~~~ + +The high-level abstraction of a system (sometimes referred to as algorithmic +level) is also often represented using traditional programming languages, but +with a reduced feature set. For example when representing a design at the high +level abstraction in C, pointers can only be used to mimic concepts that can be +found in hardware, such as memory interfaces. Full featured dynamic memory +management is not allowed as it has no corresponding concept in digital +circuits. + +Tools exist to synthesize high level code (usually in the form of C/C++/SystemC +code with additional metadata) to behavioural HDL code (usually in the form of +Verilog or VHDL code). Aside from the many commercial tools for high level +synthesis there are also a number of FOSS tools for high level synthesis . + +Behavioural level +~~~~~~~~~~~~~~~~~ + +At the behavioural abstraction level a language aimed at hardware description +such as Verilog or VHDL is used to describe the circuit, but so-called +behavioural modelling is used in at least part of the circuit description. In +behavioural modelling there must be a language feature that allows for +imperative programming to be used to describe data paths and registers. This is +the always-block in Verilog and the process-block in VHDL. + +In behavioural modelling, code fragments are provided together with a +sensitivity list; a list of signals and conditions. In simulation, the code +fragment is executed whenever a signal in the sensitivity list changes its value +or a condition in the sensitivity list is triggered. A synthesis tool must be +able to transfer this representation into an appropriate datapath followed by +the appropriate types of register. + +For example consider the following Verilog code fragment: + +.. code:: verilog + :number-lines: + + always @(posedge clk) + y <= a + b; + +In simulation the statement ``y <= a + b`` is executed whenever a positive edge +on the signal ``clk`` is detected. The synthesis result however will contain an +adder that calculates the sum ``a + b`` all the time, followed by a d-type +flip-flop with the adder output on its D-input and the signal ``y`` on its +Q-output. + +Usually the imperative code fragments used in behavioural modelling can contain +statements for conditional execution (``if``- and ``case``-statements in +Verilog) as well as loops, as long as those loops can be completely unrolled. + +Interestingly there seems to be no other FOSS Tool that is capable of performing +Verilog or VHDL behavioural syntheses besides Yosys. + +Register-Transfer Level (RTL) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On the Register-Transfer Level the design is represented by combinatorial data +paths and registers (usually d-type flip flops). The following Verilog code +fragment is equivalent to the previous Verilog example, but is in RTL +representation: + +.. code:: verilog + :number-lines: + + assign tmp = a + b; // combinatorial data path + + always @(posedge clk) // register + y <= tmp; + +A design in RTL representation is usually stored using HDLs like Verilog and +VHDL. But only a very limited subset of features is used, namely minimalistic +always-blocks (Verilog) or process-blocks (VHDL) that model the register type +used and unconditional assignments for the datapath logic. The use of HDLs on +this level simplifies simulation as no additional tools are required to simulate +a design in RTL representation. + +Many optimizations and analyses can be performed best at the RTL level. Examples +include FSM detection and optimization, identification of memories or other +larger building blocks and identification of shareable resources. + +Note that RTL is the first abstraction level in which the circuit is represented +as a graph of circuit elements (registers and combinatorial cells) and signals. +Such a graph, when encoded as list of cells and connections, is called a +netlist. + +RTL synthesis is easy as each circuit node element in the netlist can simply be +replaced with an equivalent gate-level circuit. However, usually the term RTL +synthesis does not only refer to synthesizing an RTL netlist to a gate level +netlist but also to performing a number of highly sophisticated optimizations +within the RTL representation, such as the examples listed above. + +A number of FOSS tools exist that can perform isolated tasks within the domain +of RTL synthesis steps. But there seems to be no FOSS tool that covers a wide +range of RTL synthesis operations. + +Logical gate level +~~~~~~~~~~~~~~~~~~ + +At the logical gate level the design is represented by a netlist that uses only +cells from a small number of single-bit cells, such as basic logic gates (AND, +OR, NOT, XOR, etc.) and registers (usually D-Type Flip-flops). + +A number of netlist formats exists that can be used on this level, e.g. the +Electronic Design Interchange Format (EDIF), but for ease of simulation often a +HDL netlist is used. The latter is a HDL file (Verilog or VHDL) that only uses +the most basic language constructs for instantiation and connecting of cells. + +There are two challenges in logic synthesis: First finding opportunities for +optimizations within the gate level netlist and second the optimal (or at least +good) mapping of the logic gate netlist to an equivalent netlist of physically +available gate types. + +The simplest approach to logic synthesis is two-level logic synthesis, where a +logic function is converted into a sum-of-products representation, e.g. using a +Karnaugh map. This is a simple approach, but has exponential worst-case effort +and cannot make efficient use of physical gates other than AND/NAND-, OR/NOR- +and NOT-Gates. + +Therefore modern logic synthesis tools utilize much more complicated multi-level +logic synthesis algorithms :cite:p:`MultiLevelLogicSynth`. Most of these +algorithms convert the logic function to a Binary-Decision-Diagram (BDD) or +And-Inverter-Graph (AIG) and work from that representation. The former has the +advantage that it has a unique normalized form. The latter has much better worst +case performance and is therefore better suited for the synthesis of large logic +functions. + +Good FOSS tools exists for multi-level logic synthesis . + +Yosys contains basic logic synthesis functionality but can also use ABC for the +logic synthesis step. Using ABC is recommended. + +Physical gate level +~~~~~~~~~~~~~~~~~~~ + +On the physical gate level only gates are used that are physically available on +the target architecture. In some cases this may only be NAND, NOR and NOT gates +as well as D-Type registers. In other cases this might include cells that are +more complex than the cells used at the logical gate level (e.g. complete +half-adders). In the case of an FPGA-based design the physical gate level +representation is a netlist of LUTs with optional output registers, as these are +the basic building blocks of FPGA logic cells. + +For the synthesis tool chain this abstraction is usually the lowest level. In +case of an ASIC-based design the cell library might contain further information +on how the physical cells map to individual switches (transistors). + +Switch level +~~~~~~~~~~~~ + +A switch level representation of a circuit is a netlist utilizing single +transistors as cells. Switch level modelling is possible in Verilog and VHDL, +but is seldom used in modern designs, as in modern digital ASIC or FPGA flows +the physical gates are considered the atomic build blocks of the logic circuit. + +Yosys +~~~~~ + +Yosys is a Verilog HDL synthesis tool. This means that it takes a behavioural +design description as input and generates an RTL, logical gate or physical gate +level description of the design as output. Yosys' main strengths are behavioural +and RTL synthesis. A wide range of commands (synthesis passes) exist within +Yosys that can be used to perform a wide range of synthesis tasks within the +domain of behavioural, rtl and logic synthesis. Yosys is designed to be +extensible and therefore is a good basis for implementing custom synthesis tools +for specialised tasks. + +Features of synthesizable Verilog +--------------------------------- + +The subset of Verilog :cite:p:`Verilog2005` that is synthesizable is specified +in a separate IEEE standards document, the IEEE standard 1364.1-2002 +:cite:p:`VerilogSynth`. This standard also describes how certain language +constructs are to be interpreted in the scope of synthesis. + +This section provides a quick overview of the most important features of +synthesizable Verilog, structured in order of increasing complexity. + +Structural Verilog +~~~~~~~~~~~~~~~~~~ + +Structural Verilog (also known as Verilog Netlists) is a Netlist in Verilog +syntax. Only the following language constructs are used in this +case: + +- Constant values +- Wire and port declarations +- Static assignments of signals to other signals +- Cell instantiations + +Many tools (especially at the back end of the synthesis chain) only support +structural Verilog as input. ABC is an example of such a tool. Unfortunately +there is no standard specifying what Structural Verilog actually is, leading to +some confusion about what syntax constructs are supported in structural Verilog +when it comes to features such as attributes or multi-bit signals. + +Expressions in Verilog +~~~~~~~~~~~~~~~~~~~~~~ + +In all situations where Verilog accepts a constant value or signal name, +expressions using arithmetic operations such as ``+``, ``-`` and ``*``, boolean +operations such as ``&`` (AND), ``|`` (OR) and ``^`` (XOR) and many others +(comparison operations, unary operator, etc.) can also be used. + +During synthesis these operators are replaced by cells that implement the +respective function. + +Many FOSS tools that claim to be able to process Verilog in fact only support +basic structural Verilog and simple expressions. Yosys can be used to convert +full featured synthesizable Verilog to this simpler subset, thus enabling such +applications to be used with a richer set of Verilog features. + +Behavioural modelling +~~~~~~~~~~~~~~~~~~~~~ + +Code that utilizes the Verilog always statement is using Behavioural Modelling. +In behavioural modelling, a circuit is described by means of imperative program +code that is executed on certain events, namely any change, a rising edge, or a +falling edge of a signal. This is a very flexible construct during simulation +but is only synthesizable when one +of the following is modelled: + +- | **Asynchronous or latched logic** + | In this case the sensitivity list must contain all expressions that + are used within the always block. The syntax ``@*`` can be used for + these cases. Examples of this kind include: + + .. code:: verilog + :number-lines: + + // asynchronous + always @* begin + if (add_mode) + y <= a + b; + else + y <= a - b; + end + + // latched + always @* begin + if (!hold) + y <= a + b; + end + + Note that latched logic is often considered bad style and in many + cases just the result of sloppy HDL design. Therefore many synthesis + tools generate warnings whenever latched logic is generated. + +- | **Synchronous logic (with optional synchronous reset)** + | This is logic with d-type flip-flops on the output. In this case + the sensitivity list must only contain the respective clock edge. + Example: + + .. code:: verilog + :number-lines: + + // counter with synchronous reset + always @(posedge clk) begin + if (reset) + y <= 0; + else + y <= y + 1; + end + +- | **Synchronous logic with asynchronous reset** + | This is logic with d-type flip-flops with asynchronous resets on + the output. In this case the sensitivity list must only contain the + respective clock and reset edges. The values assigned in the reset + branch must be constant. Example: + + .. code:: verilog + :number-lines: + + // counter with asynchronous reset + always @(posedge clk, posedge reset) begin + if (reset) + y <= 0; + else + y <= y + 1; + end + +Many synthesis tools support a wider subset of flip-flops that can be modelled +using always-statements (including Yosys). But only the ones listed above are +covered by the Verilog synthesis standard and when writing new designs one +should limit herself or himself to these cases. + +In behavioural modelling, blocking assignments (=) and non-blocking assignments +(<=) can be used. The concept of blocking vs. non-blocking assignment is one of +the most misunderstood constructs in Verilog :cite:p:`Cummings00`. + +The blocking assignment behaves exactly like an assignment in any imperative +programming language, while with the non-blocking assignment the right hand side +of the assignment is evaluated immediately but the actual update of the left +hand side register is delayed until the end of the time-step. For example the +Verilog code ``a <= b; b <= a;`` exchanges the values of the two registers. + + +Functions and tasks +~~~~~~~~~~~~~~~~~~~ + +Verilog supports Functions and Tasks to bundle statements that are used in +multiple places (similar to Procedures in imperative programming). Both +constructs can be implemented easily by substituting the function/task-call with +the body of the function or task. + +Conditionals, loops and generate-statements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Verilog supports ``if-else``-statements and ``for``-loops inside +``always``-statements. + +It also supports both features in ``generate``-statements on the module level. +This can be used to selectively enable or disable parts of the module based on +the module parameters (``if-else``) or to generate a set of similar subcircuits +(``for``). + +While the ``if-else``-statement inside an always-block is part of behavioural +modelling, the three other cases are (at least for a synthesis tool) part of a +built-in macro processor. Therefore it must be possible for the synthesis tool +to completely unroll all loops and evaluate the condition in all +``if-else``-statement in ``generate``-statements using const-folding.. + +Arrays and memories +~~~~~~~~~~~~~~~~~~~ + +Verilog supports arrays. This is in general a synthesizable language feature. In +most cases arrays can be synthesized by generating addressable memories. +However, when complex or asynchronous access patterns are used, it is not +possible to model an array as memory. In these cases the array must be modelled +using individual signals for each word and all accesses to the array must be +implemented using large multiplexers. + +In some cases it would be possible to model an array using memories, but it is +not desired. Consider the following delay circuit: + +.. code:: verilog + :number-lines: + + module (clk, in_data, out_data); + + parameter BITS = 8; + parameter STAGES = 4; + + input clk; + input [BITS-1:0] in_data; + output [BITS-1:0] out_data; + reg [BITS-1:0] ffs [STAGES-1:0]; + + integer i; + always @(posedge clk) begin + ffs[0] <= in_data; + for (i = 1; i < STAGES; i = i+1) + ffs[i] <= ffs[i-1]; + end + + assign out_data = ffs[STAGES-1]; + + endmodule + +This could be implemented using an addressable memory with STAGES input and +output ports. A better implementation would be to use a simple chain of +flip-flops (a so-called shift register). This better implementation can either +be obtained by first creating a memory-based implementation and then optimizing +it based on the static address signals for all ports or directly identifying +such situations in the language front end and converting all memory accesses to +direct accesses to the correct signals. + +Challenges in digital circuit synthesis +--------------------------------------- + +This section summarizes the most important challenges in digital circuit +synthesis. Tools can be characterized by how well they address these topics. + +Standards compliance +~~~~~~~~~~~~~~~~~~~~ + +The most important challenge is compliance with the HDL standards in question +(in case of Verilog the IEEE Standards 1364.1-2002 and 1364-2005). This can be +broken down in two items: + +- Completeness of implementation of the standard +- Correctness of implementation of the standard + +Completeness is mostly important to guarantee compatibility with existing HDL +code. Once a design has been verified and tested, HDL designers are very +reluctant regarding changes to the design, even if it is only about a few minor +changes to work around a missing feature in a new synthesis tool. + +Correctness is crucial. In some areas this is obvious (such as correct synthesis +of basic behavioural models). But it is also crucial for the areas that concern +minor details of the standard, such as the exact rules for handling signed +expressions, even when the HDL code does not target different synthesis tools. +This is because (unlike software source code that is only processed by +compilers), in most design flows HDL code is not only processed by the synthesis +tool but also by one or more simulators and sometimes even a formal verification +tool. It is key for this verification process that all these tools use the same +interpretation for the HDL code. + +Optimizations +~~~~~~~~~~~~~ + +Generally it is hard to give a one-dimensional description of how well a +synthesis tool optimizes the design. First of all because not all optimizations +are applicable to all designs and all synthesis tasks. Some optimizations work +(best) on a coarse-grained level (with complex cells such as adders or +multipliers) and others work (best) on a fine-grained level (single bit gates). +Some optimizations target area and others target speed. Some work well on large +designs while others don't scale well and can only be applied to small designs. + +A good tool is capable of applying a wide range of optimizations at different +levels of abstraction and gives the designer control over which optimizations +are performed (or skipped) and what the optimization goals are. + +Technology mapping +~~~~~~~~~~~~~~~~~~ + +Technology mapping is the process of converting the design into a netlist of +cells that are available in the target architecture. In an ASIC flow this might +be the process-specific cell library provided by the fab. In an FPGA flow this +might be LUT cells as well as special function units such as dedicated +multipliers. In a coarse-grain flow this might even be more complex special +function units. + +An open and vendor independent tool is especially of interest if it supports a +wide range of different types of target architectures. + +Script-based synthesis flows +---------------------------- + +A digital design is usually started by implementing a high-level or system-level +simulation of the desired function. This description is then manually +transformed (or re-implemented) into a synthesizable lower-level description +(usually at the behavioural level) and the equivalence of the two +representations is verified by simulating both and comparing the simulation +results. + +Then the synthesizable description is transformed to lower-level representations +using a series of tools and the results are again verified using simulation. +This process is illustrated in :numref:`Fig. %s `. + +.. figure:: /_images/primer/basics_flow.* + :class: width-helper + :name: fig:Basics_flow + + Typical design flow. Green boxes represent manually created models. + Orange boxes represent models generated by synthesis tools. + + +In this example the System Level Model and the Behavioural Model are both +manually written design files. After the equivalence of system level model and +behavioural model has been verified, the lower level representations of the +design can be generated using synthesis tools. Finally the RTL Model and the +Gate-Level Model are verified and the design process is finished. + +However, in any real-world design effort there will be multiple iterations for +this design process. The reason for this can be the late change of a design +requirement or the fact that the analysis of a low-abstraction model +(e.g. gate-level timing analysis) revealed that a design change is required in +order to meet the design requirements (e.g. maximum possible clock speed). + +Whenever the behavioural model or the system level model is changed their +equivalence must be re-verified by re-running the simulations and comparing the +results. Whenever the behavioural model is changed the synthesis must be re-run +and the synthesis results must be re-verified. + +In order to guarantee reproducibility it is important to be able to re-run all +automatic steps in a design project with a fixed set of settings easily. Because +of this, usually all programs used in a synthesis flow can be controlled using +scripts. This means that all functions are available via text commands. When +such a tool provides a GUI, this is complementary to, and not instead of, a +command line interface. + +Usually a synthesis flow in an UNIX/Linux environment would be controlled by a +shell script that calls all required tools (synthesis and +simulation/verification in this example) in the correct order. Each of these +tools would be called with a script file containing commands for the respective +tool. All settings required for the tool would be provided by these script files +so that no manual interaction would be necessary. These script files are +considered design sources and should be kept under version control just like the +source code of the system level and the behavioural model. + +Methods from compiler design +---------------------------- + +Some parts of synthesis tools involve problem domains that are traditionally +known from compiler design. This section addresses some of these domains. + +Lexing and parsing +~~~~~~~~~~~~~~~~~~ + +The best known concepts from compiler design are probably lexing and parsing. +These are two methods that together can be used to process complex computer +languages easily. :cite:p:`Dragonbook` + +A lexer consumes single characters from the input and generates a stream of +lexical tokens that consist of a type and a value. For example the Verilog input +:verilog:`assign foo = bar + 42;` might be translated by the lexer to the list +of lexical tokens given in :numref:`Tab. %s `. + +.. table:: Exemplary token list for the statement :verilog:`assign foo = bar + 42;` + :name: tab:Basics_tokens + + ============== =============== + Token-Type Token-Value + ============== =============== + TOK_ASSIGN \- + TOK_IDENTIFIER "foo" + TOK_EQ \- + TOK_IDENTIFIER "bar" + TOK_PLUS \- + TOK_NUMBER 42 + TOK_SEMICOLON \- + ============== =============== + +The lexer is usually generated by a lexer generator (e.g. flex ) from a +description file that is using regular expressions to specify the text pattern +that should match the individual tokens. + +The lexer is also responsible for skipping ignored characters (such as +whitespace outside string constants and comments in the case of Verilog) and +converting the original text snippet to a token value. + +Note that individual keywords use different token types (instead of a keyword +type with different token values). This is because the parser usually can only +use the Token-Type to make a decision on the grammatical role of a token. + +The parser then transforms the list of tokens into a parse tree that closely +resembles the productions from the computer languages grammar. As the lexer, the +parser is also typically generated by a code generator (e.g. bison) from a +grammar description in Backus-Naur Form (BNF). + +Let's consider the following BNF (in Bison syntax): + +.. code:: none + :number-lines: + + assign_stmt: TOK_ASSIGN TOK_IDENTIFIER TOK_EQ expr TOK_SEMICOLON; + expr: TOK_IDENTIFIER | TOK_NUMBER | expr TOK_PLUS expr; + +.. figure:: /_images/primer/basics_parsetree.* + :class: width-helper + :name: fig:Basics_parsetree + + Example parse tree for the Verilog expression + :verilog:`assign foo = bar + 42;` + +The parser converts the token list to the parse tree in :numref:`Fig. %s +`. Note that the parse tree never actually exists as a +whole as data structure in memory. Instead the parser calls user-specified code +snippets (so-called reduce-functions) for all inner nodes of the parse tree in +depth-first order. + +In some very simple applications (e.g. code generation for stack machines) it is +possible to perform the task at hand directly in the reduce functions. But +usually the reduce functions are only used to build an in-memory data structure +with the relevant information from the parse tree. This data structure is called +an abstract syntax tree (AST). + +The exact format for the abstract syntax tree is application specific (while the +format of the parse tree and token list are mostly dictated by the grammar of +the language at hand). :numref:`Figure %s ` illustrates what an +AST for the parse tree in :numref:`Fig. %s ` could look +like. + +Usually the AST is then converted into yet another representation that is more +suitable for further processing. In compilers this is often an assembler-like +three-address-code intermediate representation. :cite:p:`Dragonbook` + +.. figure:: /_images/primer/basics_ast.* + :class: width-helper + :name: fig:Basics_ast + + Example abstract syntax tree for the Verilog expression + :verilog:`assign foo = bar + 42;` + + +Multi-pass compilation +~~~~~~~~~~~~~~~~~~~~~~ + +Complex problems are often best solved when split up into smaller problems. This +is certainly true for compilers as well as for synthesis tools. The components +responsible for solving the smaller problems can be connected in two different +ways: through Single-Pass Pipelining and by using Multiple Passes. + +Traditionally a parser and lexer are connected using the pipelined approach: The +lexer provides a function that is called by the parser. This function reads data +from the input until a complete lexical token has been read. Then this token is +returned to the parser. So the lexer does not first generate a complete list of +lexical tokens and then pass it to the parser. Instead they run concurrently and +the parser can consume tokens as the lexer produces them. + +The single-pass pipelining approach has the advantage of lower memory footprint +(at no time must the complete design be kept in memory) but has the disadvantage +of tighter coupling between the interacting components. + +Therefore single-pass pipelining should only be used when the lower memory +footprint is required or the components are also conceptually tightly coupled. +The latter certainly is the case for a parser and its lexer. But when data is +passed between two conceptually loosely coupled components it is often +beneficial to use a multi-pass approach. + +In the multi-pass approach the first component processes all the data and the +result is stored in a in-memory data structure. Then the second component is +called with this data. This reduces complexity, as only one component is running +at a time. It also improves flexibility as components can be exchanged easier. + +Most modern compilers are multi-pass compilers. + +Static Single Assignment (SSA) form +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In imperative programming (and behavioural HDL design) it is possible to assign +the same variable multiple times. This can either mean that the variable is +independently used in two different contexts or that the final value of the +variable depends on a condition. + +The following examples show C code in which one variable is used independently +in two different contexts: + +.. code:: c++ + :number-lines: + + void demo1() + { + int a = 1; + printf("%d\n", a); + + a = 2; + printf("%d\n", a); + } + +.. code:: c++ + + void demo1() + { + int a = 1; + printf("%d\n", a); + + int b = 2; + printf("%d\n", b); + } + +.. code:: c++ + :number-lines: + + void demo2(bool foo) + { + int a; + if (foo) { + a = 23; + printf("%d\n", a); + } else { + a = 42; + printf("%d\n", a); + } + } + +.. code:: c++ + + void demo2(bool foo) + { + int a, b; + if (foo) { + a = 23; + printf("%d\n", a); + } else { + b = 42; + printf("%d\n", b); + } + } + +In both examples the left version (only variable ``a``) and the right version +(variables ``a`` and ``b``) are equivalent. Therefore it is desired for further +processing to bring the code in an equivalent form for both cases. + +In the following example the variable is assigned twice but it cannot be easily +replaced by two variables: + +.. code:: c++ + + void demo3(bool foo) + { + int a = 23 + if (foo) + a = 42; + printf("%d\n", a); + } + +Static single assignment (SSA) form is a representation of imperative code that +uses identical representations for the left and right version of demos 1 and 2, +but can still represent demo 3. In SSA form each assignment assigns a new +variable (usually written with an index). But it also introduces a special +:math:`\Phi`-function to merge the different instances of a variable when +needed. In C-pseudo-code the demo 3 would be written as follows using SSA from: + +.. code:: c++ + + void demo3(bool foo) + { + int a_1, a_2, a_3; + a_1 = 23 + if (foo) + a_2 = 42; + a_3 = phi(a_1, a_2); + printf("%d\n", a_3); + } + +The :math:`\Phi`-function is usually interpreted as "these variables must be +stored in the same memory location" during code generation. Most modern +compilers for imperative languages such as C/C++ use SSA form for at least some +of its passes as it is very easy to manipulate and analyse. + +.. [1] + In recent years formal equivalence checking also became an important + verification method for validating RTL and lower abstraction + representation of the design. diff --git a/docs/source/bib.rst b/docs/source/bib.rst new file mode 100644 index 00000000000..2675cec16cb --- /dev/null +++ b/docs/source/bib.rst @@ -0,0 +1,10 @@ +.. only:: html + + Literature references + ===================== + + .. rubric:: Bibliography + +.. bibliography:: literature.bib + +.. todo:: see if we can get the two hanging appnotes as lit references diff --git a/docs/source/cmd_ref.rst b/docs/source/cmd_ref.rst new file mode 100644 index 00000000000..f0c4eaaf927 --- /dev/null +++ b/docs/source/cmd_ref.rst @@ -0,0 +1,16 @@ +.. _cmd_ref: + +================================================================================ +Command line reference +================================================================================ + +.. literalinclude:: /temp/yosys + :start-at: Usage + +.. toctree:: + :caption: Command reference + :maxdepth: 1 + :glob: + + /appendix/env_vars + /cmd/* diff --git a/manual/PRESENTATION_ExSyn/.gitignore b/docs/source/code_examples/.gitignore similarity index 50% rename from manual/PRESENTATION_ExSyn/.gitignore rename to docs/source/code_examples/.gitignore index cf658897d52..b4a858a0134 100644 --- a/manual/PRESENTATION_ExSyn/.gitignore +++ b/docs/source/code_examples/.gitignore @@ -1 +1,2 @@ *.dot +*.pdf diff --git a/manual/PRESENTATION_ExOth/axis_master.v b/docs/source/code_examples/axis/axis_master.v similarity index 100% rename from manual/PRESENTATION_ExOth/axis_master.v rename to docs/source/code_examples/axis/axis_master.v diff --git a/manual/PRESENTATION_ExOth/axis_test.v b/docs/source/code_examples/axis/axis_test.v similarity index 100% rename from manual/PRESENTATION_ExOth/axis_test.v rename to docs/source/code_examples/axis/axis_test.v diff --git a/manual/PRESENTATION_ExOth/axis_test.ys b/docs/source/code_examples/axis/axis_test.ys similarity index 70% rename from manual/PRESENTATION_ExOth/axis_test.ys rename to docs/source/code_examples/axis/axis_test.ys index 19663ac7733..a55d017d9d6 100644 --- a/manual/PRESENTATION_ExOth/axis_test.ys +++ b/docs/source/code_examples/axis/axis_test.ys @@ -2,4 +2,4 @@ read_verilog -sv axis_master.v axis_test.v hierarchy -top axis_test proc; flatten;; -sat -falsify -seq 50 -prove-asserts +sat -seq 50 -prove-asserts diff --git a/manual/PRESENTATION_Prog/.gitignore b/docs/source/code_examples/extensions/.gitignore similarity index 100% rename from manual/PRESENTATION_Prog/.gitignore rename to docs/source/code_examples/extensions/.gitignore diff --git a/docs/source/code_examples/extensions/Makefile b/docs/source/code_examples/extensions/Makefile new file mode 100644 index 00000000000..288983ed356 --- /dev/null +++ b/docs/source/code_examples/extensions/Makefile @@ -0,0 +1,32 @@ +PROGRAM_PREFIX := + +YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys + +.PHONY: all dots +all: dots test0.log test1.log test2.log +dots: test1.dot + +CXXFLAGS=$(shell $(YOSYS)-config --cxxflags) +DATDIR=$(shell $(YOSYS)-config --datdir) + +my_cmd.so: my_cmd.cc + $(YOSYS)-config --exec --cxx $(subst $(DATDIR),../../../../share,$(CXXFLAGS)) --ldflags -o my_cmd.so -shared my_cmd.cc --ldlibs + +test0.log: my_cmd.so + $(YOSYS) -Ql test0.log_new -m ./my_cmd.so -p 'my_cmd foo bar' absval_ref.v + mv test0.log_new test0.log + +test1.log: my_cmd.so + $(YOSYS) -Ql test1.log_new -m ./my_cmd.so -p 'clean; test1; dump' absval_ref.v + mv test1.log_new test1.log + +test1.dot: my_cmd.so + $(YOSYS) -m ./my_cmd.so -p 'test1; show -format dot -prefix test1' + +test2.log: my_cmd.so + $(YOSYS) -Ql test2.log_new -m ./my_cmd.so -p 'hierarchy -top test; test2' sigmap_test.v + mv test2.log_new test2.log + +.PHONY: clean +clean: + rm -f *.d *.so *.dot diff --git a/manual/PRESENTATION_Prog/absval_ref.v b/docs/source/code_examples/extensions/absval_ref.v similarity index 69% rename from manual/PRESENTATION_Prog/absval_ref.v rename to docs/source/code_examples/extensions/absval_ref.v index ca0a115a0a7..3f547b50b28 100644 --- a/manual/PRESENTATION_Prog/absval_ref.v +++ b/docs/source/code_examples/extensions/absval_ref.v @@ -1,3 +1,3 @@ module absval_ref(input signed [3:0] a, output [3:0] y); - assign y = a[3] ? -a : a; + assign y = a[3] ? -a : a; endmodule diff --git a/manual/PRESENTATION_Prog/my_cmd.cc b/docs/source/code_examples/extensions/my_cmd.cc similarity index 97% rename from manual/PRESENTATION_Prog/my_cmd.cc rename to docs/source/code_examples/extensions/my_cmd.cc index 9cb4b8e38b1..36ddbe1758f 100644 --- a/manual/PRESENTATION_Prog/my_cmd.cc +++ b/docs/source/code_examples/extensions/my_cmd.cc @@ -14,7 +14,7 @@ struct MyPass : public Pass { log("Modules in current design:\n"); for (auto mod : design->modules()) - log(" %s (%zd wires, %zd cells)\n", log_id(mod), + log(" %s (%d wires, %d cells)\n", log_id(mod), GetSize(mod->wires()), GetSize(mod->cells())); } } MyPass; diff --git a/manual/PRESENTATION_Prog/sigmap_test.v b/docs/source/code_examples/extensions/sigmap_test.v similarity index 67% rename from manual/PRESENTATION_Prog/sigmap_test.v rename to docs/source/code_examples/extensions/sigmap_test.v index 18dcf5eb78e..8d0b4383689 100644 --- a/manual/PRESENTATION_Prog/sigmap_test.v +++ b/docs/source/code_examples/extensions/sigmap_test.v @@ -1,3 +1,3 @@ module test(input a, output x, y); -assign x = a, y = a; + assign x = a, y = a; endmodule diff --git a/docs/source/code_examples/fifo/Makefile b/docs/source/code_examples/fifo/Makefile new file mode 100644 index 00000000000..0a1186a62d4 --- /dev/null +++ b/docs/source/code_examples/fifo/Makefile @@ -0,0 +1,24 @@ +PROGRAM_PREFIX := + +YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys + +DOT_NAMES = addr_gen_hier addr_gen_proc addr_gen_clean +DOT_NAMES += rdata_proc rdata_flat rdata_adffe rdata_memrdv2 rdata_alumacc rdata_coarse +MAPDOT_NAMES = rdata_map_ram rdata_map_ffram rdata_map_gates +MAPDOT_NAMES += rdata_map_ffs rdata_map_luts rdata_map_cells + +DOTS := $(addsuffix .dot,$(DOT_NAMES)) +MAPDOTS := $(addsuffix .dot,$(MAPDOT_NAMES)) + +all: dots fifo.out fifo.stat +dots: $(DOTS) $(MAPDOTS) + +$(DOTS) fifo.out: fifo.v fifo.ys + $(YOSYS) fifo.ys -l fifo.out -Q -T + +$(MAPDOTS) fifo.stat: fifo.v fifo_map.ys + $(YOSYS) fifo_map.ys + +.PHONY: clean +clean: + rm -f *.dot diff --git a/docs/source/code_examples/fifo/fifo.libmap b/docs/source/code_examples/fifo/fifo.libmap new file mode 100644 index 00000000000..5889b7cfd43 --- /dev/null +++ b/docs/source/code_examples/fifo/fifo.libmap @@ -0,0 +1,65 @@ + +yosys> debug memory_libmap -lib +/ice40/brams.txt -lib +/ice40/spram.txt -no-auto-huge + +yosys> memory_libmap -lib +/ice40/brams.txt -lib +/ice40/spram.txt -no-auto-huge + +4. Executing MEMORY_LIBMAP pass (mapping memories to cells). +Memory fifo.data mapping candidates (post-geometry): +- logic fallback + - cost: 2048.000000 +- $__ICE40_RAM4K_: + - option HAS_BE 0 + - emulation score: 7 + - replicates (for ports): 1 + - replicates (for data): 1 + - mux score: 0 + - demux score: 0 + - cost: 78.000000 + - abits 11 dbits 2 4 8 16 + - chosen base width 8 + - swizzle 0 1 2 3 4 5 6 7 + - emulate read-first behavior + - write port 0: port group W + - widths 2 4 8 + - read port 0: port group R + - widths 2 4 8 16 + - emulate transparency with write port 0 +- $__ICE40_RAM4K_: + - option HAS_BE 1 + - emulation score: 7 + - replicates (for ports): 1 + - replicates (for data): 1 + - mux score: 0 + - demux score: 0 + - cost: 78.000000 + - abits 11 dbits 2 4 8 16 + - byte width 1 + - chosen base width 8 + - swizzle 0 1 2 3 4 5 6 7 + - emulate read-first behavior + - write port 0: port group W + - widths 16 + - read port 0: port group R + - widths 2 4 8 16 + - emulate transparency with write port 0 +Memory fifo.data mapping candidates (after post-geometry prune): +- logic fallback + - cost: 2048.000000 +- $__ICE40_RAM4K_: + - option HAS_BE 0 + - emulation score: 7 + - replicates (for ports): 1 + - replicates (for data): 1 + - mux score: 0 + - demux score: 0 + - cost: 78.000000 + - abits 11 dbits 2 4 8 16 + - chosen base width 8 + - swizzle 0 1 2 3 4 5 6 7 + - emulate read-first behavior + - write port 0: port group W + - widths 2 4 8 + - read port 0: port group R + - widths 2 4 8 16 + - emulate transparency with write port 0 +mapping memory fifo.data via $__ICE40_RAM4K_ diff --git a/docs/source/code_examples/fifo/fifo.out b/docs/source/code_examples/fifo/fifo.out new file mode 100644 index 00000000000..ac132ee6c3d --- /dev/null +++ b/docs/source/code_examples/fifo/fifo.out @@ -0,0 +1,425 @@ + +-- Executing script file `fifo.ys' -- +$ yosys fifo.v + +-- Parsing `fifo.v' using frontend ` -vlog2k' -- + +1. Executing Verilog-2005 frontend: fifo.v +Parsing Verilog input from `fifo.v' to AST representation. +Storing AST representation for module `$abstract\addr_gen'. +Storing AST representation for module `$abstract\fifo'. +Successfully finished Verilog frontend. +echo on + +yosys> hierarchy -top addr_gen + +2. Executing HIERARCHY pass (managing design hierarchy). + +3. Executing AST frontend in derive mode using pre-parsed AST for module `\addr_gen'. +Generating RTLIL representation for module `\addr_gen'. + +3.1. Analyzing design hierarchy.. +Top module: \addr_gen + +3.2. Analyzing design hierarchy.. +Top module: \addr_gen +Removing unused module `$abstract\fifo'. +Removing unused module `$abstract\addr_gen'. +Removed 2 unused modules. + +yosys> select -module addr_gen + +yosys [addr_gen]> select -list +addr_gen +addr_gen/$1\addr[7:0] +addr_gen/$add$fifo.v:19$3_Y +addr_gen/$eq$fifo.v:16$2_Y +addr_gen/$0\addr[7:0] +addr_gen/addr +addr_gen/rst +addr_gen/clk +addr_gen/en +addr_gen/$add$fifo.v:19$3 +addr_gen/$eq$fifo.v:16$2 +addr_gen/$proc$fifo.v:0$4 +addr_gen/$proc$fifo.v:12$1 + +yosys [addr_gen]> select t:* + +yosys [addr_gen]*> select -list +addr_gen/$add$fifo.v:19$3 +addr_gen/$eq$fifo.v:16$2 + +yosys [addr_gen]*> select -set new_cells % + +yosys [addr_gen]*> select -clear + +yosys> show -format dot -prefix addr_gen_show addr_gen + +4. Generating Graphviz representation of design. +Writing dot description to `addr_gen_show.dot'. +Dumping module addr_gen to page 1. + +yosys> show -format dot -prefix new_cells_show -notitle @new_cells + +5. Generating Graphviz representation of design. +Writing dot description to `new_cells_show.dot'. +Dumping selected parts of module addr_gen to page 1. + +yosys> show -color maroon3 @new_cells -color cornflowerblue p:* -notitle -format dot -prefix addr_gen_hier + +6. Generating Graphviz representation of design. +Writing dot description to `addr_gen_hier.dot'. +Dumping module addr_gen to page 1. + +yosys> proc -noopt + +7. Executing PROC pass (convert processes to netlists). + +yosys> proc_clean + +7.1. Executing PROC_CLEAN pass (remove empty switches from decision trees). +Cleaned up 0 empty switches. + +yosys> proc_rmdead + +7.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees). +Marked 2 switch rules as full_case in process $proc$fifo.v:12$1 in module addr_gen. +Removed a total of 0 dead cases. + +yosys> proc_prune + +7.3. Executing PROC_PRUNE pass (remove redundant assignments in processes). +Removed 0 redundant assignments. +Promoted 1 assignment to connection. + +yosys> proc_init + +7.4. Executing PROC_INIT pass (extract init attributes). +Found init rule in `\addr_gen.$proc$fifo.v:0$4'. + Set init value: \addr = 8'00000000 + +yosys> proc_arst + +7.5. Executing PROC_ARST pass (detect async resets in processes). +Found async reset \rst in `\addr_gen.$proc$fifo.v:12$1'. + +yosys> proc_rom + +7.6. Executing PROC_ROM pass (convert switches to ROMs). +Converted 0 switches. + + +yosys> proc_mux + +7.7. Executing PROC_MUX pass (convert decision trees to multiplexers). +Creating decoders for process `\addr_gen.$proc$fifo.v:0$4'. +Creating decoders for process `\addr_gen.$proc$fifo.v:12$1'. + 1/1: $0\addr[7:0] + +yosys> proc_dlatch + +7.8. Executing PROC_DLATCH pass (convert process syncs to latches). + +yosys> proc_dff + +7.9. Executing PROC_DFF pass (convert process syncs to FFs). +Creating register for signal `\addr_gen.\addr' using process `\addr_gen.$proc$fifo.v:12$1'. + created $adff cell `$procdff$10' with positive edge clock and positive level reset. + +yosys> proc_memwr + +7.10. Executing PROC_MEMWR pass (convert process memory writes to cells). + +yosys> proc_clean + +7.11. Executing PROC_CLEAN pass (remove empty switches from decision trees). +Removing empty process `addr_gen.$proc$fifo.v:0$4'. +Found and cleaned up 2 empty switches in `\addr_gen.$proc$fifo.v:12$1'. +Removing empty process `addr_gen.$proc$fifo.v:12$1'. +Cleaned up 2 empty switches. + +yosys> select -set new_cells t:$mux t:*dff + +yosys> show -color maroon3 @new_cells -notitle -format dot -prefix addr_gen_proc + +8. Generating Graphviz representation of design. +Writing dot description to `addr_gen_proc.dot'. +Dumping module addr_gen to page 1. + +yosys> opt_expr + +9. Executing OPT_EXPR pass (perform const folding). +Optimizing module addr_gen. + +yosys> clean +Removed 0 unused cells and 4 unused wires. + +yosys> select -set new_cells t:$eq + +yosys> show -color cornflowerblue @new_cells -notitle -format dot -prefix addr_gen_clean + +10. Generating Graphviz representation of design. +Writing dot description to `addr_gen_clean.dot'. +Dumping module addr_gen to page 1. + +yosys> design -reset + +yosys> read_verilog fifo.v + +11. Executing Verilog-2005 frontend: fifo.v +Parsing Verilog input from `fifo.v' to AST representation. +Generating RTLIL representation for module `\addr_gen'. +Generating RTLIL representation for module `\fifo'. +Successfully finished Verilog frontend. + +yosys> hierarchy -check -top fifo + +12. Executing HIERARCHY pass (managing design hierarchy). + +12.1. Analyzing design hierarchy.. +Top module: \fifo +Used module: \addr_gen +Parameter \MAX_DATA = 256 + +12.2. Executing AST frontend in derive mode using pre-parsed AST for module `\addr_gen'. +Parameter \MAX_DATA = 256 +Generating RTLIL representation for module `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'. +Parameter \MAX_DATA = 256 +Found cached RTLIL representation for module `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000'. + +12.3. Analyzing design hierarchy.. +Top module: \fifo +Used module: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000 + +12.4. Analyzing design hierarchy.. +Top module: \fifo +Used module: $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000 +Removing unused module `\addr_gen'. +Removed 1 unused modules. + +yosys> proc + +13. Executing PROC pass (convert processes to netlists). + +yosys> proc_clean + +13.1. Executing PROC_CLEAN pass (remove empty switches from decision trees). +Cleaned up 0 empty switches. + +yosys> proc_rmdead + +13.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees). +Marked 2 switch rules as full_case in process $proc$fifo.v:62$24 in module fifo. +Marked 1 switch rules as full_case in process $proc$fifo.v:36$16 in module fifo. +Marked 2 switch rules as full_case in process $proc$fifo.v:12$32 in module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000. +Removed a total of 0 dead cases. + +yosys> proc_prune + +13.3. Executing PROC_PRUNE pass (remove redundant assignments in processes). +Removed 0 redundant assignments. +Promoted 6 assignments to connections. + +yosys> proc_init + +13.4. Executing PROC_INIT pass (extract init attributes). +Found init rule in `\fifo.$proc$fifo.v:0$31'. + Set init value: \count = 9'000000000 +Found init rule in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'. + Set init value: \addr = 8'00000000 + +yosys> proc_arst + +13.5. Executing PROC_ARST pass (detect async resets in processes). +Found async reset \rst in `\fifo.$proc$fifo.v:62$24'. +Found async reset \rst in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. + +yosys> proc_rom + +13.6. Executing PROC_ROM pass (convert switches to ROMs). +Converted 0 switches. + + +yosys> proc_mux + +13.7. Executing PROC_MUX pass (convert decision trees to multiplexers). +Creating decoders for process `\fifo.$proc$fifo.v:0$31'. +Creating decoders for process `\fifo.$proc$fifo.v:62$24'. + 1/1: $0\count[8:0] +Creating decoders for process `\fifo.$proc$fifo.v:36$16'. + 1/3: $1$memwr$\data$fifo.v:38$15_EN[7:0]$22 + 2/3: $1$memwr$\data$fifo.v:38$15_DATA[7:0]$21 + 3/3: $1$memwr$\data$fifo.v:38$15_ADDR[7:0]$20 +Creating decoders for process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'. +Creating decoders for process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. + 1/1: $0\addr[7:0] + +yosys> proc_dlatch + +13.8. Executing PROC_DLATCH pass (convert process syncs to latches). + +yosys> proc_dff + +13.9. Executing PROC_DFF pass (convert process syncs to FFs). +Creating register for signal `\fifo.\count' using process `\fifo.$proc$fifo.v:62$24'. + created $adff cell `$procdff$55' with positive edge clock and positive level reset. +Creating register for signal `\fifo.\rdata' using process `\fifo.$proc$fifo.v:36$16'. + created $dff cell `$procdff$56' with positive edge clock. +Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_ADDR' using process `\fifo.$proc$fifo.v:36$16'. + created $dff cell `$procdff$57' with positive edge clock. +Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_DATA' using process `\fifo.$proc$fifo.v:36$16'. + created $dff cell `$procdff$58' with positive edge clock. +Creating register for signal `\fifo.$memwr$\data$fifo.v:38$15_EN' using process `\fifo.$proc$fifo.v:36$16'. + created $dff cell `$procdff$59' with positive edge clock. +Creating register for signal `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.\addr' using process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. + created $adff cell `$procdff$60' with positive edge clock and positive level reset. + +yosys> proc_memwr + +13.10. Executing PROC_MEMWR pass (convert process memory writes to cells). + +yosys> proc_clean + +13.11. Executing PROC_CLEAN pass (remove empty switches from decision trees). +Removing empty process `fifo.$proc$fifo.v:0$31'. +Found and cleaned up 2 empty switches in `\fifo.$proc$fifo.v:62$24'. +Removing empty process `fifo.$proc$fifo.v:62$24'. +Found and cleaned up 1 empty switch in `\fifo.$proc$fifo.v:36$16'. +Removing empty process `fifo.$proc$fifo.v:36$16'. +Removing empty process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:0$35'. +Found and cleaned up 2 empty switches in `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. +Removing empty process `$paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000.$proc$fifo.v:12$32'. +Cleaned up 5 empty switches. + +yosys> opt_expr -keepdc + +13.12. Executing OPT_EXPR pass (perform const folding). +Optimizing module fifo. +Optimizing module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000. + +yosys> select -set new_cells t:$memrd + +yosys> show -color maroon3 c:fifo_reader -color cornflowerblue @new_cells -notitle -format dot -prefix rdata_proc o:rdata %ci* + +14. Generating Graphviz representation of design. +Writing dot description to `rdata_proc.dot'. +Dumping selected parts of module fifo to page 1. + +yosys> flatten + +15. Executing FLATTEN pass (flatten design). +Deleting now unused module $paramod\addr_gen\MAX_DATA=s32'00000000000000000000000100000000. + + +yosys> clean +Removed 3 unused cells and 25 unused wires. + +yosys> select -set rdata_path o:rdata %ci* + +yosys> select -set new_cells @rdata_path o:rdata %ci3 %d i:* %d + +yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_flat @rdata_path + +16. Generating Graphviz representation of design. +Writing dot description to `rdata_flat.dot'. +Dumping selected parts of module fifo to page 1. + +yosys> opt_dff + +17. Executing OPT_DFF pass (perform DFF optimizations). +Adding EN signal on $procdff$55 ($adff) from module fifo (D = $0\count[8:0], Q = \count). +Adding EN signal on $flatten\fifo_writer.$procdff$60 ($adff) from module fifo (D = $flatten\fifo_writer.$procmux$51_Y, Q = \fifo_writer.addr). +Adding EN signal on $flatten\fifo_reader.$procdff$60 ($adff) from module fifo (D = $flatten\fifo_reader.$procmux$51_Y, Q = \fifo_reader.addr). + +yosys> select -set new_cells t:$adffe + +yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_adffe o:rdata %ci* + +18. Generating Graphviz representation of design. +Writing dot description to `rdata_adffe.dot'. +Dumping selected parts of module fifo to page 1. + +yosys> wreduce + +19. Executing WREDUCE pass (reducing word size of cells). +Removed top 31 bits (of 32) from port B of cell fifo.$add$fifo.v:66$27 ($add). +Removed top 23 bits (of 32) from port Y of cell fifo.$add$fifo.v:66$27 ($add). +Removed top 31 bits (of 32) from port B of cell fifo.$sub$fifo.v:68$30 ($sub). +Removed top 23 bits (of 32) from port Y of cell fifo.$sub$fifo.v:68$30 ($sub). +Removed top 1 bits (of 2) from port B of cell fifo.$auto$opt_dff.cc:195:make_patterns_logic$66 ($ne). +Removed cell fifo.$flatten\fifo_writer.$procmux$53 ($mux). +Removed top 31 bits (of 32) from port B of cell fifo.$flatten\fifo_writer.$add$fifo.v:19$34 ($add). +Removed top 24 bits (of 32) from port Y of cell fifo.$flatten\fifo_writer.$add$fifo.v:19$34 ($add). +Removed cell fifo.$flatten\fifo_reader.$procmux$53 ($mux). +Removed top 31 bits (of 32) from port B of cell fifo.$flatten\fifo_reader.$add$fifo.v:19$34 ($add). +Removed top 24 bits (of 32) from port Y of cell fifo.$flatten\fifo_reader.$add$fifo.v:19$34 ($add). +Removed top 23 bits (of 32) from wire fifo.$add$fifo.v:66$27_Y. +Removed top 24 bits (of 32) from wire fifo.$flatten\fifo_reader.$add$fifo.v:19$34_Y. + +yosys> show -notitle -format dot -prefix rdata_wreduce o:rdata %ci* + +20. Generating Graphviz representation of design. +Writing dot description to `rdata_wreduce.dot'. +Dumping selected parts of module fifo to page 1. + +yosys> opt_clean + +21. Executing OPT_CLEAN pass (remove unused cells and wires). +Finding unused cells or wires in module \fifo.. +Removed 0 unused cells and 4 unused wires. + + +yosys> memory_dff + +22. Executing MEMORY_DFF pass (merging $dff cells to $memrd). +Checking read port `\data'[0] in module `\fifo': merging output FF to cell. + Write port 0: non-transparent. + +yosys> select -set new_cells t:$memrd_v2 + +yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_memrdv2 o:rdata %ci* + +23. Generating Graphviz representation of design. +Writing dot description to `rdata_memrdv2.dot'. +Dumping selected parts of module fifo to page 1. + +yosys> alumacc + +24. Executing ALUMACC pass (create $alu and $macc cells). +Extracting $alu and $macc cells in module fifo: + creating $macc model for $add$fifo.v:66$27 ($add). + creating $macc model for $flatten\fifo_reader.$add$fifo.v:19$34 ($add). + creating $macc model for $flatten\fifo_writer.$add$fifo.v:19$34 ($add). + creating $macc model for $sub$fifo.v:68$30 ($sub). + creating $alu model for $macc $sub$fifo.v:68$30. + creating $alu model for $macc $flatten\fifo_writer.$add$fifo.v:19$34. + creating $alu model for $macc $flatten\fifo_reader.$add$fifo.v:19$34. + creating $alu model for $macc $add$fifo.v:66$27. + creating $alu cell for $add$fifo.v:66$27: $auto$alumacc.cc:485:replace_alu$80 + creating $alu cell for $flatten\fifo_reader.$add$fifo.v:19$34: $auto$alumacc.cc:485:replace_alu$83 + creating $alu cell for $flatten\fifo_writer.$add$fifo.v:19$34: $auto$alumacc.cc:485:replace_alu$86 + creating $alu cell for $sub$fifo.v:68$30: $auto$alumacc.cc:485:replace_alu$89 + created 4 $alu and 0 $macc cells. + +yosys> select -set new_cells t:$alu t:$macc + +yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_alumacc o:rdata %ci* + +25. Generating Graphviz representation of design. +Writing dot description to `rdata_alumacc.dot'. +Dumping selected parts of module fifo to page 1. + +yosys> memory_collect + +26. Executing MEMORY_COLLECT pass (generating $mem cells). + +yosys> select -set new_cells t:$mem_v2 + +yosys> select -set rdata_path @new_cells %ci*:-$mem_v2[WR_DATA,WR_ADDR,WR_EN] @new_cells %co* %% + +yosys> show -color maroon3 @new_cells -notitle -format dot -prefix rdata_coarse @rdata_path + +27. Generating Graphviz representation of design. +Writing dot description to `rdata_coarse.dot'. +Dumping selected parts of module fifo to page 1. diff --git a/docs/source/code_examples/fifo/fifo.stat b/docs/source/code_examples/fifo/fifo.stat new file mode 100644 index 00000000000..263c618e326 --- /dev/null +++ b/docs/source/code_examples/fifo/fifo.stat @@ -0,0 +1,57 @@ + +yosys> stat + +2. Printing statistics. + +=== fifo === + + Number of wires: 28 + Number of wire bits: 219 + Number of public wires: 9 + Number of public wire bits: 45 + Number of memories: 1 + Number of memory bits: 2048 + Number of processes: 3 + Number of cells: 9 + $add 1 + $logic_and 2 + $logic_not 2 + $memrd 1 + $sub 1 + addr_gen 2 + +=== addr_gen === + + Number of wires: 8 + Number of wire bits: 60 + Number of public wires: 4 + Number of public wire bits: 11 + Number of memories: 0 + Number of memory bits: 0 + Number of processes: 2 + Number of cells: 2 + $add 1 + $eq 1 + + +yosys> stat -top fifo + +17. Printing statistics. + +=== fifo === + + Number of wires: 94 + Number of wire bits: 260 + Number of public wires: 94 + Number of public wire bits: 260 + Number of memories: 0 + Number of memory bits: 0 + Number of processes: 0 + Number of cells: 138 + $scopeinfo 2 + SB_CARRY 26 + SB_DFF 26 + SB_DFFER 25 + SB_LUT4 58 + SB_RAM40_4K 1 + diff --git a/docs/source/code_examples/fifo/fifo.v b/docs/source/code_examples/fifo/fifo.v new file mode 100644 index 00000000000..769dfafd4c9 --- /dev/null +++ b/docs/source/code_examples/fifo/fifo.v @@ -0,0 +1,71 @@ +// address generator/counter +module addr_gen +#( parameter MAX_DATA=256, + localparam AWIDTH = $clog2(MAX_DATA) +) ( input en, clk, rst, + output reg [AWIDTH-1:0] addr +); + initial addr <= 0; + + // async reset + // increment address when enabled + always @(posedge clk or posedge rst) + if (rst) + addr <= 0; + else if (en) begin + if (addr == MAX_DATA-1) + addr <= 0; + else + addr <= addr + 1; + end +endmodule //addr_gen + +// Define our top level fifo entity +module fifo +#( parameter MAX_DATA=256, + localparam AWIDTH = $clog2(MAX_DATA) +) ( input wen, ren, clk, rst, + input [7:0] wdata, + output reg [7:0] rdata, + output reg [AWIDTH:0] count +); + // fifo storage + // sync read before write + wire [AWIDTH-1:0] waddr, raddr; + reg [7:0] data [MAX_DATA-1:0]; + always @(posedge clk) begin + if (wen) + data[waddr] <= wdata; + rdata <= data[raddr]; + end // storage + + // addr_gen for both write and read addresses + addr_gen #(.MAX_DATA(MAX_DATA)) + fifo_writer ( + .en (wen), + .clk (clk), + .rst (rst), + .addr (waddr) + ); + + addr_gen #(.MAX_DATA(MAX_DATA)) + fifo_reader ( + .en (ren), + .clk (clk), + .rst (rst), + .addr (raddr) + ); + + // status signals + initial count <= 0; + + always @(posedge clk or posedge rst) begin + if (rst) + count <= 0; + else if (wen && !ren) + count <= count + 1; + else if (ren && !wen) + count <= count - 1; + end + +endmodule diff --git a/docs/source/code_examples/fifo/fifo.ys b/docs/source/code_examples/fifo/fifo.ys new file mode 100644 index 00000000000..57a28e63e8e --- /dev/null +++ b/docs/source/code_examples/fifo/fifo.ys @@ -0,0 +1,82 @@ +# ======================================================== +# throw in some extra text to match what we expect if we were opening an +# interactive terminal +log $ yosys fifo.v +log +log -- Parsing `fifo.v' using frontend ` -vlog2k' -- +read_verilog -defer fifo.v + +# turn command echoes on to use the log output as a console session +echo on +hierarchy -top addr_gen +select -module addr_gen +select -list +select t:* +select -list +select -set new_cells % +select -clear +show -format dot -prefix addr_gen_show addr_gen +show -format dot -prefix new_cells_show -notitle @new_cells +show -color maroon3 @new_cells -color cornflowerblue p:* -notitle -format dot -prefix addr_gen_hier + +# ======================================================== +proc -noopt +select -set new_cells t:$mux t:*dff +show -color maroon3 @new_cells -notitle -format dot -prefix addr_gen_proc + +# ======================================================== +opt_expr; clean +select -set new_cells t:$eq +show -color cornflowerblue @new_cells -notitle -format dot -prefix addr_gen_clean + +# ======================================================== +design -reset +read_verilog fifo.v +hierarchy -check -top fifo +proc +select -set new_cells t:$memrd +show -color maroon3 c:fifo_reader -color cornflowerblue @new_cells -notitle -format dot -prefix rdata_proc o:rdata %ci* + +# ======================================================== + +flatten;; +select -set rdata_path o:rdata %ci* +select -set new_cells @rdata_path o:rdata %ci3 %d i:* %d +show -color maroon3 @new_cells -notitle -format dot -prefix rdata_flat @rdata_path + +# ======================================================== + +opt_dff +select -set new_cells t:$adffe +show -color maroon3 @new_cells -notitle -format dot -prefix rdata_adffe o:rdata %ci* + +# ======================================================== + +wreduce +show -notitle -format dot -prefix rdata_wreduce o:rdata %ci* + +# unclear if this is necessary or only because of bug(s) +opt_clean + +# ======================================================== + +memory_dff +select -set new_cells t:$memrd_v2 +show -color maroon3 @new_cells -notitle -format dot -prefix rdata_memrdv2 o:rdata %ci* + +# ======================================================== + +alumacc +select -set new_cells t:$alu t:$macc +show -color maroon3 @new_cells -notitle -format dot -prefix rdata_alumacc o:rdata %ci* + +# ======================================================== + +memory_collect +# or use the following commands: +# design -reset +# read_verilog fifo.v +# synth_ice40 -top fifo -run begin:map_ram +select -set new_cells t:$mem_v2 +select -set rdata_path @new_cells %ci*:-$mem_v2[WR_DATA,WR_ADDR,WR_EN] @new_cells %co* %% +show -color maroon3 @new_cells -notitle -format dot -prefix rdata_coarse @rdata_path diff --git a/docs/source/code_examples/fifo/fifo_map.ys b/docs/source/code_examples/fifo/fifo_map.ys new file mode 100644 index 00000000000..f5a3edeb485 --- /dev/null +++ b/docs/source/code_examples/fifo/fifo_map.ys @@ -0,0 +1,57 @@ +read_verilog fifo.v +echo on +tee -o fifo.stat stat +echo off +synth_ice40 -top fifo -run begin:map_ram +# this point should be the same as rdata_coarse + +# ======================================================== + +echo on +tee -o fifo.libmap debug memory_libmap -lib +/ice40/brams.txt -lib +/ice40/spram.txt -no-auto-huge +echo off +synth_ice40 -top fifo -run map_ram:map_ffram +select -set mem t:SB_RAM40_4K +select -set remap @mem %ci:+SB_RAM40_4K[RADDR] @mem %co %% +select -set rdata_path t:SB_RAM40_4K %ci*:-SB_RAM40_4K[WCLKE,WDATA,WADDR,WE] t:SB_RAM40_4K %co* %% +show -color cornflowerblue @remap -notitle -format dot -prefix rdata_map_ram @rdata_path + +# ======================================================== + +synth_ice40 -top fifo -run map_ffram:map_gates +select -set mem t:SB_RAM40_4K +select -set remap @mem %co @mem %d +select -set rdata_path t:SB_RAM40_4K %ci*:-SB_RAM40_4K[WCLKE,WDATA,WADDR,WE] t:SB_RAM40_4K %co* %% +show -color maroon3 @mem -color cornflowerblue @remap -notitle -format dot -prefix rdata_map_ffram @rdata_path + +# ======================================================== + +synth_ice40 -top fifo -run map_gates:map_ffs +select -set rdata_path t:SB_RAM40_4K %ci*:-SB_RAM40_4K[WCLKE,WDATA,WADDR,WE] t:SB_RAM40_4K %co* %% +select -set multibit t:$_MUX_ t:$_DFFE_*_ +select -set alu t:$_OR_ t:$_NOT_ t:$lut %% %ci %% w:fifo_reader.addr %d i:* %d +show -color maroon3 @multibit -color cornflowerblue @alu -notitle -format dot -prefix rdata_map_gates @rdata_path + +# ======================================================== + +synth_ice40 -top fifo -run map_ffs:map_luts +select -set rdata_path t:SB_RAM40_4K %ci*:-SB_RAM40_4K[WCLKE,WDATA,WADDR,WE] t:SB_RAM40_4K %co* %% +select -set dff t:SB_DFFER +select -set primitives t:$_AND_ %ci i:* %d +show -color maroon3 @dff -color cornflowerblue @primitives -notitle -format dot -prefix rdata_map_ffs @rdata_path + +# ======================================================== + +synth_ice40 -top fifo -run map_luts:map_cells +select -set rdata_path t:SB_RAM40_4K %ci*:-SB_RAM40_4K[WCLKE,WDATA,WADDR,WE] t:SB_RAM40_4K %co* %% +show -color maroon3 t:SB_CARRY -color cornflowerblue t:$lut -notitle -format dot -prefix rdata_map_luts @rdata_path + +# ======================================================== + +synth_ice40 -top fifo -run map_cells: +select -set rdata_path t:SB_RAM40_4K %ci*:-SB_RAM40_4K[WCLKE,WDATA,WADDR,WE] t:SB_RAM40_4K %co* %% +show -color maroon3 t:SB_LUT* -notitle -format dot -prefix rdata_map_cells @rdata_path + +echo on +tee -a fifo.stat stat -top fifo +echo off diff --git a/docs/source/code_examples/intro/.gitignore b/docs/source/code_examples/intro/.gitignore new file mode 100644 index 00000000000..9f8173c6d17 --- /dev/null +++ b/docs/source/code_examples/intro/.gitignore @@ -0,0 +1 @@ +synth.v diff --git a/docs/source/code_examples/intro/Makefile b/docs/source/code_examples/intro/Makefile new file mode 100644 index 00000000000..009c82c6219 --- /dev/null +++ b/docs/source/code_examples/intro/Makefile @@ -0,0 +1,15 @@ +PROGRAM_PREFIX := + +YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys + +DOTS = counter_00.dot counter_01.dot counter_02.dot counter_03.dot + +all: dots +dots: $(DOTS) + +$(DOTS): counter.v counter.ys mycells.lib + $(YOSYS) counter.ys + +.PHONY: clean +clean: + rm -f *.dot diff --git a/manual/PRESENTATION_Intro/counter.v b/docs/source/code_examples/intro/counter.v similarity index 100% rename from manual/PRESENTATION_Intro/counter.v rename to docs/source/code_examples/intro/counter.v diff --git a/docs/source/code_examples/intro/counter.ys b/docs/source/code_examples/intro/counter.ys new file mode 100644 index 00000000000..e327a6f6b81 --- /dev/null +++ b/docs/source/code_examples/intro/counter.ys @@ -0,0 +1,31 @@ +# read design +read_verilog counter.v +hierarchy -check -top counter + +show -notitle -format dot -prefix counter_00 + +# the high-level stuff +proc; opt +memory; opt +fsm; opt + +show -notitle -format dot -prefix counter_01 + +# mapping to internal cell library +techmap; opt + +splitnets -ports;; show -notitle -format dot -prefix counter_02 + +# mapping flip-flops to mycells.lib +dfflibmap -liberty mycells.lib + +# mapping logic to mycells.lib +abc -liberty mycells.lib + +# cleanup +clean + +show -notitle -lib mycells.v -format dot -prefix counter_03 + +# write synthesized design +write_verilog synth.v diff --git a/manual/PRESENTATION_Intro/mycells.lib b/docs/source/code_examples/intro/mycells.lib similarity index 100% rename from manual/PRESENTATION_Intro/mycells.lib rename to docs/source/code_examples/intro/mycells.lib diff --git a/manual/PRESENTATION_Intro/mycells.v b/docs/source/code_examples/intro/mycells.v similarity index 100% rename from manual/PRESENTATION_Intro/mycells.v rename to docs/source/code_examples/intro/mycells.v diff --git a/docs/source/code_examples/macc/Makefile b/docs/source/code_examples/macc/Makefile new file mode 100644 index 00000000000..e93fe065734 --- /dev/null +++ b/docs/source/code_examples/macc/Makefile @@ -0,0 +1,19 @@ +PROGRAM_PREFIX := + +YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys + +DOTS = macc_simple_xmap.dot macc_xilinx_xmap.dot + +all: dots +dots: $(DOTS) + +macc_simple_xmap.dot: macc_simple_*.v macc_simple_test.ys + $(YOSYS) macc_simple_test.ys + +macc_xilinx_xmap.dot: macc_xilinx_*.v macc_xilinx_test.ys + $(YOSYS) macc_xilinx_test.ys + +.PHONY: clean +clean: + rm -f *.dot + diff --git a/manual/PRESENTATION_ExAdv/macc_simple_test.v b/docs/source/code_examples/macc/macc_simple_test.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_simple_test.v rename to docs/source/code_examples/macc/macc_simple_test.v diff --git a/manual/PRESENTATION_ExAdv/macc_simple_test.ys b/docs/source/code_examples/macc/macc_simple_test.ys similarity index 60% rename from manual/PRESENTATION_ExAdv/macc_simple_test.ys rename to docs/source/code_examples/macc/macc_simple_test.ys index 8d106a28c9d..70496c05769 100644 --- a/manual/PRESENTATION_ExAdv/macc_simple_test.ys +++ b/docs/source/code_examples/macc/macc_simple_test.ys @@ -1,10 +1,10 @@ read_verilog macc_simple_test.v hierarchy -check -top test;; -show -prefix macc_simple_test_00a -format pdf -notitle -lib macc_simple_xmap.v +show -prefix macc_simple_test_00a -format dot -notitle -lib macc_simple_xmap.v extract -constports -map macc_simple_xmap.v;; -show -prefix macc_simple_test_00b -format pdf -notitle -lib macc_simple_xmap.v +show -prefix macc_simple_test_00b -format dot -notitle -lib macc_simple_xmap.v ################################################# @@ -12,10 +12,10 @@ design -reset read_verilog macc_simple_test_01.v hierarchy -check -top test;; -show -prefix macc_simple_test_01a -format pdf -notitle -lib macc_simple_xmap.v +show -prefix macc_simple_test_01a -format dot -notitle -lib macc_simple_xmap.v extract -map macc_simple_xmap.v;; -show -prefix macc_simple_test_01b -format pdf -notitle -lib macc_simple_xmap.v +show -prefix macc_simple_test_01b -format dot -notitle -lib macc_simple_xmap.v ################################################# @@ -23,10 +23,10 @@ design -reset read_verilog macc_simple_test_02.v hierarchy -check -top test;; -show -prefix macc_simple_test_02a -format pdf -notitle -lib macc_simple_xmap.v +show -prefix macc_simple_test_02a -format dot -notitle -lib macc_simple_xmap.v extract -map macc_simple_xmap.v;; -show -prefix macc_simple_test_02b -format pdf -notitle -lib macc_simple_xmap.v +show -prefix macc_simple_test_02b -format dot -notitle -lib macc_simple_xmap.v ################################################# @@ -34,4 +34,4 @@ design -reset read_verilog macc_simple_xmap.v hierarchy -check -top macc_16_16_32;; -show -prefix macc_simple_xmap -format pdf -notitle +show -prefix macc_simple_xmap -format dot -notitle diff --git a/manual/PRESENTATION_ExAdv/macc_simple_test_01.v b/docs/source/code_examples/macc/macc_simple_test_01.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_simple_test_01.v rename to docs/source/code_examples/macc/macc_simple_test_01.v diff --git a/manual/PRESENTATION_ExAdv/macc_simple_test_02.v b/docs/source/code_examples/macc/macc_simple_test_02.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_simple_test_02.v rename to docs/source/code_examples/macc/macc_simple_test_02.v diff --git a/manual/PRESENTATION_ExAdv/macc_simple_xmap.v b/docs/source/code_examples/macc/macc_simple_xmap.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_simple_xmap.v rename to docs/source/code_examples/macc/macc_simple_xmap.v diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_swap_map.v b/docs/source/code_examples/macc/macc_xilinx_swap_map.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_xilinx_swap_map.v rename to docs/source/code_examples/macc/macc_xilinx_swap_map.v diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_test.v b/docs/source/code_examples/macc/macc_xilinx_test.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_xilinx_test.v rename to docs/source/code_examples/macc/macc_xilinx_test.v diff --git a/docs/source/code_examples/macc/macc_xilinx_test.ys b/docs/source/code_examples/macc/macc_xilinx_test.ys new file mode 100644 index 00000000000..47bf399b20a --- /dev/null +++ b/docs/source/code_examples/macc/macc_xilinx_test.ys @@ -0,0 +1,53 @@ +# ============================================================================ +# part a +read_verilog macc_xilinx_test.v +read_verilog -lib -icells macc_xilinx_unwrap_map.v +read_verilog -lib -icells macc_xilinx_xmap.v +hierarchy -check ;; +# end part a +show -prefix macc_xilinx_test1a -format dot -notitle test1 +show -prefix macc_xilinx_test2a -format dot -notitle test2 + +# ============================================================================ +# part b +techmap -map macc_xilinx_swap_map.v;; +# end part b +show -prefix macc_xilinx_test1b -format dot -notitle test1 +show -prefix macc_xilinx_test2b -format dot -notitle test2 + +# ============================================================================ +# part c +techmap -map macc_xilinx_wrap_map.v + +connwrappers -unsigned $__mul_wrapper Y Y_WIDTH \ + -unsigned $__add_wrapper Y Y_WIDTH;; +# end part c +show -prefix macc_xilinx_test1c -format dot -notitle test1 +show -prefix macc_xilinx_test2c -format dot -notitle test2 + +# ============================================================================ +# part d +design -push +read_verilog macc_xilinx_xmap.v +techmap -map macc_xilinx_swap_map.v +techmap -map macc_xilinx_wrap_map.v;; +design -save __macc_xilinx_xmap +design -pop + +extract -constports -ignore_parameters \ + -map %__macc_xilinx_xmap \ + -swap $__add_wrapper A,B ;; +# end part d +show -prefix macc_xilinx_test1d -format dot -notitle test1 +show -prefix macc_xilinx_test2d -format dot -notitle test2 + +# ============================================================================ +# part e +techmap -map macc_xilinx_unwrap_map.v;; +# end part e +show -prefix macc_xilinx_test1e -format dot -notitle test1 +show -prefix macc_xilinx_test2e -format dot -notitle test2 + +design -load __macc_xilinx_xmap +show -prefix macc_xilinx_xmap -format dot -notitle + diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v b/docs/source/code_examples/macc/macc_xilinx_unwrap_map.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v rename to docs/source/code_examples/macc/macc_xilinx_unwrap_map.v diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_wrap_map.v b/docs/source/code_examples/macc/macc_xilinx_wrap_map.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_xilinx_wrap_map.v rename to docs/source/code_examples/macc/macc_xilinx_wrap_map.v diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_xmap.v b/docs/source/code_examples/macc/macc_xilinx_xmap.v similarity index 100% rename from manual/PRESENTATION_ExAdv/macc_xilinx_xmap.v rename to docs/source/code_examples/macc/macc_xilinx_xmap.v diff --git a/docs/source/code_examples/macro_commands/fsm.ys b/docs/source/code_examples/macro_commands/fsm.ys new file mode 100644 index 00000000000..627df1209ae --- /dev/null +++ b/docs/source/code_examples/macro_commands/fsm.ys @@ -0,0 +1,27 @@ +#start:It also calls opt_clean as needed: +#end:Options: +# Identify and extract FSMs: +fsm_detect +fsm_extract + +# Basic optimizations: +fsm_opt +opt_clean +fsm_opt + +# Expanding to nearby gate-logic (if called with -expand): +fsm_expand +opt_clean +fsm_opt + +# Re-code FSM states (unless called with -norecode): +fsm_recode + +# Print information about FSMs: +fsm_info + +# Export FSMs in KISS2 file format (if called with -export): +fsm_export + +# Map FSMs to RTL cells (unless called with -nomap): +fsm_map diff --git a/docs/source/code_examples/macro_commands/memory.ys b/docs/source/code_examples/macro_commands/memory.ys new file mode 100644 index 00000000000..ea4800a1cc1 --- /dev/null +++ b/docs/source/code_examples/macro_commands/memory.ys @@ -0,0 +1,15 @@ +#start:passes in a useful order: +#end:This converts memories to word-wide DFFs and address decoders +opt_mem +opt_mem_priority +opt_mem_feedback +memory_bmux2rom +memory_dff +opt_clean +memory_share +opt_mem_widen +memory_memx (when called with -memx) +opt_clean +memory_collect +memory_bram -rules (when called with -bram) +memory_map (skipped if called with -nomap) diff --git a/docs/source/code_examples/macro_commands/opt.ys b/docs/source/code_examples/macro_commands/opt.ys new file mode 100644 index 00000000000..cb883bc589a --- /dev/null +++ b/docs/source/code_examples/macro_commands/opt.ys @@ -0,0 +1,14 @@ +#start: passes in the following order: +#end: When called with -fast +opt_expr +opt_merge -nomux + +do + opt_muxtree + opt_reduce + opt_merge + opt_share (-full only) + opt_dff (except when called with -noff) + opt_clean + opt_expr +while diff --git a/docs/source/code_examples/macro_commands/proc.ys b/docs/source/code_examples/macro_commands/proc.ys new file mode 100644 index 00000000000..7a78ce0c6b8 --- /dev/null +++ b/docs/source/code_examples/macro_commands/proc.ys @@ -0,0 +1,14 @@ +#start: passes in the most common order. +#end: This replaces the processes +proc_clean # removes empty branches and processes +proc_rmdead # removes unreachable branches +proc_prune +proc_init # special handling of “initial” blocks +proc_arst # identifies modeling of async resets +proc_rom +proc_mux # converts decision trees to multiplexer networks +proc_dlatch +proc_dff # extracts registers from processes +proc_memwr +proc_clean # this should remove all the processes, provided all went fine +opt_expr -keepdc diff --git a/docs/source/code_examples/macro_commands/synth_ice40.ys b/docs/source/code_examples/macro_commands/synth_ice40.ys new file mode 100644 index 00000000000..443b8e0b026 --- /dev/null +++ b/docs/source/code_examples/macro_commands/synth_ice40.ys @@ -0,0 +1,90 @@ +#start:The following commands are executed by this synthesis command: +#end:blif: +begin: + read_verilog -D ICE40_HX -lib -specify +/ice40/cells_sim.v + hierarchy -check -top + proc + +flatten: + flatten + tribuf -logic + deminout + +coarse: + opt_expr + opt_clean + check + opt -nodffe -nosdff + fsm + opt + wreduce + peepopt + opt_clean + share + techmap + opt_expr + opt_clean + memory_dff + wreduce t:$mul + techmap + select a:mul2dsp + setattr -unset mul2dsp + opt_expr -fine + wreduce + select -clear + ice40_dsp + chtype -set $mul t:$__soft_mul + alumacc + opt + memory -nomap [-no-rw-check] + opt_clean + +map_ram: + memory_libmap + techmap + ice40_braminit + +map_ffram: + opt -fast -mux_undef -undriven -fine + memory_map + opt -undriven -fine + +map_gates: + ice40_wrapcarry + techmap + opt -fast + abc -dff -D 1 + ice40_opt + +map_ffs: + dfflegalize + techmap + opt_expr -mux_undef + simplemap + ice40_opt -full + +map_luts: + abc + ice40_opt + techmap + simplemap + techmap + flowmap + read_verilog + abc9 + ice40_wrapcarry -unwrap + techmap + clean + opt_lut -tech ice40 + +map_cells: + techmap + clean + +check: + autoname + hierarchy -check + stat + check -noinit + blackbox =A:whitebox + \ No newline at end of file diff --git a/docs/source/code_examples/opt/Makefile b/docs/source/code_examples/opt/Makefile new file mode 100644 index 00000000000..4cb51e90bec --- /dev/null +++ b/docs/source/code_examples/opt/Makefile @@ -0,0 +1,20 @@ +PROGRAM_PREFIX := + +YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys + +DOT_NAMES = opt_share opt_muxtree opt_merge opt_expr + +DOTS := $(addsuffix .dot,$(DOT_NAMES)) + +all: dots +dots: $(DOTS) + +%_full.dot: %.ys + $(YOSYS) $< + +%.dot: %_full.dot + gvpack -u $*_full.dot -o $@ + +.PHONY: clean +clean: + rm -f *.dot diff --git a/docs/source/code_examples/opt/opt_expr.ys b/docs/source/code_examples/opt/opt_expr.ys new file mode 100644 index 00000000000..e87da339ed8 --- /dev/null +++ b/docs/source/code_examples/opt/opt_expr.ys @@ -0,0 +1,17 @@ +read_verilog < ls + +1 modules: + example + +yosys> cd example + +yosys [example]> ls + +8 wires: + $0\y[1:0] + $add$example.v:5$2_Y + $ternary$example.v:5$3_Y + a + b + c + clk + y + +2 cells: + $add$example.v:5$2 + $ternary$example.v:5$3 + +1 processes: + $proc$example.v:3$1 + +yosys [example]> dump $2 + + + attribute \src "example.v:5.22-5.27" + cell $add $add$example.v:5$2 + parameter \Y_WIDTH 2 + parameter \B_WIDTH 1 + parameter \A_WIDTH 1 + parameter \B_SIGNED 0 + parameter \A_SIGNED 0 + connect \Y $add$example.v:5$2_Y + connect \B \b + connect \A \a + end + +yosys [example]> cd .. + +yosys> echo off +echo off diff --git a/manual/APPNOTE_011_Design_Investigation/example.v b/docs/source/code_examples/show/example.v similarity index 100% rename from manual/APPNOTE_011_Design_Investigation/example.v rename to docs/source/code_examples/show/example.v diff --git a/docs/source/code_examples/show/example.ys b/docs/source/code_examples/show/example.ys new file mode 100644 index 00000000000..a5332f13486 --- /dev/null +++ b/docs/source/code_examples/show/example.ys @@ -0,0 +1,6 @@ +read_verilog example.v +show -format dot -prefix example_first +proc +show -format dot -prefix example_second +opt +show -format dot -prefix example_third diff --git a/docs/source/code_examples/show/example_lscd.ys b/docs/source/code_examples/show/example_lscd.ys new file mode 100644 index 00000000000..de24842285a --- /dev/null +++ b/docs/source/code_examples/show/example_lscd.ys @@ -0,0 +1,8 @@ +read_verilog example.v +echo on +ls +cd example +ls +dump $2 +cd .. +echo off diff --git a/docs/source/code_examples/show/example_show.ys b/docs/source/code_examples/show/example_show.ys new file mode 100644 index 00000000000..711af84d9f2 --- /dev/null +++ b/docs/source/code_examples/show/example_show.ys @@ -0,0 +1,6 @@ +read_verilog example.v +show -pause # first +proc +show -pause # second +opt +show -pause # third diff --git a/manual/APPNOTE_011_Design_Investigation/splice.v b/docs/source/code_examples/show/splice.v similarity index 81% rename from manual/APPNOTE_011_Design_Investigation/splice.v rename to docs/source/code_examples/show/splice.v index 1cf7274c059..1f7c92f4d04 100644 --- a/manual/APPNOTE_011_Design_Investigation/splice.v +++ b/docs/source/code_examples/show/splice.v @@ -1,7 +1,8 @@ module splice_demo(a, b, c, d, e, f, x, y); input [1:0] a, b, c, d, e, f; -output [1:0] x = {a[0], a[1]}; +output [1:0] x; +assign x = {a[0], a[1]}; output [11:0] y; assign {y[11:4], y[1:0], y[3:2]} = diff --git a/manual/CHAPTER_Prog/.gitignore b/docs/source/code_examples/stubnets/.gitignore similarity index 100% rename from manual/CHAPTER_Prog/.gitignore rename to docs/source/code_examples/stubnets/.gitignore diff --git a/manual/CHAPTER_Prog/Makefile b/docs/source/code_examples/stubnets/Makefile similarity index 87% rename from manual/CHAPTER_Prog/Makefile rename to docs/source/code_examples/stubnets/Makefile index 8e326bdc266..ec501f006ca 100644 --- a/manual/CHAPTER_Prog/Makefile +++ b/docs/source/code_examples/stubnets/Makefile @@ -1,3 +1,8 @@ +.PHONY: all dots +all: dots +dots: + +.PHONY: test test: stubnets.so yosys -ql test1.log -m ./stubnets.so test.v -p "stubnets" yosys -ql test2.log -m ./stubnets.so test.v -p "opt; stubnets" @@ -7,6 +12,7 @@ test: stubnets.so stubnets.so: stubnets.cc yosys-config --exec --cxx --cxxflags --ldflags -o $@ -shared $^ --ldlibs +.PHONY: clean clean: rm -f test1.log test2.log test3.log rm -f stubnets.so stubnets.d diff --git a/manual/CHAPTER_Prog/stubnets.cc b/docs/source/code_examples/stubnets/stubnets.cc similarity index 100% rename from manual/CHAPTER_Prog/stubnets.cc rename to docs/source/code_examples/stubnets/stubnets.cc diff --git a/manual/CHAPTER_Prog/test.v b/docs/source/code_examples/stubnets/test.v similarity index 100% rename from manual/CHAPTER_Prog/test.v rename to docs/source/code_examples/stubnets/test.v diff --git a/docs/source/code_examples/synth_flow/Makefile b/docs/source/code_examples/synth_flow/Makefile new file mode 100644 index 00000000000..cc5a34b26b5 --- /dev/null +++ b/docs/source/code_examples/synth_flow/Makefile @@ -0,0 +1,22 @@ + +TARGETS += proc_01 proc_02 proc_03 +TARGETS += memory_01 memory_02 +TARGETS += techmap_01 + +PROGRAM_PREFIX := + +YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys + +DOTS = $(addsuffix .dot,$(TARGETS)) + +.PHONY: all dots +all: dots +dots: $(DOTS) + +%.dot: %.v %.ys + $(YOSYS) -p 'script $*.ys; show -notitle -prefix $* -format dot' + +.PHONY: clean +clean: + rm -f *.dot + diff --git a/manual/PRESENTATION_ExSyn/memory_01.v b/docs/source/code_examples/synth_flow/memory_01.v similarity index 100% rename from manual/PRESENTATION_ExSyn/memory_01.v rename to docs/source/code_examples/synth_flow/memory_01.v diff --git a/manual/PRESENTATION_ExSyn/memory_01.ys b/docs/source/code_examples/synth_flow/memory_01.ys similarity index 100% rename from manual/PRESENTATION_ExSyn/memory_01.ys rename to docs/source/code_examples/synth_flow/memory_01.ys diff --git a/manual/PRESENTATION_ExSyn/memory_02.v b/docs/source/code_examples/synth_flow/memory_02.v similarity index 100% rename from manual/PRESENTATION_ExSyn/memory_02.v rename to docs/source/code_examples/synth_flow/memory_02.v diff --git a/manual/PRESENTATION_ExSyn/memory_02.ys b/docs/source/code_examples/synth_flow/memory_02.ys similarity index 100% rename from manual/PRESENTATION_ExSyn/memory_02.ys rename to docs/source/code_examples/synth_flow/memory_02.ys diff --git a/manual/PRESENTATION_ExSyn/proc_01.v b/docs/source/code_examples/synth_flow/proc_01.v similarity index 100% rename from manual/PRESENTATION_ExSyn/proc_01.v rename to docs/source/code_examples/synth_flow/proc_01.v diff --git a/manual/PRESENTATION_ExSyn/proc_01.ys b/docs/source/code_examples/synth_flow/proc_01.ys similarity index 100% rename from manual/PRESENTATION_ExSyn/proc_01.ys rename to docs/source/code_examples/synth_flow/proc_01.ys diff --git a/manual/PRESENTATION_ExSyn/proc_02.v b/docs/source/code_examples/synth_flow/proc_02.v similarity index 100% rename from manual/PRESENTATION_ExSyn/proc_02.v rename to docs/source/code_examples/synth_flow/proc_02.v diff --git a/manual/PRESENTATION_ExSyn/proc_02.ys b/docs/source/code_examples/synth_flow/proc_02.ys similarity index 100% rename from manual/PRESENTATION_ExSyn/proc_02.ys rename to docs/source/code_examples/synth_flow/proc_02.ys diff --git a/manual/PRESENTATION_ExSyn/proc_03.v b/docs/source/code_examples/synth_flow/proc_03.v similarity index 100% rename from manual/PRESENTATION_ExSyn/proc_03.v rename to docs/source/code_examples/synth_flow/proc_03.v diff --git a/manual/PRESENTATION_ExSyn/proc_03.ys b/docs/source/code_examples/synth_flow/proc_03.ys similarity index 100% rename from manual/PRESENTATION_ExSyn/proc_03.ys rename to docs/source/code_examples/synth_flow/proc_03.ys diff --git a/manual/PRESENTATION_ExSyn/techmap_01.v b/docs/source/code_examples/synth_flow/techmap_01.v similarity index 100% rename from manual/PRESENTATION_ExSyn/techmap_01.v rename to docs/source/code_examples/synth_flow/techmap_01.v diff --git a/manual/PRESENTATION_ExSyn/techmap_01.ys b/docs/source/code_examples/synth_flow/techmap_01.ys similarity index 100% rename from manual/PRESENTATION_ExSyn/techmap_01.ys rename to docs/source/code_examples/synth_flow/techmap_01.ys diff --git a/manual/PRESENTATION_ExSyn/techmap_01_map.v b/docs/source/code_examples/synth_flow/techmap_01_map.v similarity index 100% rename from manual/PRESENTATION_ExSyn/techmap_01_map.v rename to docs/source/code_examples/synth_flow/techmap_01_map.v diff --git a/docs/source/code_examples/techmap/Makefile b/docs/source/code_examples/techmap/Makefile new file mode 100644 index 00000000000..e900fea4c5f --- /dev/null +++ b/docs/source/code_examples/techmap/Makefile @@ -0,0 +1,26 @@ +PROGRAM_PREFIX := + +YOSYS ?= ../../../../$(PROGRAM_PREFIX)yosys + +.PHONY: all dots +all: dots +dots: red_or3x1.dot sym_mul.dot mymul.dot mulshift.dot addshift.dot + +red_or3x1.dot: red_or3x1_* + $(YOSYS) red_or3x1_test.ys + +sym_mul.dot: sym_mul_* + $(YOSYS) sym_mul_test.ys + +mymul.dot: mymul_* + $(YOSYS) mymul_test.ys + +mulshift.dot: mulshift_* + $(YOSYS) mulshift_test.ys + +addshift.dot: addshift_* + $(YOSYS) addshift_test.ys + +.PHONY: clean +clean: + rm -f *.dot diff --git a/manual/PRESENTATION_ExAdv/addshift_map.v b/docs/source/code_examples/techmap/addshift_map.v similarity index 100% rename from manual/PRESENTATION_ExAdv/addshift_map.v rename to docs/source/code_examples/techmap/addshift_map.v diff --git a/manual/PRESENTATION_ExAdv/addshift_test.v b/docs/source/code_examples/techmap/addshift_test.v similarity index 100% rename from manual/PRESENTATION_ExAdv/addshift_test.v rename to docs/source/code_examples/techmap/addshift_test.v diff --git a/manual/PRESENTATION_ExAdv/addshift_test.ys b/docs/source/code_examples/techmap/addshift_test.ys similarity index 67% rename from manual/PRESENTATION_ExAdv/addshift_test.ys rename to docs/source/code_examples/techmap/addshift_test.ys index c08f1106a25..c15691b3353 100644 --- a/manual/PRESENTATION_ExAdv/addshift_test.ys +++ b/docs/source/code_examples/techmap/addshift_test.ys @@ -3,4 +3,4 @@ hierarchy -check -top test techmap -map addshift_map.v;; -show -prefix addshift -format pdf -notitle +show -prefix addshift -format dot -notitle diff --git a/manual/PRESENTATION_ExAdv/mulshift_map.v b/docs/source/code_examples/techmap/mulshift_map.v similarity index 100% rename from manual/PRESENTATION_ExAdv/mulshift_map.v rename to docs/source/code_examples/techmap/mulshift_map.v diff --git a/manual/PRESENTATION_ExAdv/mulshift_test.v b/docs/source/code_examples/techmap/mulshift_test.v similarity index 100% rename from manual/PRESENTATION_ExAdv/mulshift_test.v rename to docs/source/code_examples/techmap/mulshift_test.v diff --git a/manual/PRESENTATION_ExAdv/mulshift_test.ys b/docs/source/code_examples/techmap/mulshift_test.ys similarity index 64% rename from manual/PRESENTATION_ExAdv/mulshift_test.ys rename to docs/source/code_examples/techmap/mulshift_test.ys index c5dac49eb9b..79daaf0c429 100644 --- a/manual/PRESENTATION_ExAdv/mulshift_test.ys +++ b/docs/source/code_examples/techmap/mulshift_test.ys @@ -4,4 +4,4 @@ hierarchy -check -top test techmap -map sym_mul_map.v \ -map mulshift_map.v;; -show -prefix mulshift -format pdf -notitle -lib sym_mul_cells.v +show -prefix mulshift -format dot -notitle -lib sym_mul_cells.v diff --git a/manual/PRESENTATION_ExAdv/mymul_map.v b/docs/source/code_examples/techmap/mymul_map.v similarity index 100% rename from manual/PRESENTATION_ExAdv/mymul_map.v rename to docs/source/code_examples/techmap/mymul_map.v diff --git a/manual/PRESENTATION_ExAdv/mymul_test.v b/docs/source/code_examples/techmap/mymul_test.v similarity index 100% rename from manual/PRESENTATION_ExAdv/mymul_test.v rename to docs/source/code_examples/techmap/mymul_test.v diff --git a/manual/PRESENTATION_ExAdv/mymul_test.ys b/docs/source/code_examples/techmap/mymul_test.ys similarity index 84% rename from manual/PRESENTATION_ExAdv/mymul_test.ys rename to docs/source/code_examples/techmap/mymul_test.ys index 48203e31992..b7176fdc49a 100644 --- a/manual/PRESENTATION_ExAdv/mymul_test.ys +++ b/docs/source/code_examples/techmap/mymul_test.ys @@ -12,4 +12,4 @@ flatten miter sat -verify -prove trigger 0 miter splitnets -ports test_mapped/A -show -prefix mymul -format pdf -notitle test_mapped +show -prefix mymul -format dot -notitle test_mapped diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_cells.v b/docs/source/code_examples/techmap/red_or3x1_cells.v similarity index 100% rename from manual/PRESENTATION_ExAdv/red_or3x1_cells.v rename to docs/source/code_examples/techmap/red_or3x1_cells.v diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_map.v b/docs/source/code_examples/techmap/red_or3x1_map.v similarity index 100% rename from manual/PRESENTATION_ExAdv/red_or3x1_map.v rename to docs/source/code_examples/techmap/red_or3x1_map.v diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_test.v b/docs/source/code_examples/techmap/red_or3x1_test.v similarity index 100% rename from manual/PRESENTATION_ExAdv/red_or3x1_test.v rename to docs/source/code_examples/techmap/red_or3x1_test.v diff --git a/manual/PRESENTATION_ExAdv/red_or3x1_test.ys b/docs/source/code_examples/techmap/red_or3x1_test.ys similarity index 63% rename from manual/PRESENTATION_ExAdv/red_or3x1_test.ys rename to docs/source/code_examples/techmap/red_or3x1_test.ys index b9234603494..891b8177b89 100644 --- a/manual/PRESENTATION_ExAdv/red_or3x1_test.ys +++ b/docs/source/code_examples/techmap/red_or3x1_test.ys @@ -4,4 +4,4 @@ hierarchy -check -top test techmap -map red_or3x1_map.v;; splitnets -ports -show -prefix red_or3x1 -format pdf -notitle -lib red_or3x1_cells.v +show -prefix red_or3x1 -format dot -notitle -lib red_or3x1_cells.v diff --git a/manual/PRESENTATION_ExAdv/sym_mul_cells.v b/docs/source/code_examples/techmap/sym_mul_cells.v similarity index 100% rename from manual/PRESENTATION_ExAdv/sym_mul_cells.v rename to docs/source/code_examples/techmap/sym_mul_cells.v diff --git a/manual/PRESENTATION_ExAdv/sym_mul_map.v b/docs/source/code_examples/techmap/sym_mul_map.v similarity index 100% rename from manual/PRESENTATION_ExAdv/sym_mul_map.v rename to docs/source/code_examples/techmap/sym_mul_map.v diff --git a/manual/PRESENTATION_ExAdv/sym_mul_test.v b/docs/source/code_examples/techmap/sym_mul_test.v similarity index 100% rename from manual/PRESENTATION_ExAdv/sym_mul_test.v rename to docs/source/code_examples/techmap/sym_mul_test.v diff --git a/manual/PRESENTATION_ExAdv/sym_mul_test.ys b/docs/source/code_examples/techmap/sym_mul_test.ys similarity index 57% rename from manual/PRESENTATION_ExAdv/sym_mul_test.ys rename to docs/source/code_examples/techmap/sym_mul_test.ys index 0c07e7e877c..f19bee64a95 100644 --- a/manual/PRESENTATION_ExAdv/sym_mul_test.ys +++ b/docs/source/code_examples/techmap/sym_mul_test.ys @@ -3,4 +3,4 @@ hierarchy -check -top test techmap -map sym_mul_map.v;; -show -prefix sym_mul -format pdf -notitle -lib sym_mul_cells.v +show -prefix sym_mul -format dot -notitle -lib sym_mul_cells.v diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000000..29d36d9c45f --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +import sys +import os + +project = 'YosysHQ Yosys' +author = 'YosysHQ GmbH' +copyright ='2022 YosysHQ GmbH' + +# select HTML theme +html_theme = 'furo' +templates_path = ["_templates"] +html_logo = '_static/logo.png' +html_favicon = '_static/favico.png' +html_css_files = ['yosyshq.css', 'custom.css'] + +html_theme_options = { + "sidebar_hide_name": True, + + "light_css_variables": { + "color-brand-primary": "#d6368f", + "color-brand-content": "#4b72b8", + "color-api-name": "#8857a3", + "color-api-pre-name": "#4b72b8", + "color-link": "#8857a3", + }, + + "dark_css_variables": { + "color-brand-primary": "#e488bb", + "color-brand-content": "#98bdff", + "color-api-name": "#8857a3", + "color-api-pre-name": "#4b72b8", + "color-link": "#be95d5", + }, +} + +# These folders are copied to the documentation's HTML output +html_static_path = ['_static', "_images"] + +# code blocks style +pygments_style = 'colorful' +highlight_language = 'none' + +extensions = ['sphinx.ext.autosectionlabel', 'sphinxcontrib.bibtex'] + +# Ensure that autosectionlabel will produce unique names +autosectionlabel_prefix_document = True +autosectionlabel_maxdepth = 1 + +# assign figure numbers +numfig = True + +bibtex_bibfiles = ['literature.bib'] + +latex_elements = { + 'preamble': r''' +\usepackage{lmodern} +\usepackage{comment} + +''' +} + +# include todos during rewrite +extensions.append('sphinx.ext.todo') +todo_include_todos = False + +# custom cmd-ref parsing/linking +sys.path += [os.path.dirname(__file__) + "/../"] +extensions.append('util.cmdref') + +def setup(sphinx): + from util.RtlilLexer import RtlilLexer + sphinx.add_lexer("RTLIL", RtlilLexer) + + from util.YoscryptLexer import YoscryptLexer + sphinx.add_lexer("yoscrypt", YoscryptLexer) \ No newline at end of file diff --git a/docs/source/getting_started/example_synth.rst b/docs/source/getting_started/example_synth.rst new file mode 100644 index 00000000000..799b4ec48b4 --- /dev/null +++ b/docs/source/getting_started/example_synth.rst @@ -0,0 +1,855 @@ +Synthesis starter +----------------- + +This page will be a guided walkthrough of the prepackaged iCE40 FPGA synthesis +script - :cmd:ref:`synth_ice40`. We will take a simple design through each +step, looking at the commands being called and what they do to the design. While +:cmd:ref:`synth_ice40` is specific to the iCE40 platform, most of the operations +we will be discussing are common across the majority of FPGA synthesis scripts. +Thus, this document will provide a good foundational understanding of how +synthesis in Yosys is performed, regardless of the actual architecture being +used. + +.. seealso:: Advanced usage docs for + :doc:`/using_yosys/synthesis/synth` + +Demo design +~~~~~~~~~~~ + +.. role:: yoscrypt(code) + :language: yoscrypt + +First, let's quickly look at the design we'll be synthesizing: + +.. todo:: reconsider including the whole (~77 line) design like this + +.. literalinclude:: /code_examples/fifo/fifo.v + :language: Verilog + :linenos: + :caption: :file:`fifo.v` + :name: fifo-v + +.. todo:: fifo.v description + +Loading the design +~~~~~~~~~~~~~~~~~~ + +Let's load the design into Yosys. From the command line, we can call ``yosys +fifo.v``. This will open an interactive Yosys shell session and immediately +parse the code from :ref:`fifo-v` and convert it into an Abstract Syntax Tree +(AST). If you are interested in how this happens, there is more information in +the document, :doc:`/yosys_internals/flow/verilog_frontend`. For now, suffice +it to say that we do this to simplify further processing of the design. You +should see something like the following: + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: console + :start-at: $ yosys fifo.v + :end-before: echo on + +.. seealso:: Advanced usage docs for + :doc:`/using_yosys/more_scripting/load_design` + +Elaboration +~~~~~~~~~~~ + +Now that we are in the interactive shell, we can call Yosys commands directly. +Our overall goal is to call :yoscrypt:`synth_ice40 -top fifo`, but for now we +can run each of the commands individually for a better sense of how each part +contributes to the flow. We will also start with just a single module; +``addr_gen``. + +At the bottom of the :cmd:ref:`help` output for +:cmd:ref:`synth_ice40` is the complete list of commands called by this script. +Let's start with the section labeled ``begin``: + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: begin: + :end-before: flatten: + :dedent: + :caption: ``begin`` section + :name: synth_begin + +:yoscrypt:`read_verilog -D ICE40_HX -lib -specify +/ice40/cells_sim.v` loads the +iCE40 cell models which allows us to include platform specific IP blocks in our +design. PLLs are a common example of this, where we might need to reference +``SB_PLL40_CORE`` directly rather than being able to rely on mapping passes +later. Since our simple design doesn't use any of these IP blocks, we can skip +this command for now. Because these cell models will also be needed once we +start mapping to hardware we will still need to load them later. + +.. note:: + + ``+/`` is a dynamic reference to the Yosys ``share`` directory. By default, + this is ``/usr/local/share/yosys``. If using a locally built version of + Yosys from the source directory, this will be the ``share`` folder in the + same directory. + +.. _addr_gen_example: + +The addr_gen module +^^^^^^^^^^^^^^^^^^^ + +Since we're just getting started, let's instead begin with :yoscrypt:`hierarchy +-top addr_gen`. This command declares that the top level module is +``addr_gen``, and everything else can be discarded. + +.. literalinclude:: /code_examples/fifo/fifo.v + :language: Verilog + :start-at: module addr_gen + :end-at: endmodule //addr_gen + :lineno-match: + :caption: ``addr_gen`` module source + :name: addr_gen-v + +.. note:: + + :cmd:ref:`hierarchy` should always be the first command after the design has + been read. By specifying the top module, :cmd:ref:`hierarchy` will also set + the ``(* top *)`` attribute on it. This is used by other commands that need + to know which module is the top. + +.. use doscon for a console-like display that supports the `yosys> [command]` format. + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: yosys> hierarchy -top addr_gen + :end-before: yosys> select + :caption: :yoscrypt:`hierarchy -top addr_gen` output + :name: hierarchy_output + +Our ``addr_gen`` circuit now looks like this: + +.. figure:: /_images/code_examples/fifo/addr_gen_hier.* + :class: width-helper + :name: addr_gen_hier + + ``addr_gen`` module after :cmd:ref:`hierarchy` + +Simple operations like ``addr + 1`` and ``addr == MAX_DATA-1`` can be extracted +from our ``always @`` block in :ref:`addr_gen-v`. This gives us the highlighted +``$add`` and ``$eq`` cells we see. But control logic (like the ``if .. else``) +and memory elements (like the ``addr <= 0``) are not so straightforward. These +get put into "processes", shown in the schematic as ``PROC``. Note how the +second line refers to the line numbers of the start/end of the corresponding +``always @`` block. In the case of an ``initial`` block, we instead see the +``PROC`` referring to line 0. + +To handle these, let us now introduce the next command: :doc:`/cmd/proc`. +:cmd:ref:`proc` is a macro command like :cmd:ref:`synth_ice40`. Rather than +modifying the design directly, it instead calls a series of other commands. In +the case of :cmd:ref:`proc`, these sub-commands work to convert the behavioral +logic of processes into multiplexers and registers. Let's see what happens when +we run it. For now, we will call :yoscrypt:`proc -noopt` to prevent some +automatic optimizations which would normally happen. + +.. figure:: /_images/code_examples/fifo/addr_gen_proc.* + :class: width-helper + :name: addr_gen_proc + + ``addr_gen`` module after :yoscrypt:`proc -noopt` + +There are now a few new cells from our ``always @``, which have been +highlighted. The ``if`` statements are now modeled with ``$mux`` cells, while +the register uses an ``$adff`` cell. If we look at the terminal output we can +also see all of the different ``proc_*`` commands being called. We will look at +each of these in more detail in :doc:`/using_yosys/synthesis/proc`. + +Notice how in the top left of :ref:`addr_gen_proc` we have a floating wire, +generated from the initial assignment of 0 to the ``addr`` wire. However, this +initial assignment is not synthesizable, so this will need to be cleaned up +before we can generate the physical hardware. We can do this now by calling +:cmd:ref:`clean`. We're also going to call :cmd:ref:`opt_expr` now, which would +normally be called at the end of :cmd:ref:`proc`. We can call both commands at +the same time by separating them with a colon and space: :yoscrypt:`opt_expr; +clean`. + +.. figure:: /_images/code_examples/fifo/addr_gen_clean.* + :class: width-helper + :name: addr_gen_clean + + ``addr_gen`` module after :yoscrypt:`opt_expr; clean` + +You may also notice that the highlighted ``$eq`` cell input of ``255`` has +changed to ``8'11111111``. Constant values are presented in the format +``'``, with 32-bit values instead using the decimal number. +This indicates that the constant input has been reduced from 32-bit wide to +8-bit wide. This is a side-effect of running :cmd:ref:`opt_expr`, which +performs constant folding and simple expression rewriting. For more on why +this happens, refer to :doc:`/using_yosys/synthesis/opt` and the :ref:`section +on opt_expr `. + +.. note:: + + :doc:`/cmd/clean` can also be called with two semicolons after any command, + for example we could have called :yoscrypt:`opt_expr;;` instead of + :yoscrypt:`opt_expr; clean`. You may notice some scripts will end each line + with ``;;``. It is beneficial to run :cmd:ref:`clean` before inspecting + intermediate products to remove disconnected parts of the circuit which have + been left over, and in some cases can reduce the processing required in + subsequent commands. + +.. todo:: consider a brief glossary for terms like adff + +.. seealso:: Advanced usage docs for + + - :doc:`/using_yosys/synthesis/proc` + - :doc:`/using_yosys/synthesis/opt` + +The full example +^^^^^^^^^^^^^^^^ + +Let's now go back and check on our full design by using :yoscrypt:`hierarchy +-check -top fifo`. By passing the ``-check`` option there we are also telling +the :cmd:ref:`hierarchy` command that if the design includes any non-blackbox +modules without an implementation it should return an error. + +Note that if we tried to run this command now then we would get an error. This +is because we already removed all of the modules other than ``addr_gen``. We +could restart our shell session, but instead let's use two new commands: + +- :doc:`/cmd/design`, and +- :doc:`/cmd/read_verilog`. + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: design -reset + :end-before: yosys> proc + :caption: reloading :file:`fifo.v` and running :yoscrypt:`hierarchy -check -top fifo` + +Notice how this time we didn't see any of those `$abstract` modules? That's +because when we ran ``yosys fifo.v``, the first command Yosys called was +:yoscrypt:`read_verilog -defer fifo.v`. The ``-defer`` option there tells +:cmd:ref:`read_verilog` only read the abstract syntax tree and defer actual +compilation to a later :cmd:ref:`hierarchy` command. This is useful in cases +where the default parameters of modules yield invalid code which is not +synthesizable. This is why Yosys defers compilation automatically and is one of +the reasons why hierarchy should always be the first command after loading the +design. If we know that our design won't run into this issue, we can skip the +``-defer``. + +.. todo:: :cmd:ref:`hierarchy` failure modes + +.. note:: + + The number before a command's output increments with each command run. Don't + worry if your numbers don't match ours! The output you are seeing comes from + the same script that was used to generate the images in this document, + included in the source as :file:`fifo.ys`. There are extra commands being run + which you don't see, but feel free to try them yourself, or play around with + different commands. You can always start over with a clean slate by calling + ``exit`` or hitting :kbd:`ctrl+d` (i.e. EOF) and re-launching the Yosys + interactive terminal. :kbd:`ctrl+c` (i.e. SIGINT) will also end the terminal + session but will return an error code rather than exiting gracefully. + +We can also run :cmd:ref:`proc` now to finish off the full :ref:`synth_begin`. +Because the design schematic is quite large, we will be showing just the data +path for the ``rdata`` output. If you would like to see the entire design for +yourself, you can do so with :doc:`/cmd/show`. Note that the :cmd:ref:`show` +command only works with a single module, so you may need to call it with +:yoscrypt:`show fifo`. :ref:`show_intro` section in +:doc:`/getting_started/scripting_intro` has more on how to use :cmd:ref:`show`. + +.. figure:: /_images/code_examples/fifo/rdata_proc.* + :class: width-helper + :name: rdata_proc + + ``rdata`` output after :cmd:ref:`proc` + +The highlighted ``fifo_reader`` block contains an instance of the +:ref:`addr_gen_proc` that we looked at earlier. Notice how the type is shown as +``$paramod\\addr_gen\\MAX_DATA=s32'...``. This is a "parametric module": an +instance of the ``addr_gen`` module with the ``MAX_DATA`` parameter set to the +given value. + +The other highlighted block is a ``$memrd`` cell. At this stage of synthesis we +don't yet know what type of memory is going to be implemented, but we *do* know +that ``rdata <= data[raddr];`` could be implemented as a read from memory. Note +that the ``$memrd`` cell here is asynchronous, with both the clock and enable +signal undefined; shown with the ``1'x`` inputs. + +.. seealso:: Advanced usage docs for + :doc:`/using_yosys/synthesis/proc` + +Flattening +~~~~~~~~~~ + +At this stage of a synthesis flow there are a few other commands we could run. +In :cmd:ref:`synth_ice40` we get these: + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: flatten: + :end-before: coarse: + :dedent: + :name: synth_flatten + :caption: ``flatten`` section + +First off is :cmd:ref:`flatten`. Flattening the design like this can allow for +optimizations between modules which would otherwise be missed. Let's run +:yoscrypt:`flatten;;` on our design. + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: yosys> flatten + :end-before: yosys> select + :name: flat_clean + :caption: output of :yoscrypt:`flatten;;` + +.. figure:: /_images/code_examples/fifo/rdata_flat.* + :class: width-helper + :name: rdata_flat + + ``rdata`` output after :yoscrypt:`flatten;;` + +.. role:: yoterm(code) + :language: doscon + +The pieces have moved around a bit, but we can see :ref:`addr_gen_proc` from +earlier has replaced the ``fifo_reader`` block in :ref:`rdata_proc`. We can +also see that the ``addr`` output has been renamed to :file:`fifo_reader.addr` +and merged with the ``raddr`` wire feeding into the ``$memrd`` cell. This wire +merging happened during the call to :cmd:ref:`clean` which we can see in the +:ref:`flat_clean`. + +.. note:: + + :cmd:ref:`flatten` and :cmd:ref:`clean` would normally be combined into a + single :yoterm:`yosys> flatten;;` output, but they appear separately here as + a side effect of using :cmd:ref:`echo` for generating the terminal style + output. + +Depending on the target architecture, this stage of synthesis might also see +commands such as :cmd:ref:`tribuf` with the ``-logic`` option and +:cmd:ref:`deminout`. These remove tristate and inout constructs respectively, +replacing them with logic suitable for mapping to an FPGA. Since we do not have +any such constructs in our example running these commands does not change our +design. + +The coarse-grain representation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +At this stage, the design is in coarse-grain representation. It still looks +recognizable, and cells are word-level operators with parametrizable width. This +is the stage of synthesis where we do things like const propagation, expression +rewriting, and trimming unused parts of wires. + +This is also where we convert our FSMs and hard blocks like DSPs or memories. +Such elements have to be inferred from patterns in the design and there are +special passes for each. Detection of these patterns can also be affected by +optimizations and other transformations done previously. + +.. note:: + + While the iCE40 flow had a :ref:`synth_flatten` and put :cmd:ref:`proc` in + the :ref:`synth_begin`, some synthesis scripts will instead include these in + this section. + +Part 1 +^^^^^^ + +In the iCE40 flow, we start with the following commands: + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: coarse: + :end-before: wreduce + :dedent: + :caption: ``coarse`` section (part 1) + :name: synth_coarse1 + +We've already come across :cmd:ref:`opt_expr`, and :cmd:ref:`opt_clean` is the +same as :cmd:ref:`clean` but with more verbose output. The :cmd:ref:`check` +pass identifies a few obvious problems which will cause errors later. Calling +it here lets us fail faster rather than wasting time on something we know is +impossible. + +Next up is :yoscrypt:`opt -nodffe -nosdff` performing a set of simple +optimizations on the design. This command also ensures that only a specific +subset of FF types are included, in preparation for the next command: +:doc:`/cmd/fsm`. Both :cmd:ref:`opt` and :cmd:ref:`fsm` are macro commands +which are explored in more detail in :doc:`/using_yosys/synthesis/opt` and +:doc:`/using_yosys/synthesis/fsm` respectively. + +Up until now, the data path for ``rdata`` has remained the same since +:ref:`rdata_flat`. However the next call to :cmd:ref:`opt` does cause a change. +Specifically, the call to :cmd:ref:`opt_dff` without the ``-nodffe -nosdff`` +options is able to fold one of the ``$mux`` cells into the ``$adff`` to form an +``$adffe`` cell; highlighted below: + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: yosys> opt_dff + :end-before: yosys> select + :caption: output of :cmd:ref:`opt_dff` + +.. figure:: /_images/code_examples/fifo/rdata_adffe.* + :class: width-helper + :name: rdata_adffe + + ``rdata`` output after :cmd:ref:`opt_dff` + +.. seealso:: Advanced usage docs for + + - :doc:`/using_yosys/synthesis/fsm` + - :doc:`/using_yosys/synthesis/opt` + +Part 2 +^^^^^^ + +The next group of commands performs a series of optimizations: + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-at: wreduce + :end-before: t:$mul + :dedent: + :caption: ``coarse`` section (part 2) + :name: synth_coarse2 + +First up is :doc:`/cmd/wreduce`. If we run this we get the following: + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: yosys> wreduce + :end-before: yosys> select + :caption: output of :cmd:ref:`wreduce` + +Looking at the data path for ``rdata``, the most relevant of these width +reductions are the ones affecting ``fifo.$flatten\fifo_reader.$add$fifo.v``. +That is the ``$add`` cell incrementing the fifo_reader address. We can look at +the schematic and see the output of that cell has now changed. + +.. todo:: pending bugfix in :cmd:ref:`wreduce` and/or :cmd:ref:`opt_clean` + +.. figure:: /_images/code_examples/fifo/rdata_wreduce.* + :class: width-helper + :name: rdata_wreduce + + ``rdata`` output after :cmd:ref:`wreduce` + +The next two (new) commands are :doc:`/cmd/peepopt` and :doc:`/cmd/share`. +Neither of these affect our design, and they're explored in more detail in +:doc:`/using_yosys/synthesis/opt`, so let's skip over them. :yoscrypt:`techmap +-map +/cmp2lut.v -D LUT_WIDTH=4` optimizes certain comparison operators by +converting them to LUTs instead. The usage of :cmd:ref:`techmap` is explored +more in :doc:`/using_yosys/synthesis/techmap_synth`. + +Our next command to run is +:doc:`/cmd/memory_dff`. + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: yosys> memory_dff + :end-before: yosys> select + :caption: output of :cmd:ref:`memory_dff` + +.. figure:: /_images/code_examples/fifo/rdata_memrdv2.* + :class: width-helper + :name: rdata_memrdv2 + + ``rdata`` output after :cmd:ref:`memory_dff` + +As the title suggests, :cmd:ref:`memory_dff` has merged the output ``$dff`` into +the ``$memrd`` cell and converted it to a ``$memrd_v2`` (highlighted). This has +also connected the ``CLK`` port to the ``clk`` input as it is now a synchronous +memory read with appropriate enable (``EN=1'1``) and reset (``ARST=1'0`` and +``SRST=1'0``) inputs. + +.. seealso:: Advanced usage docs for + + - :doc:`/using_yosys/synthesis/opt` + - :doc:`/using_yosys/synthesis/techmap_synth` + - :doc:`/using_yosys/synthesis/memory` + +Part 3 +^^^^^^ + +The third part of the :cmd:ref:`synth_ice40` flow is a series of commands for +mapping to DSPs. By default, the iCE40 flow will not map to the hardware DSP +blocks and will only be performed if called with the ``-dsp`` flag: +:yoscrypt:`synth_ice40 -dsp`. While our example has nothing that could be +mapped to DSPs we can still take a quick look at the commands here and describe +what they do. + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-at: t:$mul + :end-before: alumacc + :dedent: + :caption: ``coarse`` section (part 3) + :name: synth_coarse3 + +:yoscrypt:`wreduce t:$mul` performs width reduction again, this time targetting +only cells of type ``$mul``. :yoscrypt:`techmap -map +/mul2dsp.v -map ++/ice40/dsp_map.v ... -D DSP_NAME=$__MUL16X16` uses :cmd:ref:`techmap` to map +``$mul`` cells to ``$__MUL16X16`` which are, in turn, mapped to the iCE40 +``SB_MAC16``. Any multipliers which aren't compatible with conversion to +``$__MUL16X16`` are relabelled to ``$__soft_mul`` before :cmd:ref:`chtype` +changes them back to ``$mul``. + +During the mul2dsp conversion, some of the intermediate signals are marked with +the attribute ``mul2dsp``. By calling :yoscrypt:`select a:mul2dsp` we restrict +the following commands to only operate on the cells and wires used for these +signals. :cmd:ref:`setattr` removes the now unnecessary ``mul2dsp`` attribute. +:cmd:ref:`opt_expr` we've already come across for const folding and simple +expression rewriting, the ``-fine`` option just enables more fine-grain +optimizations. Then we perform width reduction a final time and clear the +selection. + +.. todo:: ``ice40_dsp`` is pmgen + +Finally we have :cmd:ref:`ice40_dsp`: similar to the :cmd:ref:`memory_dff` +command we saw in the previous section, this merges any surrounding registers +into the ``SB_MAC16`` cell. This includes not just the input/output registers, +but also pipeline registers and even a post-adder where applicable: turning a +multiply + add into a single multiply-accumulate. + +.. seealso:: Advanced usage docs for + :doc:`/using_yosys/synthesis/techmap_synth` + +Part 4 +^^^^^^ + +That brings us to the fourth and final part for the iCE40 synthesis flow: + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-at: alumacc + :end-before: map_ram: + :dedent: + :caption: ``coarse`` section (part 4) + :name: synth_coarse4 + +Where before each type of arithmetic operation had its own cell, e.g. ``$add``, +we now want to extract these into ``$alu`` and ``$macc`` cells which can help +identify opportunities for reusing logic. We do this by running +:cmd:ref:`alumacc`, which we can see produce the following changes in our +example design: + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: yosys> alumacc + :end-before: yosys> select + :caption: output of :cmd:ref:`alumacc` + +.. figure:: /_images/code_examples/fifo/rdata_alumacc.* + :class: width-helper + :name: rdata_alumacc + + ``rdata`` output after :cmd:ref:`alumacc` + +Once these cells have been inserted, the call to :cmd:ref:`opt` can combine +cells which are now identical but may have been missed due to e.g. the +difference between ``$add`` and ``$sub``. + +The other new command in this part is :doc:`/cmd/memory`. :cmd:ref:`memory` is +another macro command which we examine in more detail in +:doc:`/using_yosys/synthesis/memory`. For this document, let us focus just on +the step most relevant to our example: :cmd:ref:`memory_collect`. Up until this +point, our memory reads and our memory writes have been totally disjoint cells; +operating on the same memory only in the abstract. :cmd:ref:`memory_collect` +combines all of the reads and writes for a memory block into a single cell. + +.. figure:: /_images/code_examples/fifo/rdata_coarse.* + :class: width-helper + :name: rdata_coarse + + ``rdata`` output after :cmd:ref:`memory_collect` + +Looking at the schematic after running :cmd:ref:`memory_collect` we see that our +``$memrd_v2`` cell has been replaced with a ``$mem_v2`` cell named ``data``, the +same name that we used in :ref:`fifo-v`. Where before we had a single set of +signals for address and enable, we now have one set for reading (``RD_*``) and +one for writing (``WR_*``), as well as both ``WR_DATA`` input and ``RD_DATA`` +output. + +.. seealso:: Advanced usage docs for + + - :doc:`/using_yosys/synthesis/opt` + - :doc:`/using_yosys/synthesis/memory` + +Final note +^^^^^^^^^^ + +Having now reached the end of the the coarse-grain representation, we could also +have gotten here by running :yoscrypt:`synth_ice40 -top fifo -run :map_ram` +after loading the design. The :yoscrypt:`-run :` option +with an empty ```` starts from the :ref:`synth_begin`, while the +```` runs up to but including the :ref:`map_ram`. + +Hardware mapping +~~~~~~~~~~~~~~~~ + +The remaining sections each map a different type of hardware and are much more +architecture dependent than the previous sections. As such we will only be +looking at each section very briefly. + +If you skipped calling :yoscrypt:`read_verilog -D ICE40_HX -lib -specify ++/ice40/cells_sim.v` earlier, do it now. + +Memory blocks +^^^^^^^^^^^^^ + +Mapping to hard memory blocks uses a combination of :cmd:ref:`memory_libmap` and +:cmd:ref:`techmap`. + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: map_ram: + :end-before: map_ffram: + :dedent: + :name: map_ram + :caption: ``map_ram`` section + +.. figure:: /_images/code_examples/fifo/rdata_map_ram.* + :class: width-helper + :name: rdata_map_ram + + ``rdata`` output after :ref:`map_ram` + +The :ref:`map_ram` converts the generic ``$mem_v2`` into the iCE40 +``SB_RAM40_4K`` (highlighted). We can also see the memory address has been +remapped, and the data bits have been reordered (or swizzled). There is also +now a ``$mux`` cell controlling the value of ``rdata``. In :ref:`fifo-v` we +wrote our memory as read-before-write, however the ``SB_RAM40_4K`` has undefined +behaviour when reading from and writing to the same address in the same cycle. +As a result, extra logic is added so that the generated circuit matches the +behaviour of the verilog. :ref:`no_rw_check` describes how we could change our +verilog to match our hardware instead. + +If we run :cmd:ref:`memory_libmap` under the :cmd:ref:`debug` command we can see +candidates which were identified for mapping, along with the costs of each and +what logic requires emulation. + +.. literalinclude:: /code_examples/fifo/fifo.libmap + :language: doscon + :lines: 2, 6- + +The ``$__ICE40_RAM4K_`` cell is defined in the file |techlibs/ice40/brams.txt|_, +with the mapping to ``SB_RAM40_4K`` done by :cmd:ref:`techmap` using +|techlibs/ice40/brams_map.v|_. Any leftover memory cells are then converted +into flip flops (the ``logic fallback``) with :cmd:ref:`memory_map`. + +.. |techlibs/ice40/brams.txt| replace:: :file:`techlibs/ice40/brams.txt` +.. _techlibs/ice40/brams.txt: https://github.com/YosysHQ/yosys/tree/master/techlibs/ice40/brams.txt +.. |techlibs/ice40/brams_map.v| replace:: :file:`techlibs/ice40/brams_map.v` +.. _techlibs/ice40/brams_map.v: https://github.com/YosysHQ/yosys/tree/master/techlibs/ice40/brams_map.v + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: map_ffram: + :end-before: map_gates: + :dedent: + :name: map_ffram + :caption: ``map_ffram`` section + +.. figure:: /_images/code_examples/fifo/rdata_map_ffram.* + :class: width-helper + :name: rdata_map_ffram + + ``rdata`` output after :ref:`map_ffram` + +.. note:: + + The visual clutter on the ``RDATA`` output port (highlighted) is an + unfortunate side effect of :cmd:ref:`opt_clean` on the swizzled data bits. In + connecting the ``$mux`` input port directly to ``RDATA`` to reduce the number + of wires, the ``$techmap579\data.0.0.RDATA`` wire becomes more visually + complex. + +.. seealso:: Advanced usage docs for + + - :doc:`/using_yosys/synthesis/techmap_synth` + - :doc:`/using_yosys/synthesis/memory` + +Arithmetic +^^^^^^^^^^ + +Uses :cmd:ref:`techmap` to map basic arithmetic logic to hardware. This sees +somewhat of an explosion in cells as multi-bit ``$mux`` and ``$adffe`` are +replaced with single-bit ``$_MUX_`` and ``$_DFFE_PP0P_`` cells, while the +``$alu`` is replaced with primitive ``$_OR_`` and ``$_NOT_`` gates and a +``$lut`` cell. + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: map_gates: + :end-before: map_ffs: + :dedent: + :name: map_gates + :caption: ``map_gates`` section + +.. figure:: /_images/code_examples/fifo/rdata_map_gates.* + :class: width-helper + :name: rdata_map_gates + + ``rdata`` output after :ref:`map_gates` + +.. seealso:: Advanced usage docs for + :doc:`/using_yosys/synthesis/techmap_synth` + +Flip-flops +^^^^^^^^^^ + +Convert FFs to the types supported in hardware with :cmd:ref:`dfflegalize`, and +then use :cmd:ref:`techmap` to map them. In our example, this converts the +``$_DFFE_PP0P_`` cells to ``SB_DFFER``. + +We also run :cmd:ref:`simplemap` here to convert any remaining cells which could +not be mapped to hardware into gate-level primitives. This includes optimizing +``$_MUX_`` cells where one of the inputs is a constant ``1'0``, replacing it +instead with an ``$_AND_`` cell. + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: map_ffs: + :end-before: map_luts: + :dedent: + :name: map_ffs + :caption: ``map_ffs`` section + +.. figure:: /_images/code_examples/fifo/rdata_map_ffs.* + :class: width-helper + :name: rdata_map_ffs + + ``rdata`` output after :ref:`map_ffs` + +.. seealso:: Advanced usage docs for + :doc:`/using_yosys/synthesis/techmap_synth` + +LUTs +^^^^ + +:cmd:ref:`abc` and :cmd:ref:`techmap` are used to map LUTs; converting primitive +cell types to use ``$lut`` and ``SB_CARRY`` cells. Note that the iCE40 flow +uses :cmd:ref:`abc9` rather than :cmd:ref:`abc`. For more on what these do, and +what the difference between these two commands are, refer to +:doc:`/using_yosys/synthesis/abc`. + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: map_luts: + :end-before: map_cells: + :dedent: + :name: map_luts + :caption: ``map_luts`` section + +.. figure:: /_images/code_examples/fifo/rdata_map_luts.* + :class: width-helper + :name: rdata_map_luts + + ``rdata`` output after :ref:`map_luts` + +Finally we use :cmd:ref:`techmap` to map the generic ``$lut`` cells to iCE40 +``SB_LUT4`` cells. + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: map_cells: + :end-before: check: + :dedent: + :name: map_cells + :caption: ``map_cells`` section + +.. figure:: /_images/code_examples/fifo/rdata_map_cells.* + :class: width-helper + :name: rdata_map_cells + + ``rdata`` output after :ref:`map_cells` + +.. seealso:: Advanced usage docs for + + - :doc:`/using_yosys/synthesis/techmap_synth` + - :doc:`/using_yosys/synthesis/abc` + +Other cells +^^^^^^^^^^^ + +The following commands may also be used for mapping other cells: + +:cmd:ref:`hilomap` + Some architectures require special driver cells for driving a constant hi or + lo value. This command replaces simple constants with instances of such + driver cells. + +:cmd:ref:`iopadmap` + Top-level input/outputs must usually be implemented using special I/O-pad + cells. This command inserts such cells to the design. + +These commands tend to either be in the :ref:`map_cells` or after the +:ref:`check` depending on the flow. + +Final steps +~~~~~~~~~~~~ + +The next section of the iCE40 synth flow performs some sanity checking and final +tidy up: + +.. literalinclude:: /cmd/synth_ice40.rst + :language: yoscrypt + :start-after: check: + :end-before: blif: + :dedent: + :name: check + :caption: ``check`` section + +The new commands here are: + +- :doc:`/cmd/autoname`, +- :doc:`/cmd/stat`, and +- :doc:`/cmd/blackbox`. + +The output from :cmd:ref:`stat` is useful for checking resource utilization; +providing a list of cells used in the design and the number of each, as well as +the number of other resources used such as wires and processes. For this +design, the final call to :cmd:ref:`stat` should look something like the +following: + +.. literalinclude:: /code_examples/fifo/fifo.stat + :language: doscon + :start-at: yosys> stat -top fifo + +Note that the :yoscrypt:`-top fifo` here is optional. :cmd:ref:`stat` will +automatically use the module with the ``top`` attribute set, which ``fifo`` was +when we called :cmd:ref:`hierarchy`. If no module is marked ``top``, then stats +will be shown for each module selected. + +The :cmd:ref:`stat` output is also useful as a kind of sanity-check: Since we +have already run :cmd:ref:`proc`, we wouldn't expect there to be any processes. +We also expect ``data`` to use hard memory; if instead of an ``SB_RAM40_4K`` saw +a high number of flip-flops being used we might suspect something was wrong. + +If we instead called :cmd:ref:`stat` immediately after :yoscrypt:`read_verilog +fifo.v` we would see something very different: + +.. literalinclude:: /code_examples/fifo/fifo.stat + :language: doscon + :start-at: yosys> stat + :end-before: yosys> stat -top fifo + +Notice how ``fifo`` and ``addr_gen`` are listed separately, and the statistics +for ``fifo`` show 2 ``addr_gen`` modules. Because this is before the memory has +been mapped, we also see that there is 1 memory with 2048 memory bits; matching +our 8-bit wide ``data`` memory with 256 values (:math:`8*256=2048`). + +Synthesis output +^^^^^^^^^^^^^^^^ + +The iCE40 synthesis flow has the following output modes available: + +- :doc:`/cmd/write_blif`, +- :doc:`/cmd/write_edif`, and +- :doc:`/cmd/write_json`. + +As an example, if we called :yoscrypt:`synth_ice40 -top fifo -json fifo.json`, +our synthesized ``fifo`` design will be output as :file:`fifo.json`. We can +then read the design back into Yosys with :cmd:ref:`read_json`, but make sure +you use :yoscrypt:`design -reset` or open a new interactive terminal first. The +JSON output we get can also be loaded into `nextpnr`_ to do place and route; but +that is beyond the scope of this documentation. + +.. _nextpnr: https://github.com/YosysHQ/nextpnr + +.. seealso:: :doc:`/cmd/synth_ice40` diff --git a/docs/source/getting_started/index.rst b/docs/source/getting_started/index.rst new file mode 100644 index 00000000000..7a92c212ae6 --- /dev/null +++ b/docs/source/getting_started/index.rst @@ -0,0 +1,13 @@ +Getting started with Yosys +========================== + +This section covers how to get started with Yosys, from installation to a guided +walkthrough of synthesizing a design for hardware, and finishing with an +introduction to writing re-usable Yosys scripts. + +.. toctree:: + :maxdepth: 3 + + installation + example_synth + scripting_intro diff --git a/docs/source/getting_started/installation.rst b/docs/source/getting_started/installation.rst new file mode 100644 index 00000000000..4dd5244b912 --- /dev/null +++ b/docs/source/getting_started/installation.rst @@ -0,0 +1,216 @@ +Installation +------------ + +This document will guide you through the process of installing Yosys. + +CAD suite(s) +~~~~~~~~~~~~ + +Yosys is part of the `Tabby CAD Suite +`_ and the `OSS CAD Suite +`_! The easiest way to use yosys +is to install the binary software suite, which contains all required +dependencies and related tools. + +* `Contact YosysHQ `_ for a `Tabby CAD Suite + `_ Evaluation License and + download link +* OR go to https://github.com/YosysHQ/oss-cad-suite-build/releases to download + the free OSS CAD Suite +* Follow the `Install Instructions on GitHub + `_ + +Make sure to get a Tabby CAD Suite Evaluation License if you need features such +as industry-grade SystemVerilog and VHDL parsers! + +For more information about the difference between Tabby CAD Suite and the OSS +CAD Suite, please visit https://www.yosyshq.com/tabby-cad-datasheet + +Many Linux distributions also provide Yosys binaries, some more up to date than +others. Check with your package manager! + +Targeted architectures +^^^^^^^^^^^^^^^^^^^^^^ + +The `OSS CAD Suite`_ releases `nightly builds`_ for the following architectures: + +.. only:: html + + - linux-x64 |linux-x64| + - Most personal Linux based computers + + - darwin-x64 |darwin-x64| + - macOS 12 or later with Intel CPU + + - darwin-arm64 |darwin-arm64| + - macOS 12 or later with M1/M2 CPU + + - windows-x64 |windows-x64| + - Targeted for Windows 10 and 11 + + - linux-arm64 |linux-arm64| + +.. _OSS CAD Suite: https://github.com/YosysHQ/oss-cad-suite-build +.. _nightly builds: https://github.com/YosysHQ/oss-cad-suite-build/releases/latest + +.. |linux-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/linux-x64.yml/badge.svg +.. |darwin-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/darwin-x64.yml/badge.svg +.. |darwin-arm64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/darwin-arm64.yml/badge.svg +.. |windows-x64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/windows-x64.yml/badge.svg +.. |linux-arm64| image:: https://github.com/YosysHQ/oss-cad-suite-build/actions/workflows/linux-arm64.yml/badge.svg + +Building from source +~~~~~~~~~~~~~~~~~~~~ + +Refer to the `readme`_ for the most up-to-date install instructions. + +.. _readme: https://github.com/YosysHQ/yosys#building-from-source + +Supported platforms +^^^^^^^^^^^^^^^^^^^ + +The following platforms are supported and regularly tested: + +- Linux +- macOS + +Other platforms which may work, but instructions may not be up to date and are +not regularly tested: + +- FreeBSD +- WSL +- Windows with (e.g.) Cygwin + +Build prerequisites +^^^^^^^^^^^^^^^^^^^ + +A C++ compiler with C++11 support is required as well as some standard tools +such as GNU Flex, GNU Bison, Make and Python. Some additional tools: readline, +libffi, Tcl and zlib; are optional but enabled by default (see +:makevar:`ENABLE_*` settings in Makefile). Graphviz and Xdot are used by the +:cmd:ref:`show` command to display schematics. + +Installing all prerequisites for Ubuntu 20.04: + +.. code:: console + + sudo sudo apt-get install build-essential clang bison flex \ + libreadline-dev gawk tcl-dev libffi-dev git make \ + graphviz xdot pkg-config python3 libboost-system-dev \ + libboost-python-dev libboost-filesystem-dev zlib1g-dev + +Installing all prerequisites for macOS 11 (with Homebrew): + +.. code:: console + + brew install bison flex gawk libffi git graphviz \ + pkg-config python3 tcl-tk xdot bash boost-python3 + +Running the build system +^^^^^^^^^^^^^^^^^^^^^^^^ + +From the root `yosys` directory, call the following commands: + +.. code:: console + + make + sudo make install + +This will build and then install Yosys, making it available on the command line +as `yosys`. Note that this also downloads, builds, and installs `ABC`_ (using +:program:`yosys-abc` as the executable name). + +.. _ABC: https://github.com/berkeley-abc/abc + +The default compiler is ``clang``, to change between ``clang`` and ``gcc``, use +one of the following: + +.. code:: console + + make config-clang + make config-gcc + +To use a compiler different than the default, use: + +.. code:: console + + make CXX="g++-11" + +.. seealso:: + + Refer to :doc:`/test_suites` for details on testing Yosys once compiled. + +Source tree and build system +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Yosys source tree is organized into the following top-level +directories: + +``backends/`` + This directory contains a subdirectory for each of the backend modules. + +``docs/`` + Contains the source for this documentation, including images and sample code. + +``examples/`` + Contains example code for using Yosys with some other tools including a demo + of the Yosys Python api, and synthesizing for various toolchains such as + Intel and Anlogic. + +``frontends/`` + This directory contains a subdirectory for each of the frontend modules. + +``guidelines/`` + Contains developer guidelines, including the code of conduct and coding style + guide. + +``kernel/`` + This directory contains all the core functionality of Yosys. This includes + the functions and definitions for working with the RTLIL data structures + (:file:`rtlil.{h|cc}`), the ``main()`` function (:file:`driver.cc`), the + internal framework for generating log messages (:file:`log.{h|cc}`), the + internal framework for registering and calling passes + (:file:`register.{h|cc}`), some core commands that are not really passes + (:file:`select.cc`, :file:`show.cc`, …) and a couple of other small utility + libraries. + +``libs/`` + Libraries packaged with Yosys builds are contained in this folder. See + :doc:`/appendix/auxlibs`. + +``misc/`` + Other miscellany which doesn't fit anywhere else. + +``passes/`` + This directory contains a subdirectory for each pass or group of passes. For + example as of this writing the directory :file:`passes/hierarchy/` contains the + code for three passes: :cmd:ref:`hierarchy`, :cmd:ref:`submod`, and + :cmd:ref:`uniquify`. + +``techlibs/`` + This directory contains simulation models and standard implementations for + the cells from the internal cell library. + +``tests/`` + This directory contains the suite of unit tests and regression tests used by + Yosys. See :doc:`/test_suites`. + +The top-level Makefile includes :file:`frontends/{*}/Makefile.inc`, +:file:`passes/{*}/Makefile.inc` and :file:`backends/{*}/Makefile.inc`. So when +extending Yosys it is enough to create a new directory in :file:`frontends/`, +:file:`passes/` or :file:`backends/` with your sources and a +:file:`Makefile.inc`. The Yosys kernel automatically detects all commands linked +with Yosys. So it is not needed to add additional commands to a central list of +commands. + +Good starting points for reading example source code to learn how to write +passes are :file:`passes/opt/opt_dff.cc` and :file:`passes/opt/opt_merge.cc`. + +See the top-level README file for a quick Getting Started guide and build +instructions. The Yosys build is based solely on Makefiles. + +Users of the Qt Creator IDE can generate a QT Creator project file using make +qtcreator. Users of the Eclipse IDE can use the "Makefile Project with Existing +Code" project type in the Eclipse "New Project" dialog (only available after the +CDT plugin has been installed) to create an Eclipse project in order to +programming extensions to Yosys or just browse the Yosys code base. diff --git a/docs/source/getting_started/scripting_intro.rst b/docs/source/getting_started/scripting_intro.rst new file mode 100644 index 00000000000..63eca990125 --- /dev/null +++ b/docs/source/getting_started/scripting_intro.rst @@ -0,0 +1,197 @@ +Scripting in Yosys +------------------ + +On the previous page we went through a synthesis script, running each command in +the interactive Yosys shell. On this page, we will be introducing the script +file format and how you can make your own synthesis scripts. + +Yosys script files typically use the :file:`.ys` extension and contain a set of +commands for Yosys to run sequentially. These commands are the same ones we +were using on the previous page like :cmd:ref:`read_verilog` and +:cmd:ref:`hierarchy`. As with the interactive shell, each command consists of +the command name, and an optional whitespace separated list of arguments. +Commands are terminated with the newline character, or by a semicolon (;). Empty +lines, and lines starting with the hash sign (#), are ignored. + +The synthesis starter script +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. role:: yoscrypt(code) + :language: yoscrypt + +All of the images and console output used in +:doc:`/getting_started/example_synth` were generated by Yosys, using Yosys +script files found in :file:`docs/source/code_examples/fifo`. If you haven't +already, let's take a look at some of those script files now. + +.. literalinclude:: /code_examples/fifo/fifo.ys + :language: yoscrypt + :lineno-match: + :start-at: echo on + :end-before: design -reset + :caption: A section of :file:`fifo.ys`, generating the images used for :ref:`addr_gen_example` + :name: fifo-ys + +The first command there, :yoscrypt:`echo on`, uses :cmd:ref:`echo` to enable +command echoes on. This is how we generated the code listing for +:ref:`hierarchy_output`. Turning command echoes on prints the ``yosys> +hierarchy -top addr_gen`` line, making the output look the same as if it were an +interactive terminal. :yoscrypt:`hierarchy -top addr_gen` is of course the +command we were demonstrating, including the output text and an image of the +design schematic after running it. + +We briefly touched on :cmd:ref:`select` when it came up in +:cmd:ref:`synth_ice40`, but let's look at it more now. + +.. _select_intro: + +Selections intro +^^^^^^^^^^^^^^^^ + +The :cmd:ref:`select` command is used to modify and view the list of selected +objects: + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: yosys> select + :end-before: yosys> show + +When we call :yoscrypt:`select -module addr_gen` we are changing the currently +active selection from the whole design, to just the ``addr_gen`` module. Notice +how this changes the ``yosys`` at the start of each command to ``yosys +[addr_gen]``? This indicates that any commands we run at this point will *only* +operate on the ``addr_gen`` module. When we then call :yoscrypt:`select -list` +we get a list of all objects in the ``addr_gen`` module, including the module +itself, as well as all of the wires, inputs, outputs, processes, and cells. + +Next we perform another selection, :yoscrypt:`select t:*`. The ``t:`` part +signifies we are matching on the *cell type*, and the ``*`` means to match +anything. For this (very simple) selection, we are trying to find all of the +cells, regardless of their type. The active selection is now shown as +``[addr_gen]*``, indicating some sub-selection of the ``addr_gen`` module. This +gives us the ``$add`` and ``$eq`` cells, which we want to highlight for the +:ref:`addr_gen_hier` image. + +.. _select_new_cells: + +We can assign a name to a selection with :yoscrypt:`select -set`. In our case +we are using the name ``new_cells``, and telling it to use the current +selection, indicated by the ``%`` symbol. We can then use this named selection +by referring to it as ``@new_cells``, which we will see later. Then we clear +the selection so that the following commands can operate on the full design. +While we split that out for this document, we could have done the same thing in +a single line by calling :yoscrypt:`select -set new_cells addr_gen/t:*`. If we +know we only have the one module in our design, we can even skip the `addr_gen/` +part. Looking further down :ref:`the fifo.ys code ` we can see this +with :yoscrypt:`select -set new_cells t:$mux t:*dff`. We can also see in that +command that selections don't have to be limited to a single statement. + +Many commands also support an optional ``[selection]`` argument which can be +used to override the currently selected objects. We could, for example, call +:yoscrypt:`clean addr_gen` to have :cmd:ref:`clean` operate on *just* the +``addr_gen`` module. + +Detailed documentation of the select framework can be found under +:doc:`/using_yosys/more_scripting/selections` or in the command reference at +:doc:`/cmd/select`. + +.. _show_intro: + +Displaying schematics +^^^^^^^^^^^^^^^^^^^^^ + +While the :cmd:ref:`select` command is very useful, sometimes nothing beats +being able to see a design for yourself. This is where :cmd:ref:`show` comes +in. Note that this document is just an introduction to the :cmd:ref:`show` +command, only covering the basics. For more information, including a guide on +what the different symbols represent, see :ref:`interactive_show` and the +:doc:`/using_yosys/more_scripting/interactive_investigation` page. + +.. figure:: /_images/code_examples/fifo/addr_gen_show.* + :class: width-helper + :name: addr_gen_show + + Calling :yoscrypt:`show addr_gen` after :cmd:ref:`hierarchy` + +.. note:: + + The :cmd:ref:`show` command requires a working installation of `GraphViz`_ + and `xdot`_ for displaying the actual circuit diagrams. + +.. _GraphViz: http://www.graphviz.org/ +.. _xdot: https://github.com/jrfonseca/xdot.py + +This is the first :yoscrypt:`show` command we called in :file:`fifo.ys`, +:ref:`as we saw above `. If we look at the log output for this image +we see the following: + +.. literalinclude:: /code_examples/fifo/fifo.out + :language: doscon + :start-at: -prefix addr_gen_show + :end-before: yosys> show + +Calling :cmd:ref:`show` with :yoscrypt:`-format dot` tells it we want to output +a :file:`.dot` file rather than opening it for display. The :yoscrypt:`-prefix +addr_gen_show` option indicates we want the file to be called +:file:`addr_gen_show.{*}`. Remember, we do this in :file:`fifo.ys` because we +need to store the image for displaying in the documentation you're reading. But +if you just want to display the images locally you can skip these two options. +The ``-format`` option internally calls the ``dot`` command line program from +GraphViz to convert to formats other than :file:`.dot`. Check `GraphViz output +docs`_ for more on available formats. + +.. _GraphViz output docs: https://graphviz.org/docs/outputs/ + +.. note:: + + If you are using a POSIX based version of Yosys (such as for Mac or Linux), + xdot will be opened in the background and Yosys can continue to be used. If + it it still open, future calls to :yoscrypt:`show` will use the same xdot + instance. + +The ``addr_gen`` at the end tells it we only want the ``addr_gen`` module, just +like when we called :yoscrypt:`select -module addr_gen` in :ref:`select_intro`. +That last parameter doesn't have to be a module name, it can be any valid +selection string. Remember when we :ref:`assigned a name to a +selection` and called it ``new_cells``? We saw in the +:yoscrypt:`select -list` output that it contained two cells, an ``$add`` and an +``$eq``. We can call :cmd:ref:`show` on that selection just as easily: + +.. figure:: /_images/code_examples/fifo/new_cells_show.* + :class: width-helper + :name: new_cells_show + + Calling :yoscrypt:`show -notitle @new_cells` + +We could have gotten the same output with :yoscrypt:`show -notitle t:$add t:$eq` +if we didn't have the named selection. By adding the :yoscrypt:`-notitle` flag +there we can also get rid of the ``addr_gen`` title that would have been +automatically added. The last two images were both added for this introduction. +The next image is the first one we saw in :doc:`/getting_started/example_synth`: +showing the full ``addr_gen`` module while also highlighting ``@new_cells`` and +the two ``PROC`` blocks. To achieve this highlight, we make use of the +:yoscrypt:`-color` option: + +.. figure:: /_images/code_examples/fifo/addr_gen_hier.* + :class: width-helper + + Calling :yoscrypt:`show -color maroon3 @new_cells -color cornflowerblue p:* -notitle` + +As described in the the :cmd:ref:`help` output for :cmd:ref:`show` (or by +clicking on the :cmd:ref:`show` link), colors are specified as :yoscrypt:`-color + `. Color names for the ```` portion can be found on the +`GraphViz color docs`_. Unlike the final :cmd:ref:`show` parameter which can +have be any selection string, the ```` part must be a single selection +expression or named selection. That means while we can use ``@new_cells``, we +couldn't use ``t:$eq t:$add``. In general, if a command lists ``[selection]`` +as its final parameter it can be any selection string. Any selections that are +not the final parameter, such as those used in options, must be a single +expression instead. + +.. _GraphViz color docs: https://graphviz.org/doc/info/colors + +For all of the options available to :cmd:ref:`show`, check the command reference +at :doc:`/cmd/show`. + +.. seealso:: :ref:`interactive_show` on the + :doc:`/using_yosys/more_scripting/interactive_investigation` page. diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000000..582551f7486 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,45 @@ +================================================================================ +Yosys Open SYnthesis Suite +================================================================================ + +Yosys is an open source framework for RTL synthesis. To learn more about Yosys, +see :doc:`/introduction`. For a quick guide on how to get started using Yosys, +check out :doc:`/getting_started/index`. For the complete list of commands +available, go to :ref:`commandindex`. + +.. note:: + + This documentation recently went through a major restructure. If you're + looking for something from the previous version and can't find it here, + please `let us know`_. Documentation from before the restructure can still + be found by switching to `version 0.36`_ or earlier. Note that the previous + theme does not include a version switcher. + +.. _let us know: https://github.com/YosysHQ/yosys/issues/new/choose +.. _version 0.36: https://yosyshq.readthedocs.io/projects/yosys/en/0.36/ + +.. todo:: look into command ref improvements + + - Search bar with live drop down suggestions for matching on title / + autocompleting commands + - Scroll the left sidebar to the current location on page load + - Also the formatting/linking in pdf is broken + +.. todolist:: + +.. only:: html + + Table of contents + ----------------- + +.. toctree:: + :maxdepth: 3 + :includehidden: + + introduction + getting_started/index + using_yosys/index + yosys_internals/index + test_suites + + appendix diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst new file mode 100644 index 00000000000..936784d7411 --- /dev/null +++ b/docs/source/introduction.rst @@ -0,0 +1,248 @@ +What is Yosys +============= + +Yosys began as a BSc thesis project by Claire Wolf intended to support synthesis +for a CGRA (coarse-grained reconfigurable architecture). It then expanded into +more general infrastructure for research on synthesis. + +Modern Yosys has full support for the synthesizable subset of Verilog-2005 and +has been described as "the GCC of hardware synthesis." Freely available and +`open source`_, Yosys finds use across hobbyist and commercial applications as +well as academic. + +.. _open source: https://github.com/YosysHQ/yosys + +.. note:: Yosys is released under the ISC License: + + A permissive license lets people do anything with your code with proper + attribution and without warranty. The ISC license is functionally equivalent + to the BSD 2-Clause and MIT licenses, removing some language that is no + longer necessary. + +Together with the place and route tool `nextpnr`_, Yosys can be used to program +some FPGAs with a fully end-to-end open source flow (Lattice iCE40 and ECP5). It +also does the synthesis portion for the `OpenLane flow`_, targeting the SkyWater +130nm open source PDK for fully open source ASIC design. Yosys can also do +formal verification with backends for solver formats like `SMT2`_. + +.. _nextpnr: https://github.com/YosysHQ/nextpnr +.. _OpenLane flow: https://github.com/The-OpenROAD-Project/OpenLane +.. _SMT2: https://smtlib.cs.uiowa.edu/ + +Yosys, and the accompanying Open Source EDA ecosystem, is currently maintained +by `Yosys Headquarters`_, with many of the core developers employed by `YosysHQ +GmbH`_. A commercial extension, `Tabby CAD Suite`_, includes the Verific +frontend for industry-grade SystemVerilog and VHDL support, formal verification +with SVA, and formal apps. + +.. _Yosys Headquarters: https://github.com/YosysHQ +.. _YosysHQ GmbH: https://www.yosyshq.com/about +.. _Tabby CAD Suite: https://www.yosyshq.com/tabby-cad-datasheet + +.. figure:: /_static/logo.png + :class: width-helper + +What you can do with Yosys +-------------------------- + +- Read and process (most of) modern Verilog-2005 code +- Perform all kinds of operations on netlist (RTL, Logic, Gate) +- Perform logic optimizations and gate mapping with ABC + +Typical applications for Yosys +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Synthesis of final production designs +- Pre-production synthesis (trial runs before investing in other tools) +- Conversion of full-featured Verilog to simple Verilog +- Conversion of Verilog to other formats (BLIF, BTOR, etc) +- Demonstrating synthesis algorithms (e.g. for educational purposes) +- Framework for experimenting with new algorithms +- Framework for building custom flows (Not limited to synthesis but also formal + verification, reverse engineering, ...) + +Things you can't do +~~~~~~~~~~~~~~~~~~~ + +- Process high-level languages such as C/C++/SystemC +- Create physical layouts (place&route) + + - Check out `nextpnr`_ for that + +.. todo:: nextpnr for FPGAs, consider mentioning openlane, vpr, coriolis + +.. _nextpnr: https://github.com/YosysHQ/nextpnr + +The Yosys family +---------------- + +As mentioned above, `YosysHQ`_ maintains not just Yosys but an entire family of +tools built around it. In no particular order: + +.. _YosysHQ: https://github.com/YosysHQ + +SBY for formal verification + Yosys provides input parsing and conversion to the formats used by the solver + engines. Yosys also provides a unified witness framework for providing cover + traces and counter examples for engines which don't natively support this. + `SBY source`_ | `SBY docs`_ + +.. _SBY source: https://github.com/YosysHQ/sby +.. _SBY docs: https://yosyshq.readthedocs.io/projects/sby + +EQY for equivalence checking + In addition to input parsing and preparation, Yosys provides the plugin + support enabling EQY to operate on designs directly. `EQY source`_ | `EQY + docs`_ + +.. _EQY source: https://github.com/YosysHQ/eqy +.. _EQY docs: https://yosyshq.readthedocs.io/projects/eqy + +MCY for mutation coverage + Yosys is used to read the source design, generate a list of possible + mutations to maximise design coverage, and then perform selected mutations. + `MCY source`_ | `MCY docs`_ + +.. _MCY source: https://github.com/YosysHQ/mcy +.. _MCY docs: https://yosyshq.readthedocs.io/projects/mcy + +SCY for deep formal traces + Since SCY generates and runs SBY, Yosys provides the same utility for SCY as + it does for SBY. Yosys additionally provides the trace concatenation needed + for outputting the deep traces. `SCY source`_ + +.. _SCY source: https://github.com/YosysHQ/scy + +The original thesis abstract +---------------------------- + +The first version of the Yosys documentation was published as a bachelor thesis +at the Vienna University of Technology :cite:p:`BACC`. + +:Abstract: + Most of today's digital design is done in HDL code (mostly Verilog or + VHDL) and with the help of HDL synthesis tools. + + In special cases such as synthesis for coarse-grain cell libraries or + when testing new synthesis algorithms it might be necessary to write a + custom HDL synthesis tool or add new features to an existing one. In + these cases the availability of a Free and Open Source (FOSS) synthesis + tool that can be used as basis for custom tools would be helpful. + + In the absence of such a tool, the Yosys Open SYnthesis Suite (Yosys) + was developed. This document covers the design and implementation of + this tool. At the moment the main focus of Yosys lies on the high-level + aspects of digital synthesis. The pre-existing FOSS logic-synthesis tool + ABC is used by Yosys to perform advanced gate-level optimizations. + + An evaluation of Yosys based on real-world designs is included. It is + shown that Yosys can be used as-is to synthesize such designs. The + results produced by Yosys in this tests where successfully verified + using formal verification and are comparable in quality to the results + produced by a commercial synthesis tool. + +Yosys is a Verilog HDL synthesis tool. This means that it takes a behavioural +design description as input and generates an RTL, logical gate or physical gate +level description of the design as output. Yosys' main strengths are behavioural +and RTL synthesis. A wide range of commands (synthesis passes) exist within +Yosys that can be used to perform a wide range of synthesis tasks within the +domain of behavioural, rtl and logic synthesis. Yosys is designed to be +extensible and therefore is a good basis for implementing custom synthesis tools +for specialised tasks. + +.. figure:: /_images/primer/levels_of_abstraction.* + :class: width-helper + :name: fig:Levels_of_abstraction + + Where Yosys exists in the layers of abstraction + +Benefits of open source HDL synthesis +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Cost (also applies to ``free as in free beer`` solutions): + + Today the cost for a mask set in 180nm technology is far less than + the cost for the design tools needed to design the mask layouts. Open Source + ASIC flows are an important enabler for ASIC-level Open Source Hardware. + +- Availability and Reproducibility: + + If you are a researcher who is publishing, you want to use tools that everyone + else can also use. Even if most universities have access to all major + commercial tools, you usually do not have easy access to the version that was + used in a research project a couple of years ago. With Open Source tools you + can even release the source code of the tool you have used alongside your data. + +- Framework: + + Yosys is not only a tool. It is a framework that can be used as basis for other + developments, so researchers and hackers alike do not need to re-invent the + basic functionality. Extensibility was one of Yosys' design goals. + +- All-in-one: + + Because of the framework characteristics of Yosys, an increasing number of features + become available in one tool. Yosys not only can be used for circuit synthesis but + also for formal equivalence checking, SAT solving, and for circuit analysis, to + name just a few other application domains. With proprietary software one needs to + learn a new tool for each of these applications. + +- Educational Tool: + + Proprietary synthesis tools are at times very secretive about their inner + workings. They often are ``black boxes``. Yosys is very open about its + internals and it is easy to observe the different steps of synthesis. + +History of Yosys +~~~~~~~~~~~~~~~~ + +.. todo:: Consider a less academic version of the History of Yosys + +A Hardware Description Language (HDL) is a computer language used to describe +circuits. A HDL synthesis tool is a computer program that takes a formal +description of a circuit written in an HDL as input and generates a netlist that +implements the given circuit as output. + +Currently the most widely used and supported HDLs for digital circuits are +Verilog :cite:p:`Verilog2005,VerilogSynth` and :abbr:`VHDL (VHSIC HDL, where +VHSIC is an acronym for Very-High-Speed Integrated Circuits)` +:cite:p:`VHDL,VHDLSynth`. Both HDLs are used for test and verification purposes +as well as logic synthesis, resulting in a set of synthesizable and a set of +non-synthesizable language features. In this document we only look at the +synthesizable subset of the language features. + +In recent work on heterogeneous coarse-grain reconfigurable logic +:cite:p:`intersynth` the need for a custom application-specific HDL synthesis +tool emerged. It was soon realised that a synthesis tool that understood Verilog +or VHDL would be preferred over a synthesis tool for a custom HDL. Given an +existing Verilog or VHDL front end, the work for writing the necessary +additional features and integrating them in an existing tool can be estimated to +be about the same as writing a new tool with support for a minimalistic custom +HDL. + +The proposed custom HDL synthesis tool should be licensed under a Free and Open +Source Software (FOSS) licence. So an existing FOSS Verilog or VHDL synthesis +tool would have been needed as basis to build upon. The main advantages of +choosing Verilog or VHDL is the ability to synthesize existing HDL code and to +mitigate the requirement for circuit-designers to learn a new language. In order +to take full advantage of any existing FOSS Verilog or VHDL tool, such a tool +would have to provide a feature-complete implementation of the synthesizable HDL +subset. + +Basic RTL synthesis is a well understood field :cite:p:`LogicSynthesis`. Lexing, +parsing and processing of computer languages :cite:p:`Dragonbook` is a +thoroughly researched field. All the information required to write such tools +has been openly available for a long time, and it is therefore likely that a +FOSS HDL synthesis tool with a feature-complete Verilog or VHDL front end must +exist which can be used as a basis for a custom RTL synthesis tool. + +Due to the author's preference for Verilog over VHDL it was decided early on to +go for Verilog instead of VHDL [#]_. So the existing FOSS Verilog synthesis +tools were evaluated. The results of this evaluation are utterly devastating. +Therefore a completely new Verilog synthesis tool was implemented and is +recommended as basis for custom synthesis tools. This is the tool that is +discussed in this document. + +.. [#] + A quick investigation into FOSS VHDL tools yielded similar grim results for + FOSS VHDL synthesis tools. diff --git a/docs/source/literature.bib b/docs/source/literature.bib new file mode 100644 index 00000000000..143e3aa36d9 --- /dev/null +++ b/docs/source/literature.bib @@ -0,0 +1,202 @@ + +@inproceedings{intersynth, + title={Example-driven interconnect synthesis for heterogeneous coarse-grain reconfigurable logic}, + author={C. Wolf and Johann Glaser and Florian Schupfer and Jan Haase and Christoph Grimm}, + booktitle={FDL Proceeding of the 2012 Forum on Specification and Design Languages}, + pages={194--201}, + year={2012} +} + +@incollection{intersynthFdlBookChapter, + title={Methodology and Example-Driven Interconnect Synthesis for Designing Heterogeneous Coarse-Grain Reconfigurable Architectures}, + author={Johann Glaser and C. Wolf}, + booktitle={Advances in Models, Methods, and Tools for Complex Chip Design --- Selected contributions from FDL'12}, + editor={Jan Haase}, + publisher={Springer}, + year={2013}, + note={to appear} +} + +@unpublished{BACC, + author = {C. Wolf}, + title = {Design and Implementation of the Yosys Open SYnthesis Suite}, + note = {Bachelor Thesis, Vienna University of Technology}, + year = {2013} +} + +@unpublished{VerilogFossEval, + author = {C. Wolf}, + title = {Evaluation of Open Source Verilog Synthesis Tools for Feature-Completeness and Extensibility}, + note = {Unpublished Student Research Paper, Vienna University of Technology}, + year = {2012} +} + +@article{ABEL, + title={A High-Level Design Language for Programmable Logic Devices}, + author={Kyu Y. Lee and Michael Holley and Mary Bailey and Walter Bright}, + journal={VLSI Design (Manhasset NY: CPM Publications)}, + year={June 1985}, + pages={50-62} +} + +@MISC{Cheng93vl2mv:a, + author = {S-T Cheng and G York and R K Brayton}, + title = {VL2MV: A Compiler from Verilog to BLIF-MV}, + year = {1993} +} + +@MISC{Odin, + author = {Peter Jamieson and Jonathan Rose}, + title = {A VERILOG RTL SYNTHESIS TOOL FOR HETEROGENEOUS FPGAS}, + year = {2005} +} + +@inproceedings{vtr2012, + title={The VTR Project: Architecture and CAD for FPGAs from Verilog to Routing}, + author={Jonathan Rose and Jason Luu and Chi Wai Yu and Opal Densmore and Jeff Goeders and Andrew Somerville and Kenneth B. Kent and Peter Jamieson and Jason Anderson}, + booktitle={Proceedings of the 20th ACM/SIGDA International Symposium on Field-Programmable Gate Arrays}, + pages={77--86}, + year={2012}, + organization={ACM} +} + +@MISC{LogicSynthesis, + author = {G D Hachtel and F Somenzi}, + title = {Logic Synthesis and Verification Algorithms}, + year = {1996} +} + +@ARTICLE{Verilog2005, + journal={IEEE Std 1364-2005 (Revision of IEEE Std 1364-2001)}, + title={IEEE Standard for Verilog Hardware Description Language}, + author={IEEE Standards Association and others}, + year={2006}, + doi={10.1109/IEEESTD.2006.99495} +} + +@ARTICLE{VerilogSynth, + journal={IEEE Std 1364.1-2002}, + title={IEEE Standard for Verilog Register Transfer Level Synthesis}, + author={IEEE Standards Association and others}, + year={2002}, + doi={10.1109/IEEESTD.2002.94220} +} + +@ARTICLE{VHDL, + journal={IEEE Std 1076-2008 (Revision of IEEE Std 1076-2002)}, + title={IEEE Standard VHDL Language Reference Manual}, + author={IEEE Standards Association and others}, + year={2009}, + month={26}, + doi={10.1109/IEEESTD.2009.4772740} +} + +@ARTICLE{VHDLSynth, + journal={IEEE Std 1076.6-2004 (Revision of IEEE Std 1076.6-1999)}, + title={IEEE Standard for VHDL Register Transfer Level (RTL) Synthesis}, + author={IEEE Standards Association and others}, + year={2004}, + doi={10.1109/IEEESTD.2004.94802} +} + +@ARTICLE{IP-XACT, + journal={IEEE Std 1685-2009}, + title={IEEE Standard for IP-XACT, Standard Structure for Packaging, Integrating, and Reusing IP within Tools Flows}, + author={IEEE Standards Association and others}, + year={2010}, + pages={C1-360}, + keywords={abstraction definitions, address space specification, bus definitions, design environment, EDA, electronic design automation, electronic system level, ESL, implementation constraints, IP-XACT, register transfer level, RTL, SCRs, semantic consistency rules, TGI, tight generator interface, tool and data interoperability, use models, XML design meta-data, XML schema}, + doi={10.1109/IEEESTD.2010.5417309} +} + +@book{Dragonbook, + author = {Aho, Alfred V. and Sethi, Ravi and Ullman, Jeffrey D.}, + title = {Compilers: principles, techniques, and tools}, + year = {1986}, + isbn = {0-201-10088-6}, + publisher = {Addison-Wesley Longman Publishing Co., Inc.}, + address = {Boston, MA, USA} +} + +@INPROCEEDINGS{Cummings00, + author = {Clifford E. Cummings and Sunburst Design Inc}, + title = {Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill}, + booktitle = {SNUG (Synopsys Users Group) 2000 User Papers, section-MC1 (1 st paper}, + year = {2000} +} + +@ARTICLE{MURPHY, + author={D. L. Klipstein}, + journal={Cahners Publishing Co., EEE Magazine, Vol. 15, No. 8}, + title={The Contributions of Edsel Murphy to the Understanding of the Behavior of Inanimate Objects}, + year={August 1967} +} + +@INPROCEEDINGS{fsmextract, + author={Yiqiong Shi and Chan Wai Ting and Bah-Hwee Gwee and Ye Ren}, + booktitle={Circuits and Systems (ISCAS), Proceedings of 2010 IEEE International Symposium on}, + title={A highly efficient method for extracting FSMs from flattened gate-level netlist}, + year={2010}, + pages={2610-2613}, + keywords={circuit CAD;finite state machines;microcontrollers;FSM;control-intensive circuits;finite state machines;flattened gate-level netlist;state register elimination technique;Automata;Circuit synthesis;Continuous wavelet transforms;Design automation;Digital circuits;Hardware design languages;Logic;Microcontrollers;Registers;Signal processing}, + doi={10.1109/ISCAS.2010.5537093}, +} + +@ARTICLE{MultiLevelLogicSynth, + author={Brayton, R.K. and Hachtel, G.D. and Sangiovanni-Vincentelli, A.L.}, + journal={Proceedings of the IEEE}, + title={Multilevel logic synthesis}, + year={1990}, + volume={78}, + number={2}, + pages={264-300}, + keywords={circuit layout CAD;integrated logic circuits;logic CAD;capsule summaries;definitions;detailed analysis;in-depth background;logic decomposition;logic minimisation;logic synthesis;logic synthesis techniques;multilevel combinational logic;multilevel logic synthesis;notation;perspective;survey;synthesis methods;technology mapping;testing;Application specific integrated circuits;Design automation;Integrated circuit synthesis;Logic design;Logic devices;Logic testing;Network synthesis;Programmable logic arrays;Signal synthesis;Silicon}, + doi={10.1109/5.52213}, + ISSN={0018-9219}, +} + +@article{UllmannSubgraphIsomorphism, + author = {Ullmann, J. R.}, + title = {An Algorithm for Subgraph Isomorphism}, + journal = {J. ACM}, + issue_date = {Jan. 1976}, + volume = {23}, + number = {1}, + month = jan, + year = {1976}, + issn = {0004-5411}, + pages = {31--42}, + numpages = {12}, + doi = {10.1145/321921.321925}, + acmid = {321925}, + publisher = {ACM}, + address = {New York, NY, USA}, +} + +@article{een2003temporal, + title={Temporal induction by incremental SAT solving}, + author={E{\'e}n, Niklas and S{\"o}rensson, Niklas}, + journal={Electronic Notes in Theoretical Computer Science}, + volume={89}, + number={4}, + pages={543--560}, + year={2003}, + publisher={Elsevier} +} + +@inproceedings{btor, + title={BTOR: bit-precise modelling of word-level problems for model checking}, + author={Brummayer, Robert and Biere, Armin and Lonsing, Florian}, + booktitle={Proceedings of the joint workshops of the 6th international workshop on satisfiability modulo theories and 1st international workshop on bit-precise reasoning}, + pages={33--38}, + year={2008} +} + +@inproceedings{VIS, + title={VIS: A system for verification and synthesis}, + author={Brayton, Robert K and Hachtel, Gary D and Sangiovanni-Vincentelli, Alberto and Somenzi, Fabio and Aziz, Adnan and Cheng, Szu-Tsung and Edwards, Stephen and Khatri, Sunil and Kukimoto, Yuji and Pardo, Abelardo and others}, + booktitle={Proceedings of the 8th International Conference on Computer Aided Verification}, + pages={428--432}, + year={1996}, + organization={Springer} +} diff --git a/docs/source/requirements.txt b/docs/source/requirements.txt new file mode 100644 index 00000000000..74c8dd090cb --- /dev/null +++ b/docs/source/requirements.txt @@ -0,0 +1,2 @@ +furo +sphinxcontrib-bibtex diff --git a/docs/source/test_suites.rst b/docs/source/test_suites.rst new file mode 100644 index 00000000000..2edb0e67d1a --- /dev/null +++ b/docs/source/test_suites.rst @@ -0,0 +1,25 @@ +Testing Yosys +============= + +.. todo:: more about the included test suite + +Automatic testing +----------------- + +.. only:: html + + The `Yosys Git repo`_ has automatic testing of builds and running of the + included test suite on the following platforms: + + - Ubuntu |test-linux| + - macOS |test-macos| + +.. _Yosys Git repo: https://github.com/YosysHQ/yosys + +.. |test-linux| image:: https://github.com/YosysHQ/yosys/actions/workflows/test-linux.yml/badge.svg?branch=master +.. |test-macos| image:: https://github.com/YosysHQ/yosys/actions/workflows/test-macos.yml/badge.svg?branch=master + +For up to date information, including OS versions, refer to `the git actions +page`_. + +.. _the git actions page: https://github.com/YosysHQ/yosys/actions diff --git a/docs/source/using_yosys/index.rst b/docs/source/using_yosys/index.rst new file mode 100644 index 00000000000..55bd5c29151 --- /dev/null +++ b/docs/source/using_yosys/index.rst @@ -0,0 +1,17 @@ +Using Yosys (advanced) +====================== + +While much of Yosys is focused around synthesis, there are also a number of +other useful things that can be accomplished with Yosys scripts or in an +interactive shell. As such this section is broken into two parts: +:doc:`/using_yosys/synthesis/index` expands on the +:doc:`/getting_started/example_synth` and goes into further detail on the major +commands used in synthesis; :doc:`/using_yosys/more_scripting/index` covers the +ways Yosys can interact with designs for a deeper investigation. + +.. toctree:: + :maxdepth: 2 + :hidden: + + synthesis/index + more_scripting/index diff --git a/docs/source/using_yosys/more_scripting/index.rst b/docs/source/using_yosys/more_scripting/index.rst new file mode 100644 index 00000000000..490a5a7ad8b --- /dev/null +++ b/docs/source/using_yosys/more_scripting/index.rst @@ -0,0 +1,14 @@ +More scripting +-------------- + +.. todo:: brief overview for the more scripting index + +.. toctree:: + :maxdepth: 3 + + load_design + selections + interactive_investigation + model_checking + +.. troubleshooting diff --git a/docs/source/using_yosys/more_scripting/interactive_investigation.rst b/docs/source/using_yosys/more_scripting/interactive_investigation.rst new file mode 100644 index 00000000000..ed798d6b694 --- /dev/null +++ b/docs/source/using_yosys/more_scripting/interactive_investigation.rst @@ -0,0 +1,813 @@ +Interactive design investigation +-------------------------------- + +.. todo:: interactive design opening text + +.. role:: yoscrypt(code) + :language: yoscrypt + +.. _interactive_show: + +A look at the show command +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. TODO:: merge into :doc:`/getting_started/scripting_intro` show section + +This section explores the :cmd:ref:`show` command and explains the symbols used +in the circuit diagrams generated by it. The code used is included in the Yosys +code base under |code_examples/show|_. + +.. |code_examples/show| replace:: :file:`docs/source/code_examples/show` +.. _code_examples/show: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/show + +A simple circuit +^^^^^^^^^^^^^^^^ + +:ref:`example_v` below provides the Verilog code for a simple circuit which we +will use to demonstrate the usage of :cmd:ref:`show` in a simple setting. + +.. literalinclude:: /code_examples/show/example.v + :language: Verilog + :caption: :file:`example.v` + :name: example_v + +The Yosys synthesis script we will be running is included as +:numref:`example_ys`. Note that :cmd:ref:`show` is called with the ``-pause`` +option, that halts execution of the Yosys script until the user presses the +Enter key. Using :yoscrypt:`show -pause` also allows the user to enter an +interactive shell to further investigate the circuit before continuing +synthesis. + +.. literalinclude:: /code_examples/show/example_show.ys + :language: yoscrypt + :caption: :file:`example_show.ys` + :name: example_ys + +This script, when executed, will show the design after each of the three +synthesis commands. We will now look at each of these diagrams and explain what +is shown. + +.. note:: + + The images uses in this document are generated from the :file:`example.ys` + file, rather than :file:`example_show.ys`. :file:`example.ys` outputs the + schematics as :file:`.dot` files rather than displaying them directly. You + can view these images yourself by running :file:`yosys example.ys` and then + ``xdot example_first.dot`` etc. + +.. figure:: /_images/code_examples/show/example_first.* + :class: width-helper + + Output of the first :cmd:ref:`show` command in :numref:`example_ys` + +The first output shows the design directly after being read by the Verilog +front-end. Input and output ports are displayed as octagonal shapes. Cells are +displayed as rectangles with inputs on the left and outputs on the right side. +The cell labels are two lines long: The first line contains a unique identifier +for the cell and the second line contains the cell type. Internal cell types are +prefixed with a dollar sign. For more details on the internal cell library, see +:doc:`/yosys_internals/formats/cell_library`. + +Constants are shown as ellipses with the constant value as label. The syntax +``'`` is used for constants that are not 32-bit wide and/or +contain bits that are not 0 or 1 (i.e. ``x`` or ``z``). Ordinary 32-bit +constants are written using decimal numbers. + +Single-bit signals are shown as thin arrows pointing from the driver to the +load. Signals that are multiple bits wide are shown as think arrows. + +Finally *processes* are shown in boxes with round corners. Processes are Yosys' +internal representation of the decision-trees and synchronization events +modelled in a Verilog ``always``-block. The label reads ``PROC`` followed by a +unique identifier in the first line and contains the source code location of the +original ``always``-block in the second line. Note how the multiplexer from the +``?:``-expression is represented as a ``$mux`` cell but the multiplexer from the +``if``-statement is yet still hidden within the process. + +The :cmd:ref:`proc` command transforms the process from the first diagram into a +multiplexer and a d-type flip-flop, which brings us to the second diagram: + +.. figure:: /_images/code_examples/show/example_second.* + :class: width-helper + + Output of the second :cmd:ref:`show` command in :numref:`example_ys` + +The Rhombus shape to the right is a dangling wire. (Wire nodes are only shown if +they are dangling or have "public" names, for example names assigned from the +Verilog input.) Also note that the design now contains two instances of a +``BUF``-node. These are artefacts left behind by the :cmd:ref:`proc` command. It +is quite usual to see such artefacts after calling commands that perform changes +in the design, as most commands only care about doing the transformation in the +least complicated way, not about cleaning up after them. The next call to +:cmd:ref:`clean` (or :cmd:ref:`opt`, which includes :cmd:ref:`clean` as one of +its operations) will clean up these artefacts. This operation is so common in +Yosys scripts that it can simply be abbreviated with the ``;;`` token, which +doubles as separator for commands. Unless one wants to specifically analyze this +artefacts left behind some operations, it is therefore recommended to always +call :cmd:ref:`clean` before calling :cmd:ref:`show`. + +In this script we directly call :cmd:ref:`opt` as the next step, which finally +leads us to the third diagram: + +.. figure:: /_images/code_examples/show/example_third.* + :class: width-helper + :name: example_out + + Output of the third :cmd:ref:`show` command in :ref:`example_ys` + +Here we see that the :cmd:ref:`proc` command not only has removed the artifacts +left behind by :cmd:ref:`proc`, but also determined correctly that it can remove +the first ``$mux`` cell without changing the behavior of the circuit. + +Break-out boxes for signal vectors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The code listing below shows a simple circuit which uses a lot of spliced signal +accesses. + +.. literalinclude:: /code_examples/show/splice.v + :caption: :file:`splice.v` + :name: splice_src + +Notice how the output for this circuit from the :cmd:ref:`show` command +(:numref:`splice_dia`) appears quite complex. This is an unfortunate side effect +of the way Yosys handles signal vectors (aka. multi-bit wires or buses) as +native objects. While this provides great advantages when analyzing circuits +that operate on wide integers, it also introduces some additional complexity +when the individual bits of of a signal vector are accessed. + +.. figure:: /_images/code_examples/show/splice.* + :class: width-helper + :name: splice_dia + + Output of ``yosys -p 'prep -top splice_demo; show' splice.v`` + +The key elements in understanding this circuit diagram are of course the boxes +with round corners and rows labeled ``: - +:``. Each of these boxes have one signal per row on one +side and a common signal for all rows on the other side. The ``:`` +tuples specify which bits of the signals are broken out and connected. So the +top row of the box connecting the signals ``a`` and ``x`` indicates that the bit +0 (i.e. the range 0:0) from signal ``a`` is connected to bit 1 (i.e. the range +1:1) of signal ``x``. + +Lines connecting such boxes together and lines connecting such boxes to cell +ports have a slightly different look to emphasise that they are not actual +signal wires but a necessity of the graphical representation. This distinction +seems like a technicality, until one wants to debug a problem related to the way +Yosys internally represents signal vectors, for example when writing custom +Yosys commands. + +Gate level netlists +^^^^^^^^^^^^^^^^^^^ + +:numref:`first_pitfall` shows two common pitfalls when working with designs +mapped to a cell library: + +.. figure:: /_images/code_examples/show/cmos_00.* + :class: width-helper + :name: first_pitfall + + A half-adder built from simple CMOS gates, demonstrating common pitfalls when + using :cmd:ref:`show` + +.. literalinclude:: /code_examples/show/cmos.ys + :language: yoscrypt + :start-after: pitfall + :end-at: cmos_00 + :name: pitfall_code + :caption: Generating :numref:`first_pitfall` + +First, Yosys did not have access to the cell library when this diagram was +generated, resulting in all cell ports defaulting to being inputs. This is why +all ports are drawn on the left side the cells are awkwardly arranged in a large +column. Secondly the two-bit vector ``y`` requires breakout-boxes for its +individual bits, resulting in an unnecessary complex diagram. + +.. figure:: /_images/code_examples/show/cmos_01.* + :class: width-helper + :name: second_pitfall + + Effects of :cmd:ref:`splitnets` command and of providing a cell library on + design in :numref:`first_pitfall` + +.. literalinclude:: /code_examples/show/cmos.ys + :language: yoscrypt + :start-after: fixed + :end-at: cmos_01 + :name: pitfall_avoided + :caption: Generating :numref:`second_pitfall` + +For :numref:`second_pitfall`, Yosys has been given a description of the cell +library as Verilog file containing blackbox modules. There are two ways to load +cell descriptions into Yosys: First the Verilog file for the cell library can be +passed directly to the :cmd:ref:`show` command using the ``-lib `` +option. Secondly it is possible to load cell libraries into the design with the +:yoscrypt:`read_verilog -lib ` command. The second method has the +great advantage that the library only needs to be loaded once and can then be +used in all subsequent calls to the :cmd:ref:`show` command. + +In addition to that, :numref:`second_pitfall` was generated after +:yoscrypt:`splitnet -ports` was run on the design. This command splits all +signal vectors into individual signal bits, which is often desirable when +looking at gate-level circuits. The ``-ports`` option is required to also split +module ports. Per default the command only operates on interior signals. + +Miscellaneous notes +^^^^^^^^^^^^^^^^^^^ + +Per default the :cmd:ref:`show` command outputs a temporary dot file and +launches ``xdot`` to display it. The options ``-format``, ``-viewer`` and +``-prefix`` can be used to change format, viewer and filename prefix. Note that +the ``pdf`` and ``ps`` format are the only formats that support plotting +multiple modules in one run. The ``dot`` format can be used to output multiple +modules, however ``xdot`` will raise an error when trying to read them. + +In densely connected circuits it is sometimes hard to keep track of the +individual signal wires. For these cases it can be useful to call +:cmd:ref:`show` with the ``-colors `` argument, which randomly assigns +colors to the nets. The integer (> 0) is used as seed value for the random color +assignments. Sometimes it is necessary it try some values to find an assignment +of colors that looks good. + +The command :yoscrypt:`help show` prints a complete listing of all options +supported by the :cmd:ref:`show` command. + +Navigating the design +~~~~~~~~~~~~~~~~~~~~~ + +Plotting circuit diagrams for entire modules in the design brings us only helps +in simple cases. For complex modules the generated circuit diagrams are just +stupidly big and are no help at all. In such cases one first has to select the +relevant portions of the circuit. + +In addition to *what* to display one also needs to carefully decide *when* to +display it, with respect to the synthesis flow. In general it is a good idea to +troubleshoot a circuit in the earliest state in which a problem can be +reproduced. So if, for example, the internal state before calling the +:cmd:ref:`techmap` command already fails to verify, it is better to troubleshoot +the coarse-grain version of the circuit before :cmd:ref:`techmap` than the +gate-level circuit after :cmd:ref:`techmap`. + +.. Note:: + + It is generally recommended to verify the internal state of a design by + writing it to a Verilog file using :yoscrypt:`write_verilog -noexpr` and + using the simulation models from :file:`simlib.v` and :file:`simcells.v` from + the Yosys data directory (as printed by ``yosys-config --datdir``). + +Interactive navigation +^^^^^^^^^^^^^^^^^^^^^^ + +Once the right state within the synthesis flow for debugging the circuit has +been identified, it is recommended to simply add the :cmd:ref:`shell` command to +the matching place in the synthesis script. This command will stop the synthesis +at the specified moment and go to shell mode, where the user can interactively +enter commands. + +For most cases, the shell will start with the whole design selected (i.e. when +the synthesis script does not already narrow the selection). The command +:cmd:ref:`ls` can now be used to create a list of all modules. The command +:cmd:ref:`cd` can be used to switch to one of the modules (type ``cd ..`` to +switch back). Now the :cmd:ref:`ls` command lists the objects within that +module. This is demonstrated below using :file:`example.v` from `A simple +circuit`_: + +.. literalinclude:: /code_examples/show/example.out + :language: doscon + :start-at: yosys> ls + :end-before: yosys [example]> dump + :caption: Output of :cmd:ref:`ls` and :cmd:ref:`cd` after running :file:`yosys example.v` + :name: lscd + +When a module is selected using the :cmd:ref:`cd` command, all commands (with a +few exceptions, such as the ``read_`` and ``write_`` commands) operate only on +the selected module. This can also be useful for synthesis scripts where +different synthesis strategies should be applied to different modules in the +design. + +We can see that the cell names from :numref:`example_out` are just abbreviations +of the actual cell names, namely the part after the last dollar-sign. Most +auto-generated names (the ones starting with a dollar sign) are rather long and +contains some additional information on the origin of the named object. But in +most cases those names can simply be abbreviated using the last part. + +Usually all interactive work is done with one module selected using the +:cmd:ref:`cd` command. But it is also possible to work from the design-context +(``cd ..``). In this case all object names must be prefixed with +``/``. For example ``a*/b*`` would refer to all objects whose names +start with ``b`` from all modules whose names start with ``a``. + +The :cmd:ref:`dump` command can be used to print all information about an +object. For example, calling :yoscrypt:`dump $2` after the :yoscrypt:`cd +example` above: + +.. literalinclude:: /code_examples/show/example.out + :language: RTLIL + :start-after: yosys [example]> dump + :end-before: yosys [example]> cd + :dedent: + :caption: Output of :yoscrypt:`dump $2` after :numref:`lscd` + :name: dump2 + +This can for example be useful to determine the names of nets connected to +cells, as the net-names are usually suppressed in the circuit diagram if they +are auto-generated. Note that the output is in the RTLIL representation, +described in :doc:`/yosys_internals/formats/rtlil_rep`. + +Interactive Design Investigation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yosys can also be used to investigate designs (or netlists created from other +tools). + +- The selection mechanism, especially patterns such as ``%ci`` and ``%co``, can + be used to figure out how parts of the design are connected. +- Commands such as :cmd:ref:`submod`, :cmd:ref:`expose`, and :cmd:ref:`splice` + can be used to transform the design into an equivalent design that is easier + to analyse. +- Commands such as :cmd:ref:`eval` and :cmd:ref:`sat` can be used to investigate + the behavior of the circuit. +- :doc:`/cmd/show`. +- :doc:`/cmd/dump`. +- :doc:`/cmd/add` and :doc:`/cmd/delete` can be used to modify and reorganize a + design dynamically. + +The code used is included in the Yosys code base under +|code_examples/scrambler|_. + +.. |code_examples/scrambler| replace:: :file:`docs/source/code_examples/scrambler` +.. _code_examples/scrambler: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/scrambler + +Changing design hierarchy +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Commands such as :cmd:ref:`flatten` and :cmd:ref:`submod` can be used to change +the design hierarchy, i.e. flatten the hierarchy or moving parts of a module to +a submodule. This has applications in synthesis scripts as well as in reverse +engineering and analysis. An example using :cmd:ref:`submod` is shown below for +reorganizing a module in Yosys and checking the resulting circuit. + +.. literalinclude:: /code_examples/scrambler/scrambler.v + :language: verilog + :caption: :file:`scrambler.v` + +.. literalinclude:: /code_examples/scrambler/scrambler.ys + :language: yoscrypt + :caption: :file:`scrambler.ys` + :end-before: cd .. + +.. figure:: /_images/code_examples/scrambler/scrambler_p01.* + :class: width-helper + +.. figure:: /_images/code_examples/scrambler/scrambler_p02.* + :class: width-helper + +Analyzing the resulting circuit with :doc:`/cmd/eval`: + +.. todo:: replace inline code + +.. code:: text + + > cd xorshift32 + > rename n2 in + > rename n1 out + + > eval -set in 1 -show out + Eval result: \out = 270369. + + > eval -set in 270369 -show out + Eval result: \out = 67634689. + + > sat -set out 632435482 + Signal Name Dec Hex Bin + -------------------- ---------- ---------- ------------------------------------- + \in 745495504 2c6f5bd0 00101100011011110101101111010000 + \out 632435482 25b2331a 00100101101100100011001100011010 + +Behavioral changes +^^^^^^^^^^^^^^^^^^ + +Commands such as :cmd:ref:`techmap` can be used to make behavioral changes to +the design, for example changing asynchronous resets to synchronous resets. This +has applications in design space exploration (evaluation of various +architectures for one circuit). + +The following techmap map file replaces all positive-edge async reset flip-flops +with positive-edge sync reset flip-flops. The code is taken from the example +Yosys script for ASIC synthesis of the Amber ARMv2 CPU. + +.. todo:: replace inline code + +.. code:: verilog + + (* techmap_celltype = "$adff" *) + module adff2dff (CLK, ARST, D, Q); + + parameter WIDTH = 1; + parameter CLK_POLARITY = 1; + parameter ARST_POLARITY = 1; + parameter ARST_VALUE = 0; + + input CLK, ARST; + input [WIDTH-1:0] D; + output reg [WIDTH-1:0] Q; + + wire [1023:0] _TECHMAP_DO_ = "proc"; + + wire _TECHMAP_FAIL_ = !CLK_POLARITY || !ARST_POLARITY; + + always @(posedge CLK) + if (ARST) + Q <= ARST_VALUE; + else + Q <= D; + + endmodule + +For more on the :cmd:ref:`techmap` command, see the page on +:doc:`/yosys_internals/techmap`. + +Advanced investigation techniques +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When working with very large modules, it is often not enough to just select the +interesting part of the module. Instead it can be useful to extract the +interesting part of the circuit into a separate module. This can for example be +useful if one wants to run a series of synthesis commands on the critical part +of the module and wants to carefully read all the debug output created by the +commands in order to spot a problem. This kind of troubleshooting is much easier +if the circuit under investigation is encapsulated in a separate module. + +Recall the ``memdemo`` design from :ref:`advanced_logic_cones`: + +.. figure:: /_images/code_examples/selections/memdemo_00.* + :class: width-helper + + ``memdemo`` + +Because this produces a rather large circuit, it can be useful to split it into +smaller parts for viewing and working with. :numref:`submod` does exactly that, +utilising the :cmd:ref:`submod` command to split the circuit into three +sections: ``outstage``, ``selstage``, and ``scramble``. + +.. literalinclude:: /code_examples/selections/submod.ys + :language: yoscrypt + :caption: Using :cmd:ref:`submod` to break up the circuit from :file:`memdemo.v` + :start-after: cd memdemo + :end-before: cd .. + :name: submod + +The ``-name`` option is used to specify the name of the new module and also the +name of the new cell in the current module. The resulting circuits are shown +below. + +.. figure:: /_images/code_examples/selections/submod_02.* + :class: width-helper + + ``outstage`` + +.. figure:: /_images/code_examples/selections/submod_03.* + :class: width-helper + :name: selstage + + ``selstage`` + +.. figure:: /_images/code_examples/selections/submod_01.* + :class: width-helper + + ``scramble`` + +Evaluation of combinatorial circuits +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The :cmd:ref:`eval` command can be used to evaluate combinatorial circuits. As +an example, we will use the ``selstage`` subnet of ``memdemo`` which we found +above and is shown in :numref:`selstage`. + +.. todo:: replace inline code + +:: + + yosys [selstage]> eval -set s2,s1 4'b1001 -set d 4'hc -show n2 -show n1 + + 1. Executing EVAL pass (evaluate the circuit given an input). + Full command line: eval -set s2,s1 4'b1001 -set d 4'hc -show n2 -show n1 + Eval result: \n2 = 2'10. + Eval result: \n1 = 2'10. + +So the ``-set`` option is used to set input values and the ``-show`` option is +used to specify the nets to evaluate. If no ``-show`` option is specified, all +selected output ports are used per default. + +If a necessary input value is not given, an error is produced. The option +``-set-undef`` can be used to instead set all unspecified input nets to undef +(``x``). + +The ``-table`` option can be used to create a truth table. For example: + +:: + + yosys [selstage]> eval -set-undef -set d[3:1] 0 -table s1,d[0] + + 10. Executing EVAL pass (evaluate the circuit given an input). + Full command line: eval -set-undef -set d[3:1] 0 -table s1,d[0] + + \s1 \d [0] | \n1 \n2 + ---- ------ | ---- ---- + 2'00 1'0 | 2'00 2'00 + 2'00 1'1 | 2'xx 2'00 + 2'01 1'0 | 2'00 2'00 + 2'01 1'1 | 2'xx 2'01 + 2'10 1'0 | 2'00 2'00 + 2'10 1'1 | 2'xx 2'10 + 2'11 1'0 | 2'00 2'00 + 2'11 1'1 | 2'xx 2'11 + + Assumed undef (x) value for the following signals: \s2 + +Note that the :cmd:ref:`eval` command (as well as the :cmd:ref:`sat` command +discussed in the next sections) does only operate on flattened modules. It can +not analyze signals that are passed through design hierarchy levels. So the +:cmd:ref:`flatten` command must be used on modules that instantiate other +modules before these commands can be applied. + +Solving combinatorial SAT problems +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Often the opposite of the :cmd:ref:`eval` command is needed, i.e. the circuits +output is given and we want to find the matching input signals. For small +circuits with only a few input bits this can be accomplished by trying all +possible input combinations, as it is done by the ``eval -table`` command. For +larger circuits however, Yosys provides the :cmd:ref:`sat` command that uses a +`SAT`_ solver, `MiniSAT`_, to solve this kind of problems. + +.. _SAT: http://en.wikipedia.org/wiki/Circuit_satisfiability + +.. _MiniSAT: http://minisat.se/ + +.. note:: + + While it is possible to perform model checking directly in Yosys, it + is highly recommended to use SBY or EQY for formal hardware verification. + +The :cmd:ref:`sat` command works very similar to the :cmd:ref:`eval` command. +The main difference is that it is now also possible to set output values and +find the corresponding input values. For Example: + +.. todo:: replace inline code + +:: + + yosys [selstage]> sat -show s1,s2,d -set s1 s2 -set n2,n1 4'b1001 + + 11. Executing SAT pass (solving SAT problems in the circuit). + Full command line: sat -show s1,s2,d -set s1 s2 -set n2,n1 4'b1001 + + Setting up SAT problem: + Import set-constraint: \s1 = \s2 + Import set-constraint: { \n2 \n1 } = 4'1001 + Final constraint equation: { \n2 \n1 \s1 } = { 4'1001 \s2 } + Imported 3 cells to SAT database. + Import show expression: { \s1 \s2 \d } + + Solving problem with 81 variables and 207 clauses.. + SAT solving finished - model found: + + Signal Name Dec Hex Bin + -------------------- ---------- ---------- --------------- + \d 9 9 1001 + \s1 0 0 00 + \s2 0 0 00 + +Note that the :cmd:ref:`sat` command supports signal names in both arguments to +the ``-set`` option. In the above example we used ``-set s1 s2`` to constraint +``s1`` and ``s2`` to be equal. When more complex constraints are needed, a +wrapper circuit must be constructed that checks the constraints and signals if +the constraint was met using an extra output port, which then can be forced to a +value using the ``-set`` option. (Such a circuit that contains the circuit under +test plus additional constraint checking circuitry is called a ``miter`` +circuit.) + +.. literalinclude:: /code_examples/primetest.v + :language: verilog + :caption: :file:`primetest.v`, a simple miter circuit for testing if a number is + prime. But it has a problem. + :name: primetest + +:numref:`primetest` shows a miter circuit that is supposed to be used as a prime +number test. If ``ok`` is 1 for all input values ``a`` and ``b`` for a given +``p``, then ``p`` is prime, or at least that is the idea. + +.. todo:: replace inline code + +.. code-block:: + :caption: Experiments with the miter circuit from :file:`primetest.v`. + :name: prime_shell + + yosys [primetest]> sat -prove ok 1 -set p 31 + + 1. Executing SAT pass (solving SAT problems in the circuit). + Full command line: sat -prove ok 1 -set p 31 + + Setting up SAT problem: + Import set-constraint: \p = 16'0000000000011111 + Final constraint equation: \p = 16'0000000000011111 + Imported 6 cells to SAT database. + Import proof-constraint: \ok = 1'1 + Final proof equation: \ok = 1'1 + + Solving problem with 2790 variables and 8241 clauses.. + SAT proof finished - model found: FAIL! + + ______ ___ ___ _ _ _ _ + (_____ \ / __) / __) (_) | | | | + _____) )___ ___ ___ _| |__ _| |__ _____ _| | _____ __| | | + | ____/ ___) _ \ / _ (_ __) (_ __|____ | | || ___ |/ _ |_| + | | | | | |_| | |_| || | | | / ___ | | || ____( (_| |_ + |_| |_| \___/ \___/ |_| |_| \_____|_|\_)_____)\____|_| + + + Signal Name Dec Hex Bin + -------------------- ---------- ---------- --------------------- + \a 15029 3ab5 0011101010110101 + \b 4099 1003 0001000000000011 + \ok 0 0 0 + \p 31 1f 0000000000011111 + +The Yosys shell session shown in :numref:`prime_shell` demonstrates that SAT +solvers can even find the unexpected solutions to a problem: Using integer +overflow there actually is a way of "factorizing" 31. The clean solution would +of course be to perform the test in 32 bits, for example by replacing ``p != +a*b`` in the miter with ``p != {16'd0,a}b``, or by using a temporary variable +for the 32 bit product ``a*b``. But as 31 fits well into 8 bits (and as the +purpose of this document is to show off Yosys features) we can also simply force +the upper 8 bits of ``a`` and ``b`` to zero for the :cmd:ref:`sat` call, as is +done below. + +.. todo:: replace inline code + +.. code-block:: + :caption: Miter circuit from :file:`primetest.v`, with the upper 8 bits of ``a`` + and ``b`` constrained to prevent overflow. + :name: prime_fixed + + yosys [primetest]> sat -prove ok 1 -set p 31 -set a[15:8],b[15:8] 0 + + 1. Executing SAT pass (solving SAT problems in the circuit). + Full command line: sat -prove ok 1 -set p 31 -set a[15:8],b[15:8] 0 + + Setting up SAT problem: + Import set-constraint: \p = 16'0000000000011111 + Import set-constraint: { \a [15:8] \b [15:8] } = 16'0000000000000000 + Final constraint equation: { \a [15:8] \b [15:8] \p } = { 16'0000000000000000 16'0000000000011111 } + Imported 6 cells to SAT database. + Import proof-constraint: \ok = 1'1 + Final proof equation: \ok = 1'1 + + Solving problem with 2790 variables and 8257 clauses.. + SAT proof finished - no model found: SUCCESS! + + /$$$$$$ /$$$$$$$$ /$$$$$$$ + /$$__ $$ | $$_____/ | $$__ $$ + | $$ \ $$ | $$ | $$ \ $$ + | $$ | $$ | $$$$$ | $$ | $$ + | $$ | $$ | $$__/ | $$ | $$ + | $$/$$ $$ | $$ | $$ | $$ + | $$$$$$/ /$$| $$$$$$$$ /$$| $$$$$$$//$$ + \____ $$$|__/|________/|__/|_______/|__/ + \__/ + +The ``-prove`` option used in :numref:`prime_fixed` works similar to ``-set``, +but tries to find a case in which the two arguments are not equal. If such a +case is not found, the property is proven to hold for all inputs that satisfy +the other constraints. + +It might be worth noting, that SAT solvers are not particularly efficient at +factorizing large numbers. But if a small factorization problem occurs as part +of a larger circuit problem, the Yosys SAT solver is perfectly capable of +solving it. + +Solving sequential SAT problems +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The SAT solver functionality in Yosys can not only be used to solve +combinatorial problems, but can also solve sequential problems. Let's consider +the ``memdemo`` design from :ref:`advanced_logic_cones` again, and suppose we +want to know which sequence of input values for ``d`` will cause the output y to +produce the sequence 1, 2, 3 from any initial state. Let's use the following +command: + +.. todo:: replace inline code? + +.. code-block:: yoscrypt + + sat -seq 6 -show y -show d -set-init-undef \ + -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3 + +The ``-seq 6`` option instructs the :cmd:ref:`sat` command to solve a sequential +problem in 6 time steps. (Experiments with lower number of steps have show that +at least 3 cycles are necessary to bring the circuit in a state from which the +sequence 1, 2, 3 can be produced.) + +The ``-set-init-undef`` option tells the :cmd:ref:`sat` command to initialize +all registers to the undef (``x``) state. The way the ``x`` state is treated in +Verilog will ensure that the solution will work for any initial state. + +The ``-max_undef`` option instructs the :cmd:ref:`sat` command to find a +solution with a maximum number of undefs. This way we can see clearly which +inputs bits are relevant to the solution. + +Finally the three ``-set-at`` options add constraints for the ``y`` signal to +play the 1, 2, 3 sequence, starting with time step 4. + +This produces the following output: + +.. todo:: replace inline code + +.. code-block:: + :caption: Solving a sequential SAT problem in the ``memdemo`` module. + :name: memdemo_sat + + yosys [memdemo]> sat -seq 6 -show y -show d -set-init-undef \ + -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3 + + 1. Executing SAT pass (solving SAT problems in the circuit). + Full command line: sat -seq 6 -show y -show d -set-init-undef + -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3 + + Setting up time step 1: + Final constraint equation: { } = { } + Imported 29 cells to SAT database. + + Setting up time step 2: + Final constraint equation: { } = { } + Imported 29 cells to SAT database. + + Setting up time step 3: + Final constraint equation: { } = { } + Imported 29 cells to SAT database. + + Setting up time step 4: + Import set-constraint for timestep: \y = 4'0001 + Final constraint equation: \y = 4'0001 + Imported 29 cells to SAT database. + + Setting up time step 5: + Import set-constraint for timestep: \y = 4'0010 + Final constraint equation: \y = 4'0010 + Imported 29 cells to SAT database. + + Setting up time step 6: + Import set-constraint for timestep: \y = 4'0011 + Final constraint equation: \y = 4'0011 + Imported 29 cells to SAT database. + + Setting up initial state: + Final constraint equation: { \y \s2 \s1 \mem[3] \mem[2] \mem[1] + \mem[0] } = 24'xxxxxxxxxxxxxxxxxxxxxxxx + + Import show expression: \y + Import show expression: \d + + Solving problem with 10322 variables and 27881 clauses.. + SAT model found. maximizing number of undefs. + SAT solving finished - model found: + + Time Signal Name Dec Hex Bin + ---- -------------------- ---------- ---------- --------------- + init \mem[0] -- -- xxxx + init \mem[1] -- -- xxxx + init \mem[2] -- -- xxxx + init \mem[3] -- -- xxxx + init \s1 -- -- xx + init \s2 -- -- xx + init \y -- -- xxxx + ---- -------------------- ---------- ---------- --------------- + 1 \d 0 0 0000 + 1 \y -- -- xxxx + ---- -------------------- ---------- ---------- --------------- + 2 \d 1 1 0001 + 2 \y -- -- xxxx + ---- -------------------- ---------- ---------- --------------- + 3 \d 2 2 0010 + 3 \y 0 0 0000 + ---- -------------------- ---------- ---------- --------------- + 4 \d 3 3 0011 + 4 \y 1 1 0001 + ---- -------------------- ---------- ---------- --------------- + 5 \d -- -- 001x + 5 \y 2 2 0010 + ---- -------------------- ---------- ---------- --------------- + 6 \d -- -- xxxx + 6 \y 3 3 0011 + +It is not surprising that the solution sets ``d = 0`` in the first step, as this +is the only way of setting the ``s1`` and ``s2`` registers to a known value. The +input values for the other steps are a bit harder to work out manually, but the +SAT solver finds the correct solution in an instant. + +There is much more to write about the :cmd:ref:`sat` command. For example, there +is a set of options that can be used to performs sequential proofs using +temporal induction :cite:p:`een2003temporal`. The command ``help sat`` can be +used to print a list of all options with short descriptions of their functions. diff --git a/docs/source/using_yosys/more_scripting/load_design.rst b/docs/source/using_yosys/more_scripting/load_design.rst new file mode 100644 index 00000000000..d64c50959bd --- /dev/null +++ b/docs/source/using_yosys/more_scripting/load_design.rst @@ -0,0 +1,40 @@ +Loading a design +~~~~~~~~~~~~~~~~ + +keyword: Frontends + +- :doc:`/cmd/read_verilog` + +.. todo:: include ``read_verilog </`` prefix. For example: + +.. code:: yoscrypt + + cd foo # switch to module foo + delete bar # delete object foo/bar + + cd mycpu # switch to module mycpu + dump reg_* # print details on all objects whose names start with reg_ + + cd .. # switch back to design + +Note: Most synthesis scripts never switch to module context. But it is a very +powerful tool which we explore more in +:doc:`/using_yosys/more_scripting/interactive_investigation`. + +Selecting by object property or type +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Special patterns can be used to select by object property or type. For example: + +- select all wires whose names start with ``reg_``: :yoscrypt:`select w:reg_*` +- select all objects with the attribute ``foobar`` set: :yoscrypt:`select + a:foobar` +- select all objects with the attribute ``foobar`` set to 42: :yoscrypt:`select + a:foobar=42` +- select all modules with the attribute ``blabla`` set: :yoscrypt:`select + A:blabla` +- select all $add cells from the module foo: :yoscrypt:`select foo/t:$add` + +A complete list of pattern expressions can be found in :doc:`/cmd/select`. + +Operations on selections +~~~~~~~~~~~~~~~~~~~~~~~~ + +Combining selections +^^^^^^^^^^^^^^^^^^^^ + +The :cmd:ref:`select` command is actually much more powerful than it might seem +at first glance. When it is called with multiple arguments, each argument is +evaluated and pushed separately on a stack. After all arguments have been +processed it simply creates the union of all elements on the stack. So +:yoscrypt:`select t:$add a:foo` will select all ``$add`` cells and all objects +with the ``foo`` attribute set: + +.. literalinclude:: /code_examples/selections/foobaraddsub.v + :caption: Test module for operations on selections + :name: foobaraddsub + :language: verilog + +.. code-block:: + :caption: Output for command ``select t:$add a:foo -list`` on :numref:`foobaraddsub` + + yosys> select t:$add a:foo -list + foobaraddsub/$add$foobaraddsub.v:6$3 + foobaraddsub/$sub$foobaraddsub.v:5$2 + foobaraddsub/$add$foobaraddsub.v:4$1 + +In many cases simply adding more and more stuff to the selection is an +ineffective way of selecting the interesting part of the design. Special +arguments can be used to combine the elements on the stack. For example the +``%i`` arguments pops the last two elements from the stack, intersects them, and +pushes the result back on the stack. So :yoscrypt:`select t:$add a:foo %i` will +select all ``$add`` cells that have the ``foo`` attribute set: + +.. code-block:: + :caption: Output for command ``select t:$add a:foo %i -list`` on :numref:`foobaraddsub` + + yosys> select t:$add a:foo %i -list + foobaraddsub/$add$foobaraddsub.v:4$1 + +Some of the special ``%``-codes: + +- ``%u``: union of top two elements on stack -- pop 2, push 1 +- ``%d``: difference of top two elements on stack -- pop 2, push 1 +- ``%i``: intersection of top two elements on stack -- pop 2, push 1 +- ``%n``: inverse of top element on stack -- pop 1, push 1 + +See :doc:`/cmd/select` for the full list. + +Expanding selections +^^^^^^^^^^^^^^^^^^^^ + +:numref:`sumprod` uses the Yosys non-standard ``{... *}`` syntax to set the +attribute ``sumstuff`` on all cells generated by the first assign statement. +(This works on arbitrary large blocks of Verilog code and can be used to mark +portions of code for analysis.) + +.. literalinclude:: /code_examples/selections/sumprod.v + :caption: Another test module for operations on selections + :name: sumprod + :language: verilog + +Selecting ``a:sumstuff`` in this module will yield the following circuit +diagram: + +.. figure:: /_images/code_examples/selections/sumprod_00.* + :class: width-helper + :name: sumprod_00 + + Output of ``show a:sumstuff`` on :numref:`sumprod` + +As only the cells themselves are selected, but not the temporary wire ``$1_Y``, +the two adders are shown as two disjunct parts. This can be very useful for +global signals like clock and reset signals: just unselect them using a command +such as :yoscrypt:`select -del clk rst` and each cell using them will get its +own net label. + +In this case however we would like to see the cells connected properly. This can +be achieved using the ``%x`` action, that broadens the selection, i.e. for each +selected wire it selects all cells connected to the wire and vice versa. So +:yoscrypt:`show a:sumstuff %x` yields the diagram shown in :numref:`sumprod_01`: + +.. figure:: /_images/code_examples/selections/sumprod_01.* + :class: width-helper + :name: sumprod_01 + + Output of ``show a:sumstuff %x`` on :numref:`sumprod` + +.. _selecting_logic_cones: + +Selecting logic cones +^^^^^^^^^^^^^^^^^^^^^ + +:numref:`sumprod_01` shows what is called the ``input cone`` of ``sum``, i.e. +all cells and signals that are used to generate the signal ``sum``. The ``%ci`` +action can be used to select the input cones of all object in the top selection +in the stack maintained by the :cmd:ref:`select` command. + +As with the ``%x`` action, these commands broaden the selection by one "step". +But this time the operation only works against the direction of data flow. That +means, wires only select cells via output ports and cells only select wires via +input ports. + +The following sequence of diagrams demonstrates this step-wise expansion: + +.. figure:: /_images/code_examples/selections/sumprod_02.* + :class: width-helper + + Output of :yoscrypt:`show prod` on :numref:`sumprod` + +.. figure:: /_images/code_examples/selections/sumprod_03.* + :class: width-helper + + Output of :yoscrypt:`show prod %ci` on :numref:`sumprod` + +.. figure:: /_images/code_examples/selections/sumprod_04.* + :class: width-helper + + Output of :yoscrypt:`show prod %ci %ci` on :numref:`sumprod` + +.. figure:: /_images/code_examples/selections/sumprod_05.* + :class: width-helper + + Output of :yoscrypt:`show prod %ci %ci %ci` on :numref:`sumprod` + +Notice the subtle difference between :yoscrypt:`show prod %ci` and +:yoscrypt:`show prod %ci %ci`. Both images show the ``$mul`` cell driven by +some inputs ``$3_Y`` and ``c``. However it is not until the second image, +having called ``%ci`` the second time, that :cmd:ref:`show` is able to +distinguish between ``$3_Y`` being a wire and ``c`` being an input. We can see +this better with the :cmd:ref:`dump` command instead: + +.. literalinclude:: /code_examples/selections/sumprod.out + :language: RTLIL + :end-at: end + :caption: Output of :yoscrypt:`dump prod %ci` + +.. literalinclude:: /code_examples/selections/sumprod.out + :language: RTLIL + :start-after: end + :caption: Output of :yoscrypt:`dump prod %ci %ci` + +When selecting many levels of logic, repeating ``%ci`` over and over again can +be a bit dull. So there is a shortcut for that: the number of iterations can be +appended to the action. So for example the action ``%ci3`` is identical to +performing the ``%ci`` action three times. + +The action ``%ci*`` performs the ``%ci`` action over and over again until it +has no effect anymore. + +.. _advanced_logic_cones: + +Advanced logic cone selection +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In most cases there are certain cell types and/or ports that should not be +considered for the ``%ci`` action, or we only want to follow certain cell types +and/or ports. This can be achieved using additional patterns that can be +appended to the ``%ci`` action. + +Lets consider :numref:`memdemo_src`. It serves no purpose other than being a +non-trivial circuit for demonstrating some of the advanced Yosys features. This +code is available in ``docs/source/code_examples/selections`` of the Yosys +source repository. + +.. literalinclude:: /code_examples/selections/memdemo.v + :caption: :file:`memdemo.v` + :name: memdemo_src + :language: verilog + +The script :file:`memdemo.ys` is used to generate the images included here. Let's +look at the first section: + +.. literalinclude:: /code_examples/selections/memdemo.ys + :caption: Synthesizing :ref:`memdemo_src` + :name: memdemo_ys + :language: yoscrypt + :end-at: opt + +This loads :numref:`memdemo_src` and synthesizes the included module. Note that +this code can be copied and run directly in a Yosys command line session, +provided :file:`memdemo.v` is in the same directory. We can now change to the +``memdemo`` module with ``cd memdemo``, and call :cmd:ref:`show` to see the +diagram in :numref:`memdemo_00`. + +.. figure:: /_images/code_examples/selections/memdemo_00.* + :class: width-helper + :name: memdemo_00 + + Complete circuit diagram for the design shown in :numref:`memdemo_src` + +There's a lot going on there, but maybe we are only interested in the tree of +multiplexers that select the output value. Let's start by just showing the +output signal, ``y``, and its immediate predecessors. Remember `Selecting logic +cones`_ from above, we can use :yoscrypt:`show y %ci2`: + +.. figure:: /_images/code_examples/selections/memdemo_01.* + :class: width-helper + :name: memdemo_01 + + Output of :yoscrypt:`show y %ci2` + +From this we would learn that ``y`` is driven by a ``$dff cell``, that ``y`` is +connected to the output port ``Q``, that the ``clk`` signal goes into the +``CLK`` input port of the cell, and that the data comes from an auto-generated +wire into the input ``D`` of the flip-flop cell (indicated by the ``$`` at the +start of the name). Let's go a bit further now and try :yoscrypt:`show y %ci5`: + +.. figure:: /_images/code_examples/selections/memdemo_02.* + :class: width-helper + :name: memdemo_02 + + Output of :yoscrypt:`show y %ci5` + +That's starting to get a bit messy, so maybe we want to ignore the mux select +inputs. To add a pattern we add a colon followed by the pattern to the ``%ci`` +action. The pattern itself starts with ``-`` or ``+``, indicating if it is an +include or exclude pattern, followed by an optional comma separated list of cell +types, followed by an optional comma separated list of port names in square +brackets. In this case, we want to exclude the ``S`` port of the ``$mux`` cell +type with :yoscrypt:`show y %ci5:-$mux[S]`: + +.. figure:: /_images/code_examples/selections/memdemo_03.* + :class: width-helper + :name: memdemo_03 + + Output of :yoscrypt:`show y %ci5:-$mux[S]` + +We could use a command such as :yoscrypt:`show y %ci2:+$dff[Q,D] +%ci*:-$mux[S]:-$dff` in which the first ``%ci`` jumps over the initial d-type +flip-flop and the 2nd action selects the entire input cone without going over +multiplexer select inputs and flip-flop cells: + +.. figure:: /_images/code_examples/selections/memdemo_05.* + :class: width-helper + :name: memdemo_05 + + Output of ``show y %ci2:+$dff[Q,D] %ci*:-$mux[S]:-$dff`` + +Or we could use :yoscrypt:`show y %ci*:-[CLK,S]:+$dff:+$mux` instead, following +the input cone all the way but only following ``$dff`` and ``$mux`` cells, and +ignoring any ports named ``CLK`` or ``S``: + +.. TODO:: pending discussion on whether rule ordering is a bug or a feature + +.. figure:: /_images/code_examples/selections/memdemo_04.* + :class: width-helper + :name: memdemo_04 + + Output of :yoscrypt:`show y %ci*:-[CLK,S]:+$dff,$mux` + +Similar to ``%ci`` exists an action ``%co`` to select output cones that accepts +the same syntax for pattern and repetition. The ``%x`` action mentioned +previously also accepts this advanced syntax. + +These actions for traversing the circuit graph, combined with the actions for +boolean operations such as intersection (``%i``) and difference (``%d``) are +powerful tools for extracting the relevant portions of the circuit under +investigation. + +Again, see :doc:`/cmd/select` for full documentation of these expressions. + +Incremental selection +^^^^^^^^^^^^^^^^^^^^^ + +Sometimes a selection can most easily be described by a series of add/delete +operations. As mentioned previously, the commands :yoscrypt:`select -add` and +:yoscrypt:`select -del` respectively add or remove objects from the current +selection instead of overwriting it. + +.. code:: yoscrypt + + select -none # start with an empty selection + select -add reg_* # select a bunch of objects + select -del reg_42 # but not this one + select -add state %ci # and add more stuff + +Within a select expression the token ``%`` can be used to push the previous selection +on the stack. + +.. code:: yoscrypt + + select t:$add t:$sub # select all $add and $sub cells + select % %ci % %d # select only the input wires to those cells + +Storing and recalling selections +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. todo:: reflow for not presentation + +The current selection can be stored in memory with the command ``select -set +``. It can later be recalled using ``select @``. In fact, the +``@`` expression pushes the stored selection on the stack maintained by +the :cmd:ref:`select` command. So for example :yoscrypt:`select @foo @bar %i` +will select the intersection between the stored selections ``foo`` and ``bar``. + +In larger investigation efforts it is highly recommended to maintain a script +that sets up relevant selections, so they can easily be recalled, for example +when Yosys needs to be re-run after a design or source code change. + +The :cmd:ref:`history` command can be used to list all recent interactive +commands. This feature can be useful for creating such a script from the +commands used in an interactive session. + +Remember that select expressions can also be used directly as arguments to most +commands. Some commands also accept a single select argument to some options. In +those cases selection variables must be used to capture more complex selections. + +Example code from |code_examples/selections|_: + +.. |code_examples/selections| replace:: :file:`docs/source/code_examples/selections` +.. _code_examples/selections: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/selections + +.. literalinclude:: /code_examples/selections/select.v + :language: verilog + :caption: :file:`select.v` + +.. literalinclude:: /code_examples/selections/select.ys + :language: yoscrypt + :caption: :file:`select.ys` + :name: select_ys + +.. figure:: /_images/code_examples/selections/select.* + :class: width-helper + + Circuit diagram produced by :numref:`select_ys` diff --git a/docs/source/using_yosys/more_scripting/troubleshooting.rst b/docs/source/using_yosys/more_scripting/troubleshooting.rst new file mode 100644 index 00000000000..a17a552d2e4 --- /dev/null +++ b/docs/source/using_yosys/more_scripting/troubleshooting.rst @@ -0,0 +1,6 @@ +Troubleshooting +~~~~~~~~~~~~~~~ + +.. todo:: troubleshooting document(?) + +See :doc:`/cmd/bugpoint` diff --git a/docs/source/using_yosys/synthesis/abc.rst b/docs/source/using_yosys/synthesis/abc.rst new file mode 100644 index 00000000000..928b320186d --- /dev/null +++ b/docs/source/using_yosys/synthesis/abc.rst @@ -0,0 +1,103 @@ +The ABC toolbox +--------------- + +.. role:: yoscrypt(code) + :language: yoscrypt + +ABC_, from the University of California, Berkeley, is a logic toolbox used for +fine-grained optimisation and LUT mapping. + +Yosys has two different commands, which both use this logic toolbox, but use it +in different ways. + +The :cmd:ref:`abc` pass can be used for both ASIC (e.g. :yoscrypt:`abc +-liberty`) and FPGA (:yoscrypt:`abc -lut`) mapping, but this page will focus on +FPGA mapping. + +The :cmd:ref:`abc9` pass generally provides superior mapping quality due to +being aware of combination boxes and DFF and LUT timings, giving it a more +global view of the mapping problem. + +.. _ABC: https://github.com/berkeley-abc/abc + +ABC: the unit delay model, simple and efficient +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :cmd:ref:`abc` pass uses a highly simplified view of an FPGA: + +- An FPGA is made up of a network of inputs that connect through LUTs to a + network of outputs. These inputs may actually be I/O pins, D flip-flops, + memory blocks or DSPs, but ABC is unaware of this. +- Each LUT has 1 unit of delay between an input and its output, and this applies + for all inputs of a LUT, and for all sizes of LUT up to the maximum LUT size + allowed; e.g. the delay between the input of a LUT2 and its output is the same + as the delay between the input of a LUT6 and its output. +- A LUT may take up a variable number of area units. This is constant for each + size of LUT; e.g. a LUT4 may take up 1 unit of area, but a LUT5 may take up 2 + units of area, but this applies for all LUT4s and LUT5s. + +This is known as the "unit delay model", because each LUT uses one unit of +delay. + +From this view, the problem ABC has to solve is finding a mapping of the network +to LUTs that has the lowest delay, and then optimising the mapping for size +while maintaining this delay. + +This approach has advantages: + +- It is simple and easy to implement. +- Working with unit delays is fast to manipulate. +- It reflects *some* FPGA families, for example, the iCE40HX/LP fits the + assumptions of the unit delay model quite well (almost all synchronous blocks, + except for adders). + +But this approach has drawbacks, too: + +- The network of inputs and outputs with only LUTs means that a lot of + combinational cells (multipliers and LUTRAM) are invisible to the unit delay + model, meaning the critical path it optimises for is not necessarily the + actual critical path. +- LUTs are implemented as multiplexer trees, so there is a delay caused by the + result propagating through the remaining multiplexers. This means the + assumption of delay being equal isn't true in physical hardware, and is + proportionally larger for larger LUTs. +- Even synchronous blocks have arrival times (propagation delay between clock + edge to output changing) and setup times (requirement for input to be stable + before clock edge) which affect the delay of a path. + +ABC9: the generalised delay model, realistic and flexible +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ABC9 uses a more detailed and accurate model of an FPGA: + +- An FPGA is made up of a network of inputs that connect through LUTs and + combinational boxes to a network of outputs. These boxes have specified delays + between inputs and outputs, and may have an associated network ("white boxes") + or not ("black boxes"), but must be treated as a whole. +- Each LUT has a specified delay between an input and its output in arbitrary + delay units, and this varies for all inputs of a LUT and for all sizes of LUT, + but each size of LUT has the same associated delay; e.g. the delay between + input A and output is different between a LUT2 and a LUT6, but is constant for + all LUT6s. +- A LUT may take up a variable number of area units. This is constant for each + size of LUT; e.g. a LUT4 may take up 1 unit of area, but a LUT5 may take up 2 + units of area, but this applies for all LUT4s and LUT5s. + +This is known as the "generalised delay model", because it has been generalised +to arbitrary delay units. ABC9 doesn't actually care what units you use here, +but the Yosys convention is picoseconds. Note the introduction of boxes as a +concept. While the generalised delay model does not require boxes, they +naturally fit into it to represent combinational delays. Even synchronous delays +like arrival and setup can be emulated with combinational boxes that act as a +delay. This is further extended to white boxes, where the mapper is able to see +inside a box, and remove orphan boxes with no outputs, such as adders. + +Again, ABC9 finds a mapping of the network to LUTs that has the lowest delay, +and then minimises it to find the lowest area, but it has a lot more information +to work with about the network. + +The result here is that ABC9 can remove boxes (like adders) to reduce area, +optimise better around those boxes, and also permute inputs to give the critical +path the fastest inputs. + +.. todo:: more about logic minimization & register balancing et al with ABC diff --git a/docs/source/using_yosys/synthesis/cell_libs.rst b/docs/source/using_yosys/synthesis/cell_libs.rst new file mode 100644 index 00000000000..a723845379a --- /dev/null +++ b/docs/source/using_yosys/synthesis/cell_libs.rst @@ -0,0 +1,125 @@ +Mapping to cell libraries +------------------------- + +.. role:: yoscrypt(code) + :language: yoscrypt + +While much of this documentation focuses on the use of Yosys with FPGAs, it is +also possible to map to cell libraries which can be used in designing ASICs. +This section will cover a brief `example project`_, available in the Yosys +source code under :file:`docs/source/code_examples/intro/`. The project +contains a simple ASIC synthesis script (:file:`counter.ys`), a digital design +written in Verilog (:file:`counter.v`), and a simple CMOS cell library +(:file:`mycells.lib`). Many of the early steps here are already covered in more +detail in the :doc:`/getting_started/example_synth` document. + +.. note:: + + The :file:`counter.ys` script includes the commands used to generate the + images in this document. Code snippets in this document skip these commands; + including line numbers to allow the reader to follow along with the source. + + To learn more about these commands, check out :ref:`interactive_show`. + +.. _example project: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/intro + +A simple counter +~~~~~~~~~~~~~~~~ + +First, let's quickly look at the design: + +.. literalinclude:: /code_examples/intro/counter.v + :language: Verilog + :linenos: + :name: counter-v + :caption: :file:`counter.v` + +This is a simple counter with reset and enable. If the reset signal, ``rst``, +is high then the counter will reset to 0. Otherwise, if the enable signal, +``en``, is high then the ``count`` register will increment by 1 each rising edge +of the clock, ``clk``. + +Loading the design +~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: /code_examples/intro/counter.ys + :language: yoscrypt + :lines: 1-3 + :lineno-match: + :caption: :file:`counter.ys` - read design + +Our circuit now looks like this: + +.. figure:: /_images/code_examples/intro/counter_00.* + :class: width-helper + :name: counter-hierarchy + + ``counter`` after :cmd:ref:`hierarchy` + +Coarse-grain representation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: /code_examples/intro/counter.ys + :language: yoscrypt + :lines: 7-10 + :lineno-match: + :caption: :file:`counter.ys` - the high-level stuff + +.. figure:: /_images/code_examples/intro/counter_01.* + :class: width-helper + + Coarse-grain representation of the ``counter`` module + +Logic gate mapping +~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: /code_examples/intro/counter.ys + :language: yoscrypt + :lines: 14-15 + :lineno-match: + :caption: :file:`counter.ys` - mapping to internal cell library + +.. figure:: /_images/code_examples/intro/counter_02.* + :class: width-helper + + ``counter`` after :cmd:ref:`techmap` + +Mapping to hardware +~~~~~~~~~~~~~~~~~~~ + +For this example, we are using a Liberty file to describe a cell library which +our internal cell library will be mapped to: + +.. literalinclude:: /code_examples/intro/mycells.lib + :language: Liberty + :linenos: + :name: mycells-lib + :caption: :file:`mycells.lib` + +Recall that the Yosys built-in logic gate types are ``$_NOT_``, ``$_AND_``, +``$_OR_``, ``$_XOR_``, and ``$_MUX_`` with an assortment of dff memory types. +:ref:`mycells-lib` defines our target cells as ``BUF``, ``NOT``, ``NAND``, +``NOR``, and ``DFF``. Mapping between these is performed with the commands +:cmd:ref:`dfflibmap` and :cmd:ref:`abc` as follows: + +.. literalinclude:: /code_examples/intro/counter.ys + :language: yoscrypt + :lines: 20-27 + :lineno-match: + :caption: :file:`counter.ys` - mapping to hardware + +The final version of our ``counter`` module looks like this: + +.. figure:: /_images/code_examples/intro/counter_03.* + :class: width-helper + + ``counter`` after hardware cell mapping + +Before finally being output as a verilog file with :cmd:ref:`write_verilog`, +which can then be loaded into another tool: + +.. literalinclude:: /code_examples/intro/counter.ys + :language: yoscrypt + :lines: 30-31 + :lineno-match: + :caption: :file:`counter.ys` - write synthesized design diff --git a/docs/source/using_yosys/synthesis/extract.rst b/docs/source/using_yosys/synthesis/extract.rst new file mode 100644 index 00000000000..678efba868e --- /dev/null +++ b/docs/source/using_yosys/synthesis/extract.rst @@ -0,0 +1,231 @@ +The extract pass +---------------- + +- Like the :cmd:ref:`techmap` pass, the :cmd:ref:`extract` pass is called with a + map file. It compares the circuits inside the modules of the map file with the + design and looks for sub-circuits in the design that match any of the modules + in the map file. +- If a match is found, the :cmd:ref:`extract` pass will replace the matching + subcircuit with an instance of the module from the map file. +- In a way the :cmd:ref:`extract` pass is the inverse of the techmap pass. + +.. todo:: add/expand supporting text, also mention custom pattern matching and + pmgen + +Example code can be found in |code_examples/macc|_. + +.. |code_examples/macc| replace:: :file:`docs/source/code_examples/macc` +.. _code_examples/macc: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/macc + + +.. literalinclude:: /code_examples/macc/macc_simple_test.ys + :language: yoscrypt + :lines: 1-2 + +.. figure:: /_images/code_examples/macc/macc_simple_test_00a.* + :class: width-helper + + before :cmd:ref:`extract` + +.. literalinclude:: /code_examples/macc/macc_simple_test.ys + :language: yoscrypt + :lines: 6 + +.. figure:: /_images/code_examples/macc/macc_simple_test_00b.* + :class: width-helper + + after :cmd:ref:`extract` + +.. literalinclude:: /code_examples/macc/macc_simple_test.v + :language: verilog + :caption: :file:`macc_simple_test.v` + +.. literalinclude:: /code_examples/macc/macc_simple_xmap.v + :language: verilog + :caption: :file:`macc_simple_xmap.v` + +.. literalinclude:: /code_examples/macc/macc_simple_test_01.v + :language: verilog + :caption: :file:`macc_simple_test_01.v` + +.. figure:: /_images/code_examples/macc/macc_simple_test_01a.* + :class: width-helper + +.. figure:: /_images/code_examples/macc/macc_simple_test_01b.* + :class: width-helper + +.. literalinclude:: /code_examples/macc/macc_simple_test_02.v + :language: verilog + :caption: :file:`macc_simple_test_02.v` + +.. figure:: /_images/code_examples/macc/macc_simple_test_02a.* + :class: width-helper + +.. figure:: /_images/code_examples/macc/macc_simple_test_02b.* + :class: width-helper + +The wrap-extract-unwrap method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Often a coarse-grain element has a constant bit-width, but can be used to +implement operations with a smaller bit-width. For example, a 18x25-bit multiplier +can also be used to implement 16x20-bit multiplication. + +A way of mapping such elements in coarse grain synthesis is the +wrap-extract-unwrap method: + +wrap + Identify candidate-cells in the circuit and wrap them in a cell with a + constant wider bit-width using :cmd:ref:`techmap`. The wrappers use the same + parameters as the original cell, so the information about the original width + of the ports is preserved. Then use the :cmd:ref:`connwrappers` command to + connect up the bit-extended in- and outputs of the wrapper cells. + +extract + Now all operations are encoded using the same bit-width as the coarse grain + element. The :cmd:ref:`extract` command can be used to replace circuits with + cells of the target architecture. + +unwrap + The remaining wrapper cell can be unwrapped using :cmd:ref:`techmap`. + +Example: DSP48_MACC +~~~~~~~~~~~~~~~~~~~ + +This section details an example that shows how to map MACC operations of +arbitrary size to MACC cells with a 18x25-bit multiplier and a 48-bit adder +(such as the Xilinx DSP48 cells). + +Preconditioning: :file:`macc_xilinx_swap_map.v` + +Make sure ``A`` is the smaller port on all multipliers + +.. todo:: add/expand supporting text + +.. literalinclude:: /code_examples/macc/macc_xilinx_swap_map.v + :language: verilog + :caption: :file:`macc_xilinx_swap_map.v` + +Wrapping multipliers: :file:`macc_xilinx_wrap_map.v` + +.. literalinclude:: /code_examples/macc/macc_xilinx_wrap_map.v + :language: verilog + :lines: 1-46 + :caption: :file:`macc_xilinx_wrap_map.v` + +Wrapping adders: :file:`macc_xilinx_wrap_map.v` + +.. literalinclude:: /code_examples/macc/macc_xilinx_wrap_map.v + :language: verilog + :lines: 48-89 + :caption: :file:`macc_xilinx_wrap_map.v` + +Extract: :file:`macc_xilinx_xmap.v` + +.. literalinclude:: /code_examples/macc/macc_xilinx_xmap.v + :language: verilog + :caption: :file:`macc_xilinx_xmap.v` + +... simply use the same wrapping commands on this module as on the design to +create a template for the :cmd:ref:`extract` command. + +Unwrapping multipliers: :file:`macc_xilinx_unwrap_map.v` + +.. literalinclude:: /code_examples/macc/macc_xilinx_unwrap_map.v + :language: verilog + :lines: 1-30 + :caption: ``$__mul_wrapper`` module in :file:`macc_xilinx_unwrap_map.v` + +Unwrapping adders: :file:`macc_xilinx_unwrap_map.v` + +.. literalinclude:: /code_examples/macc/macc_xilinx_unwrap_map.v + :language: verilog + :lines: 32-61 + :caption: ``$__add_wrapper`` module in :file:`macc_xilinx_unwrap_map.v` + +.. literalinclude:: /code_examples/macc/macc_xilinx_test.v + :language: verilog + :lines: 1-6 + :caption: ``test1`` of :file:`macc_xilinx_test.v` + +.. figure:: /_images/code_examples/macc/macc_xilinx_test1a.* + :class: width-helper + +.. figure:: /_images/code_examples/macc/macc_xilinx_test1b.* + :class: width-helper + +.. literalinclude:: /code_examples/macc/macc_xilinx_test.v + :language: verilog + :lines: 8-13 + :caption: ``test2`` of :file:`macc_xilinx_test.v` + +.. figure:: /_images/code_examples/macc/macc_xilinx_test2a.* + :class: width-helper + +.. figure:: /_images/code_examples/macc/macc_xilinx_test2b.* + :class: width-helper + +Wrapping in ``test1``: + +.. figure:: /_images/code_examples/macc/macc_xilinx_test1b.* + :class: width-helper + +.. literalinclude:: /code_examples/macc/macc_xilinx_test.ys + :language: yoscrypt + :start-after: part c + :end-before: end part c + +.. figure:: /_images/code_examples/macc/macc_xilinx_test1c.* + :class: width-helper + +Wrapping in ``test2``: + +.. figure:: /_images/code_examples/macc/macc_xilinx_test2b.* + :class: width-helper + +.. literalinclude:: /code_examples/macc/macc_xilinx_test.ys + :language: yoscrypt + :start-after: part c + :end-before: end part c + +.. figure:: /_images/code_examples/macc/macc_xilinx_test2c.* + :class: width-helper + +Extract in ``test1``: + +.. figure:: /_images/code_examples/macc/macc_xilinx_test1c.* + :class: width-helper + +.. literalinclude:: /code_examples/macc/macc_xilinx_test.ys + :language: yoscrypt + :start-after: part d + :end-before: end part d + +.. figure:: /_images/code_examples/macc/macc_xilinx_test1d.* + :class: width-helper + +Extract in ``test2``: + +.. figure:: /_images/code_examples/macc/macc_xilinx_test2c.* + :class: width-helper + +.. literalinclude:: /code_examples/macc/macc_xilinx_test.ys + :language: yoscrypt + :start-after: part d + :end-before: end part d + +.. figure:: /_images/code_examples/macc/macc_xilinx_test2d.* + :class: width-helper + +Unwrap in ``test2``: + +.. figure:: /_images/code_examples/macc/macc_xilinx_test2d.* + :class: width-helper + +.. literalinclude:: /code_examples/macc/macc_xilinx_test.ys + :language: yoscrypt + :start-after: part e + :end-before: end part e + +.. figure:: /_images/code_examples/macc/macc_xilinx_test2e.* + :class: width-helper \ No newline at end of file diff --git a/docs/source/using_yosys/synthesis/fsm.rst b/docs/source/using_yosys/synthesis/fsm.rst new file mode 100644 index 00000000000..e1ed5513397 --- /dev/null +++ b/docs/source/using_yosys/synthesis/fsm.rst @@ -0,0 +1,142 @@ +FSM handling +============ + +The :cmd:ref:`fsm` command identifies, extracts, optimizes (re-encodes), and +re-synthesizes finite state machines. It again is a macro that calls a series of +other commands: + +.. literalinclude:: /code_examples/macro_commands/fsm.ys + :language: yoscrypt + :start-after: #end: + :caption: Passes called by :cmd:ref:`fsm` + +See also :doc:`/cmd/fsm`. + +The algorithms used for FSM detection and extraction are influenced by a more +general reported technique :cite:p:`fsmextract`. + +FSM detection +~~~~~~~~~~~~~ + +The :cmd:ref:`fsm_detect` pass identifies FSM state registers. It sets the +``\fsm_encoding = "auto"`` attribute on any (multi-bit) wire that matches the +following description: + +- Does not already have the ``\fsm_encoding`` attribute. +- Is not an output of the containing module. +- Is driven by single ``$dff`` or ``$adff`` cell. +- The ``\D``-Input of this ``$dff`` or ``$adff`` cell is driven by a + multiplexer tree that only has constants or the old state value on its + leaves. +- The state value is only used in the said multiplexer tree or by simple + relational cells that compare the state value to a constant (usually ``$eq`` + cells). + +This heuristic has proven to work very well. It is possible to overwrite it by +setting ``\fsm_encoding = "auto"`` on registers that should be considered FSM +state registers and setting ``\fsm_encoding = "none"`` on registers that match +the above criteria but should not be considered FSM state registers. + +Note however that marking state registers with ``\fsm_encoding`` that are not +suitable for FSM recoding can cause synthesis to fail or produce invalid +results. + +FSM extraction +~~~~~~~~~~~~~~ + +The :cmd:ref:`fsm_extract` pass operates on all state signals marked with the +(``\fsm_encoding != "none"``) attribute. For each state signal the following +information is determined: + +- The state registers + +- The asynchronous reset state if the state registers use asynchronous reset + +- All states and the control input signals used in the state transition + functions + +- The control output signals calculated from the state signals and control + inputs + +- A table of all state transitions and corresponding control inputs- and + outputs + +The state registers (and asynchronous reset state, if applicable) is simply +determined by identifying the driver for the state signal. + +From there the ``$mux-tree`` driving the state register inputs is recursively +traversed. All select inputs are control signals and the leaves of the +``$mux-tree`` are the states. The algorithm fails if a non-constant leaf that is +not the state signal itself is found. + +The list of control outputs is initialized with the bits from the state signal. +It is then extended by adding all values that are calculated by cells that +compare the state signal with a constant value. + +In most cases this will cover all uses of the state register, thus rendering the +state encoding arbitrary. If however a design uses e.g. a single bit of the +state value to drive a control output directly, this bit of the state signal +will be transformed to a control output of the same value. + +Finally, a transition table for the FSM is generated. This is done by using the +ConstEval C++ helper class (defined in kernel/consteval.h) that can be used to +evaluate parts of the design. The ConstEval class can be asked to calculate a +given set of result signals using a set of signal-value assignments. It can also +be passed a list of stop-signals that abort the ConstEval algorithm if the value +of a stop-signal is needed in order to calculate the result signals. + +The :cmd:ref:`fsm_extract` pass uses the ConstEval class in the following way to +create a transition table. For each state: + +1. Create a ConstEval object for the module containing the FSM +2. Add all control inputs to the list of stop signals +3. Set the state signal to the current state +4. Try to evaluate the next state and control output +5. If step 4 was not successful: + + - Recursively goto step 4 with the offending stop-signal set to 0. + - Recursively goto step 4 with the offending stop-signal set to 1. + +6. If step 4 was successful: Emit transition + +Finally a ``$fsm`` cell is created with the generated transition table and added +to the module. This new cell is connected to the control signals and the old +drivers for the control outputs are disconnected. + +FSM optimization +~~~~~~~~~~~~~~~~ + +The :cmd:ref:`fsm_opt` pass performs basic optimizations on ``$fsm`` cells (not +including state recoding). The following optimizations are performed (in this +order): + +- Unused control outputs are removed from the ``$fsm`` cell. The attribute + ``\unused_bits`` (that is usually set by the :cmd:ref:`opt_clean` pass) is + used to determine which control outputs are unused. + +- Control inputs that are connected to the same driver are merged. + +- When a control input is driven by a control output, the control input is + removed and the transition table altered to give the same performance without + the external feedback path. + +- Entries in the transition table that yield the same output and only differ in + the value of a single control input bit are merged and the different bit is + removed from the sensitivity list (turned into a don't-care bit). + +- Constant inputs are removed and the transition table is altered to give an + unchanged behaviour. + +- Unused inputs are removed. + +FSM recoding +~~~~~~~~~~~~ + +The :cmd:ref:`fsm_recode` pass assigns new bit pattern to the states. Usually +this also implies a change in the width of the state signal. At the moment of +this writing only one-hot encoding with all-zero for the reset state is +supported. + +The :cmd:ref:`fsm_recode` pass can also write a text file with the changes +performed by it that can be used when verifying designs synthesized by Yosys +using Synopsys Formality. diff --git a/docs/source/using_yosys/synthesis/index.rst b/docs/source/using_yosys/synthesis/index.rst new file mode 100644 index 00000000000..c00a940daa6 --- /dev/null +++ b/docs/source/using_yosys/synthesis/index.rst @@ -0,0 +1,35 @@ +Synthesis in detail +------------------- + +Synthesis can generally be broken down into coarse-grain synthesis, and +fine-grain synthesis. We saw this in :doc:`/getting_started/example_synth` +where a design was loaded and elaborated and then went through a series of +coarse-grain optimizations before being mapped to hard blocks and fine-grain +cells. Most commands in Yosys will target either coarse-grain representation or +fine-grain representation, with only a select few compatible with both states. + +Commands such as :cmd:ref:`proc`, :cmd:ref:`fsm`, and :cmd:ref:`memory` rely on +the additional information in the coarse-grain representation, along with a +number of optimizations such as :cmd:ref:`wreduce`, :cmd:ref:`share`, and +:cmd:ref:`alumacc`. :cmd:ref:`opt` provides optimizations which are useful in +both states, while :cmd:ref:`techmap` is used to convert coarse-grain cells +to the corresponding fine-grain representation. + +Single-bit cells (logic gates, FFs) as well as LUTs, half-adders, and +full-adders make up the bulk of the fine-grain representation and are necessary +for commands such as :cmd:ref:`abc`\ /:cmd:ref:`abc9`, :cmd:ref:`simplemap`, +:cmd:ref:`dfflegalize`, and :cmd:ref:`memory_map`. + +.. toctree:: + :maxdepth: 3 + + synth + proc + fsm + memory + opt + techmap_synth + extract + abc + cell_libs + diff --git a/docs/source/using_yosys/synthesis/memory.rst b/docs/source/using_yosys/synthesis/memory.rst new file mode 100644 index 00000000000..7df75fb8809 --- /dev/null +++ b/docs/source/using_yosys/synthesis/memory.rst @@ -0,0 +1,759 @@ +Memory handling +=============== + +The :cmd:ref:`memory` command +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In the RTL netlist, memory reads and writes are individual cells. This makes +consolidating the number of ports for a memory easier. The :cmd:ref:`memory` +pass transforms memories to an implementation. Per default that is logic for +address decoders and registers. It also is a macro command that calls the other +common ``memory_*`` passes in a sensible order: + +.. literalinclude:: /code_examples/macro_commands/memory.ys + :language: yoscrypt + :start-after: #end: + :caption: Passes called by :cmd:ref:`memory` + +.. todo:: Make ``memory_*`` notes less quick + +Some quick notes: + +- :cmd:ref:`memory_dff` merges registers into the memory read- and write cells. +- :cmd:ref:`memory_collect` collects all read and write cells for a memory and + transforms them into one multi-port memory cell. +- :cmd:ref:`memory_map` takes the multi-port memory cell and transforms it to + address decoder logic and registers. + +For more information about :cmd:ref:`memory`, such as disabling certain sub +commands, see :doc:`/cmd/memory`. + +Example +------- + +.. todo:: describe ``memory`` images + +|code_examples/synth_flow|_. + +.. |code_examples/synth_flow| replace:: :file:`docs/source/code_examples/synth_flow` +.. _code_examples/synth_flow: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/synth_flow + +.. figure:: /_images/code_examples/synth_flow/memory_01.* + :class: width-helper + +.. literalinclude:: /code_examples/synth_flow/memory_01.ys + :language: yoscrypt + :caption: :file:`memory_01.ys` + +.. literalinclude:: /code_examples/synth_flow/memory_01.v + :language: verilog + :caption: :file:`memory_01.v` + +.. figure:: /_images/code_examples/synth_flow/memory_02.* + :class: width-helper + +.. literalinclude:: /code_examples/synth_flow/memory_02.v + :language: verilog + :caption: :file:`memory_02.v` + +.. literalinclude:: /code_examples/synth_flow/memory_02.ys + :language: yoscrypt + :caption: :file:`memory_02.ys` + +.. _memory_map: + +Memory mapping +^^^^^^^^^^^^^^ + +Usually it is preferred to use architecture-specific RAM resources for memory. +For example: + +.. code-block:: yoscrypt + + memory -nomap + memory_libmap -lib my_memory_map.txt + techmap -map my_memory_map.v + memory_map + +:cmd:ref:`memory_libmap` attempts to convert memory cells (``$mem_v2`` etc) into +hardware supported memory using a provided library (:file:`my_memory_map.txt` in the +example above). Where necessary, emulation logic is added to ensure functional +equivalence before and after this conversion. :yoscrypt:`techmap -map +my_memory_map.v` then uses :cmd:ref:`techmap` to map to hardware primitives. Any +leftover memory cells unable to be converted are then picked up by +:cmd:ref:`memory_map` and mapped to DFFs and address decoders. + +.. note:: + + More information about what mapping options are available and associated + costs of each can be found by enabling debug outputs. This can be done with + the :cmd:ref:`debug` command, or by using the ``-g`` flag when calling Yosys + to globally enable debug messages. + +For more on the lib format for :cmd:ref:`memory_libmap`, see +`passes/memory/memlib.md +`_ + +Supported memory patterns +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Note that not all supported patterns are included in this document, of +particular note is that combinations of multiple patterns should generally work. +For example, `wbe`_ could be used in conjunction with any of the simple dual +port (SDP) models. In general if a hardware memory definition does not support +a given configuration, additional logic will be instantiated to guarantee +behaviour is consistent with simulation. + +Notes +----- + +Memory kind selection +~~~~~~~~~~~~~~~~~~~~~ + +The memory inference code will automatically pick target memory primitive based on memory geometry +and features used. Depending on the target, there can be up to four memory primitive classes +available for selection: + +- FF RAM (aka logic): no hardware primitive used, memory lowered to a bunch of FFs and multiplexers + + - Can handle arbitrary number of write ports, as long as all write ports are in the same clock domain + - Can handle arbitrary number and kind of read ports + +- LUT RAM (aka distributed RAM): uses LUT storage as RAM + + - Supported on most FPGAs (with notable exception of ice40) + - Usually has one synchronous write port, one or more asynchronous read ports + - Small + - Will never be used for ROMs (lowering to plain LUTs is always better) + +- Block RAM: dedicated memory tiles + + - Supported on basically all FPGAs + - Supports only synchronous reads + - Two ports with separate clocks + - Usually supports true dual port (with notable exception of ice40 that only supports SDP) + - Usually supports asymmetric memories and per-byte write enables + - Several kilobits in size + +- Huge RAM: + + - Only supported on several targets: + + - Some Xilinx UltraScale devices (UltraRAM) + + - Two ports, both with mutually exclusive synchronous read and write + - Single clock + - Initial data must be all-0 + + - Some ice40 devices (SPRAM) + + - Single port with mutually exclusive synchronous read and write + - Does not support initial data + + - Nexus (large RAM) + + - Two ports, both with mutually exclusive synchronous read and write + - Single clock + + - Will not be automatically selected by memory inference code, needs explicit opt-in via + ram_style attribute + +In general, you can expect the automatic selection process to work roughly like this: + +- If any read port is asynchronous, only LUT RAM (or FF RAM) can be used. +- If there is more than one write port, only block RAM can be used, and this needs to be a + hardware-supported true dual port pattern + + - … unless all write ports are in the same clock domain, in which case FF RAM can also be used, + but this is generally not what you want for anything but really small memories + +- Otherwise, either FF RAM, LUT RAM, or block RAM will be used, depending on memory size + +This process can be overridden by attaching a ram_style attribute to the memory: + +- `(* ram_style = "logic" *)` selects FF RAM +- `(* ram_style = "distributed" *)` selects LUT RAM +- `(* ram_style = "block" *)` selects block RAM +- `(* ram_style = "huge" *)` selects huge RAM + +It is an error if this override cannot be realized for the given target. + +Many alternate spellings of the attribute are also accepted, for compatibility with other software. + +Initial data +~~~~~~~~~~~~ + +Most FPGA targets support initializing all kinds of memory to user-provided values. If explicit +initialization is not used the initial memory value is undefined. Initial data can be provided by +either initial statements writing memory cells one by one of ``$readmemh`` or ``$readmemb`` system +tasks. For an example pattern, see `sr_init`_. + +.. _wbe: + +Write port with byte enables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Byte enables can be used with any supported pattern +- To ensure that multiple writes will be merged into one port, they need to have disjoint bit + ranges, have the same address, and the same clock +- Any write enable granularity will be accepted (down to per-bit write enables), but using smaller + granularity than natively supported by the target is very likely to be inefficient (eg. using + 4-bit bytes on ECP5 will result in either padding the bytes with 5 dummy bits to native 9-bit + units or splitting the RAM into two block RAMs) + +.. code:: verilog + + reg [31 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable[0]) + mem[write_addr][7:0] <= write_data[7:0]; + if (write_enable[1]) + mem[write_addr][15:8] <= write_data[15:8]; + if (write_enable[2]) + mem[write_addr][23:16] <= write_data[23:16]; + if (write_enable[3]) + mem[write_addr][31:24] <= write_data[31:24]; + if (read_enable) + read_data <= mem[read_addr]; + end + +Simple dual port (SDP) memory patterns +-------------------------------------- + +.. todo:: assorted enables, e.g. cen, wen+ren + +Asynchronous-read SDP +~~~~~~~~~~~~~~~~~~~~~ + +- This will result in LUT RAM on supported targets + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + always @(posedge clk) + if (write_enable) + mem[write_addr] <= write_data; + assign read_data = mem[read_addr]; + +Synchronous SDP with clock domain crossing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in block RAM or LUT RAM depending on size +- No behavior guarantees in case of simultaneous read and write to the same address + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge write_clk) begin + if (write_enable) + mem[write_addr] <= write_data; + end + + always @(posedge read_clk) begin + if (read_enable) + read_data <= mem[read_addr]; + end + +Synchronous SDP read first +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- The read and write parts can be in the same or different processes. +- Will result in block RAM or LUT RAM depending on size +- As long as the same clock is used for both, yosys will ensure read-first behavior. This may + require extra circuitry on some targets for block RAM. If this is not necessary, use one of the + patterns below. + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + if (read_enable) + read_data <= mem[read_addr]; + end + +.. _no_rw_check: + +Synchronous SDP with undefined collision behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Like above, but the read value is undefined when read and write ports target the same address in + the same cycle + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + + if (read_enable) begin + read_data <= mem[read_addr]; + + if (write_enable && read_addr == write_addr) + // this if block + read_data <= 'x; + end + end + +- Or below, using the no_rw_check attribute + +.. code:: verilog + + (* no_rw_check *) + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + + if (read_enable) + read_data <= mem[read_addr]; + end + +.. _sdp_wf: + +Synchronous SDP with write-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in block RAM or LUT RAM depending on size +- May use additional circuitry for block RAM if write-first is not natively supported. Will always + use additional circuitry for LUT RAM. + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + + if (read_enable) begin + read_data <= mem[read_addr]; + if (write_enable && read_addr == write_addr) + read_data <= write_data; + end + end + +Synchronous SDP with write-first behavior (alternate pattern) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- This pattern is supported for compatibility, but is much less flexible than the above + +.. code:: verilog + + reg [ADDR_WIDTH - 1 : 0] read_addr_reg; + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + read_addr_reg <= read_addr; + end + + assign read_data = mem[read_addr_reg]; + +Single-port RAM memory patterns +------------------------------- + +Asynchronous-read single-port RAM +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in single-port LUT RAM on supported targets + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + always @(posedge clk) + if (write_enable) + mem[addr] <= write_data; + assign read_data = mem[addr]; + +Synchronous single-port RAM with mutually exclusive read/write +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in single-port block RAM or LUT RAM depending on size +- This is the correct pattern to infer ice40 SPRAM (with manual ram_style selection) +- On targets that don't support read/write block RAM ports (eg. ice40), will result in SDP block RAM instead +- For block RAM, will use "NO_CHANGE" mode if available + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[addr] <= write_data; + else if (read_enable) + read_data <= mem[addr]; + end + +Synchronous single-port RAM with read-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will only result in single-port block RAM when read-first behavior is natively supported; + otherwise, SDP RAM with additional circuitry will be used +- Many targets (Xilinx, ECP5, …) can only natively support read-first/write-first single-port RAM + (or TDP RAM) where the write_enable signal implies the read_enable signal (ie. can never write + without reading). The memory inference code will run a simple SAT solver on the control signals to + determine if this is the case, and insert emulation circuitry if it cannot be easily proven. + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[addr] <= write_data; + if (read_enable) + read_data <= mem[addr]; + end + +Synchronous single-port RAM with write-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Will result in single-port block RAM or LUT RAM when supported +- Block RAMs will require extra circuitry if write-first behavior not natively supported + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[addr] <= write_data; + if (read_enable) + if (write_enable) + read_data <= write_data; + else + read_data <= mem[addr]; + end + +.. _sr_init: + +Synchronous read port with initial value +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Initial read port values can be combined with any other supported pattern +- If block RAM is used and initial read port values are not natively supported by the target, small + emulation circuit will be inserted + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + reg [DATA_WIDTH - 1 : 0] read_data; + initial read_data = 'h1234; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + if (read_enable) + read_data <= mem[read_addr]; + end + +Read register reset patterns +---------------------------- + +Resets can be combined with any other supported pattern (except that synchronous reset and +asynchronous reset cannot both be used on a single read port). If block RAM is used and the +selected reset (synchronous or asynchronous) is used but not natively supported by the target, small +emulation circuitry will be inserted. + +Synchronous reset, reset priority over enable +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + + if (read_reset) + read_data <= 'h1234; + else if (read_enable) + read_data <= mem[read_addr]; + end + +Synchronous reset, enable priority over reset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + if (read_enable) + if (read_reset) + read_data <= 'h1234; + else + read_data <= mem[read_addr]; + end + +Synchronous read port with asynchronous reset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + end + + always @(posedge clk, posedge read_reset) begin + if (read_reset) + read_data <= 'h1234; + else if (read_enable) + read_data <= mem[read_addr]; + end + +Asymmetric memory patterns +-------------------------- + +To construct an asymmetric memory (memory with read/write ports of differing widths): + +- Declare the memory with the width of the narrowest intended port +- Split all wide ports into multiple narrow ports +- To ensure the wide ports will be correctly merged: + + - For the address, use a concatenation of actual address in the high bits and a constant in the + low bits + - Ensure the actual address is identical for all ports belonging to the wide port + - Ensure that clock is identical + - For read ports, ensure that enable/reset signals are identical (for write ports, the enable + signal may vary — this will result in using the byte enable functionality) + +Asymmetric memory is supported on all targets, but may require emulation circuitry where not +natively supported. Note that when the memory is larger than the underlying block RAM primitive, +hardware asymmetric memory support is likely not to be used even if present as it is more expensive. + +.. _wide_sr: + +Wide synchronous read port +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [7:0] mem [0:255]; + wire [7:0] write_addr; + wire [5:0] read_addr; + wire [7:0] write_data; + reg [31:0] read_data; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + if (read_enable) begin + read_data[7:0] <= mem[{read_addr, 2'b00}]; + read_data[15:8] <= mem[{read_addr, 2'b01}]; + read_data[23:16] <= mem[{read_addr, 2'b10}]; + read_data[31:24] <= mem[{read_addr, 2'b11}]; + end + end + +Wide asynchronous read port +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Note: the only target natively supporting this pattern is Xilinx UltraScale + +.. code:: verilog + + reg [7:0] mem [0:511]; + wire [8:0] write_addr; + wire [5:0] read_addr; + wire [7:0] write_data; + wire [63:0] read_data; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + end + + assign read_data[7:0] = mem[{read_addr, 3'b000}]; + assign read_data[15:8] = mem[{read_addr, 3'b001}]; + assign read_data[23:16] = mem[{read_addr, 3'b010}]; + assign read_data[31:24] = mem[{read_addr, 3'b011}]; + assign read_data[39:32] = mem[{read_addr, 3'b100}]; + assign read_data[47:40] = mem[{read_addr, 3'b101}]; + assign read_data[55:48] = mem[{read_addr, 3'b110}]; + assign read_data[63:56] = mem[{read_addr, 3'b111}]; + +Wide write port +~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [7:0] mem [0:255]; + wire [5:0] write_addr; + wire [7:0] read_addr; + wire [31:0] write_data; + reg [7:0] read_data; + + always @(posedge clk) begin + if (write_enable[0]) + mem[{write_addr, 2'b00}] <= write_data[7:0]; + if (write_enable[1]) + mem[{write_addr, 2'b01}] <= write_data[15:8]; + if (write_enable[2]) + mem[{write_addr, 2'b10}] <= write_data[23:16]; + if (write_enable[3]) + mem[{write_addr, 2'b11}] <= write_data[31:24]; + if (read_enable) + read_data <= mem[read_addr]; + end + +True dual port (TDP) patterns +----------------------------- + +- Many different variations of true dual port memory can be created by combining two single-port RAM + patterns on the same memory +- When TDP memory is used, memory inference code has much less maneuver room to create requested + semantics compared to individual single-port patterns (which can end up lowered to SDP memory + where necessary) — supported patterns depend strongly on the target +- In particular, when both ports have the same clock, it's likely that "undefined collision" mode + needs to be manually selected to enable TDP memory inference +- The examples below are non-exhaustive — many more combinations of port types are possible +- Note: if two write ports are in the same process, this defines a priority relation between them + (if both ports are active in the same clock, the later one wins). On almost all targets, this will + result in a bit of extra circuitry to ensure the priority semantics. If this is not what you want, + put them in separate processes. + + - Priority is not supported when using the verific front end and any priority semantics are ignored. + +TDP with different clocks, exclusive read/write +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk_a) begin + if (write_enable_a) + mem[addr_a] <= write_data_a; + else if (read_enable_a) + read_data_a <= mem[addr_a]; + end + + always @(posedge clk_b) begin + if (write_enable_b) + mem[addr_b] <= write_data_b; + else if (read_enable_b) + read_data_b <= mem[addr_b]; + end + +TDP with same clock, read-first behavior +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- This requires hardware inter-port read-first behavior, and will only work on some targets (Xilinx, Nexus) + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable_a) + mem[addr_a] <= write_data_a; + if (read_enable_a) + read_data_a <= mem[addr_a]; + end + + always @(posedge clk) begin + if (write_enable_b) + mem[addr_b] <= write_data_b; + if (read_enable_b) + read_data_b <= mem[addr_b]; + end + +TDP with multiple read ports +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- The combination of a single write port with an arbitrary amount of read ports is supported on all + targets — if a multi-read port primitive is available (like Xilinx RAM64M), it'll be used as + appropriate. Otherwise, the memory will be automatically split into multiple primitives. + +.. code:: verilog + + reg [31:0] mem [0:31]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] <= write_data; + end + + assign read_data_a = mem[read_addr_a]; + assign read_data_b = mem[read_addr_b]; + assign read_data_c = mem[read_addr_c]; + +Patterns only supported with Verific +------------------------------------ + +Synchronous SDP with write-first behavior via blocking assignments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Use `sdp_wf`_ for compatibility with Yosys + Verilog frontend. + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr] = write_data; + + if (read_enable) + read_data <= mem[read_addr]; + end + +Asymmetric memories via part selection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Build wide ports out of narrow ports instead (see `wide_sr`_) for + compatibility with Yosys Verilog frontend. + +.. code:: verilog + + reg [31:0] mem [2**ADDR_WIDTH - 1 : 0]; + + wire [1:0] byte_lane; + wire [7:0] write_data; + + always @(posedge clk) begin + if (write_enable) + mem[write_addr][byte_lane * 8 +: 8] <= write_data; + + if (read_enable) + read_data <= mem[read_addr]; + end + + +Undesired patterns +------------------ + +Asynchronous writes +~~~~~~~~~~~~~~~~~~~ + +- Not supported in modern FPGAs +- Not supported in yosys code anyhow + +.. code:: verilog + + reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0]; + + always @* begin + if (write_enable) + mem[write_addr] = write_data; + end + + assign read_data = mem[read_addr]; + diff --git a/docs/source/using_yosys/synthesis/opt.rst b/docs/source/using_yosys/synthesis/opt.rst new file mode 100644 index 00000000000..7861f66d4cd --- /dev/null +++ b/docs/source/using_yosys/synthesis/opt.rst @@ -0,0 +1,230 @@ +Optimization passes +=================== + +Yosys employs a number of optimizations to generate better and cleaner results. +This chapter outlines these optimizations. + +.. todo:: "outlines these optimizations" or "outlines *some*.."? + +The :cmd:ref:`opt` macro command +-------------------------------- + +The Yosys pass :cmd:ref:`opt` runs a number of simple optimizations. This +includes removing unused signals and cells and const folding. It is recommended +to run this pass after each major step in the synthesis script. As listed in +:doc:`/cmd/opt`, this macro command calls the following ``opt_*`` commands: + +.. literalinclude:: /code_examples/macro_commands/opt.ys + :language: yoscrypt + :start-after: #end: + :caption: Passes called by :cmd:ref:`opt` + +.. _adv_opt_expr: + +Constant folding and simple expression rewriting - :cmd:ref:`opt_expr` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. todo:: unsure if this is too much detail and should be in :doc:`/yosys_internals/index` + +This pass performs constant folding on the internal combinational cell types +described in :doc:`/yosys_internals/formats/cell_library`. This means a cell +with all constant inputs is replaced with the constant value this cell drives. +In some cases this pass can also optimize cells with some constant inputs. + +.. table:: Const folding rules for ``$_AND_`` cells as used in :cmd:ref:`opt_expr`. + :name: tab:opt_expr_and + :align: center + + ========= ========= =========== + A-Input B-Input Replacement + ========= ========= =========== + any 0 0 + 0 any 0 + 1 1 1 + --------- --------- ----------- + X/Z X/Z X + 1 X/Z X + X/Z 1 X + --------- --------- ----------- + any X/Z 0 + X/Z any 0 + --------- --------- ----------- + :math:`a` 1 :math:`a` + 1 :math:`b` :math:`b` + ========= ========= =========== + +:numref:`Table %s ` shows the replacement rules used for +optimizing an ``$_AND_`` gate. The first three rules implement the obvious const +folding rules. Note that 'any' might include dynamic values calculated by other +parts of the circuit. The following three lines propagate undef (X) states. +These are the only three cases in which it is allowed to propagate an undef +according to Sec. 5.1.10 of IEEE Std. 1364-2005 :cite:p:`Verilog2005`. + +The next two lines assume the value 0 for undef states. These two rules are only +used if no other substitutions are possible in the current module. If other +substitutions are possible they are performed first, in the hope that the 'any' +will change to an undef value or a 1 and therefore the output can be set to +undef. + +The last two lines simply replace an ``$_AND_`` gate with one constant-1 input +with a buffer. + +Besides this basic const folding the :cmd:ref:`opt_expr` pass can replace 1-bit +wide ``$eq`` and ``$ne`` cells with buffers or not-gates if one input is +constant. Equality checks may also be reduced in size if there are redundant +bits in the arguments (i.e. bits which are constant on both inputs). This can, +for example, result in a 32-bit wide constant like ``255`` being reduced to the +8-bit value of ``8'11111111`` if the signal being compared is only 8-bit as in +:ref:`addr_gen_clean` of :doc:`/getting_started/example_synth`. + +The :cmd:ref:`opt_expr` pass is very conservative regarding optimizing ``$mux`` +cells, as these cells are often used to model decision-trees and breaking these +trees can interfere with other optimizations. + +.. literalinclude:: /code_examples/opt/opt_expr.ys + :language: Verilog + :start-after: read_verilog < B) = 123; // simple combinational path from A to B with a delay of 123. + (A *> B) = 123; // simple combinational path from A to all bits of B with a delay of 123 for all. + if (FOO) (A => B) = 123; // paths may apply under specific conditions. + (posedge CLK => (Q : D)) = 123; // combinational path triggered on the positive edge of CLK; used for clock-to-Q arrival paths. + $setup(A, posedge CLK, 123); // setup constraint for an input relative to a clock. + endspecify // ends a specify block + +By convention, all delays in ``specify`` blocks are in integer picoseconds. +Files containing ``specify`` blocks should be read with the ``-specify`` option +to :cmd:ref:`read_verilog` so that they aren't skipped. + +LUTs +^^^^ + +LUTs need to be annotated with an ``(* abc9_lut=N *)`` attribute, where ``N`` is +the relative area of that LUT model. For example, if an architecture can combine +LUTs to produce larger LUTs, then the combined LUTs would have increasingly +larger ``N``. Conversely, if an architecture can split larger LUTs into smaller +LUTs, then the smaller LUTs would have smaller ``N``. + +LUTs are generally specified with simple combinational paths from the LUT inputs +to the LUT output. + +DFFs +^^^^ + +DFFs should be annotated with an ``(* abc9_flop *)`` attribute, however ABC9 has +some specific requirements for this to be valid: - the DFF must initialise to +zero (consider using :cmd:ref:`dfflegalize` to ensure this). - the DFF cannot +have any asynchronous resets/sets (see the simplification idiom and the Boxes +section for what to do here). + +It is worth noting that in pure ``abc9`` mode, only the setup and arrival times +are passed to ABC9 (specifically, they are modelled as buffers with the given +delay). In ``abc9 -dff``, the flop itself is passed to ABC9, permitting +sequential optimisations. + +Some vendors have universal DFF models which include async sets/resets even when +they're unused. Therefore *the simplification idiom* exists to handle this: by +using a ``techmap`` file to discover flops which have a constant driver to those +asynchronous controls, they can be mapped into an intermediate, simplified flop +which qualifies as an ``(* abc9_flop *)``, ran through :cmd:ref:`abc9`, and then +mapped back to the original flop. This is used in :cmd:ref:`synth_intel_alm` and +:cmd:ref:`synth_quicklogic` for the PolarPro3. + +DFFs are usually specified to have setup constraints against the clock on the +input signals, and an arrival time for the ``Q`` output. + +Boxes +^^^^^ + +A "box" is a purely-combinational piece of hard logic. If the logic is exposed +to ABC9, it's a "whitebox", otherwise it's a "blackbox". Carry chains would be +best implemented as whiteboxes, but a DSP would be best implemented as a +blackbox (multipliers are too complex to easily work with). LUT RAMs can be +implemented as whiteboxes too. + +Boxes are arguably the biggest advantage that ABC9 has over ABC: by being aware +of carry chains and DSPs, it avoids optimising for a path that isn't the actual +critical path, while the generally-longer paths result in ABC9 being able to +reduce design area by mapping other logic to larger-but-slower cells. diff --git a/docs/source/yosys_internals/extending_yosys/extensions.rst b/docs/source/yosys_internals/extending_yosys/extensions.rst new file mode 100644 index 00000000000..2c159e3f089 --- /dev/null +++ b/docs/source/yosys_internals/extending_yosys/extensions.rst @@ -0,0 +1,272 @@ +Writing extensions +================== + +.. role:: yoscrypt(code) + :language: yoscrypt + +.. todo:: check text is coherent + +.. todo:: update to use :file:`/code_examples/extensions/test*.log` + +This chapter contains some bits and pieces of information about programming +yosys extensions. Don't be afraid to ask questions on the YosysHQ Slack. + +The `guidelines/` directory of the Yosys source code contains notes on various +aspects of Yosys development. In particular, the files GettingStarted and +CodingStyle may be of interest. + +.. todo:: what's in guidelines/GettingStarted that's missing from the manual? + +Quick guide +----------- + +Code examples from this section are included in the +|code_examples/extensions|_ directory of the Yosys source code. + +.. |code_examples/extensions| replace:: :file:`docs/source/code_examples/extensions` +.. _code_examples/extensions: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/extensions + + +Program components and data formats +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See :doc:`/yosys_internals/formats/rtlil_rep` document for more information +about the internal data storage format used in Yosys and the classes that it +provides. + +This document will focus on the much simpler version of RTLIL left after the +commands :cmd:ref:`proc` and :cmd:ref:`memory` (or :yoscrypt:`memory -nomap`): + +.. figure:: /_images/internals/simplified_rtlil.* + :class: width-helper + :name: fig:Simplified_RTLIL + + Simplified RTLIL entity-relationship diagram without memories and processes + +It is possible to only work on this simpler version: + +.. todo:: consider replacing inline code + +.. code:: c++ + + for (RTLIL::Module *module : design->selected_modules() { + if (module->has_memories_warn() || module->has_processes_warn()) + continue; + .... + } + +When trying to understand what a command does, creating a small test case to +look at the output of :cmd:ref:`dump` and :cmd:ref:`show` before and after the +command has been executed can be helpful. +:doc:`/using_yosys/more_scripting/selections` has more information on using +these commands. + +Creating a command +~~~~~~~~~~~~~~~~~~ + +.. todo:: add/expand supporting text + +Let's create a very simple test command which prints the arguments we called it +with, and lists off the current design's modules. + +.. literalinclude:: /code_examples/extensions/my_cmd.cc + :language: c++ + :lines: 1, 4, 6, 7-20 + :caption: Example command :yoscrypt:`my_cmd` from :file:`my_cmd.cc` + +Note that we are making a global instance of a class derived from +``Yosys::Pass``, which we get by including :file:`kernel/yosys.h`. + +Compiling to a plugin +~~~~~~~~~~~~~~~~~~~~~ + +Yosys can be extended by adding additional C++ code to the Yosys code base, or +by loading plugins into Yosys. For maintainability it is generally recommended +to create plugins. + +The following command compiles our example :yoscrypt:`my_cmd` to a Yosys plugin: + +.. todo:: replace inline code + +.. code:: shell + + yosys-config --exec --cxx --cxxflags --ldflags \ + -o my_cmd.so -shared my_cmd.cc --ldlibs + +Or shorter: + +.. code:: shell + + yosys-config --build my_cmd.so my_cmd.cc + +Running Yosys with the ``-m`` option allows the plugin to be used. Here's a +quick example that also uses the ``-p`` option to run :yoscrypt:`my_cmd foo +bar`. + +.. code:: shell-session + + $ yosys -m ./my_cmd.so -p 'my_cmd foo bar' + + -- Running command `my_cmd foo bar' -- + Arguments to my_cmd: + my_cmd + foo + bar + Modules in current design: + +Creating modules from scratch +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let's create the following module using the RTLIL API: + +.. literalinclude:: /code_examples/extensions/absval_ref.v + :language: Verilog + :caption: absval_ref.v + +We'll do the same as before and format it as a a ``Yosys::Pass``. + +.. literalinclude:: /code_examples/extensions/my_cmd.cc + :language: c++ + :lines: 23-47 + :caption: :yoscrypt:`test1` - creating the absval module, from :file:`my_cmd.cc` + +.. code:: shell-session + + $ yosys -m ./my_cmd.so -p 'test1' -Q + + -- Running command `test1' -- + Name of this module: absval + +And if we look at the schematic for this new module we see the following: + +.. figure:: /_images/code_examples/extensions/test1.* + :class: width-helper + + Output of ``yosys -m ./my_cmd.so -p 'test1; show'`` + +Modifying modules +~~~~~~~~~~~~~~~~~ + +Most commands modify existing modules, not create new ones. + +When modifying existing modules, stick to the following DOs and DON'Ts: + +- Do not remove wires. Simply disconnect them and let a successive + :cmd:ref:`clean` command worry about removing it. +- Use ``module->fixup_ports()`` after changing the ``port_*`` properties of + wires. +- You can safely remove cells or change the ``connections`` property of a cell, + but be careful when changing the size of the ``SigSpec`` connected to a cell + port. +- Use the ``SigMap`` helper class (see next section) when you need a unique + handle for each signal bit. + +Using the SigMap helper class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Consider the following module: + +.. literalinclude:: /code_examples/extensions/sigmap_test.v + :language: Verilog + :caption: :file:`sigmap_test.v` + +In this case ``a``, ``x``, and ``y`` are all different names for the same +signal. However: + +.. todo:: use my_cmd.cc literalincludes + +.. code:: C++ + + RTLIL::SigSpec a(module->wire("\\a")), x(module->wire("\\x")), + y(module->wire("\\y")); + log("%d %d %d\n", a == x, x == y, y == a); // will print "0 0 0" + +The ``SigMap`` helper class can be used to map all such aliasing signals to a +unique signal from the group (usually the wire that is directly driven by a cell +or port). + +.. code:: C++ + + SigMap sigmap(module); + log("%d %d %d\n", sigmap(a) == sigmap(x), sigmap(x) == sigmap(y), + sigmap(y) == sigmap(a)); // will print "1 1 1" + +Printing log messages +~~~~~~~~~~~~~~~~~~~~~ + +The ``log()`` function is a ``printf()``-like function that can be used to +create log messages. + +Use ``log_signal()`` to create a C-string for a SigSpec object: + +.. code:: C++ + + log("Mapped signal x: %s\n", log_signal(sigmap(x))); + +The pointer returned by ``log_signal()`` is automatically freed by the log +framework at a later time. + +Use ``log_id()`` to create a C-string for an ``RTLIL::IdString``: + +.. code:: C++ + + log("Name of this module: %s\n", log_id(module->name)); + +Use ``log_header()`` and ``log_push()``/\ ``log_pop()`` to structure log +messages: + +.. todo:: replace inline code + +.. code:: C++ + + log_header(design, "Doing important stuff!\n"); + log_push(); + for (int i = 0; i < 10; i++) + log("Log message #%d.\n", i); + log_pop(); + +Error handling +~~~~~~~~~~~~~~ + +Use ``log_error()`` to report a non-recoverable error: + +.. todo:: replace inline code + +.. code:: C++ + + if (design->modules.count(module->name) != 0) + log_error("A module with the name %s already exists!\n", + RTLIL::id2cstr(module->name)); + +Use ``log_cmd_error()`` to report a recoverable error: + +.. code:: C++ + + if (design->selection_stack.back().empty()) + log_cmd_error("This command can't operator on an empty selection!\n"); + +Use ``log_assert()`` and ``log_abort()`` instead of ``assert()`` and ``abort()``. + +The "stubnets" example module +------------------------------ + +The following is the complete code of the "stubnets" example module. It is +included in the Yosys source distribution under |code_examples/stubnets|_. + +.. |code_examples/stubnets| replace:: :file:`docs/source/code_examples/stubnets` +.. _code_examples/stubnets: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/stubnets + +.. literalinclude:: /code_examples/stubnets/stubnets.cc + :language: c++ + :linenos: + :caption: :file:`stubnets.cc` + +.. literalinclude:: /code_examples/stubnets/Makefile + :language: makefile + :linenos: + :caption: :file:`Makefile` + +.. literalinclude:: /code_examples/stubnets/test.v + :language: verilog + :linenos: + :caption: :file:`test.v` diff --git a/docs/source/yosys_internals/extending_yosys/index.rst b/docs/source/yosys_internals/extending_yosys/index.rst new file mode 100644 index 00000000000..c2dc6cd2b8b --- /dev/null +++ b/docs/source/yosys_internals/extending_yosys/index.rst @@ -0,0 +1,11 @@ +Extending Yosys +--------------- + +.. todo:: brief overview for the extending Yosys index + +.. toctree:: + :maxdepth: 3 + + extensions + abc_flow + diff --git a/docs/source/yosys_internals/flow/control_and_data.rst b/docs/source/yosys_internals/flow/control_and_data.rst new file mode 100644 index 00000000000..5dcbe073025 --- /dev/null +++ b/docs/source/yosys_internals/flow/control_and_data.rst @@ -0,0 +1,31 @@ +Control and data flow +===================== + +.. todo:: less academic + +The data- and control-flow of a typical synthesis tool is very similar to the +data- and control-flow of a typical compiler: different subsystems are called in +a predetermined order, each consuming the data generated by the last subsystem +and generating the data for the next subsystem (see :numref:`Fig. %s +`). + +.. figure:: /_images/internals/approach_flow.* + :class: width-helper + :name: fig:approach_flow + + General data- and control-flow of a synthesis tool + +The first subsystem to be called is usually called a frontend. It does not +process the data generated by another subsystem but instead reads the user +input—in the case of a HDL synthesis tool, the behavioural HDL code. + +The subsystems that consume data from previous subsystems and produce data for +the next subsystems (usually in the same or a similar format) are called passes. + +The last subsystem that is executed transforms the data generated by the last +pass into a suitable output format and writes it to a disk file. This subsystem +is usually called the backend. + +In Yosys all frontends, passes and backends are directly available as commands +in the synthesis script. Thus the user can easily create a custom synthesis flow +just by calling passes in the right order in a synthesis script. diff --git a/docs/source/yosys_internals/flow/index.rst b/docs/source/yosys_internals/flow/index.rst new file mode 100644 index 00000000000..c7ab0ebccda --- /dev/null +++ b/docs/source/yosys_internals/flow/index.rst @@ -0,0 +1,19 @@ +Internal flow +============= + +A (usually short) synthesis script controls Yosys. + +These scripts contain three types of commands: + +- **Frontends**, that read input files (usually Verilog); +- **Passes**, that perform transformations on the design in memory; +- **Backends**, that write the design in memory to a file (various formats are + available: Verilog, BLIF, EDIF, SPICE, BTOR, . . .). + +.. toctree:: + :maxdepth: 3 + + overview + control_and_data + verilog_frontend + diff --git a/docs/source/yosys_internals/flow/overview.rst b/docs/source/yosys_internals/flow/overview.rst new file mode 100644 index 00000000000..f7589df031f --- /dev/null +++ b/docs/source/yosys_internals/flow/overview.rst @@ -0,0 +1,49 @@ +Flow overview +============= + +.. todo:: less academic + +:numref:`Figure %s ` shows the simplified data flow within +Yosys. Rectangles in the figure represent program modules and ellipses internal +data structures that are used to exchange design data between the program +modules. + +Design data is read in using one of the frontend modules. The high-level HDL +frontends for Verilog and VHDL code generate an abstract syntax tree (AST) that +is then passed to the AST frontend. Note that both HDL frontends use the same +AST representation that is powerful enough to cover the Verilog HDL and VHDL +language. + +The AST Frontend then compiles the AST to Yosys's main internal data format, the +RTL Intermediate Language (RTLIL). A more detailed description of this format is +given in :doc:`/yosys_internals/formats/rtlil_rep`. + +There is also a text representation of the RTLIL data structure that can be +parsed using the RTLIL Frontend which is described in +:doc:`/yosys_internals/formats/rtlil_text`. + +The design data may then be transformed using a series of passes that all +operate on the RTLIL representation of the design. + +Finally the design in RTLIL representation is converted back to text by one of +the backends, namely the Verilog Backend for generating Verilog netlists and the +RTLIL Backend for writing the RTLIL data in the same format that is understood +by the RTLIL Frontend. + +With the exception of the AST Frontend, which is called by the high-level HDL +frontends and can't be called directly by the user, all program modules are +called by the user (usually using a synthesis script that contains text commands +for Yosys). + +By combining passes in different ways and/or adding additional passes to Yosys +it is possible to adapt Yosys to a wide range of applications. For this to be +possible it is key that (1) all passes operate on the same data structure +(RTLIL) and (2) that this data structure is powerful enough to represent the +design in different stages of the synthesis. + +.. figure:: /_images/internals/overview_flow.* + :class: width-helper + :name: fig:Overview_flow + + Yosys simplified data flow (ellipses: data structures, rectangles: + program modules) diff --git a/docs/source/yosys_internals/flow/verilog_frontend.rst b/docs/source/yosys_internals/flow/verilog_frontend.rst new file mode 100644 index 00000000000..127fa7be388 --- /dev/null +++ b/docs/source/yosys_internals/flow/verilog_frontend.rst @@ -0,0 +1,656 @@ +.. _chapter:verilog: + +The Verilog and AST frontends +============================= + +This chapter provides an overview of the implementation of the Yosys Verilog and +AST frontends. The Verilog frontend reads Verilog-2005 code and creates an +abstract syntax tree (AST) representation of the input. This AST representation +is then passed to the AST frontend that converts it to RTLIL data, as +illustrated in :numref:`Fig. %s `. + +.. figure:: /_images/internals/verilog_flow.* + :class: width-helper + :name: fig:Verilog_flow + + Simplified Verilog to RTLIL data flow + +Transforming Verilog to AST +--------------------------- + +The Verilog frontend converts the Verilog sources to an internal AST +representation that closely resembles the structure of the original Verilog +code. The Verilog frontend consists of three components, the Preprocessor, the +Lexer and the Parser. + +The source code to the Verilog frontend can be found in +:file:`frontends/verilog/` in the Yosys source tree. + +The Verilog preprocessor +~~~~~~~~~~~~~~~~~~~~~~~~ + +The Verilog preprocessor scans over the Verilog source code and interprets some +of the Verilog compiler directives such as :literal:`\`include`, +:literal:`\`define` and :literal:`\`ifdef`. + +It is implemented as a C++ function that is passed a file descriptor as input +and returns the pre-processed Verilog code as a ``std::string``. + +The source code to the Verilog Preprocessor can be found in +:file:`frontends/verilog/preproc.cc` in the Yosys source tree. + +The Verilog lexer +~~~~~~~~~~~~~~~~~ + +The Verilog Lexer is written using the lexer generator flex. Its source code can +be found in :file:`frontends/verilog/verilog_lexer.l` in the Yosys source tree. +The lexer does little more than identifying all keywords and literals recognised +by the Yosys Verilog frontend. + +The lexer keeps track of the current location in the Verilog source code using +some global variables. These variables are used by the constructor of AST nodes +to annotate each node with the source code location it originated from. + +Finally the lexer identifies and handles special comments such as "``// synopsys +translate_off``" and "``// synopsys full_case``". (It is recommended to use +:literal:`\`ifdef` constructs instead of the Synsopsys translate_on/off comments +and attributes such as ``(* full_case *)`` over "``// synopsys full_case``" +whenever possible.) + +The Verilog parser +~~~~~~~~~~~~~~~~~~ + +The Verilog Parser is written using the parser generator bison. Its source code +can be found in :file:`frontends/verilog/verilog_parser.y` in the Yosys source +tree. + +It generates an AST using the ``AST::AstNode`` data structure defined in +:file:`frontends/ast/ast.h`. An ``AST::AstNode`` object has the following +properties: + +.. list-table:: AST node types with their corresponding Verilog constructs. + :name: tab:Verilog_AstNodeType + :widths: 50 50 + + * - AST Node Type + - Corresponding Verilog Construct + * - AST_NONE + - This Node type should never be used. + * - AST_DESIGN + - This node type is used for the top node of the AST tree. It has no + corresponding Verilog construct. + * - AST_MODULE, AST_TASK, AST_FUNCTION + - ``module``, ``task`` and ``function`` + * - AST_WIRE + - ``input``, ``output``, ``wire``, ``reg`` and ``integer`` + * - AST_MEMORY + - Verilog Arrays + * - AST_AUTOWIRE + - Created by the simplifier when an undeclared signal name is used. + * - AST_PARAMETER, AST_LOCALPARAM + - ``parameter`` and ``localparam`` + * - AST_PARASET + - Parameter set in cell instantiation + * - AST_ARGUMENT + - Port connection in cell instantiation + * - AST_RANGE + - Bit-Index in a signal or element index in array + * - AST_CONSTANT + - A literal value + * - AST_CELLTYPE + - The type of cell in cell instantiation + * - AST_IDENTIFIER + - An Identifier (signal name in expression or cell/task/etc. name in other + contexts) + * - AST_PREFIX + - Construct an identifier in the form []. (used + only in advanced generate constructs) + * - AST_FCALL, AST_TCALL + - Call to function or task + * - AST_TO_SIGNED, AST_TO_UNSIGNED + - The ``$signed()`` and ``$unsigned()`` functions + * - AST_CONCAT, AST_REPLICATE + - The ``{...}`` and ``{...{...}}`` operators + * - AST_BIT_NOT, AST_BIT_AND, AST_BIT_OR, AST_BIT_XOR, AST_BIT_XNOR + - The bitwise operators ``~``, ``&``, ``|``, ``^`` and ``~^`` + * - AST_REDUCE_AND, AST_REDUCE_OR, AST_REDUCE_XOR, AST_REDUCE_XNOR + - The unary reduction operators ``~``, ``&``, ``|``, ``^`` and ``~^`` + * - AST_REDUCE_BOOL + - Conversion from multi-bit value to boolean value (equivalent to + AST_REDUCE_OR) + * - AST_SHIFT_LEFT, AST_SHIFT_RIGHT, AST_SHIFT_SLEFT, AST_SHIFT_SRIGHT + - The shift operators ``<<``, ``>>``, ``<<<`` and ``>>>`` + * - AST_LT, AST_LE, AST_EQ, AST_NE, AST_GE, AST_GT + - The relational operators ``<``, ``<=``, ``==``, ``!=``, ``>=`` and ``>`` + * - AST_ADD, AST_SUB, AST_MUL, AST_DIV, AST_MOD, AST_POW + - The binary operators ``+``, ``-``, ``*``, ``/``, ``%`` and ``**`` + * - AST_POS, AST_NEG + - The prefix operators ``+`` and ``-`` + * - AST_LOGIC_AND, AST_LOGIC_OR, AST_LOGIC_NOT + - The logic operators ``&&``, ``||`` and ``!`` + * - AST_TERNARY + - The ternary ``?:``-operator + * - AST_MEMRD AST_MEMWR + - Read and write memories. These nodes are generated by the AST simplifier + for writes/reads to/from Verilog arrays. + * - AST_ASSIGN + - An ``assign`` statement + * - AST_CELL + - A cell instantiation + * - AST_PRIMITIVE + - A primitive cell (``and``, ``nand``, ``or``, etc.) + * - AST_ALWAYS, AST_INITIAL + - Verilog ``always``- and ``initial``-blocks + * - AST_BLOCK + - A ``begin``-``end``-block + * - AST_ASSIGN_EQ. AST_ASSIGN_LE + - Blocking (``=``) and nonblocking (``<=``) assignments within an + ``always``- or ``initial``-block + * - AST_CASE. AST_COND, AST_DEFAULT + - The ``case`` (``if``) statements, conditions within a case and the + default case respectively + * - AST_FOR + - A ``for``-loop with an ``always``- or ``initial``-block + * - AST_GENVAR, AST_GENBLOCK, AST_GENFOR, AST_GENIF + - The ``genvar`` and ``generate`` keywords and ``for`` and ``if`` within a + generate block. + * - AST_POSEDGE, AST_NEGEDGE, AST_EDGE + - Event conditions for ``always`` blocks. + +- | The node type + | This enum (``AST::AstNodeType``) specifies the role of the node. + :numref:`Table %s ` contains a list of all node + types. + +- | The child nodes + | This is a list of pointers to all children in the abstract syntax tree. + +- | Attributes + | As almost every AST node might have Verilog attributes assigned to it, the + ``AST::AstNode`` has direct support for attributes. Note that the attribute + values are again AST nodes. + +- | Node content + | Each node might have additional content data. A series of member variables + exist to hold such data. For example the member ``std::string str`` can + hold a string value and is used e.g. in the ``AST_IDENTIFIER`` node type to + store the identifier name. + +- | Source code location + | Each ``AST::AstNode`` is automatically annotated with the current source + code location by the ``AST::AstNode`` constructor. It is stored in the + ``std::string filename`` and ``int linenum`` member variables. + +The ``AST::AstNode`` constructor can be called with up to two child nodes that +are automatically added to the list of child nodes for the new object. This +simplifies the creation of AST nodes for simple expressions a bit. For example +the bison code for parsing multiplications: + +.. code:: none + :number-lines: + + basic_expr '*' attr basic_expr { + $$ = new AstNode(AST_MUL, $1, $4); + append_attr($$, $3); + } | + +The generated AST data structure is then passed directly to the AST frontend +that performs the actual conversion to RTLIL. + +Note that the Yosys command ``read_verilog`` provides the options ``-yydebug`` +and ``-dump_ast`` that can be used to print the parse tree or abstract syntax +tree respectively. + +Transforming AST to RTLIL +------------------------- + +The AST Frontend converts a set of modules in AST representation to modules in +RTLIL representation and adds them to the current design. This is done in two +steps: simplification and RTLIL generation. + +The source code to the AST frontend can be found in ``frontends/ast/`` in the +Yosys source tree. + +AST simplification +~~~~~~~~~~~~~~~~~~ + +A full-featured AST is too complex to be transformed into RTLIL directly. +Therefore it must first be brought into a simpler form. This is done by calling +the ``AST::AstNode::simplify()`` method of all ``AST_MODULE`` nodes in the AST. +This initiates a recursive process that performs the following transformations +on the AST data structure: + +- Inline all task and function calls. + +- Evaluate all ``generate``-statements and unroll all ``for``-loops. + +- Perform const folding where it is necessary (e.g. in the value part of + ``AST_PARAMETER``, ``AST_LOCALPARAM``, ``AST_PARASET`` and ``AST_RANGE`` + nodes). + +- Replace ``AST_PRIMITIVE`` nodes with appropriate ``AST_ASSIGN`` nodes. + +- Replace dynamic bit ranges in the left-hand-side of assignments with + ``AST_CASE`` nodes with ``AST_COND`` children for each possible case. + +- Detect array access patterns that are too complicated for the + ``RTLIL::Memory`` abstraction and replace them with a set of signals and + cases for all reads and/or writes. + +- Otherwise replace array accesses with ``AST_MEMRD`` and ``AST_MEMWR`` nodes. + +In addition to these transformations, the simplifier also annotates the +AST with additional information that is needed for the RTLIL generator, +namely: + +- All ranges (width of signals and bit selections) are not only const + folded but (when a constant value is found) are also written to + member variables in the AST_RANGE node. + +- All identifiers are resolved and all ``AST_IDENTIFIER`` nodes are annotated + with a pointer to the AST node that contains the declaration of the + identifier. If no declaration has been found, an ``AST_AUTOWIRE`` node is + created and used for the annotation. + +This produces an AST that is fairly easy to convert to the RTLIL format. + +Generating RTLIL +~~~~~~~~~~~~~~~~ + +After AST simplification, the ``AST::AstNode::genRTLIL()`` method of each +``AST_MODULE`` node in the AST is called. This initiates a recursive process +that generates equivalent RTLIL data for the AST data. + +The ``AST::AstNode::genRTLIL()`` method returns an ``RTLIL::SigSpec`` structure. +For nodes that represent expressions (operators, constants, signals, etc.), the +cells needed to implement the calculation described by the expression are +created and the resulting signal is returned. That way it is easy to generate +the circuits for large expressions using depth-first recursion. For nodes that +do not represent an expression (such as ``AST_CELL``), the corresponding circuit +is generated and an empty ``RTLIL::SigSpec`` is returned. + +Synthesizing Verilog always blocks +-------------------------------------- + +For behavioural Verilog code (code utilizing ``always``- and ``initial``-blocks) +it is necessary to also generate ``RTLIL::Process`` objects. This is done in the +following way: + +Whenever ``AST::AstNode::genRTLIL()`` encounters an ``always``- or +``initial``-block, it creates an instance of ``AST_INTERNAL::ProcessGenerator``. +This object then generates the ``RTLIL::Process`` object for the block. It also +calls ``AST::AstNode::genRTLIL()`` for all right-hand-side expressions contained +within the block. + +First the ``AST_INTERNAL::ProcessGenerator`` creates a list of all signals +assigned within the block. It then creates a set of temporary signals using the +naming scheme ``$ \ `` for each of the assigned signals. + +Then an ``RTLIL::Process`` is created that assigns all intermediate values for +each left-hand-side signal to the temporary signal in its +``RTLIL::CaseRule``/``RTLIL::SwitchRule`` tree. + +Finally a ``RTLIL::SyncRule`` is created for the ``RTLIL::Process`` that assigns +the temporary signals for the final values to the actual signals. + +A process may also contain memory writes. A ``RTLIL::MemWriteAction`` is created +for each of them. + +Calls to ``AST::AstNode::genRTLIL()`` are generated for right hand sides as +needed. When blocking assignments are used, ``AST::AstNode::genRTLIL()`` is +configured using global variables to use the temporary signals that hold the +correct intermediate values whenever one of the previously assigned signals is +used in an expression. + +Unfortunately the generation of a correct +``RTLIL::CaseRule``/\ ``RTLIL::SwitchRule`` tree for behavioural code is a +non-trivial task. The AST frontend solves the problem using the approach +described on the following pages. The following example illustrates what the +algorithm is supposed to do. Consider the following Verilog code: + +.. code:: verilog + :number-lines: + + always @(posedge clock) begin + out1 = in1; + if (in2) + out1 = !out1; + out2 <= out1; + if (in3) + out2 <= out2; + if (in4) + if (in5) + out3 <= in6; + else + out3 <= in7; + out1 = out1 ^ out2; + end + +This is translated by the Verilog and AST frontends into the following RTLIL +code (attributes, cell parameters and wire declarations not included): + +.. code:: RTLIL + :number-lines: + + cell $logic_not $logic_not$:4$2 + connect \A \in1 + connect \Y $logic_not$:4$2_Y + end + cell $xor $xor$:13$3 + connect \A $1\out1[0:0] + connect \B \out2 + connect \Y $xor$:13$3_Y + end + process $proc$:1$1 + assign $0\out3[0:0] \out3 + assign $0\out2[0:0] $1\out1[0:0] + assign $0\out1[0:0] $xor$:13$3_Y + switch \in2 + case 1'1 + assign $1\out1[0:0] $logic_not$:4$2_Y + case + assign $1\out1[0:0] \in1 + end + switch \in3 + case 1'1 + assign $0\out2[0:0] \out2 + case + end + switch \in4 + case 1'1 + switch \in5 + case 1'1 + assign $0\out3[0:0] \in6 + case + assign $0\out3[0:0] \in7 + end + case + end + sync posedge \clock + update \out1 $0\out1[0:0] + update \out2 $0\out2[0:0] + update \out3 $0\out3[0:0] + end + +Note that the two operators are translated into separate cells outside the +generated process. The signal ``out1`` is assigned using blocking assignments +and therefore ``out1`` has been replaced with a different signal in all +expressions after the initial assignment. The signal ``out2`` is assigned using +nonblocking assignments and therefore is not substituted on the right-hand-side +expressions. + +The ``RTLIL::CaseRule``/\ ``RTLIL::SwitchRule`` tree must be interpreted the +following way: + +- On each case level (the body of the process is the root case), first the + actions on this level are evaluated and then the switches within the case are + evaluated. (Note that the last assignment on line 13 of the Verilog code has + been moved to the beginning of the RTLIL process to line 13 of the RTLIL + listing.) + + I.e. the special cases deeper in the switch hierarchy override the defaults + on the upper levels. The assignments in lines 12 and 22 of the RTLIL code + serve as an example for this. + + Note that in contrast to this, the order within the ``RTLIL::SwitchRule`` + objects within a ``RTLIL::CaseRule`` is preserved with respect to the + original AST and Verilog code. + +- The whole ``RTLIL::CaseRule``/\ ``RTLIL::SwitchRule`` tree describes an + asynchronous circuit. I.e. the decision tree formed by the switches can be + seen independently for each assigned signal. Whenever one assigned signal + changes, all signals that depend on the changed signals are to be updated. + For example the assignments in lines 16 and 18 in the RTLIL code in fact + influence the assignment in line 12, even though they are in the "wrong + order". + +The only synchronous part of the process is in the ``RTLIL::SyncRule`` object +generated at line 35 in the RTLIL code. The sync rule is the only part of the +process where the original signals are assigned. The synchronization event from +the original Verilog code has been translated into the synchronization type +(posedge) and signal (``\clock``) for the ``RTLIL::SyncRule`` object. In the +case of this simple example the ``RTLIL::SyncRule`` object is later simply +transformed into a set of d-type flip-flops and the +``RTLIL::CaseRule``/\ ``RTLIL::SwitchRule`` tree to a decision tree using +multiplexers. + +In more complex examples (e.g. asynchronous resets) the part of the +``RTLIL::CaseRule``/\ ``RTLIL::SwitchRule`` tree that describes the asynchronous +reset must first be transformed to the correct ``RTLIL::SyncRule`` objects. This +is done by the ``proc_arst`` pass. + +The ProcessGenerator algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``AST_INTERNAL::ProcessGenerator`` uses the following internal state +variables: + +- | ``subst_rvalue_from`` and ``subst_rvalue_to`` + | These two variables hold the replacement pattern that should be used by + ``AST::AstNode::genRTLIL()`` for signals with blocking assignments. After + initialization of ``AST_INTERNAL::ProcessGenerator`` these two variables are + empty. + +- | ``subst_lvalue_from`` and ``subst_lvalue_to`` + | These two variables contain the mapping from left-hand-side signals (``\ + ``) to the current temporary signal for the same thing (initially + ``$0\ ``). + +- | ``current_case`` + | A pointer to a ``RTLIL::CaseRule`` object. Initially this is the root case + of the generated ``RTLIL::Process``. + +As the algorithm runs these variables are continuously modified as well as +pushed to the stack and later restored to their earlier values by popping from +the stack. + +On startup the ProcessGenerator generates a new ``RTLIL::Process`` object with +an empty root case and initializes its state variables as described above. Then +the ``RTLIL::SyncRule`` objects are created using the synchronization events +from the AST_ALWAYS node and the initial values of ``subst_lvalue_from`` and +``subst_lvalue_to``. Then the AST for this process is evaluated recursively. + +During this recursive evaluation, three different relevant types of AST nodes +can be discovered: ``AST_ASSIGN_LE`` (nonblocking assignments), +``AST_ASSIGN_EQ`` (blocking assignments) and ``AST_CASE`` (``if`` or ``case`` +statement). + +Handling of nonblocking assignments +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When an ``AST_ASSIGN_LE`` node is discovered, the following actions are +performed by the ProcessGenerator: + +- The left-hand-side is evaluated using ``AST::AstNode::genRTLIL()`` and mapped + to a temporary signal name using ``subst_lvalue_from`` and + ``subst_lvalue_to``. + +- The right-hand-side is evaluated using ``AST::AstNode::genRTLIL()``. For this + call, the values of ``subst_rvalue_from`` and ``subst_rvalue_to`` are used to + map blocking-assigned signals correctly. + +- Remove all assignments to the same left-hand-side as this assignment from the + ``current_case`` and all cases within it. + +- Add the new assignment to the ``current_case``. + +Handling of blocking assignments +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When an ``AST_ASSIGN_EQ`` node is discovered, the following actions are +performed by the ProcessGenerator: + +- Perform all the steps that would be performed for a nonblocking assignment + (see above). + +- Remove the found left-hand-side (before lvalue mapping) from + ``subst_rvalue_from`` and also remove the respective bits from + ``subst_rvalue_to``. + +- Append the found left-hand-side (before lvalue mapping) to + ``subst_rvalue_from`` and append the found right-hand-side to + ``subst_rvalue_to``. + +Handling of cases and if-statements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When an ``AST_CASE`` node is discovered, the following actions are performed by +the ProcessGenerator: + +- The values of ``subst_rvalue_from``, ``subst_rvalue_to``, + ``subst_lvalue_from`` and ``subst_lvalue_to`` are pushed to the stack. + +- A new ``RTLIL::SwitchRule`` object is generated, the selection expression is + evaluated using ``AST::AstNode::genRTLIL()`` (with the use of + ``subst_rvalue_from`` and ``subst_rvalue_to``) and added to the + ``RTLIL::SwitchRule`` object and the object is added to the ``current_case``. + +- All lvalues assigned to within the ``AST_CASE`` node using blocking + assignments are collected and saved in the local variable + ``this_case_eq_lvalue``. + +- New temporary signals are generated for all signals in + ``this_case_eq_lvalue`` and stored in ``this_case_eq_ltemp``. + +- The signals in ``this_case_eq_lvalue`` are mapped using ``subst_rvalue_from`` + and ``subst_rvalue_to`` and the resulting set of signals is stored in + ``this_case_eq_rvalue``. + +Then the following steps are performed for each ``AST_COND`` node within the +``AST_CASE`` node: + +- Set ``subst_rvalue_from``, ``subst_rvalue_to``, ``subst_lvalue_from`` and + ``subst_lvalue_to`` to the values that have been pushed to the stack. + +- Remove ``this_case_eq_lvalue`` from + ``subst_lvalue_from``/``subst_lvalue_to``. + +- Append ``this_case_eq_lvalue`` to ``subst_lvalue_from`` and append + ``this_case_eq_ltemp`` to ``subst_lvalue_to``. + +- Push the value of ``current_case``. + +- Create a new ``RTLIL::CaseRule``. Set ``current_case`` to the new object and + add the new object to the ``RTLIL::SwitchRule`` created above. + +- Add an assignment from ``this_case_eq_rvalue`` to ``this_case_eq_ltemp`` to + the new ``current_case``. + +- Evaluate the compare value for this case using + ``AST::AstNode::genRTLIL()`` (with the use of ``subst_rvalue_from`` + and ``subst_rvalue_to``) modify the new ``current_case`` accordingly. + +- Recursion into the children of the ``AST_COND`` node. + +- Restore ``current_case`` by popping the old value from the stack. + +Finally the following steps are performed: + +- The values of ``subst_rvalue_from``, ``subst_rvalue_to``, + ``subst_lvalue_from`` and ``subst_lvalue_to`` are popped from the stack. + +- The signals from ``this_case_eq_lvalue`` are removed from the + ``subst_rvalue_from``/``subst_rvalue_to``-pair. + +- The value of ``this_case_eq_lvalue`` is appended to ``subst_rvalue_from`` and + the value of ``this_case_eq_ltemp`` is appended to ``subst_rvalue_to``. + +- Map the signals in ``this_case_eq_lvalue`` using + ``subst_lvalue_from``/``subst_lvalue_to``. + +- Remove all assignments to signals in ``this_case_eq_lvalue`` in + ``current_case`` and all cases within it. + +- Add an assignment from ``this_case_eq_ltemp`` to ``this_case_eq_lvalue`` to + ``current_case``. + +Further analysis of the algorithm for cases and if-statements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +With respect to nonblocking assignments the algorithm is easy: later assignments +invalidate earlier assignments. For each signal assigned using nonblocking +assignments exactly one temporary variable is generated (with the ``$0``-prefix) +and this variable is used for all assignments of the variable. + +Note how all the ``_eq_``-variables become empty when no blocking assignments +are used and many of the steps in the algorithm can then be ignored as a result +of this. + +For a variable with blocking assignments the algorithm shows the following +behaviour: First a new temporary variable is created. This new temporary +variable is then registered as the assignment target for all assignments for +this variable within the cases for this ``AST_CASE`` node. Then for each case +the new temporary variable is first assigned the old temporary variable. This +assignment is overwritten if the variable is actually assigned in this case and +is kept as a default value otherwise. + +This yields an ``RTLIL::CaseRule`` that assigns the new temporary variable in +all branches. So when all cases have been processed a final assignment is added +to the containing block that assigns the new temporary variable to the old one. +Note how this step always overrides a previous assignment to the old temporary +variable. Other than nonblocking assignments, the old assignment could still +have an effect somewhere in the design, as there have been calls to +``AST::AstNode::genRTLIL()`` with a +``subst_rvalue_from``/\ ``subst_rvalue_to``-tuple that contained the +right-hand-side of the old assignment. + +The proc pass +~~~~~~~~~~~~~ + +The ProcessGenerator converts a behavioural model in AST representation to a +behavioural model in ``RTLIL::Process`` representation. The actual conversion +from a behavioural model to an RTL representation is performed by the +:cmd:ref:`proc` pass and the passes it launches: + +- | :cmd:ref:`proc_clean` and :cmd:ref:`proc_rmdead` + | These two passes just clean up the ``RTLIL::Process`` structure. The + :cmd:ref:`proc_clean` pass removes empty parts (eg. empty assignments) from + the process and :cmd:ref:`proc_rmdead` detects and removes unreachable + branches from the process's decision trees. + +- | :cmd:ref:`proc_arst` + | This pass detects processes that describe d-type flip-flops with + asynchronous resets and rewrites the process to better reflect what they + are modelling: Before this pass, an asynchronous reset has two + edge-sensitive sync rules and one top-level ``RTLIL::SwitchRule`` for the + reset path. After this pass the sync rule for the reset is level-sensitive + and the top-level ``RTLIL::SwitchRule`` has been removed. + +- | :cmd:ref:`proc_mux` + | This pass converts the ``RTLIL::CaseRule``/\ ``RTLIL::SwitchRule``-tree to a + tree of multiplexers per written signal. After this, the ``RTLIL::Process`` + structure only contains the ``RTLIL::SyncRule`` s that describe the output + registers. + +- | :cmd:ref:`proc_dff` + | This pass replaces the ``RTLIL::SyncRule``\ s to d-type flip-flops (with + asynchronous resets if necessary). + +- | :cmd:ref:`proc_dff` + | This pass replaces the ``RTLIL::MemWriteAction``\ s with ``$memwr`` cells. + +- | :cmd:ref:`proc_clean` + | A final call to :cmd:ref:`proc_clean` removes the now empty + ``RTLIL::Process`` objects. + +Performing these last processing steps in passes instead of in the Verilog +frontend has two important benefits: + +First it improves the transparency of the process. Everything that happens in a +separate pass is easier to debug, as the RTLIL data structures can be easily +investigated before and after each of the steps. + +Second it improves flexibility. This scheme can easily be extended to support +other types of storage-elements, such as sr-latches or d-latches, without having +to extend the actual Verilog frontend. + +.. todo:: Synthesizing Verilog arrays + + Add some information on the generation of ``$memrd`` and ``$memwr`` cells and + how they are processed in the memory pass. + + +.. todo:: Synthesizing parametric designs + + Add some information on the ``RTLIL::Module::derive()`` method and how it is + used to synthesize parametric modules via the hierarchy pass. diff --git a/docs/source/yosys_internals/formats/cell_library.rst b/docs/source/yosys_internals/formats/cell_library.rst new file mode 100644 index 00000000000..c80b0740255 --- /dev/null +++ b/docs/source/yosys_internals/formats/cell_library.rst @@ -0,0 +1,1155 @@ +.. role:: verilog(code) + :language: Verilog + +.. _chapter:celllib: + +Internal cell library +===================== + +.. todo:: less academic, also check formatting consistency + +Most of the passes in Yosys operate on netlists, i.e. they only care about the +``RTLIL::Wire`` and ``RTLIL::Cell`` objects in an ``RTLIL::Module``. This +chapter discusses the cell types used by Yosys to represent a behavioural design +internally. + +.. TODO:: is this chapter split preserved + +This chapter is split in two parts. In the first part the internal RTL cells are +covered. These cells are used to represent the design on a coarse grain level. +Like in the original HDL code on this level the cells operate on vectors of +signals and complex cells like adders exist. In the second part the internal +gate cells are covered. These cells are used to represent the design on a +fine-grain gate-level. All cells from this category operate on single bit +signals. + +RTL cells +--------- + +Most of the RTL cells closely resemble the operators available in HDLs such as +Verilog or VHDL. Therefore Verilog operators are used in the following sections +to define the behaviour of the RTL cells. + +Note that all RTL cells have parameters indicating the size of inputs and +outputs. When passes modify RTL cells they must always keep the values of these +parameters in sync with the size of the signals connected to the inputs and +outputs. + +Simulation models for the RTL cells can be found in the file +:file:`techlibs/common/simlib.v` in the Yosys source tree. + +Unary operators +~~~~~~~~~~~~~~~ + +All unary RTL cells have one input port ``\A`` and one output port ``\Y``. They +also have the following parameters: + +``\A_SIGNED`` + Set to a non-zero value if the input ``\A`` is signed and therefore should be + sign-extended when needed. + +``\A_WIDTH`` + The width of the input port ``\A``. + +``\Y_WIDTH`` + The width of the output port ``\Y``. + +:numref:`tab:CellLib_unary` lists all cells for unary RTL operators. + +.. table:: Cell types for unary operators with their corresponding Verilog expressions. + :name: tab:CellLib_unary + + ================== ============ + Verilog Cell Type + ================== ============ + :verilog:`Y = ~A` $not + :verilog:`Y = +A` $pos + :verilog:`Y = -A` $neg + :verilog:`Y = &A` $reduce_and + :verilog:`Y = |A` $reduce_or + :verilog:`Y = ^A` $reduce_xor + :verilog:`Y = ~^A` $reduce_xnor + :verilog:`Y = |A` $reduce_bool + :verilog:`Y = !A` $logic_not + ================== ============ + +For the unary cells that output a logical value (``$reduce_and``, +``$reduce_or``, ``$reduce_xor``, ``$reduce_xnor``, ``$reduce_bool``, +``$logic_not``), when the ``\Y_WIDTH`` parameter is greater than 1, the output +is zero-extended, and only the least significant bit varies. + +Note that ``$reduce_or`` and ``$reduce_bool`` actually represent the same logic +function. But the HDL frontends generate them in different situations. A +``$reduce_or`` cell is generated when the prefix ``|`` operator is being used. A +``$reduce_bool`` cell is generated when a bit vector is used as a condition in +an ``if``-statement or ``?:``-expression. + +Binary operators +~~~~~~~~~~~~~~~~ + +All binary RTL cells have two input ports ``\A`` and ``\B`` and one output port +``\Y``. They also have the following parameters: + +``\A_SIGNED`` + Set to a non-zero value if the input ``\A`` is signed and therefore + should be sign-extended when needed. + +``\A_WIDTH`` + The width of the input port ``\A``. + +``\B_SIGNED`` + Set to a non-zero value if the input ``\B`` is signed and therefore + should be sign-extended when needed. + +``\B_WIDTH`` + The width of the input port ``\B``. + +``\Y_WIDTH`` + The width of the output port ``\Y``. + +:numref:`tab:CellLib_binary` lists all cells for binary RTL operators. + +.. table:: Cell types for binary operators with their corresponding Verilog expressions. + :name: tab:CellLib_binary + + ======================= ============= ======================= ========= + Verilog Cell Type Verilog Cell Type + ======================= ============= ======================= ========= + :verilog:`Y = A & B` $and :verilog:`Y = A < B` $lt + :verilog:`Y = A | B` $or :verilog:`Y = A <= B` $le + :verilog:`Y = A ^ B` $xor :verilog:`Y = A == B` $eq + :verilog:`Y = A ~^ B` $xnor :verilog:`Y = A != B` $ne + :verilog:`Y = A << B` $shl :verilog:`Y = A >= B` $ge + :verilog:`Y = A >> B` $shr :verilog:`Y = A > B` $gt + :verilog:`Y = A <<< B` $sshl :verilog:`Y = A + B` $add + :verilog:`Y = A >>> B` $sshr :verilog:`Y = A - B` $sub + :verilog:`Y = A && B` $logic_and :verilog:`Y = A * B` $mul + :verilog:`Y = A || B` $logic_or :verilog:`Y = A / B` $div + :verilog:`Y = A === B` $eqx :verilog:`Y = A % B` $mod + :verilog:`Y = A !== B` $nex ``N/A`` $divfloor + :verilog:`Y = A ** B` $pow ``N/A`` $modfloor + ======================= ============= ======================= ========= + +The ``$shl`` and ``$shr`` cells implement logical shifts, whereas the ``$sshl`` +and ``$sshr`` cells implement arithmetic shifts. The ``$shl`` and ``$sshl`` +cells implement the same operation. All four of these cells interpret the second +operand as unsigned, and require ``\B_SIGNED`` to be zero. + +Two additional shift operator cells are available that do not directly +correspond to any operator in Verilog, ``$shift`` and ``$shiftx``. The +``$shift`` cell performs a right logical shift if the second operand is positive +(or unsigned), and a left logical shift if it is negative. The ``$shiftx`` cell +performs the same operation as the ``$shift`` cell, but the vacated bit +positions are filled with undef (x) bits, and corresponds to the Verilog indexed +part-select expression. + +For the binary cells that output a logical value (``$logic_and``, ``$logic_or``, +``$eqx``, ``$nex``, ``$lt``, ``$le``, ``$eq``, ``$ne``, ``$ge``, ``$gt``), when +the ``\Y_WIDTH`` parameter is greater than 1, the output is zero-extended, and +only the least significant bit varies. + +Division and modulo cells are available in two rounding modes. The original +``$div`` and ``$mod`` cells are based on truncating division, and correspond to +the semantics of the verilog ``/`` and ``%`` operators. The ``$divfloor`` and +``$modfloor`` cells represent flooring division and flooring modulo, the latter +of which is also known as "remainder" in several languages. See +:numref:`tab:CellLib_divmod` for a side-by-side comparison between the different +semantics. + +.. table:: Comparison between different rounding modes for division and modulo cells. + :name: tab:CellLib_divmod + + +-----------+--------+-----------+-----------+-----------+-----------+ + | Division | Result | Truncating | Flooring | + +-----------+--------+-----------+-----------+-----------+-----------+ + | | | $div | $mod | $divfloor | $modfloor | + +===========+========+===========+===========+===========+===========+ + | -10 / 3 | -3.3 | -3 | -1 | -4 | 2 | + +-----------+--------+-----------+-----------+-----------+-----------+ + | 10 / -3 | -3.3 | -3 | 1 | -4 | -2 | + +-----------+--------+-----------+-----------+-----------+-----------+ + | -10 / -3 | 3.3 | 3 | -1 | 3 | -1 | + +-----------+--------+-----------+-----------+-----------+-----------+ + | 10 / 3 | 3.3 | 3 | 1 | 3 | 1 | + +-----------+--------+-----------+-----------+-----------+-----------+ + +Multiplexers +~~~~~~~~~~~~ + +Multiplexers are generated by the Verilog HDL frontend for ``?:``-expressions. +Multiplexers are also generated by the proc pass to map the decision trees from +RTLIL::Process objects to logic. + +The simplest multiplexer cell type is ``$mux``. Cells of this type have a +``\WITDH`` parameter and data inputs ``\A`` and ``\B`` and a data output ``\Y``, +all of the specified width. This cell also has a single bit control input +``\S``. If ``\S`` is 0 the value from the input ``\A`` is sent to the output, if +it is 1 the value from the ``\B`` input is sent to the output. So the ``$mux`` +cell implements the function :verilog:`Y = S ? B : A`. + +The ``$pmux`` cell is used to multiplex between many inputs using a one-hot +select signal. Cells of this type have a ``\WIDTH`` and a ``\S_WIDTH`` parameter +and inputs ``\A``, ``\B``, and ``\S`` and an output ``\Y``. The ``\S`` input is +``\S_WIDTH`` bits wide. The ``\A`` input and the output are both ``\WIDTH`` bits +wide and the ``\B`` input is ``\WIDTH*\S_WIDTH`` bits wide. When all bits of +``\S`` are zero, the value from ``\A`` input is sent to the output. If the +:math:`n`\ 'th bit from ``\S`` is set, the value :math:`n`\ 'th ``\WIDTH`` bits +wide slice of the ``\B`` input is sent to the output. When more than one bit +from ``\S`` is set the output is undefined. Cells of this type are used to model +"parallel cases" (defined by using the ``parallel_case`` attribute or detected +by an optimization). + +The ``$tribuf`` cell is used to implement tristate logic. Cells of this type +have a ``\WIDTH`` parameter and inputs ``\A`` and ``\EN`` and an output ``\Y``. The +``\A`` input and ``\Y`` output are ``\WIDTH`` bits wide, and the ``\EN`` input +is one bit wide. When ``\EN`` is 0, the output is not driven. When ``\EN`` is 1, +the value from ``\A`` input is sent to the ``\Y`` output. Therefore, the +``$tribuf`` cell implements the function :verilog:`Y = EN ? A : 'bz`. + +Behavioural code with cascaded if-then-else- and case-statements usually results +in trees of multiplexer cells. Many passes (from various optimizations to FSM +extraction) heavily depend on these multiplexer trees to understand dependencies +between signals. Therefore optimizations should not break these multiplexer +trees (e.g. by replacing a multiplexer between a calculated signal and a +constant zero with an ``$and`` gate). + +Registers +~~~~~~~~~ + +SR-type latches are represented by ``$sr`` cells. These cells have input ports +``\SET`` and ``\CLR`` and an output port ``\Q``. They have the following +parameters: + +``\WIDTH`` + The width of inputs ``\SET`` and ``\CLR`` and output ``\Q``. + +``\SET_POLARITY`` + The set input bits are active-high if this parameter has the value + ``1'b1`` and active-low if this parameter is ``1'b0``. + +``\CLR_POLARITY`` + The reset input bits are active-high if this parameter has the value + ``1'b1`` and active-low if this parameter is ``1'b0``. + +Both set and reset inputs have separate bits for every output bit. When both the +set and reset inputs of an ``$sr`` cell are active for a given bit index, the +reset input takes precedence. + +D-type flip-flops are represented by ``$dff`` cells. These cells have a clock +port ``\CLK``, an input port ``\D`` and an output port ``\Q``. The following +parameters are available for ``$dff`` cells: + +``\WIDTH`` + The width of input ``\D`` and output ``\Q``. + +``\CLK_POLARITY`` + Clock is active on the positive edge if this parameter has the value + ``1'b1`` and on the negative edge if this parameter is ``1'b0``. + +D-type flip-flops with asynchronous reset are represented by ``$adff`` cells. As +the ``$dff`` cells they have ``\CLK``, ``\D`` and ``\Q`` ports. In addition they +also have a single-bit ``\ARST`` input port for the reset pin and the following +additional two parameters: + +``\ARST_POLARITY`` + The asynchronous reset is active-high if this parameter has the value + ``1'b1`` and active-low if this parameter is ``1'b0``. + +``\ARST_VALUE`` + The state of ``\Q`` will be set to this value when the reset is active. + +Usually these cells are generated by the :cmd:ref:`proc` pass using the +information in the designs RTLIL::Process objects. + +D-type flip-flops with synchronous reset are represented by ``$sdff`` cells. As +the ``$dff`` cells they have ``\CLK``, ``\D`` and ``\Q`` ports. In addition they +also have a single-bit ``\SRST`` input port for the reset pin and the following +additional two parameters: + +``\SRST_POLARITY`` + The synchronous reset is active-high if this parameter has the value + ``1'b1`` and active-low if this parameter is ``1'b0``. + +``\SRST_VALUE`` + The state of ``\Q`` will be set to this value when the reset is active. + +Note that the ``$adff`` and ``$sdff`` cells can only be used when the reset +value is constant. + +D-type flip-flops with asynchronous load are represented by ``$aldff`` cells. As +the ``$dff`` cells they have ``\CLK``, ``\D`` and ``\Q`` ports. In addition they +also have a single-bit ``\ALOAD`` input port for the async load enable pin, a +``\AD`` input port with the same width as data for the async load data, and the +following additional parameter: + +``\ALOAD_POLARITY`` + The asynchronous load is active-high if this parameter has the value + ``1'b1`` and active-low if this parameter is ``1'b0``. + +D-type flip-flops with asynchronous set and reset are represented by ``$dffsr`` +cells. As the ``$dff`` cells they have ``\CLK``, ``\D`` and ``\Q`` ports. In +addition they also have multi-bit ``\SET`` and ``\CLR`` input ports and the +corresponding polarity parameters, like ``$sr`` cells. + +D-type flip-flops with enable are represented by ``$dffe``, ``$adffe``, +``$aldffe``, ``$dffsre``, ``$sdffe``, and ``$sdffce`` cells, which are enhanced +variants of ``$dff``, ``$adff``, ``$aldff``, ``$dffsr``, ``$sdff`` (with reset +over enable) and ``$sdff`` (with enable over reset) cells, respectively. They +have the same ports and parameters as their base cell. In addition they also +have a single-bit ``\EN`` input port for the enable pin and the following +parameter: + +``\EN_POLARITY`` + The enable input is active-high if this parameter has the value ``1'b1`` + and active-low if this parameter is ``1'b0``. + +D-type latches are represented by ``$dlatch`` cells. These cells have an enable +port ``\EN``, an input port ``\D``, and an output port ``\Q``. The following +parameters are available for ``$dlatch`` cells: + +``\WIDTH`` + The width of input ``\D`` and output ``\Q``. + +``\EN_POLARITY`` + The enable input is active-high if this parameter has the value ``1'b1`` + and active-low if this parameter is ``1'b0``. + +The latch is transparent when the ``\EN`` input is active. + +D-type latches with reset are represented by ``$adlatch`` cells. In addition to +``$dlatch`` ports and parameters, they also have a single-bit ``\ARST`` input +port for the reset pin and the following additional parameters: + +``\ARST_POLARITY`` + The asynchronous reset is active-high if this parameter has the value + ``1'b1`` and active-low if this parameter is ``1'b0``. + +``\ARST_VALUE`` + The state of ``\Q`` will be set to this value when the reset is active. + +D-type latches with set and reset are represented by ``$dlatchsr`` cells. In +addition to ``$dlatch`` ports and parameters, they also have multi-bit ``\SET`` +and ``\CLR`` input ports and the corresponding polarity parameters, like ``$sr`` +cells. + +.. _sec:memcells: + +Memories +~~~~~~~~ + +Memories are either represented using ``RTLIL::Memory`` objects, ``$memrd_v2``, +``$memwr_v2``, and ``$meminit_v2`` cells, or by ``$mem_v2`` cells alone. + +In the first alternative the ``RTLIL::Memory`` objects hold the general metadata +for the memory (bit width, size in number of words, etc.) and for each port a +``$memrd_v2`` (read port) or ``$memwr_v2`` (write port) cell is created. Having +individual cells for read and write ports has the advantage that they can be +consolidated using resource sharing passes. In some cases this drastically +reduces the number of required ports on the memory cell. In this alternative, +memory initialization data is represented by ``$meminit_v2`` cells, which allow +delaying constant folding for initialization addresses and data until after the +frontend finishes. + +The ``$memrd_v2`` cells have a clock input ``\CLK``, an enable input ``\EN``, an +address input ``\ADDR``, a data output ``\DATA``, an asynchronous reset input +``\ARST``, and a synchronous reset input ``\SRST``. They also have the following +parameters: + +``\MEMID`` + The name of the ``RTLIL::Memory`` object that is associated with this read + port. + +``\ABITS`` + The number of address bits (width of the ``\ADDR`` input port). + +``\WIDTH`` + The number of data bits (width of the ``\DATA`` output port). Note that + this may be a power-of-two multiple of the underlying memory's width -- + such ports are called wide ports and access an aligned group of cells at + once. In this case, the corresponding low bits of ``\ADDR`` must be + tied to 0. + +``\CLK_ENABLE`` + When this parameter is non-zero, the clock is used. Otherwise this read + port is asynchronous and the ``\CLK`` input is not used. + +``\CLK_POLARITY`` + Clock is active on the positive edge if this parameter has the value + ``1'b1`` and on the negative edge if this parameter is ``1'b0``. + +``\TRANSPARENCY_MASK`` + This parameter is a bitmask of write ports that this read port is + transparent with. The bits of this parameter are indexed by the write + port's ``\PORTID`` parameter. Transparency can only be enabled between + synchronous ports sharing a clock domain. When transparency is enabled + for a given port pair, a read and write to the same address in the same + cycle will return the new value. Otherwise the old value is returned. + +``\COLLISION_X_MASK`` + This parameter is a bitmask of write ports that have undefined collision + behavior with this port. The bits of this parameter are indexed by the + write port's ``\PORTID`` parameter. This behavior can only be enabled + between synchronous ports sharing a clock domain. When undefined + collision is enabled for a given port pair, a read and write to the same + address in the same cycle will return the undefined (all-X) value.This + option is exclusive (for a given port pair) with the transparency + option. + +``\ARST_VALUE`` + Whenever the ``\ARST`` input is asserted, the data output will be reset + to this value. Only used for synchronous ports. + +``\SRST_VALUE`` + Whenever the ``\SRST`` input is synchronously asserted, the data output + will be reset to this value. Only used for synchronous ports. + +``\INIT_VALUE`` + The initial value of the data output, for synchronous ports. + +``\CE_OVER_SRST`` + If this parameter is non-zero, the ``\SRST`` input is only recognized + when ``\EN`` is true. Otherwise, ``\SRST`` is recognized regardless of + ``\EN``. + +The ``$memwr_v2`` cells have a clock input ``\CLK``, an enable input ``\EN`` +(one enable bit for each data bit), an address input ``\ADDR`` and a data input +``\DATA``. They also have the following parameters: + +``\MEMID`` + The name of the ``RTLIL::Memory`` object that is associated with this write + port. + +``\ABITS`` + The number of address bits (width of the ``\ADDR`` input port). + +``\WIDTH`` + The number of data bits (width of the ``\DATA`` output port). Like with + ``$memrd_v2`` cells, the width is allowed to be any power-of-two + multiple of memory width, with the corresponding restriction on address. + +``\CLK_ENABLE`` + When this parameter is non-zero, the clock is used. Otherwise this write + port is asynchronous and the ``\CLK`` input is not used. + +``\CLK_POLARITY`` + Clock is active on positive edge if this parameter has the value + ``1'b1`` and on the negative edge if this parameter is ``1'b0``. + +``\PORTID`` + An identifier for this write port, used to index write port bit mask + parameters. + +``\PRIORITY_MASK`` + This parameter is a bitmask of write ports that this write port has priority + over in case of writing to the same address. The bits of this parameter are + indexed by the other write port's ``\PORTID`` parameter. Write ports can + only have priority over write ports with lower port ID. When two ports write + to the same address and neither has priority over the other, the result is + undefined. Priority can only be set between two synchronous ports sharing + the same clock domain. + +The ``$meminit_v2`` cells have an address input ``\ADDR``, a data input +``\DATA``, with the width of the ``\DATA`` port equal to ``\WIDTH`` parameter +times ``\WORDS`` parameter, and a bit enable mask input ``\EN`` with width equal +to ``\WIDTH`` parameter. All three of the inputs must resolve to a constant for +synthesis to succeed. + +``\MEMID`` + The name of the ``RTLIL::Memory`` object that is associated with this + initialization cell. + +``\ABITS`` + The number of address bits (width of the ``\ADDR`` input port). + +``\WIDTH`` + The number of data bits per memory location. + +``\WORDS`` + The number of consecutive memory locations initialized by this cell. + +``\PRIORITY`` + The cell with the higher integer value in this parameter wins an + initialization conflict. + +The HDL frontend models a memory using ``RTLIL::Memory`` objects and +asynchronous ``$memrd_v2`` and ``$memwr_v2`` cells. The :cmd:ref:`memory` pass +(i.e. its various sub-passes) migrates ``$dff`` cells into the ``$memrd_v2`` and +``$memwr_v2`` cells making them synchronous, then converts them to a single +``$mem_v2`` cell and (optionally) maps this cell type to ``$dff`` cells for the +individual words and multiplexer-based address decoders for the read and write +interfaces. When the last step is disabled or not possible, a ``$mem_v2`` cell +is left in the design. + +The ``$mem_v2`` cell provides the following parameters: + +``\MEMID`` + The name of the original ``RTLIL::Memory`` object that became this + ``$mem_v2`` cell. + +``\SIZE`` + The number of words in the memory. + +``\ABITS`` + The number of address bits. + +``\WIDTH`` + The number of data bits per word. + +``\INIT`` + The initial memory contents. + +``\RD_PORTS`` + The number of read ports on this memory cell. + +``\RD_WIDE_CONTINUATION`` + This parameter is ``\RD_PORTS`` bits wide, containing a bitmask of + "wide continuation" read ports. Such ports are used to represent the + extra data bits of wide ports in the combined cell, and must have all + control signals identical with the preceding port, except for address, + which must have the proper sub-cell address encoded in the low bits. + +``\RD_CLK_ENABLE`` + This parameter is ``\RD_PORTS`` bits wide, containing a clock enable bit + for each read port. + +``\RD_CLK_POLARITY`` + This parameter is ``\RD_PORTS`` bits wide, containing a clock polarity + bit for each read port. + +``\RD_TRANSPARENCY_MASK`` + This parameter is ``\RD_PORTS*\WR_PORTS`` bits wide, containing a + concatenation of all ``\TRANSPARENCY_MASK`` values of the original + ``$memrd_v2`` cells. + +``\RD_COLLISION_X_MASK`` + This parameter is ``\RD_PORTS*\WR_PORTS`` bits wide, containing a + concatenation of all ``\COLLISION_X_MASK`` values of the original + ``$memrd_v2`` cells. + +``\RD_CE_OVER_SRST`` + This parameter is ``\RD_PORTS`` bits wide, determining relative + synchronous reset and enable priority for each read port. + +``\RD_INIT_VALUE`` + This parameter is ``\RD_PORTS*\WIDTH`` bits wide, containing the initial + value for each synchronous read port. + +``\RD_ARST_VALUE`` + This parameter is ``\RD_PORTS*\WIDTH`` bits wide, containing the + asynchronous reset value for each synchronous read port. + +``\RD_SRST_VALUE`` + This parameter is ``\RD_PORTS*\WIDTH`` bits wide, containing the + synchronous reset value for each synchronous read port. + +``\WR_PORTS`` + The number of write ports on this memory cell. + +``\WR_WIDE_CONTINUATION`` + This parameter is ``\WR_PORTS`` bits wide, containing a bitmask of + "wide continuation" write ports. + +``\WR_CLK_ENABLE`` + This parameter is ``\WR_PORTS`` bits wide, containing a clock enable bit + for each write port. + +``\WR_CLK_POLARITY`` + This parameter is ``\WR_PORTS`` bits wide, containing a clock polarity + bit for each write port. + +``\WR_PRIORITY_MASK`` + This parameter is ``\WR_PORTS*\WR_PORTS`` bits wide, containing a + concatenation of all ``\PRIORITY_MASK`` values of the original + ``$memwr_v2`` cells. + +The ``$mem_v2`` cell has the following ports: + +``\RD_CLK`` + This input is ``\RD_PORTS`` bits wide, containing all clock signals for + the read ports. + +``\RD_EN`` + This input is ``\RD_PORTS`` bits wide, containing all enable signals for + the read ports. + +``\RD_ADDR`` + This input is ``\RD_PORTS*\ABITS`` bits wide, containing all address + signals for the read ports. + +``\RD_DATA`` + This output is ``\RD_PORTS*\WIDTH`` bits wide, containing all data + signals for the read ports. + +``\RD_ARST`` + This input is ``\RD_PORTS`` bits wide, containing all asynchronous reset + signals for the read ports. + +``\RD_SRST`` + This input is ``\RD_PORTS`` bits wide, containing all synchronous reset + signals for the read ports. + +``\WR_CLK`` + This input is ``\WR_PORTS`` bits wide, containing all clock signals for + the write ports. + +``\WR_EN`` + This input is ``\WR_PORTS*\WIDTH`` bits wide, containing all enable + signals for the write ports. + +``\WR_ADDR`` + This input is ``\WR_PORTS*\ABITS`` bits wide, containing all address + signals for the write ports. + +``\WR_DATA`` + This input is ``\WR_PORTS*\WIDTH`` bits wide, containing all data + signals for the write ports. + +The :cmd:ref:`memory_collect` pass can be used to convert discrete +``$memrd_v2``, ``$memwr_v2``, and ``$meminit_v2`` cells belonging to the same +memory to a single ``$mem_v2`` cell, whereas the :cmd:ref:`memory_unpack` pass +performs the inverse operation. The :cmd:ref:`memory_dff` pass can combine +asynchronous memory ports that are fed by or feeding registers into synchronous +memory ports. The :cmd:ref:`memory_bram` pass can be used to recognize +``$mem_v2`` cells that can be implemented with a block RAM resource on an FPGA. +The :cmd:ref:`memory_map` pass can be used to implement ``$mem_v2`` cells as +basic logic: word-wide DFFs and address decoders. + +Finite state machines +~~~~~~~~~~~~~~~~~~~~~ + +Add a brief description of the ``$fsm`` cell type. + +Specify rules +~~~~~~~~~~~~~ + +Add information about ``$specify2``, ``$specify3``, and ``$specrule`` cells. + +Formal verification cells +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Add information about ``$check``, ``$assert``, ``$assume``, ``$live``, ``$fair``, +``$cover``, ``$equiv``, ``$initstate``, ``$anyconst``, ``$anyseq``, +``$anyinit``, ``$allconst``, ``$allseq`` cells. + +Add information about ``$ff`` and ``$_FF_`` cells. + +Debugging cells +~~~~~~~~~~~~~~~ + +The ``$print`` cell is used to log the values of signals, akin to (and +translatable to) the ``$display`` and ``$write`` family of tasks in Verilog. It +has the following parameters: + +``\FORMAT`` + The internal format string. The syntax is described below. + +``\ARGS_WIDTH`` + The width (in bits) of the signal on the ``\ARGS`` port. + +``\TRG_ENABLE`` + True if triggered on specific signals defined in ``\TRG``; false if + triggered whenever ``\ARGS`` or ``\EN`` change and ``\EN`` is 1. + +If ``\TRG_ENABLE`` is true, the following parameters also apply: + +``\TRG_WIDTH`` + The number of bits in the ``\TRG`` port. + +``\TRG_POLARITY`` + For each bit in ``\TRG``, 1 if that signal is positive-edge triggered, 0 if + negative-edge triggered. + +``\PRIORITY`` + When multiple ``$print`` or ``$$check`` cells fire on the same trigger, they\ + execute in descending priority order. + +Ports: + +``\TRG`` + The signals that control when this ``$print`` cell is triggered. + If the width of this port is zero and ``\TRG_ENABLE`` is true, the cell is + triggered during initial evaluation (time zero) only. + +``\EN`` + Enable signal for the whole cell. + +``\ARGS`` + The values to be displayed, in format string order. + +Format string syntax +^^^^^^^^^^^^^^^^^^^^ + +The format string syntax resembles Python f-strings. Regular text is passed +through unchanged until a format specifier is reached, starting with a ``{``. + +Format specifiers have the following syntax. Unless noted, all items are +required: + +``{`` + Denotes the start of the format specifier. + +size + Signal size in bits; this many bits are consumed from the ``\ARGS`` port by + this specifier. + +``:`` + Separates the size from the remaining items. + +justify + ``>`` for right-justified, ``<`` for left-justified. + +padding + ``0`` for zero-padding, or a space for space-padding. + +width\ *?* + (optional) The number of characters wide to pad to. + +base + * ``b`` for base-2 integers (binary) + * ``o`` for base-8 integers (octal) + * ``d`` for base-10 integers (decimal) + * ``h`` for base-16 integers (hexadecimal) + * ``c`` for ASCII characters/strings + * ``t`` and ``r`` for simulation time (corresponding to :verilog:`$time` and :verilog:`$realtime`) + +For integers, this item may follow: + +``+``\ *?* + (optional, decimals only) Include a leading plus for non-negative numbers. + This can assist with symmetry with negatives in tabulated output. + +signedness + ``u`` for unsigned, ``s`` for signed. This distinction is only respected + when rendering decimals. + +ASCII characters/strings have no special options, but the signal size must be +divisible by 8. + +For simulation time, the signal size must be zero. + +Finally: + +``}`` + Denotes the end of the format specifier. + +Some example format specifiers: + ++ ``{8:>02hu}`` - 8-bit unsigned integer rendered as hexadecimal, + right-justified, zero-padded to 2 characters wide. ++ ``{32:< 15d+s}`` - 32-bit signed integer rendered as decimal, left-justified, + space-padded to 15 characters wide, positive values prefixed with ``+``. ++ ``{16:< 10hu}`` - 16-bit unsigned integer rendered as hexadecimal, + left-justified, space-padded to 10 characters wide. ++ ``{0:>010t}`` - simulation time, right-justified, zero-padded to 10 characters + wide. + +To include literal ``{`` and ``}`` characters in your format string, use ``{{`` +and ``}}`` respectively. + +It is an error for a format string to consume more or less bits from ``\ARGS`` +than the port width. + +Values are never truncated, regardless of the specified width. + +Note that further restrictions on allowable combinations of options may apply +depending on the backend used. + +For example, Verilog does not have a format specifier that allows zero-padding a +string (i.e. more than 1 ASCII character), though zero-padding a single +character is permitted. + +Thus, while the RTLIL format specifier ``{8:>02c}`` translates to ``%02c``, +``{16:>02c}`` cannot be represented in Verilog and will fail to emit. In this +case, ``{16:> 02c}`` must be used, which translates to ``%2s``. + +.. _sec:celllib_gates: + +Gates +----- + +For gate level logic networks, fixed function single bit cells are used that do +not provide any parameters. + +Simulation models for these cells can be found in the file +techlibs/common/simcells.v in the Yosys source tree. + +.. table:: Cell types for gate level logic networks (main list) + :name: tab:CellLib_gates + + ======================================= ============ + Verilog Cell Type + ======================================= ============ + :verilog:`Y = A` $_BUF_ + :verilog:`Y = ~A` $_NOT_ + :verilog:`Y = A & B` $_AND_ + :verilog:`Y = ~(A & B)` $_NAND_ + :verilog:`Y = A & ~B` $_ANDNOT_ + :verilog:`Y = A | B` $_OR_ + :verilog:`Y = ~(A | B)` $_NOR_ + :verilog:`Y = A | ~B` $_ORNOT_ + :verilog:`Y = A ^ B` $_XOR_ + :verilog:`Y = ~(A ^ B)` $_XNOR_ + :verilog:`Y = ~((A & B) | C)` $_AOI3_ + :verilog:`Y = ~((A | B) & C)` $_OAI3_ + :verilog:`Y = ~((A & B) | (C & D))` $_AOI4_ + :verilog:`Y = ~((A | B) & (C | D))` $_OAI4_ + :verilog:`Y = S ? B : A` $_MUX_ + :verilog:`Y = ~(S ? B : A)` $_NMUX_ + (see below) $_MUX4_ + (see below) $_MUX8_ + (see below) $_MUX16_ + :verilog:`Y = EN ? A : 1'bz` $_TBUF_ + :verilog:`always @(negedge C) Q <= D` $_DFF_N_ + :verilog:`always @(posedge C) Q <= D` $_DFF_P_ + :verilog:`always @* if (!E) Q <= D` $_DLATCH_N_ + :verilog:`always @* if (E) Q <= D` $_DLATCH_P_ + ======================================= ============ + +.. table:: Cell types for gate level logic networks (FFs with reset) + :name: tab:CellLib_gates_adff + + ================== ============== ============== ======================= + :math:`ClkEdge` :math:`RstLvl` :math:`RstVal` Cell Type + ================== ============== ============== ======================= + :verilog:`negedge` ``0`` ``0`` $_DFF_NN0_, $_SDFF_NN0_ + :verilog:`negedge` ``0`` ``1`` $_DFF_NN1_, $_SDFF_NN1_ + :verilog:`negedge` ``1`` ``0`` $_DFF_NP0_, $_SDFF_NP0_ + :verilog:`negedge` ``1`` ``1`` $_DFF_NP1_, $_SDFF_NP1_ + :verilog:`posedge` ``0`` ``0`` $_DFF_PN0_, $_SDFF_PN0_ + :verilog:`posedge` ``0`` ``1`` $_DFF_PN1_, $_SDFF_PN1_ + :verilog:`posedge` ``1`` ``0`` $_DFF_PP0_, $_SDFF_PP0_ + :verilog:`posedge` ``1`` ``1`` $_DFF_PP1_, $_SDFF_PP1_ + ================== ============== ============== ======================= + + +.. table:: Cell types for gate level logic networks (FFs with enable) + :name: tab:CellLib_gates_dffe + + ================== ============= =========== + :math:`ClkEdge` :math:`EnLvl` Cell Type + ================== ============= =========== + :verilog:`negedge` ``0`` $_DFFE_NN_ + :verilog:`negedge` ``1`` $_DFFE_NP_ + :verilog:`posedge` ``0`` $_DFFE_PN_ + :verilog:`posedge` ``1`` $_DFFE_PP_ + ================== ============= =========== + + +.. table:: Cell types for gate level logic networks (FFs with reset and enable) + :name: tab:CellLib_gates_adffe + + ================== ============== ============== ============= =========================================== + :math:`ClkEdge` :math:`RstLvl` :math:`RstVal` :math:`EnLvl` Cell Type + ================== ============== ============== ============= =========================================== + :verilog:`negedge` ``0`` ``0`` ``0`` $_DFFE_NN0N_, $_SDFFE_NN0N_, $_SDFFCE_NN0N_ + :verilog:`negedge` ``0`` ``0`` ``1`` $_DFFE_NN0P_, $_SDFFE_NN0P_, $_SDFFCE_NN0P_ + :verilog:`negedge` ``0`` ``1`` ``0`` $_DFFE_NN1N_, $_SDFFE_NN1N_, $_SDFFCE_NN1N_ + :verilog:`negedge` ``0`` ``1`` ``1`` $_DFFE_NN1P_, $_SDFFE_NN1P_, $_SDFFCE_NN1P_ + :verilog:`negedge` ``1`` ``0`` ``0`` $_DFFE_NP0N_, $_SDFFE_NP0N_, $_SDFFCE_NP0N_ + :verilog:`negedge` ``1`` ``0`` ``1`` $_DFFE_NP0P_, $_SDFFE_NP0P_, $_SDFFCE_NP0P_ + :verilog:`negedge` ``1`` ``1`` ``0`` $_DFFE_NP1N_, $_SDFFE_NP1N_, $_SDFFCE_NP1N_ + :verilog:`negedge` ``1`` ``1`` ``1`` $_DFFE_NP1P_, $_SDFFE_NP1P_, $_SDFFCE_NP1P_ + :verilog:`posedge` ``0`` ``0`` ``0`` $_DFFE_PN0N_, $_SDFFE_PN0N_, $_SDFFCE_PN0N_ + :verilog:`posedge` ``0`` ``0`` ``1`` $_DFFE_PN0P_, $_SDFFE_PN0P_, $_SDFFCE_PN0P_ + :verilog:`posedge` ``0`` ``1`` ``0`` $_DFFE_PN1N_, $_SDFFE_PN1N_, $_SDFFCE_PN1N_ + :verilog:`posedge` ``0`` ``1`` ``1`` $_DFFE_PN1P_, $_SDFFE_PN1P_, $_SDFFCE_PN1P_ + :verilog:`posedge` ``1`` ``0`` ``0`` $_DFFE_PP0N_, $_SDFFE_PP0N_, $_SDFFCE_PP0N_ + :verilog:`posedge` ``1`` ``0`` ``1`` $_DFFE_PP0P_, $_SDFFE_PP0P_, $_SDFFCE_PP0P_ + :verilog:`posedge` ``1`` ``1`` ``0`` $_DFFE_PP1N_, $_SDFFE_PP1N_, $_SDFFCE_PP1N_ + :verilog:`posedge` ``1`` ``1`` ``1`` $_DFFE_PP1P_, $_SDFFE_PP1P_, $_SDFFCE_PP1P_ + ================== ============== ============== ============= =========================================== + +.. table:: Cell types for gate level logic networks (FFs with set and reset) + :name: tab:CellLib_gates_dffsr + + ================== ============== ============== ============ + :math:`ClkEdge` :math:`SetLvl` :math:`RstLvl` Cell Type + ================== ============== ============== ============ + :verilog:`negedge` ``0`` ``0`` $_DFFSR_NNN_ + :verilog:`negedge` ``0`` ``1`` $_DFFSR_NNP_ + :verilog:`negedge` ``1`` ``0`` $_DFFSR_NPN_ + :verilog:`negedge` ``1`` ``1`` $_DFFSR_NPP_ + :verilog:`posedge` ``0`` ``0`` $_DFFSR_PNN_ + :verilog:`posedge` ``0`` ``1`` $_DFFSR_PNP_ + :verilog:`posedge` ``1`` ``0`` $_DFFSR_PPN_ + :verilog:`posedge` ``1`` ``1`` $_DFFSR_PPP_ + ================== ============== ============== ============ + + +.. table:: Cell types for gate level logic networks (FFs with set and reset and enable) + :name: tab:CellLib_gates_dffsre + + ================== ============== ============== ============= ============== + :math:`ClkEdge` :math:`SetLvl` :math:`RstLvl` :math:`EnLvl` Cell Type + ================== ============== ============== ============= ============== + :verilog:`negedge` ``0`` ``0`` ``0`` $_DFFSRE_NNNN_ + :verilog:`negedge` ``0`` ``0`` ``1`` $_DFFSRE_NNNP_ + :verilog:`negedge` ``0`` ``1`` ``0`` $_DFFSRE_NNPN_ + :verilog:`negedge` ``0`` ``1`` ``1`` $_DFFSRE_NNPP_ + :verilog:`negedge` ``1`` ``0`` ``0`` $_DFFSRE_NPNN_ + :verilog:`negedge` ``1`` ``0`` ``1`` $_DFFSRE_NPNP_ + :verilog:`negedge` ``1`` ``1`` ``0`` $_DFFSRE_NPPN_ + :verilog:`negedge` ``1`` ``1`` ``1`` $_DFFSRE_NPPP_ + :verilog:`posedge` ``0`` ``0`` ``0`` $_DFFSRE_PNNN_ + :verilog:`posedge` ``0`` ``0`` ``1`` $_DFFSRE_PNNP_ + :verilog:`posedge` ``0`` ``1`` ``0`` $_DFFSRE_PNPN_ + :verilog:`posedge` ``0`` ``1`` ``1`` $_DFFSRE_PNPP_ + :verilog:`posedge` ``1`` ``0`` ``0`` $_DFFSRE_PPNN_ + :verilog:`posedge` ``1`` ``0`` ``1`` $_DFFSRE_PPNP_ + :verilog:`posedge` ``1`` ``1`` ``0`` $_DFFSRE_PPPN_ + :verilog:`posedge` ``1`` ``1`` ``1`` $_DFFSRE_PPPP_ + ================== ============== ============== ============= ============== + + +.. table:: Cell types for gate level logic networks (latches with reset) + :name: tab:CellLib_gates_adlatch + + ============= ============== ============== ============= + :math:`EnLvl` :math:`RstLvl` :math:`RstVal` Cell Type + ============= ============== ============== ============= + ``0`` ``0`` ``0`` $_DLATCH_NN0_ + ``0`` ``0`` ``1`` $_DLATCH_NN1_ + ``0`` ``1`` ``0`` $_DLATCH_NP0_ + ``0`` ``1`` ``1`` $_DLATCH_NP1_ + ``1`` ``0`` ``0`` $_DLATCH_PN0_ + ``1`` ``0`` ``1`` $_DLATCH_PN1_ + ``1`` ``1`` ``0`` $_DLATCH_PP0_ + ``1`` ``1`` ``1`` $_DLATCH_PP1_ + ============= ============== ============== ============= + + +.. table:: Cell types for gate level logic networks (latches with set and reset) + :name: tab:CellLib_gates_dlatchsr + + ============= ============== ============== =============== + :math:`EnLvl` :math:`SetLvl` :math:`RstLvl` Cell Type + ============= ============== ============== =============== + ``0`` ``0`` ``0`` $_DLATCHSR_NNN_ + ``0`` ``0`` ``1`` $_DLATCHSR_NNP_ + ``0`` ``1`` ``0`` $_DLATCHSR_NPN_ + ``0`` ``1`` ``1`` $_DLATCHSR_NPP_ + ``1`` ``0`` ``0`` $_DLATCHSR_PNN_ + ``1`` ``0`` ``1`` $_DLATCHSR_PNP_ + ``1`` ``1`` ``0`` $_DLATCHSR_PPN_ + ``1`` ``1`` ``1`` $_DLATCHSR_PPP_ + ============= ============== ============== =============== + + + +.. table:: Cell types for gate level logic networks (SR latches) + :name: tab:CellLib_gates_sr + + ============== ============== ========= + :math:`SetLvl` :math:`RstLvl` Cell Type + ============== ============== ========= + ``0`` ``0`` $_SR_NN_ + ``0`` ``1`` $_SR_NP_ + ``1`` ``0`` $_SR_PN_ + ``1`` ``1`` $_SR_PP_ + ============== ============== ========= + + +Tables :numref:`%s `, :numref:`%s `, +:numref:`%s `, :numref:`%s `, +:numref:`%s `, :numref:`%s `, +:numref:`%s `, :numref:`%s +` and :numref:`%s ` list all +cell types used for gate level logic. The cell types ``$_BUF_``, ``$_NOT_``, +``$_AND_``, ``$_NAND_``, ``$_ANDNOT_``, ``$_OR_``, ``$_NOR_``, ``$_ORNOT_``, +``$_XOR_``, ``$_XNOR_``, ``$_AOI3_``, ``$_OAI3_``, ``$_AOI4_``, ``$_OAI4_``, +``$_MUX_``, ``$_MUX4_``, ``$_MUX8_``, ``$_MUX16_`` and ``$_NMUX_`` are used to +model combinatorial logic. The cell type ``$_TBUF_`` is used to model tristate +logic. + +The ``$_MUX4_``, ``$_MUX8_`` and ``$_MUX16_`` cells are used to model wide +muxes, and correspond to the following Verilog code: + +.. code-block:: verilog + :force: + + // $_MUX4_ + assign Y = T ? (S ? D : C) : + (S ? B : A); + // $_MUX8_ + assign Y = U ? T ? (S ? H : G) : + (S ? F : E) : + T ? (S ? D : C) : + (S ? B : A); + // $_MUX16_ + assign Y = V ? U ? T ? (S ? P : O) : + (S ? N : M) : + T ? (S ? L : K) : + (S ? J : I) : + U ? T ? (S ? H : G) : + (S ? F : E) : + T ? (S ? D : C) : + (S ? B : A); + +The cell types ``$_DFF_N_`` and ``$_DFF_P_`` represent d-type flip-flops. + +The cell types ``$_DFFE_[NP][NP]_`` implement d-type flip-flops with enable. The +values in the table for these cell types relate to the following Verilog code +template. + +.. code-block:: verilog + :force: + + always @(CLK_EDGE C) + if (EN == EN_LVL) + Q <= D; + +The cell types ``$_DFF_[NP][NP][01]_`` implement d-type flip-flops with +asynchronous reset. The values in the table for these cell types relate to the +following Verilog code template, where ``RST_EDGE`` is ``posedge`` if +``RST_LVL`` if ``1``, and ``negedge`` otherwise. + +.. code-block:: verilog + :force: + + always @(CLK_EDGE C, RST_EDGE R) + if (R == RST_LVL) + Q <= RST_VAL; + else + Q <= D; + +The cell types ``$_SDFF_[NP][NP][01]_`` implement d-type flip-flops with +synchronous reset. The values in the table for these cell types relate to the +following Verilog code template: + +.. code-block:: verilog + :force: + + always @(CLK_EDGE C) + if (R == RST_LVL) + Q <= RST_VAL; + else + Q <= D; + +The cell types ``$_DFFE_[NP][NP][01][NP]_`` implement d-type flip-flops with +asynchronous reset and enable. The values in the table for these cell types +relate to the following Verilog code template, where ``RST_EDGE`` is +``posedge`` if ``RST_LVL`` if ``1``, and ``negedge`` otherwise. + +.. code-block:: verilog + :force: + + always @(CLK_EDGE C, RST_EDGE R) + if (R == RST_LVL) + Q <= RST_VAL; + else if (EN == EN_LVL) + Q <= D; + +The cell types ``$_SDFFE_[NP][NP][01][NP]_`` implement d-type flip-flops with +synchronous reset and enable, with reset having priority over enable. The values +in the table for these cell types relate to the following Verilog code template: + +.. code-block:: verilog + :force: + + always @(CLK_EDGE C) + if (R == RST_LVL) + Q <= RST_VAL; + else if (EN == EN_LVL) + Q <= D; + +The cell types ``$_SDFFCE_[NP][NP][01][NP]_`` implement d-type flip-flops with +synchronous reset and enable, with enable having priority over reset. The values +in the table for these cell types relate to the following Verilog code template: + +.. code-block:: verilog + :force: + + always @(CLK_EDGE C) + if (EN == EN_LVL) + if (R == RST_LVL) + Q <= RST_VAL; + else + Q <= D; + +The cell types ``$_DFFSR_[NP][NP][NP]_`` implement d-type flip-flops with +asynchronous set and reset. The values in the table for these cell types relate +to the following Verilog code template, where ``RST_EDGE`` is ``posedge`` if +``RST_LVL`` if ``1``, ``negedge`` otherwise, and ``SET_EDGE`` is ``posedge`` +if ``SET_LVL`` if ``1``, ``negedge`` otherwise. + +.. code-block:: verilog + :force: + + always @(CLK_EDGE C, RST_EDGE R, SET_EDGE S) + if (R == RST_LVL) + Q <= 0; + else if (S == SET_LVL) + Q <= 1; + else + Q <= D; + +The cell types ``$_DFFSRE_[NP][NP][NP][NP]_`` implement d-type flip-flops with +asynchronous set and reset and enable. The values in the table for these cell +types relate to the following Verilog code template, where ``RST_EDGE`` is +``posedge`` if ``RST_LVL`` if ``1``, ``negedge`` otherwise, and ``SET_EDGE`` +is ``posedge`` if ``SET_LVL`` if ``1``, ``negedge`` otherwise. + +.. code-block:: verilog + :force: + + always @(CLK_EDGE C, RST_EDGE R, SET_EDGE S) + if (R == RST_LVL) + Q <= 0; + else if (S == SET_LVL) + Q <= 1; + else if (E == EN_LVL) + Q <= D; + +The cell types ``$_DLATCH_N_`` and ``$_DLATCH_P_`` represent d-type latches. + +The cell types ``$_DLATCH_[NP][NP][01]_`` implement d-type latches with reset. +The values in the table for these cell types relate to the following Verilog +code template: + +.. code-block:: verilog + :force: + + always @* + if (R == RST_LVL) + Q <= RST_VAL; + else if (E == EN_LVL) + Q <= D; + +The cell types ``$_DLATCHSR_[NP][NP][NP]_`` implement d-type latches with set +and reset. The values in the table for these cell types relate to the following +Verilog code template: + +.. code-block:: verilog + :force: + + always @* + if (R == RST_LVL) + Q <= 0; + else if (S == SET_LVL) + Q <= 1; + else if (E == EN_LVL) + Q <= D; + +The cell types ``$_SR_[NP][NP]_`` implement sr-type latches. The values in the +table for these cell types relate to the following Verilog code template: + +.. code-block:: verilog + :force: + + always @* + if (R == RST_LVL) + Q <= 0; + else if (S == SET_LVL) + Q <= 1; + +In most cases gate level logic networks are created from RTL networks using the +techmap pass. The flip-flop cells from the gate level logic network can be +mapped to physical flip-flop cells from a Liberty file using the dfflibmap pass. +The combinatorial logic cells can be mapped to physical cells from a Liberty +file via ABC using the abc pass. + +.. todo:: Add information about ``$slice`` and ``$concat`` cells. + +.. todo:: Add information about ``$lut`` and ``$sop`` cells. + +.. todo:: Add information about ``$alu``, ``$macc``, ``$fa``, and ``$lcu`` cells. diff --git a/docs/source/yosys_internals/formats/index.rst b/docs/source/yosys_internals/formats/index.rst new file mode 100644 index 00000000000..c187a8238a8 --- /dev/null +++ b/docs/source/yosys_internals/formats/index.rst @@ -0,0 +1,13 @@ +Internal formats +================ + +.. todo:: brief overview for the internal formats index + +.. toctree:: + :maxdepth: 3 + + overview + rtlil_rep + rtlil_text + cell_library + diff --git a/docs/source/yosys_internals/formats/overview.rst b/docs/source/yosys_internals/formats/overview.rst new file mode 100644 index 00000000000..cbf5369bc06 --- /dev/null +++ b/docs/source/yosys_internals/formats/overview.rst @@ -0,0 +1,53 @@ +Format overview +=============== + +Yosys uses two different internal formats. The first is used to store an +abstract syntax tree (AST) of a Verilog input file. This format is simply called +AST and is generated by the Verilog Frontend. This data structure is consumed by +a subsystem called AST Frontend [1]_. This AST Frontend then generates a design +in Yosys' main internal format, the +Register-Transfer-Level-Intermediate-Language (RTLIL) representation. It does +that by first performing a number of simplifications within the AST +representation and then generating RTLIL from the simplified AST data structure. + +The RTLIL representation is used by all passes as input and outputs. This has +the following advantages over using different representational formats between +different passes: + +- The passes can be rearranged in a different order and passes can be removed + or inserted. + +- Passes can simply pass-thru the parts of the design they don't change without + the need to convert between formats. In fact Yosys passes output the same + data structure they received as input and performs all changes in place. + +- All passes use the same interface, thus reducing the effort required to + understand a pass when reading the Yosys source code, e.g. when adding + additional features. + +The RTLIL representation is basically a netlist representation with the +following additional features: + +- An internal cell library with fixed-function cells to represent RTL datapath + and register cells as well as logical gate-level cells (single-bit gates and + registers). + +- Support for multi-bit values that can use individual bits from wires as well + as constant bits to represent coarse-grain netlists. + +- Support for basic behavioural constructs (if-then-else structures and + multi-case switches with a sensitivity list for updating the outputs). + +- Support for multi-port memories. + +The use of RTLIL also has the disadvantage of having a very powerful format +between all passes, even when doing gate-level synthesis where the more advanced +features are not needed. In order to reduce complexity for passes that operate +on a low-level representation, these passes check the features used in the input +RTLIL and fail to run when unsupported high-level constructs are used. In such +cases a pass that transforms the higher-level constructs to lower-level +constructs must be called from the synthesis script first. + +.. [1] + In Yosys the term pass is only used to refer to commands that operate on the + RTLIL data structure. \ No newline at end of file diff --git a/docs/source/yosys_internals/formats/rtlil_rep.rst b/docs/source/yosys_internals/formats/rtlil_rep.rst new file mode 100644 index 00000000000..5de3430f228 --- /dev/null +++ b/docs/source/yosys_internals/formats/rtlil_rep.rst @@ -0,0 +1,416 @@ +The RTL Intermediate Language (RTLIL) +===================================== + +All frontends, passes and backends in Yosys operate on a design in RTLIL +representation. The only exception are the high-level frontends that use the AST +representation as an intermediate step before generating RTLIL data. + +In order to avoid reinventing names for the RTLIL classes, they are simply +referred to by their full C++ name, i.e. including the ``RTLIL::`` namespace +prefix, in this document. + +:numref:`Figure %s ` shows a simplified Entity-Relationship +Diagram (ER Diagram) of RTLIL. In :math:`1:N` relationships the arrow points +from the :math:`N` side to the :math:`1`. For example one ``RTLIL::Design`` +contains :math:`N` (zero to many) instances of ``RTLIL::Module`` . A two-pointed +arrow indicates a :math:`1:1` relationship. + +The ``RTLIL::Design`` is the root object of the RTLIL data structure. There is +always one "current design" in memory which passes operate on, frontends add +data to and backends convert to exportable formats. But in some cases passes +internally generate additional ``RTLIL::Design`` objects. For example when a +pass is reading an auxiliary Verilog file such as a cell library, it might +create an additional ``RTLIL::Design`` object and call the Verilog frontend with +this other object to parse the cell library. + +.. figure:: /_images/internals/overview_rtlil.* + :class: width-helper + :name: fig:Overview_RTLIL + + Simplified RTLIL Entity-Relationship Diagram + +There is only one active ``RTLIL::Design`` object that is used by all frontends, +passes and backends called by the user, e.g. using a synthesis script. The +``RTLIL::Design`` then contains zero to many ``RTLIL::Module`` objects. This +corresponds to modules in Verilog or entities in VHDL. Each module in turn +contains objects from three different categories: + +- ``RTLIL::Cell`` and ``RTLIL::Wire`` objects represent classical netlist data. + +- ``RTLIL::Process`` objects represent the decision trees (if-then-else statements, + etc.) and synchronization declarations (clock signals and sensitivity) from + Verilog always and VHDL process blocks. + +- ``RTLIL::Memory`` objects represent addressable memories (arrays). + +Usually the output of the synthesis procedure is a netlist, i.e. all +``RTLIL::Process`` and ``RTLIL::Memory`` objects must be replaced by +``RTLIL::Cell`` and ``RTLIL::Wire`` objects by synthesis passes. + +All features of the HDL that cannot be mapped directly to these RTLIL classes +must be transformed to an RTLIL-compatible representation by the HDL frontend. +This includes Verilog-features such as generate-blocks, loops and parameters. + +The following sections contain a more detailed description of the different +parts of RTLIL and rationale behind some of the design decisions. + +RTLIL identifiers +----------------- + +All identifiers in RTLIL (such as module names, port names, signal names, cell +types, etc.) follow the following naming convention: they must either start with +a backslash (``\``) or a dollar sign (``$``). + +Identifiers starting with a backslash are public visible identifiers. Usually +they originate from one of the HDL input files. For example the signal name +``\sig42`` is most likely a signal that was declared using the name ``sig42`` in +an HDL input file. On the other hand the signal name ``$sig42`` is an +auto-generated signal name. The backends convert all identifiers that start with +a dollar sign to identifiers that do not collide with identifiers that start +with a backslash. + +This has three advantages: + +- First, it is impossible that an auto-generated identifier collides with an + identifier that was provided by the user. + +- Second, the information about which identifiers were originally provided by + the user is always available which can help guide some optimizations. For + example, :cmd:ref:`opt_clean` tries to preserve signals with a user-provided + name but doesn't hesitate to delete signals that have auto-generated names + when they just duplicate other signals. Note that this can be overridden + with the `-purge` option to also delete internal nets with user-provided + names. + +- Third, the delicate job of finding suitable auto-generated public visible + names is deferred to one central location. Internally auto-generated names + that may hold important information for Yosys developers can be used without + disturbing external tools. For example the Verilog backend assigns names in + the form ``_123_``. + +Whitespace and control characters (any character with an ASCII code 32 or less) +are not allowed in RTLIL identifiers; most frontends and backends cannot support +these characters in identifiers. + +In order to avoid programming errors, the RTLIL data structures check if all +identifiers start with either a backslash or a dollar sign, and contain no +whitespace or control characters. Violating these rules results in a runtime +error. + +All RTLIL identifiers are case sensitive. + +Some transformations, such as flattening, may have to change identifiers +provided by the user to avoid name collisions. When that happens, attribute +``hdlname`` is attached to the object with the changed identifier. This +attribute contains one name (if emitted directly by the frontend, or is a result +of disambiguation) or multiple names separated by spaces (if a result of +flattening). All names specified in the ``hdlname`` attribute are public and do +not include the leading ``\``. + +RTLIL::Design and RTLIL::Module +------------------------------- + +The ``RTLIL::Design`` object is basically just a container for ``RTLIL::Module`` +objects. In addition to a list of ``RTLIL::Module`` objects the +``RTLIL::Design`` also keeps a list of selected objects, i.e. the objects that +passes should operate on. In most cases the whole design is selected and +therefore passes operate on the whole design. But this mechanism can be useful +for more complex synthesis jobs in which only parts of the design should be +affected by certain passes. + +Besides the objects shown in the :ref:`ER diagram ` above, +an ``RTLIL::Module`` object contains the following additional properties: + +- The module name +- A list of attributes +- A list of connections between wires +- An optional frontend callback used to derive parametrized variations of the + module + +The attributes can be Verilog attributes imported by the Verilog frontend or +attributes assigned by passes. They can be used to store additional metadata +about modules or just mark them to be used by certain part of the synthesis +script but not by others. + +Verilog and VHDL both support parametric modules (known as "generic entities" in +VHDL). The RTLIL format does not support parametric modules itself. Instead each +module contains a callback function into the AST frontend to generate a +parametrized variation of the ``RTLIL::Module`` as needed. This callback then +returns the auto-generated name of the parametrized variation of the module. (A +hash over the parameters and the module name is used to prohibit the same +parametrized variation from being generated twice. For modules with only a few +parameters, a name directly containing all parameters is generated instead of a +hash string.) + +.. _sec:rtlil_cell_wire: + +RTLIL::Cell and RTLIL::Wire +--------------------------- + +A module contains zero to many ``RTLIL::Cell`` and ``RTLIL::Wire`` objects. +Objects of these types are used to model netlists. Usually the goal of all +synthesis efforts is to convert all modules to a state where the functionality +of the module is implemented only by cells from a given cell library and wires +to connect these cells with each other. Note that module ports are just wires +with a special property. + +An ``RTLIL::Wire`` object has the following properties: + +- The wire name +- A list of attributes +- A width (buses are just wires with a width more than 1) +- Bus direction (MSB to LSB or vice versa) +- Lowest valid bit index (LSB or MSB depending on bus direction) +- If the wire is a port: port number and direction (input/output/inout) + +As with modules, the attributes can be Verilog attributes imported by the +Verilog frontend or attributes assigned by passes. + +In Yosys, busses (signal vectors) are represented using a single wire object +with a width more than 1. So Yosys does not convert signal vectors to individual +signals. This makes some aspects of RTLIL more complex but enables Yosys to be +used for coarse grain synthesis where the cells of the target architecture +operate on entire signal vectors instead of single bit wires. + +In Verilog and VHDL, busses may have arbitrary bounds, and LSB can have either +the lowest or the highest bit index. In RTLIL, bit 0 always corresponds to LSB; +however, information from the HDL frontend is preserved so that the bus will be +correctly indexed in error messages, backend output, constraint files, etc. + +An ``RTLIL::Cell`` object has the following properties: + +- The cell name and type +- A list of attributes +- A list of parameters (for parametric cells) +- Cell ports and the connections of ports to wires and constants + +The connections of ports to wires are coded by assigning an ``RTLIL::SigSpec`` +to each cell port. The ``RTLIL::SigSpec`` data type is described in the next +section. + +.. _sec:rtlil_sigspec: + +RTLIL::SigSpec +-------------- + +A "signal" is everything that can be applied to a cell port. I.e. + +- | Any constant value of arbitrary bit-width + | 1em For example: ``1337, 16'b0000010100111001, 1'b1, 1'bx`` + +- | All bits of a wire or a selection of bits from a wire + | 1em For example: ``mywire, mywire[24], mywire[15:8]`` + +- | Concatenations of the above + | 1em For example: ``{16'd1337, mywire[15:8]}`` + +The ``RTLIL::SigSpec`` data type is used to represent signals. The ``RTLIL::Cell`` +object contains one ``RTLIL::SigSpec`` for each cell port. + +In addition, connections between wires are represented using a pair of +``RTLIL::SigSpec`` objects. Such pairs are needed in different locations. +Therefore the type name ``RTLIL::SigSig`` was defined for such a pair. + +.. _sec:rtlil_process: + +RTLIL::Process +-------------- + +When a high-level HDL frontend processes behavioural code it splits it up into +data path logic (e.g. the expression ``a + b`` is replaced by the output of an +adder that takes a and b as inputs) and an ``RTLIL::Process`` that models the +control logic of the behavioural code. Let's consider a simple example: + +.. code:: verilog + :number-lines: + + module ff_with_en_and_async_reset(clock, reset, enable, d, q); + input clock, reset, enable, d; + output reg q; + always @(posedge clock, posedge reset) + if (reset) + q <= 0; + else if (enable) + q <= d; + endmodule + +In this example there is no data path and therefore the ``RTLIL::Module`` generated +by the frontend only contains a few ``RTLIL::Wire`` objects and an ``RTLIL::Process`` . +The ``RTLIL::Process`` in RTLIL syntax: + +.. code:: RTLIL + :number-lines: + + process $proc$ff_with_en_and_async_reset.v:4$1 + assign $0\q[0:0] \q + switch \reset + case 1'1 + assign $0\q[0:0] 1'0 + case + switch \enable + case 1'1 + assign $0\q[0:0] \d + case + end + end + sync posedge \clock + update \q $0\q[0:0] + sync posedge \reset + update \q $0\q[0:0] + end + +This ``RTLIL::Process`` contains two ``RTLIL::SyncRule`` objects, two +``RTLIL::SwitchRule`` objects and five ``RTLIL::CaseRule`` objects. The wire +``$0\q[0:0]`` is an automatically created wire that holds the next value of +``\q``. The lines 2..12 describe how ``$0\q[0:0]`` should be calculated. The +lines 13..16 describe how the value of ``$0\q[0:0]`` is used to update ``\q``. + +An ``RTLIL::Process`` is a container for zero or more ``RTLIL::SyncRule`` +objects and exactly one ``RTLIL::CaseRule`` object, which is called the root +case. + +An ``RTLIL::SyncRule`` object contains an (optional) synchronization condition +(signal and edge-type), zero or more assignments (``RTLIL::SigSig``), and zero +or more memory writes (``RTLIL::MemWriteAction``). The always synchronization +condition is used to break combinatorial loops when a latch should be inferred +instead. + +An ``RTLIL::CaseRule`` is a container for zero or more assignments +(``RTLIL::SigSig``) and zero or more ``RTLIL::SwitchRule`` objects. An +``RTLIL::SwitchRule`` objects is a container for zero or more +``RTLIL::CaseRule`` objects. + +In the above example the lines 2..12 are the root case. Here ``$0\q[0:0]`` is +first assigned the old value ``\q`` as default value (line 2). The root case +also contains an ``RTLIL::SwitchRule`` object (lines 3..12). Such an object is +very similar to the C switch statement as it uses a control signal (``\reset`` +in this case) to determine which of its cases should be active. The +``RTLIL::SwitchRule`` object then contains one ``RTLIL::CaseRule`` object per +case. In this example there is a case [1]_ for ``\reset == 1`` that causes +``$0\q[0:0]`` to be set (lines 4 and 5) and a default case that in turn contains +a switch that sets ``$0\q[0:0]`` to the value of ``\d`` if ``\enable`` is active +(lines 6..11). + +A case can specify zero or more compare values that will determine whether it +matches. Each of the compare values must be the exact same width as the control +signal. When more than one compare value is specified, the case matches if any +of them matches the control signal; when zero compare values are specified, the +case always matches (i.e. it is the default case). + +A switch prioritizes cases from first to last: multiple cases can match, but +only the first matched case becomes active. This normally synthesizes to a +priority encoder. The parallel_case attribute allows passes to assume that no +more than one case will match, and full_case attribute allows passes to assume +that exactly one case will match; if these invariants are ever dynamically +violated, the behavior is undefined. These attributes are useful when an +invariant invisible to the synthesizer causes the control signal to never take +certain bit patterns. + +The lines 13..16 then cause ``\q`` to be updated whenever there is a positive +clock edge on ``\clock`` or ``\reset``. + +In order to generate such a representation, the language frontend must be able +to handle blocking and nonblocking assignments correctly. However, the language +frontend does not need to identify the correct type of storage element for the +output signal or generate multiplexers for the decision tree. This is done by +passes that work on the RTLIL representation. Therefore it is relatively easy to +substitute these steps with other algorithms that target different target +architectures or perform optimizations or other transformations on the decision +trees before further processing them. + +One of the first actions performed on a design in RTLIL representation in most +synthesis scripts is identifying asynchronous resets. This is usually done using +the :cmd:ref:`proc_arst` pass. This pass transforms the above example to the +following ``RTLIL::Process``: + +.. code:: RTLIL + :number-lines: + + process $proc$ff_with_en_and_async_reset.v:4$1 + assign $0\q[0:0] \q + switch \enable + case 1'1 + assign $0\q[0:0] \d + case + end + sync posedge \clock + update \q $0\q[0:0] + sync high \reset + update \q 1'0 + end + +This pass has transformed the outer ``RTLIL::SwitchRule`` into a modified +``RTLIL::SyncRule`` object for the ``\reset`` signal. Further processing converts the +``RTLIL::Process`` into e.g. a d-type flip-flop with asynchronous reset and a +multiplexer for the enable signal: + +.. code:: RTLIL + :number-lines: + + cell $adff $procdff$6 + parameter \ARST_POLARITY 1'1 + parameter \ARST_VALUE 1'0 + parameter \CLK_POLARITY 1'1 + parameter \WIDTH 1 + connect \ARST \reset + connect \CLK \clock + connect \D $0\q[0:0] + connect \Q \q + end + cell $mux $procmux$3 + parameter \WIDTH 1 + connect \A \q + connect \B \d + connect \S \enable + connect \Y $0\q[0:0] + end + +Different combinations of passes may yield different results. Note that +``$adff`` and ``$mux`` are internal cell types that still need to be mapped to +cell types from the target cell library. + +Some passes refuse to operate on modules that still contain ``RTLIL::Process`` +objects as the presence of these objects in a module increases the complexity. +Therefore the passes to translate processes to a netlist of cells are usually +called early in a synthesis script. The proc pass calls a series of other passes +that together perform this conversion in a way that is suitable for most +synthesis tasks. + +.. _sec:rtlil_memory: + +RTLIL::Memory +------------- + +For every array (memory) in the HDL code an ``RTLIL::Memory`` object is created. +A memory object has the following properties: + +- The memory name +- A list of attributes +- The width of an addressable word +- The size of the memory in number of words + +All read accesses to the memory are transformed to ``$memrd`` cells and all +write accesses to ``$memwr`` cells by the language frontend. These cells consist +of independent read- and write-ports to the memory. Memory initialization is +transformed to ``$meminit`` cells by the language frontend. The ``\MEMID`` +parameter on these cells is used to link them together and to the +``RTLIL::Memory`` object they belong to. + +The rationale behind using separate cells for the individual ports versus +creating a large multiport memory cell right in the language frontend is that +the separate ``$memrd`` and ``$memwr`` cells can be consolidated using resource +sharing. As resource sharing is a non-trivial optimization problem where +different synthesis tasks can have different requirements it lends itself to do +the optimisation in separate passes and merge the ``RTLIL::Memory`` objects and +``$memrd`` and ``$memwr`` cells to multiport memory blocks after resource +sharing is completed. + +The memory pass performs this conversion and can (depending on the options +passed to it) transform the memories directly to d-type flip-flops and address +logic or yield multiport memory blocks (represented using ``$mem`` cells). + +See :ref:`sec:memcells` for details about the memory cell types. + +.. [1] + The syntax ``1'1`` in the RTLIL code specifies a constant with a length of + one bit (the first ``1``), and this bit is a one (the second ``1``). diff --git a/docs/source/yosys_internals/formats/rtlil_text.rst b/docs/source/yosys_internals/formats/rtlil_text.rst new file mode 100644 index 00000000000..8b5c1068120 --- /dev/null +++ b/docs/source/yosys_internals/formats/rtlil_text.rst @@ -0,0 +1,297 @@ +.. _chapter:textrtlil: + +RTLIL text representation +------------------------- + +This appendix documents the text representation of RTLIL in extended Backus-Naur +form (EBNF). + +The grammar is not meant to represent semantic limitations. That is, the grammar +is "permissive", and later stages of processing perform more rigorous checks. + +The grammar is also not meant to represent the exact grammar used in the RTLIL +frontend, since that grammar is specific to processing by lex and yacc, is even +more permissive, and is somewhat less understandable than simple EBNF notation. + +Finally, note that all statements (rules ending in ``-stmt``) terminate in an +end-of-line. Because of this, a statement cannot be broken into multiple lines. + +Lexical elements +~~~~~~~~~~~~~~~~ + +Characters +^^^^^^^^^^ + +An RTLIL file is a stream of bytes. Strictly speaking, a "character" in an RTLIL +file is a single byte. The lexer treats multi-byte encoded characters as +consecutive single-byte characters. While other encodings *may* work, UTF-8 is +known to be safe to use. Byte order marks at the beginning of the file will +cause an error. + +ASCII spaces (32) and tabs (9) separate lexer tokens. + +A ``nonws`` character, used in identifiers, is any character whose encoding +consists solely of bytes above ASCII space (32). + +An ``eol`` is one or more consecutive ASCII newlines (10) and carriage returns +(13). + +Identifiers +^^^^^^^^^^^ + +There are two types of identifiers in RTLIL: + +- Publically visible identifiers +- Auto-generated identifiers + +.. code:: BNF + + ::= | + ::= \ + + ::= $ + + +Values +^^^^^^ + +A *value* consists of a width in bits and a bit representation, most +significant bit first. Bits may be any of: + +- ``0``: A logic zero value +- ``1``: A logic one value +- ``x``: An unknown logic value (or don't care in case patterns) +- ``z``: A high-impedance value (or don't care in case patterns) +- ``m``: A marked bit (internal use only) +- ``-``: A don't care value + +An *integer* is simply a signed integer value in decimal format. **Warning:** +Integer constants are limited to 32 bits. That is, they may only be in the range +:math:`[-2147483648, 2147483648)`. Integers outside this range will result in an +error. + +.. code:: BNF + + ::= + ' * + ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 + ::= 0 | 1 | x | z | m | - + ::= -? + + +Strings +^^^^^^^ + +A string is a series of characters delimited by double-quote characters. Within +a string, any character except ASCII NUL (0) may be used. In addition, certain +escapes can be used: + +- ``\n``: A newline +- ``\t``: A tab +- ``\ooo``: A character specified as a one, two, or three digit octal value + +All other characters may be escaped by a backslash, and become the following +character. Thus: + +- ``\\``: A backslash +- ``\"``: A double-quote +- ``\r``: An 'r' character + +Comments +^^^^^^^^ + +A comment starts with a ``#`` character and proceeds to the end of the line. All +comments are ignored. + +File +~~~~ + +A file consists of an optional autoindex statement followed by zero or more +modules. + +.. code:: BNF + + ::= ? * + +Autoindex statements +^^^^^^^^^^^^^^^^^^^^ + +The autoindex statement sets the global autoindex value used by Yosys when it +needs to generate a unique name, e.g. ``flattenN``. The N part is filled with +the value of the global autoindex value, which is subsequently incremented. This +global has to be dumped into RTLIL, otherwise e.g. dumping and running a pass +would have different properties than just running a pass on a warm design. + +.. code:: BNF + + ::= autoidx + +Modules +^^^^^^^ + +Declares a module, with zero or more attributes, consisting of zero or more +wires, memories, cells, processes, and connections. + +.. code:: BNF + + ::= * + ::= module + ::= ( + | + | + | + | )* + ::= parameter ? + ::= | | + ::= end + +Attribute statements +^^^^^^^^^^^^^^^^^^^^ + +Declares an attribute with the given identifier and value. + +.. code:: BNF + + ::= attribute + +Signal specifications +^^^^^^^^^^^^^^^^^^^^^ + +A signal is anything that can be applied to a cell port, i.e. a constant value, +all bits or a selection of bits from a wire, or concatenations of those. + +**Warning:** When an integer constant is a sigspec, it is always 32 bits wide, +2's complement. For example, a constant of :math:`-1` is the same as +``32'11111111111111111111111111111111``, while a constant of :math:`1` is the +same as ``32'1``. + +See :ref:`sec:rtlil_sigspec` for an overview of signal specifications. + +.. code:: BNF + + ::= + | + | [ (:)? ] + | { * } + +Connections +^^^^^^^^^^^ + +Declares a connection between the given signals. + +.. code:: BNF + + ::= connect + +Wires +^^^^^ + +Declares a wire, with zero or more attributes, with the given identifier and +options in the enclosing module. + +See :ref:`sec:rtlil_cell_wire` for an overview of wires. + +.. code:: BNF + + ::= * + ::= wire * + ::= + ::= width + | offset + | input + | output + | inout + | upto + | signed + +Memories +^^^^^^^^ + +Declares a memory, with zero or more attributes, with the given identifier and +options in the enclosing module. + +See :ref:`sec:rtlil_memory` for an overview of memory cells, and +:ref:`sec:memcells` for details about memory cell types. + +.. code:: BNF + + ::= * + ::= memory * + ::= width + | size + | offset + +Cells +^^^^^ + +Declares a cell, with zero or more attributes, with the given identifier and +type in the enclosing module. + +Cells perform functions on input signals. See +:doc:`/yosys_internals/formats/cell_library` for a detailed list of cell types. + +.. code:: BNF + + ::= * * + ::= cell + ::= + ::= + ::= parameter (signed | real)? + | connect + ::= end + + +Processes +^^^^^^^^^ + +Declares a process, with zero or more attributes, with the given identifier in +the enclosing module. The body of a process consists of zero or more +assignments, exactly one switch, and zero or more syncs. + +See :ref:`sec:rtlil_process` for an overview of processes. + +.. code:: BNF + + ::= * + ::= process + ::= * ? * * + ::= assign + ::= + ::= + ::= end + +Switches +^^^^^^^^ + +Switches test a signal for equality against a list of cases. Each case specifies +a comma-separated list of signals to check against. If there are no signals in +the list, then the case is the default case. The body of a case consists of zero +or more switches and assignments. Both switches and cases may have zero or more +attributes. + +.. code:: BNF + + ::= * + := * switch + ::= * + ::= case ? + ::= (, )* + ::= ( | )* + ::= end + +Syncs +^^^^^ + +Syncs update signals with other signals when an event happens. Such an event may +be: + +- An edge or level on a signal +- Global clock ticks +- Initialization +- Always + +.. code:: BNF + + ::= * + ::= sync + | sync global + | sync init + | sync always + ::= low | high | posedge | negedge | edge + ::= update diff --git a/docs/source/yosys_internals/index.rst b/docs/source/yosys_internals/index.rst new file mode 100644 index 00000000000..b04f136995d --- /dev/null +++ b/docs/source/yosys_internals/index.rst @@ -0,0 +1,40 @@ +.. _chapter:overview: + +Yosys internals +=============== + +.. todo:: less academic + +Yosys is an extensible open source hardware synthesis tool. It is aimed at +designers who are looking for an easily accessible, universal, and +vendor-independent synthesis tool, as well as scientists who do research in +electronic design automation (EDA) and are looking for an open synthesis +framework that can be used to test algorithms on complex real-world designs. + +Yosys can synthesize a large subset of Verilog 2005 and has been tested with a +wide range of real-world designs, including the `OpenRISC 1200 CPU`_, the +`openMSP430 CPU`_, the `OpenCores I2C master`_, and the `k68 CPU`_. + +.. todo:: add RISC-V core example + +.. _OpenRISC 1200 CPU: https://github.com/openrisc/or1200 + +.. _openMSP430 CPU: http://opencores.org/projects/openmsp430 + +.. _OpenCores I2C master: http://opencores.org/projects/i2c + +.. _k68 CPU: http://opencores.org/projects/k68 + +Yosys is written in C++, targeting C++11 at minimum. This chapter describes some +of the fundamental Yosys data structures. For the sake of simplicity the C++ +type names used in the Yosys implementation are used in this chapter, even +though the chapter only explains the conceptual idea behind it and can be used +as reference to implement a similar system in any language. + +.. toctree:: + :maxdepth: 3 + + flow/index + formats/index + extending_yosys/index + techmap diff --git a/docs/source/yosys_internals/techmap.rst b/docs/source/yosys_internals/techmap.rst new file mode 100644 index 00000000000..ef2bbd87a28 --- /dev/null +++ b/docs/source/yosys_internals/techmap.rst @@ -0,0 +1,191 @@ +Techmap by example +------------------ + +As a quick recap, the :cmd:ref:`techmap` command replaces cells in the design +with implementations given as Verilog code (called "map files"). It can replace +Yosys' internal cell types (such as ``$or``) as well as user-defined cell types. + +- Verilog parameters are used extensively to customize the internal cell types. +- Additional special parameters are used by techmap to communicate meta-data to + the map files. +- Special wires are used to instruct techmap how to handle a module in the map + file. +- Generate blocks and recursion are powerful tools for writing map files. + +Code examples used in this document are included in the Yosys code base under +|code_examples/techmap|_. + +.. |code_examples/techmap| replace:: :file:`docs/source/code_examples/techmap` +.. _code_examples/techmap: https://github.com/YosysHQ/yosys/tree/master/docs/source/code_examples/techmap + + +Mapping OR3X1 +~~~~~~~~~~~~~ + +.. todo:: add/expand supporting text + +.. note:: + + This is a simple example for demonstration only. Techmap shouldn't be used + to implement basic logic optimization. + +.. literalinclude:: /code_examples/techmap/red_or3x1_map.v + :language: verilog + :caption: :file:`red_or3x1_map.v` + +.. figure:: /_images/code_examples/techmap/red_or3x1.* + :class: width-helper + +.. literalinclude:: /code_examples/techmap/red_or3x1_test.ys + :language: yoscrypt + :caption: :file:`red_or3x1_test.ys` + +.. literalinclude:: /code_examples/techmap/red_or3x1_test.v + :language: verilog + :caption: :file:`red_or3x1_test.v` + +Conditional techmap +~~~~~~~~~~~~~~~~~~~ + +- In some cases only cells with certain properties should be substituted. +- The special wire ``_TECHMAP_FAIL_`` can be used to disable a module in the map + file for a certain set of parameters. +- The wire ``_TECHMAP_FAIL_`` must be set to a constant value. If it is non-zero + then the module is disabled for this set of parameters. +- Example use-cases: + + - coarse-grain cell types that only operate on certain bit widths + - memory resources for different memory geometries (width, depth, ports, + etc.) + +Example: + +.. figure:: /_images/code_examples/techmap/sym_mul.* + :class: width-helper + +.. literalinclude:: /code_examples/techmap/sym_mul_map.v + :language: verilog + :caption: :file:`sym_mul_map.v` + +.. literalinclude:: /code_examples/techmap/sym_mul_test.v + :language: verilog + :caption: :file:`sym_mul_test.v` + +.. literalinclude:: /code_examples/techmap/sym_mul_test.ys + :language: yoscrypt + :caption: :file:`sym_mul_test.ys` + + +Scripting in map modules +~~~~~~~~~~~~~~~~~~~~~~~~ + +- The special wires ``_TECHMAP_DO_*`` can be used to run Yosys scripts in the + context of the replacement module. +- The wire that comes first in alphabetical oder is interpreted as string (must + be connected to constants) that is executed as script. Then the wire is + removed. Repeat. +- You can even call techmap recursively! +- Example use-cases: + + - Using always blocks in map module: call :cmd:ref:`proc` + - Perform expensive optimizations (such as :cmd:ref:`freduce`) on cells + where this is known to work well. + - Interacting with custom commands. + +.. note:: PROTIP: + + Commands such as :cmd:ref:`shell`, ``show -pause``, and :cmd:ref:`dump` can + be used in the ``_TECHMAP_DO_*`` scripts for debugging map modules. + +Example: + +.. figure:: /_images/code_examples/techmap/mymul.* + :class: width-helper + +.. literalinclude:: /code_examples/techmap/mymul_map.v + :language: verilog + :caption: :file:`mymul_map.v` + +.. literalinclude:: /code_examples/techmap/mymul_test.v + :language: verilog + :caption: :file:`mymul_test.v` + +.. literalinclude:: /code_examples/techmap/mymul_test.ys + :language: yoscrypt + :caption: :file:`mymul_test.ys` + +Handling constant inputs +~~~~~~~~~~~~~~~~~~~~~~~~ + +- The special parameters ``_TECHMAP_CONSTMSK__`` and + ``_TECHMAP_CONSTVAL__`` can be used to handle constant input values + to cells. +- The former contains 1-bits for all constant input bits on the port. +- The latter contains the constant bits or undef (x) for non-constant bits. +- Example use-cases: + + - Converting arithmetic (for example multiply to shift). + - Identify constant addresses or enable bits in memory interfaces. + +Example: + +.. figure:: /_images/code_examples/techmap/mulshift.* + :class: width-helper + +.. literalinclude:: /code_examples/techmap/mulshift_map.v + :language: verilog + :caption: :file:`mulshift_map.v` + +.. literalinclude:: /code_examples/techmap/mulshift_test.v + :language: verilog + :caption: :file:`mulshift_test.v` + +.. literalinclude:: /code_examples/techmap/mulshift_test.ys + :language: yoscrypt + :caption: :file:`mulshift_test.ys` + +Handling shorted inputs +~~~~~~~~~~~~~~~~~~~~~~~ + +- The special parameters ``_TECHMAP_BITS_CONNMAP_`` and + ``_TECHMAP_CONNMAP__`` can be used to handle shorted inputs. +- Each bit of the port correlates to an ``_TECHMAP_BITS_CONNMAP_`` bits wide + number in ``_TECHMAP_CONNMAP__``. +- Each unique signal bit is assigned its own number. Identical fields in the + ``_TECHMAP_CONNMAP__`` parameters mean shorted signal bits. +- The numbers 0-3 are reserved for ``0``, ``1``, ``x``, and ``z`` respectively. +- Example use-cases: + + - Detecting shared clock or control signals in memory interfaces. + - In some cases this can be used for for optimization. + +Example: + +.. figure:: /_images/code_examples/techmap/addshift.* + :class: width-helper + +.. literalinclude:: /code_examples/techmap/addshift_map.v + :language: verilog + :caption: :file:`addshift_map.v` + +.. literalinclude:: /code_examples/techmap/addshift_test.v + :language: verilog + :caption: :file:`addshift_test.v` + +.. literalinclude:: /code_examples/techmap/addshift_test.ys + :language: yoscrypt + :caption: :file:`addshift_test.ys` + +Notes on using techmap +~~~~~~~~~~~~~~~~~~~~~~ + +- Don't use positional cell parameters in map modules. +- You can use the ``$__``-prefix for internal cell types to avoid collisions + with the user-namespace. But always use two underscores or the internal + consistency checker will trigger on these cells. +- Techmap has two major use cases: + + - Creating good logic-level representation of arithmetic functions. This + also means using dedicated hardware resources such as half- and full-adder + cells in ASICS or dedicated carry logic in FPGAs. + - Mapping of coarse-grain resources such as block memory or DSP cells. diff --git a/docs/tests/macro_commands.py b/docs/tests/macro_commands.py new file mode 100755 index 00000000000..faf2baa530b --- /dev/null +++ b/docs/tests/macro_commands.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 + +import logging +from pathlib import Path +import re +import subprocess +import sys + +# basic logging setup +logging.basicConfig(level=logging.INFO) + +# expects __file__ = yosys/docs/tests/macro_commands.py +TESTS_DIR = Path(__file__).parent +ROOT_DIR = TESTS_DIR.parent.parent +THIS_FILE = (TESTS_DIR / "macro_commands.py").relative_to(ROOT_DIR) +MACRO_SOURCE = TESTS_DIR.parent / "source" / "code_examples" / "macro_commands" +assert MACRO_SOURCE.exists(), f"can't find macro_commands in {MACRO_SOURCE}" + +YOSYS = TESTS_DIR.parent.parent / "yosys" +assert YOSYS.exists(), f"can't find yosys executable in {YOSYS}" + +raise_error = False +# get all macro commands being used +for macro in MACRO_SOURCE.glob("*.ys"): + # log current test + relative_path = macro.relative_to(ROOT_DIR) + logging.log(logging.INFO, f"Checking {relative_path}") + # files in macros_commands should be named {command}.ys + command = macro.stem + # run `yosys -h {command}` to capture current sub-commands + proc = subprocess.run([YOSYS, "-QTh", command], capture_output=True, text=True) + with open(macro) as f: + # {command.ys} starts with two commented lines, the first is the text + # immediately prior to the macro body. The second is the text + # immediately after. + start = f.readline() + end = f.readline() + file_content = f.readlines() + expected_content = [] + for line in file_content: + line = line.split("#")[0].strip() + if line: + expected_content.append(line) + # parse {command.ys} + if "#start:" not in start or "#end:" not in end: + logging.error(f"Missing start and/or end string in {relative_path}, see {THIS_FILE}") + raise_error = True + continue + start = start.replace("#start:", "").strip() + end = end.replace("#end:", "").strip() + # attempt to find macro body in help output + match = re.fullmatch(f".*{start}(.*){end}.*", proc.stdout, re.DOTALL) + if not match: + logging.error(f"Couldn't find {start!r} and/or {end!r} in `yosys -h {command}` output") + raise_error = True + continue + match_content = match.group(1).strip().splitlines() + actual_content = [] + for line in match_content: + if line: + actual_content.append(line) + # iterate over and compare expected v actual + for (expected, actual) in zip(expected_content, actual_content): + expected = expected.strip() + actual = actual.strip() + # raw match + if expected == actual: + continue + + # rip apart formatting to match line parts + pattern = r"(?P\S+)(?P \[.*\])?(?P.*?)(?P\s+\(.*\))?" + try: + expected_dict = re.fullmatch(pattern, expected).groupdict() + except AttributeError: + logging.error(f"Bad formatting in {relative_path}: {expected!r}") + raise_error = True + continue + + try: + actual_dict = re.fullmatch(pattern, actual).groupdict() + except AttributeError: + logging.error(f"Unexpected formatting in `yosys -h {command}` output, {actual!r}") + raise_error = True + continue + + does_match = expected_dict["cmd"] == actual_dict["cmd"] + + #todo: check expected_dict["pass"] is a subset of actual_dict["pass"] + # only check opt and cond match if they're in {command}.ys + match_keys = ["opt", "cond"] + for key in match_keys: + if expected_dict[key] and expected_dict[key] != actual_dict[key]: + does_match = False + + # raise error on mismatch + if not does_match: + logging.error(f"Expected {expected!r}, got {actual!r}") + raise_error = True + +sys.exit(raise_error) diff --git a/docs/util/RtlilLexer.py b/docs/util/RtlilLexer.py new file mode 100644 index 00000000000..75aa53ec84c --- /dev/null +++ b/docs/util/RtlilLexer.py @@ -0,0 +1,45 @@ +from pygments.lexer import RegexLexer, bygroups, include +from pygments.token import Comment, Keyword, Name, Number, String, Whitespace + +__all__ = ['RtlilLexer'] + +class RtlilLexer(RegexLexer): + name = 'RTLIL' + aliases = ['rtlil'] + filenames = ['*.il'] + + keyword_re = r'(always|assign|attribute|autoidx|case|cell|connect|edge|end|global|high|init|inout|input|low|memory|module|negedge|offset|output|parameter|posedge|process|real|signed|size|switch|sync|update|upto|width|wire)' + + tokens = { + 'common': [ + (r'\s+', Whitespace), + (r'#.*', Comment.Single), + (keyword_re, Keyword), + (r'([\\\$][^ \t\r\n]+|\.[0-9]+)', Name.Variable), + (r"[0-9]+'[01xzm-]*", Number), + (r'-?[0-9]+', Number.Integer), + (r'"', String, 'string'), + ], + 'root': [ + (r'cell', Keyword, 'cell_definition'), + (r'(module|wire|memory|process)', Keyword, 'definition'), + include('common'), + ], + 'definition': [ + (r'([\\\$][^ \t\r\n]+|\.[0-9]+)', Name.Entity, '#pop'), + include('common') + ], + 'cell_definition': [ + (r'(\$[^ \t\r\n]+)\b', Name.Function), + (r'(\\[^ \t\r\n]+|\.[0-9]+)', Name.Variable), + (r'$', Whitespace, '#pop'), + include('common'), + ], + 'string': [ + (r'"', String, '#pop'), + (r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})', String.Escape), + (r'[^\\"\n]+', String), # all other characters + (r'(\\)(\n)', bygroups(String.Escape, Whitespace)), # line continuation + (r'\\', String), # stray backslash + ] + } diff --git a/docs/util/YoscryptLexer.py b/docs/util/YoscryptLexer.py new file mode 100644 index 00000000000..8cb31c81aee --- /dev/null +++ b/docs/util/YoscryptLexer.py @@ -0,0 +1,73 @@ +from pygments.lexer import RegexLexer, bygroups, include +from pygments.token import (Comment, Error, Keyword, Name, Number, Operator, + String, Whitespace) + +__all__ = ['YoscryptLexer'] + +class YoscryptLexer(RegexLexer): + name = 'Yosys Script' + aliases = ['yoscrypt'] + filenames = ['*.ys'] + + + + tokens = { + 'common': [ + (r'\s+', Whitespace), + (r'#.*', Comment.Single), + (r'"', String, 'string'), + (r'(\d+)(\')([bdho]? ?\w+)', bygroups(Number, Operator, Number)), + (r'(\d+\.\d+)', Number.Float), + (r'(\d+)', Number), + (r'(\$[A-Za-z_0-9]*)', Name.Builtin), + (r'([A-Za-z_][A-Za-z_0-9\.\\/:-]*)', Name), + (r'(\[)(-\S*)(\])', # optional command + bygroups(Operator, Name.Attribute, Operator)), + (r'([\[<]\w*[\]>])', Name), # arguments + (r'[\{\}\|=\[\],]', Operator), + (r'.', Comment), + ], + 'root': [ + (r'([A-Za-z_][A-Za-z_0-9]*)', Keyword, 'command'), + (r'(-[A-Za-z_][A-Za-z_0-9]*)', Name.Attribute, 'command'), # shortcut for options + include('common'), + ], + 'command': [ + (r'(-[A-Za-z_][A-Za-z_0-9]*)', Name.Attribute), + (r'\+/[^\s]+', Name.Class), + (r'$', Whitespace, '#pop'), + (r';(?=\s)', Operator, '#pop'), + (r';{2,3}(?=\s)', Name.Class, '#pop'), + (r';{1,3}', Error, '#pop'), + (r'([ANwismctparn]:)', Keyword.Type, 'pattern'), + (r'@', Keyword.Type), + (r'%(x|ci|co)e?', Keyword.Type, 'expansion'), + (r'%[%nuidDcasmMCR]?', Keyword.Type), + (r'/', Operator), + include('common'), + ], + 'pattern': [ + (r'<<', Name), # Not an operator + (r'(=|<|<=|>|>=)', Operator), + (r':', Keyword.Type), + (r'$', Whitespace, '#pop:2'), + (r'\s+', Whitespace, '#pop'), + include('common'), + ], + 'expansion': [ + (r'$', Name.Class, '#pop:2'), + (r';(?=\s)', Operator, '#pop:2'), + (r';{2,3}(?=\s)', Name.Class, '#pop:2'), + (r'\s', Whitespace, '#pop'), + (r'[0-9\*]{1,3}', Number), + (r'[:+-,\[\]]', Operator), + include('common'), + ], + 'string': [ + (r'"', String, '#pop'), + (r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})', String.Escape), + (r'[^\\"\n]+', String), # all other characters + (r'(\\)(\n)', bygroups(String.Escape, Whitespace)), # line continuation + (r'\\', String), # stray backslash + ] + } diff --git a/docs/util/__init__.py b/docs/util/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/docs/util/cmdref.py b/docs/util/cmdref.py new file mode 100644 index 00000000000..ec146e23173 --- /dev/null +++ b/docs/util/cmdref.py @@ -0,0 +1,246 @@ +# based on https://github.com/ofosos/sphinxrecipes/blob/master/sphinxrecipes/sphinxrecipes.py +# license: +# Copyright 2019 Mark Meyer +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import docutils +from docutils import nodes +import sphinx +from docutils.parsers import rst +from docutils.parsers.rst import directives +from sphinx.domains import Domain, Index +from sphinx.domains.std import StandardDomain +from sphinx.roles import XRefRole +from sphinx.directives import ObjectDescription +from sphinx.util.nodes import make_refnode +from sphinx import addnodes + +class CommandNode(ObjectDescription): + """A custom node that describes a command.""" + + required_arguments = 1 + + option_spec = { + 'title': directives.unchanged_required, + 'tags': directives.unchanged + } + + def handle_signature(self, sig, signode: addnodes.desc_signature): + signode += addnodes.desc_addname(text="yosys> help ") + signode += addnodes.desc_name(text=sig) + return sig + + def add_target_and_index(self, name_cls, sig, signode): + signode['ids'].append('cmd' + '-' + sig) + if 'noindex' not in self.options: + name = "{}.{}.{}".format('cmd', type(self).__name__, sig) + tagmap = self.env.domaindata['cmd']['obj2tag'] + tagmap[name] = list(self.options.get('tags', '').split(' ')) + title = self.options.get('title') + titlemap = self.env.domaindata['cmd']['obj2title'] + titlemap[name] = title + objs = self.env.domaindata['cmd']['objects'] + objs.append((name, + sig, + title, + self.env.docname, + 'cmd' + '-' + sig, + 0)) + +class TagIndex(Index): + """A custom directive that creates an tag matrix.""" + + name = 'tag' + localname = 'Tag Index' + shortname = 'Tag' + + def __init__(self, *args, **kwargs): + super(TagIndex, self).__init__(*args, **kwargs) + + def generate(self, docnames=None): + """Return entries for the index given by *name*. If *docnames* is + given, restrict to entries referring to these docnames. + The return value is a tuple of ``(content, collapse)``, where + * collapse* is a boolean that determines if sub-entries should + start collapsed (for output formats that support collapsing + sub-entries). + *content* is a sequence of ``(letter, entries)`` tuples, where *letter* + is the "heading" for the given *entries*, usually the starting letter. + *entries* is a sequence of single entries, where a single entry is a + sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``. + The items in this sequence have the following meaning: + - `name` -- the name of the index entry to be displayed + - `subtype` -- sub-entry related type: + 0 -- normal entry + 1 -- entry with sub-entries + 2 -- sub-entry + - `docname` -- docname where the entry is located + - `anchor` -- anchor for the entry within `docname` + - `extra` -- extra info for the entry + - `qualifier` -- qualifier for the description + - `descr` -- description for the entry + Qualifier and description are not rendered e.g. in LaTeX output. + """ + + content = {} + + objs = {name: (dispname, typ, docname, anchor) + for name, dispname, typ, docname, anchor, prio + in self.domain.get_objects()} + + tmap = {} + tags = self.domain.data['obj2tag'] + for name, tags in tags.items(): + for tag in tags: + tmap.setdefault(tag,[]) + tmap[tag].append(name) + + for tag in tmap.keys(): + lis = content.setdefault(tag, []) + objlis = tmap[tag] + for objname in objlis: + dispname, typ, docname, anchor = objs[objname] + lis.append(( + dispname, 0, docname, + anchor, + docname, '', typ + )) + re = [(k, v) for k, v in sorted(content.items())] + + return (re, True) + + +class CommandIndex(Index): + name = 'cmd' + localname = 'Command Reference' + shortname = 'Command' + + def __init__(self, *args, **kwargs): + super(CommandIndex, self).__init__(*args, **kwargs) + + def generate(self, docnames=None): + """Return entries for the index given by *name*. If *docnames* is + given, restrict to entries referring to these docnames. + The return value is a tuple of ``(content, collapse)``, where + * collapse* is a boolean that determines if sub-entries should + start collapsed (for output formats that support collapsing + sub-entries). + *content* is a sequence of ``(letter, entries)`` tuples, where *letter* + is the "heading" for the given *entries*, usually the starting letter. + *entries* is a sequence of single entries, where a single entry is a + sequence ``[name, subtype, docname, anchor, extra, qualifier, descr]``. + The items in this sequence have the following meaning: + - `name` -- the name of the index entry to be displayed + - `subtype` -- sub-entry related type: + 0 -- normal entry + 1 -- entry with sub-entries + 2 -- sub-entry + - `docname` -- docname where the entry is located + - `anchor` -- anchor for the entry within `docname` + - `extra` -- extra info for the entry + - `qualifier` -- qualifier for the description + - `descr` -- description for the entry + Qualifier and description are not rendered e.g. in LaTeX output. + """ + + content = {} + items = ((name, dispname, typ, docname, anchor) + for name, dispname, typ, docname, anchor, prio + in self.domain.get_objects()) + items = sorted(items, key=lambda item: item[0]) + for name, dispname, typ, docname, anchor in items: + lis = content.setdefault('Command', []) + lis.append(( + dispname, 0, docname, + anchor, + '', '', typ + )) + re = [(k, v) for k, v in sorted(content.items())] + + return (re, True) + + +class CommandDomain(Domain): + name = 'cmd' + label = 'Command Sample' + + roles = { + 'ref': XRefRole() + } + + directives = { + 'def': CommandNode, + } + + indices = { + CommandIndex, + TagIndex + } + + initial_data = { + 'objects': [], # object list + 'obj2tag': {}, # name -> tags + 'obj2title': {}, # name -> title + } + + def get_full_qualified_name(self, node): + """Return full qualified name for a given node""" + return "{}.{}.{}".format('cmd', + type(node).__name__, + node.arguments[0]) + + def get_objects(self): + for obj in self.data['objects']: + yield(obj) + + def resolve_xref(self, env, fromdocname, builder, typ, + target, node, contnode): + + match = [(docname, anchor, name) + for name, sig, typ, docname, anchor, prio + in self.get_objects() if sig == target] + + if match: + todocname = match[0][0] + targ = match[0][1] + qual_name = match[0][2] + title = self.data['obj2title'].get(qual_name, targ) + + return make_refnode(builder,fromdocname,todocname, + targ, contnode, title) + else: + print(f"Missing ref for {target} in {fromdocname} ") + return None + +def setup(app): + app.add_domain(CommandDomain) + + StandardDomain.initial_data['labels']['commandindex'] =\ + ('cmd-cmd', '', 'Command Reference') + StandardDomain.initial_data['labels']['tagindex'] =\ + ('cmd-tag', '', 'Tag Index') + + StandardDomain.initial_data['anonlabels']['commandindex'] =\ + ('cmd-cmd', '') + StandardDomain.initial_data['anonlabels']['tagindex'] =\ + ('cmd-tag', '') + + return {'version': '0.1'} diff --git a/examples/cxx-api/scopeinfo_example.cc b/examples/cxx-api/scopeinfo_example.cc new file mode 100644 index 00000000000..f163dff9eee --- /dev/null +++ b/examples/cxx-api/scopeinfo_example.cc @@ -0,0 +1,144 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2023 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// build: yosys-config --build scopeinfo_example.so scopeinfo_example.cc +// use: yosys -m scopeinfo_example.so + +#include "backends/rtlil/rtlil_backend.h" +#include "kernel/scopeinfo.h" +#include "kernel/yosys.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ScopeinfoExamplePass : public Pass { + ScopeinfoExamplePass() : Pass("scopeinfo_example", "dump scopeinfo") {} + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" scopeinfo_example [options] [selection]\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing SCOPEINFO_EXAMPLE pass.\n"); + + bool do_wires = false; + bool do_common = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-wires") { + do_wires = true; + continue; + } + if (args[argidx] == "-common") { + do_common = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + + if (do_wires) { + for (auto module : design->selected_modules()) { + log("Source hierarchy for all selected wires within %s:\n", log_id(module)); + ModuleHdlnameIndex index(module); + + index.index_scopeinfo_cells(); + + for (auto wire : module->selected_wires()) { + if (!wire->name.isPublic()) + continue; + + auto wire_scope = index.containing_scope(wire); + + if (!wire_scope.first.valid()) { + log_warning("Couldn't find containing scope for %s in index\n", log_id(wire)); + continue; + } + + log("%s %s\n", wire_scope.first.path_str().c_str(), log_id(wire_scope.second)); + for (auto src : index.sources(wire)) + log(" - %s\n", src.c_str()); + } + } + } + + if (do_common) { + for (auto module : design->selected_modules()) { + std::vector wires = module->selected_wires(); + + // Shuffle wires so this example produces more interesting outputs + std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) { + return mkhash_xorshift(a->name.hash() * 0x2c9277b5) < mkhash_xorshift(b->name.hash() * 0x2c9277b5); + }); + + ModuleHdlnameIndex index(module); + + index.index_scopeinfo_cells(); + + for (auto wire_i = wires.begin(), wire_end = wires.end(); wire_i != wire_end; ++wire_i) { + if (!(*wire_i)->name.isPublic()) + continue; + + std::pair scope_i = index.containing_scope(*wire_i); + if (!scope_i.first.valid()) + continue; + + int limit = 0; + + for (auto wire_j = wire_i + 1; wire_j != wire_end; ++wire_j) { + if (!(*wire_j)->name.isPublic()) + continue; + + std::pair scope_j = index.containing_scope(*wire_j); + if (!scope_j.first.valid()) + continue; + + // Skip wires in the same hierarchy level + if (scope_i.first == scope_j.first) + continue; + + + ModuleHdlnameIndex::Cursor common = scope_i.first.common_ancestor(scope_j.first); + + // Try to show at least some non-root common ancestors + if (common.is_root() && limit > 5) + continue; + + log("common_ancestor(%s %s%s%s, %s %s%s%s) = %s %s\n", + log_id(module), scope_i.first.path_str().c_str(), scope_i.first.is_root() ? "" : " ", log_id(scope_i.second), + log_id(module), scope_j.first.path_str().c_str(), scope_j.first.is_root() ? "" : " ", log_id(scope_j.second), + log_id(module), common.path_str().c_str() + ); + + if (++limit == 10) + break; + } + } + } + } + } +} ScopeinfoExamplePass; + +PRIVATE_NAMESPACE_END diff --git a/examples/python-api/pass.py b/examples/python-api/pass.py index d67cf4a23c5..dbef0a13fb9 100755 --- a/examples/python-api/pass.py +++ b/examples/python-api/pass.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -from pyosys import libyosys as ys +import libyosys as ys import matplotlib.pyplot as plt import numpy as np diff --git a/examples/smtbmc/Makefile b/examples/smtbmc/Makefile index 61994f942fb..af937ea74d0 100644 --- a/examples/smtbmc/Makefile +++ b/examples/smtbmc/Makefile @@ -1,5 +1,5 @@ -all: demo1 demo2 demo3 demo4 demo5 demo6 demo7 demo8 demo9 +all: demo1 demo2 demo3 demo4 demo5 demo6 demo7 demo8 demo9 glift_mux demo1: demo1.smt2 yosys-smtbmc --dump-vcd demo1.vcd demo1.smt2 @@ -31,6 +31,9 @@ demo8: demo8.smt2 demo9: demo9.smt2 yosys-smtbmc -s z3 -t 1 -g demo9.smt2 +glift_mux: + yosys -ql glift_mux.yslog glift/mux2.ys + demo1.smt2: demo1.v yosys -ql demo1.yslog -p 'read_verilog -formal demo1.v; prep -top demo1 -nordff; write_smt2 -wires demo1.smt2' @@ -68,6 +71,7 @@ clean: rm -f demo7.yslog demo7.smt2 rm -f demo8.yslog demo8.smt2 rm -f demo9.yslog demo9.smt2 + rm -f glift_mux.ys .PHONY: demo1 demo2 demo3 demo4 demo5 demo6 demo7 demo8 demo9 clean diff --git a/examples/smtbmc/glift/C7552.v b/examples/smtbmc/glift/C7552.v new file mode 100755 index 00000000000..47a8b0d37d7 --- /dev/null +++ b/examples/smtbmc/glift/C7552.v @@ -0,0 +1,4194 @@ +module C7552_lev2(pi000, pi001, pi002, pi003, pi004, pi005, pi006, pi007, pi008, pi009, + pi010, pi011, pi012, pi013, pi014, pi015, pi016, pi017, pi018, pi019, + pi020, pi021, pi022, pi023, pi024, pi025, pi026, pi027, pi028, pi029, + pi030, pi031, pi032, pi033, pi034, pi035, pi036, pi037, pi038, pi039, + pi040, pi041, pi042, pi043, pi044, pi045, pi046, pi047, pi048, pi049, + pi050, pi051, pi052, pi053, pi054, pi055, pi056, pi057, pi058, pi059, + pi060, pi061, pi062, pi063, pi064, pi065, pi066, pi067, pi068, pi069, + pi070, pi071, pi072, pi073, pi074, pi075, pi076, pi077, pi078, pi079, + pi080, pi081, pi082, pi083, pi084, pi085, pi086, pi087, pi088, pi089, + pi090, pi091, pi092, pi093, pi094, pi095, pi096, pi097, pi098, pi099, + pi100, pi101, pi102, pi103, pi104, pi105, pi106, pi107, pi108, pi109, + pi110, pi111, pi112, pi113, pi114, pi115, pi116, pi117, pi118, pi119, + pi120, pi121, pi122, pi123, pi124, pi125, pi126, pi127, pi128, pi129, + pi130, pi131, pi132, pi133, pi134, pi135, pi136, pi137, pi138, pi139, + pi140, pi141, pi142, pi143, pi144, pi145, pi146, pi147, pi148, pi149, + pi150, pi151, pi152, pi153, pi154, pi155, pi156, pi157, pi158, pi159, + pi160, pi161, pi162, pi163, pi164, pi165, pi166, pi167, pi168, pi169, + pi170, pi171, pi172, pi173, pi174, pi175, pi176, pi177, pi178, pi179, + pi180, pi181, pi182, pi183, pi184, pi185, pi186, pi187, pi188, pi189, + pi190, pi191, pi192, pi193, pi194, pi195, pi196, pi197, pi198, pi199, + pi200, pi201, pi202, pi203, pi204, pi205, pi206, po000, po001, po002, + po003, po004, po005, po006, po007, po008, po009, po010, po011, po012, + po013, po014, po015, po016, po017, po018, po019, po020, po021, po022, + po023, po024, po025, po026, po027, po028, po029, po030, po031, po032, + po033, po034, po035, po036, po037, po038, po039, po040, po041, po042, + po043, po044, po045, po046, po047, po048, po049, po050, po051, po052, + po053, po054, po055, po056, po057, po058, po059, po060, po061, po062, + po063, po064, po065, po066, po067, po068, po069, po070, po071, po072, + po073, po074, po075, po076, po077, po078, po079, po080, po081, po082, + po083, po084, po085, po086, po087, po088, po089, po090, po091, po092, + po093, po094, po095, po096, po097, po098, po099, po100, po101, po102, + po103, po104, po105, po106, po107); + +input pi000, pi001, pi002, pi003, pi004, pi005, pi006, pi007, pi008, pi009, + pi010, pi011, pi012, pi013, pi014, pi015, pi016, pi017, pi018, pi019, + pi020, pi021, pi022, pi023, pi024, pi025, pi026, pi027, pi028, pi029, + pi030, pi031, pi032, pi033, pi034, pi035, pi036, pi037, pi038, pi039, + pi040, pi041, pi042, pi043, pi044, pi045, pi046, pi047, pi048, pi049, + pi050, pi051, pi052, pi053, pi054, pi055, pi056, pi057, pi058, pi059, + pi060, pi061, pi062, pi063, pi064, pi065, pi066, pi067, pi068, pi069, + pi070, pi071, pi072, pi073, pi074, pi075, pi076, pi077, pi078, pi079, + pi080, pi081, pi082, pi083, pi084, pi085, pi086, pi087, pi088, pi089, + pi090, pi091, pi092, pi093, pi094, pi095, pi096, pi097, pi098, pi099, + pi100, pi101, pi102, pi103, pi104, pi105, pi106, pi107, pi108, pi109, + pi110, pi111, pi112, pi113, pi114, pi115, pi116, pi117, pi118, pi119, + pi120, pi121, pi122, pi123, pi124, pi125, pi126, pi127, pi128, pi129, + pi130, pi131, pi132, pi133, pi134, pi135, pi136, pi137, pi138, pi139, + pi140, pi141, pi142, pi143, pi144, pi145, pi146, pi147, pi148, pi149, + pi150, pi151, pi152, pi153, pi154, pi155, pi156, pi157, pi158, pi159, + pi160, pi161, pi162, pi163, pi164, pi165, pi166, pi167, pi168, pi169, + pi170, pi171, pi172, pi173, pi174, pi175, pi176, pi177, pi178, pi179, + pi180, pi181, pi182, pi183, pi184, pi185, pi186, pi187, pi188, pi189, + pi190, pi191, pi192, pi193, pi194, pi195, pi196, pi197, pi198, pi199, + pi200, pi201, pi202, pi203, pi204, pi205, pi206; + +output po000, po001, po002, po003, po004, po005, po006, po007, po008, po009, + po010, po011, po012, po013, po014, po015, po016, po017, po018, po019, + po020, po021, po022, po023, po024, po025, po026, po027, po028, po029, + po030, po031, po032, po033, po034, po035, po036, po037, po038, po039, + po040, po041, po042, po043, po044, po045, po046, po047, po048, po049, + po050, po051, po052, po053, po054, po055, po056, po057, po058, po059, + po060, po061, po062, po063, po064, po065, po066, po067, po068, po069, + po070, po071, po072, po073, po074, po075, po076, po077, po078, po079, + po080, po081, po082, po083, po084, po085, po086, po087, po088, po089, + po090, po091, po092, po093, po094, po095, po096, po097, po098, po099, + po100, po101, po102, po103, po104, po105, po106, po107; + +wire n2822, n2823, n2824, n2825, n2826, n2827, n2828, n2829, n2830, n2831, + n2832, n2833, n2834, n2835, n2836, n2837, n2838, n2839, n2840, n2841, + n2842, n2843, n2844, n2845, n2846, n2847, n2848, n2849, n2850, n2851, + n2852, n2853, n2854, n2855, n2856, n2857, n2858, n2859, n2860, n2861, + n2862, n2863, n2864, n2865, n2866, n2867, n2868, n2869, n2870, n2871, + n2872, n2873, n2874, n2875, n2876, n2877, n2878, n2879, n2880, n2881, + n2882, n2883, n2884, n2885, n2886, n2887, n2888, n2889, n2890, n2891, + n2892, n2893, n2894, n2895, n2896, n2897, n2898, n2899, n2900, n2901, + n2902, n2903, n2904, n2905, n2906, n2907, n2908, n2909, n2910, n2911, + n2912, n2913, n2914, n2915, n2916, n2917, n2918, n2919, n2920, n2921, + n2922, n2923, n2924, n2925, n2926, n2927, n2928, n2929, n2930, n2931, + n2932, n2933, n2934, n2935, n2936, n2937, n2938, n2939, n2940, n2941, + n2942, n2943, n2944, n2945, n2946, n2947, n2948, n2949, n2950, n2951, + n2952, n2953, n2954, n2955, n2956, n2957, n2958, n2959, n2960, n2961, + n2962, n2963, n2964, n2965, n2966, n2967, n2968, n2969, n2970, n2971, + n2972, n2973, n2974, n2975, n2976, n2977, n2978, n2979, n2980, n2981, + n2982, n2983, n2984, n2985, n2986, n2987, n2988, n2989, n2990, n2991, + n2992, n2993, n2994, n2995, n2996, n2997, n2998, n2999, n3000, n3001, + n3002, n3003, n3004, n3005, n3006, n3007, n3008, n3009, n3010, n3011, + n3012, n3013, n3014, n3015, n3016, n3017, n3018, n3019, n3020, n3021, + n3022, n3023, n3024, n3025, n3026, n3027, n3028, n3029, n3030, n3031, + n3032, n3033, n3034, n3035, n3036, n3037, n3038, n3039, n3040, n3041, + n3042, n3043, n3044, n3045, n3046, n3047, n3048, n3049, n3050, n3051, + n3052, n3053, n3054, n3055, n3056, n3057, n3058, n3059, n3060, n3061, + n3062, n3063, n3064, n3065, n3066, n3067, n3068, n3069, n3070, n3071, + n3072, n3073, n3074, n3075, n3076, n3077, n3078, n3079, n3080, n3081, + n3082, n3083, n3084, n3085, n3086, n3087, n3088, n3089, n3090, n3091, + n3092, n3093, n3094, n3095, n3096, n3097, n3098, n3099, n3100, n3101, + n3102, n3103, n3104, n3105, n3106, n3107, n3108, n3109, n3110, n3111, + n3112, n3113, n3114, n3115, n3116, n3117, n3118, n3119, n3120, n3121, + n3122, n3123, n3124, n3125, n3126, n3127, n3128, n3129, n3130, n3131, + n3132, n3133, n3134, n3135, n3136, n3137, n3138, n3139, n3140, n3141, + n3142, n3143, n3144, n3145, n3146, n3147, n3148, n3149, n3150, n3151, + n3152, n3153, n3154, n3155, n3156, n3157, n3158, n3159, n3160, n3161, + n3162, n3163, n3164, n3165, n3166, n3167, n3168, n3169, n3170, n3171, + n3172, n3173, n3174, n3175, n3176, n3177, n3178, n3179, n3180, n3181, + n3182, n3183, n3184, n3185, n3186, n3187, n3188, n3189, n3190, n3191, + n3192, n3193, n3194, n3195, n3196, n3197, n3198, n3199, n3200, n3201, + n3202, n3203, n3204, n3205, n3206, n3207, n3208, n3209, n3210, n3211, + n3212, n3213, n3214, n3215, n3216, n3217, n3218, n3219, n3220, n3221, + n3222, n3223, n3224, n3225, n3226, n3227, n3228, n3229, n3230, n3231, + n3232, n3233, n3234, n3235, n3236, n3237, n3238, n3239, n3240, n3241, + n3242, n3243, n3244, n3245, n3246, n3247, n3248, n3249, n3250, n3251, + n3252, n3253, n3254, n3255, n3256, n3257, n3258, n3259, n3260, n3261, + n3262, n3263, n3264, n3265, n3266, n3267, n3268, n3269, n3270, n3271, + n3272, n3273, n3274, n3275, n3276, n3277, n3278, n3279, n3280, n3281, + n3282, n3283, n3284, n3285, n3286, n3287, n3288, n3289, n3290, n3291, + n3292, n3293, n3294, n3295, n3296, n3297, n3298, n3299, n3300, n3301, + n3302, n3303, n3304, n3305, n3306, n3307, n3308, n3309, n3310, n3311, + n3312, n3313, n3314, n3315, n3316, n3317, n3318, n3319, n3320, n3321, + n3322, n3323, n3324, n3325, n3326, n3327, n3328, n3329, n3330, n3331, + n3332, n3333, n3334, n3335, n3336, n3337, n3338, n3339, n3340, n3341, + n3342, n3343, n3344, n3345, n3346, n3347, n3348, n3349, n3350, n3351, + n3352, n3353, n3354, n3355, n3356, n3357, n3358, n3359, n3360, n3361, + n3362, n3363, n3364, n3365, n3366, n3367, n3368, n3369, n3370, n3371, + n3372, n3373, n3374, n3375, n3376, n3377, n3378, n3379, n3380, n3381, + n3382, n3383, n3384, n3385, n3386, n3387, n3388, n3389, n3390, n3391, + n3392, n3393, n3394, n3395, n3396, n3397, n3398, n3399, n3400, n3401, + n3402, n3403, n3404, n3405, n3406, n3407, n3408, n3409, n3410, n3411, + n3412, n3413, n3414, n3415, n3416, n3417, n3418, n3419, n3420, n3421, + n3422, n3423, n3424, n3425, n3426, n3427, n3428, n3429, n3430, n3431, + n3432, n3433, n3434, n3435, n3436, n3437, n3438, n3439, n3440, n3441, + n3442, n3443, n3444, n3445, n3446, n3447, n3448, n3449, n3450, n3451, + n3452, n3453, n3454, n3455, n3456, n3457, n3458, n3459, n3460, n3461, + n3462, n3463, n3464, n3465, n3466, n3467, n3468, n3469, n3470, n3471, + n3472, n3473, n3474, n3475, n3476, n3477, n3478, n3479, n3480, n3481, + n3482, n3483, n3484, n3485, n3486, n3487, n3488, n3489, n3490, n3491, + n3492, n3493, n3494, n3495, n3496, n3497, n3498, n3499, n3500, n3501, + n3502, n3503, n3504, n3505, n3506, n3507, n3508, n3509, n3510, n3511, + n3512, n3513, n3514, n3515, n3516, n3517, n3518, n3519, n3520, n3521, + n3522, n3523, n3524, n3525, n3526, n3527, n3528, n3529, n3530, n3531, + n3532, n3533, n3534, n3535, n3536, n3537, n3538, n3539, n3540, n3541, + n3542, n3543, n3544, n3545, n3546, n3547, n3548, n3549, n3550, n3551, + n3552, n3553, n3554, n3555, n3556, n3557, n3558, n3559, n3560, n3561, + n3562, n3563, n3564, n3565, n3566, n3567, n3568, n3569, n3570, n3571, + n3572, n3573, n3574, n3575, n3576, n3577, n3578, n3579, n3580, n3581, + n3582, n3583, n3584, n3585, n3586, n3587, n3588, n3589, n3590, n3591, + n3592, n3593, n3594, n3595, n3596, n3597, n3598, n3599, n3600, n3601, + n3602, n3603, n3604, n3605, n3606, n3607, n3608, n3609, n3610, n3611, + n3612, n3613, n3614, n3615, n3616, n3617, n3618, n3619, n3620, n3621, + n3622, n3623, n3624, n3625, n3626, n3627, n3628, n3629, n3630, n3631, + n3632, n3633, n3634, n3635, n3636, n3637, n3638, n3639, n3640, n3641, + n3642, n3643, n3644, n3645, n3646, n3647, n3648, n3649, n3650, n3651, + n3652, n3653, n3654, n3655, n3656, n3657, n3658, n3659, n3660, n3661, + n3662, n3663, n3664, n3665, n3666, n3667, n3668, n3669, n3670, n3671, + n3672, n3673, n3674, n3675, n3676, n3677, n3678, n3679, n3680, n3681, + n3682, n3683, n3684, n3685, n3686, n3687, n3688, n3689, n3690, n3691, + n3692, n3693, n3694, n3695, n3696, n3697, n3698, n3699, n3700, n3701, + n3702, n3703, n3704, n3705, n3706, n3707, n3708, n3709, n3710, n3711, + n3712, n3713, n3714, n3715, n3716, n3717, n3718, n3719, n3720, n3721, + n3722, n3723, n3724, n3725, n3726, n3727, n3728, n3729, n3730, n3731, + n3732, n3733, n3734, n3735, n3736, n3737, n3738, n3739, n3740, n3741, + n3742, n3743, n3744, n3745, n3746, n3747, n3748, n3749, n3750, n3751, + n3752, n3753, n3754, n3755, n3756, n3757, n3758, n3759, n3760, n3761, + n3762, n3763, n3764, n3765, n3766, n3767, n3768, n3769, n3770, n3771, + n3772, n3773, n3774, n3775, n3776, n3777, n3778, n3779, n3780, n3781, + n3782, n3783, n3784, n3785, n3786, n3787, n3788, n3789, n3790, n3791, + n3792, n3793, n3794, n3795, n3796, n3797, n3798, n3799, n3800, n3801, + n3802, n3803, n3804, n3805, n3806, n3807, n3808, n3809, n3810, n3811, + n3812, n3813, n3814, n3815, n3816, n3817, n3818, n3819, n3820, n3821, + n3822, n3823, n3824, n3825, n3826, n3827, n3828, n3829, n3830, n3831, + n3832, n3833, n3834, n3835, n3836, n3837, n3838, n3839, n3840, n3841, + n3842, n3843, n3844, n3845, n3846, n3847, n3848, n3849, n3850, n3851, + n3852, n3853, n3854, n3855, n3856, n3857, n3858, n3859, n3860, n3861, + n3862, n3863, n3864, n3865, n3866, n3867, n3868, n3869, n3870, n3871, + n3872, n3873, n3874, n3875, n3876, n3877, n3878, n3879, n3880, n3881, + n3882, n3883, n3884, n3885, n3886, n3887, n3888, n3889, n3890, n3891, + n3892, n3893, n3894, n3895, n3896, n3897, n3898, n3899, n3900, n3901, + n3902, n3903, n3904, n3905, n3906, n3907, n3908, n3909, n3910, n3911, + n3912, n3913, n3914, n3915, n3916, n3917, n3918, n3919, n3920, n3921, + n3922, n3923, n3924, n3925, n3926, n3927, n3928, n3929, n3930, n3931, + n3932, n3933, n3934, n3935, n3936, n3937, n3938, n3939, n3940, n3941, + n3942, n3943, n3944, n3945, n3946, n3947, n3948, n3949, n3950, n3951, + n3952, n3953, n3954, n3955, n3956, n3957, n3958, n3959, n3960, n3961, + n3962, n3963, n3964, n3965, n3966, n3967, n3968, n3969, n3970, n3971, + n3972, n3973, n3974, n3975, n3976, n3977, n3978, n3979, n3980, n3981, + n3982, n3983, n3984, n3985, n3986, n3987, n3988, n3989, n3990, n3991, + n3992, n3993, n3994, n3995, n3996, n3997, n3998, n3999, n4000, n4001, + n4002, n4003, n4004, n4005, n4006, n4007, n4008, n4009, n4010, n4011, + n4012, n4013, n4014, n4015, n4016, n4017, n4018, n4019, n4020, n4021, + n4022, n4023, n4024, n4025, n4026, n4027, n4028, n4029, n4030, n4031, + n4032, n4033, n4034, n4035, n4036, n4037, n4038, n4039, n4040, n4041, + n4042, n4043, n4044, n4045, n4046, n4047, n4048, n4049, n4050, n4051, + n4052, n4053, n4054, n4055, n4056, n4057, n4058, n4059, n4060, n4061, + n4062, n4063, n4064, n4065, n4066, n4067, n4068, n4069, n4070, n4071, + n4072, n4073, n4074, n4075, n4076, n4077, n4078, n4079, n4080, n4081, + n4082, n4083, n4084, n4085, n4086, n4087, n4088, n4089, n4090, n4091, + n4092, n4093, n4094, n4095, n4096, n4097, n4098, n4099, n4100, n4101, + n4102, n4103, n4104, n4105, n4106, n4107, n4108, n4109, n4110, n4111, + n4112, n4113, n4114, n4115, n4116, n4117, n4118, n4119, n4120, n4121, + n4122, n4123, n4124, n4125, n4126, n4127, n4128, n4129, n4130, n4131, + n4132, n4133, n4134, n4135, n4136, n4137, n4138, n4139, n4140, n4141, + n4142, n4143, n4144, n4145, n4146, n4147, n4148, n4149, n4150, n4151, + n4152, n4153, n4154, n4155, n4156, n4157, n4158, n4159, n4160, n4161, + n4162, n4163, n4164, n4165, n4166, n4167, n4168, n4169, n4170, n4171, + n4172, n4173, n4174, n4175, n4176, n4177, n4178, n4179, n4180, n4181, + n4182, n4183, n4184, n4185, n4186, n4187, n4188, n4189, n4190, n4191, + n4192, n4193, n4194, n4195, n4196, n4197, n4198, n4199, n4200, n4201, + n4202, n4203, n4204, n4205, n4206, n4207, n4208, n4209, n4210, n4211, + n4212, n4213, n4214, n4215, n4216, n4217, n4218, n4219, n4220, n4221, + n4222, n4223, n4224, n4225, n4226, n4227, n4228, n4229, n4230, n4231, + n4232, n4233, n4234, n4235, n4236, n4237, n4238, n4239, n4240, n4241, + n4242, n4243, n4244, n4245, n4246, n4247, n4248, n4249, n4250, n4251, + n4252, n4253, n4254, n4255, n4256, n4257, n4258, n4259, n4260, n4261, + n4262, n4263, n4264, n4265, n4266, n4267, n4268, n4269, n4270, n4271, + n4272, n4273, n4274, n4275, n4276, n4277, n4278, n4279, n4280, n4281, + n4282, n4283, n4284, n4285, n4286, n4287, n4288, n4289, n4290, n4291, + n4292, n4293, n4294, n4295, n4296, n4297, n4298, n4299, n4300, n4301, + n4302, n4303, n4304, n4305, n4306, n4307, n4308, n4309, n4310, n4311, + n4312, n4313, n4314, n4315, n4316, n4317, n4318, n4319, n4320, n4321, + n4322, n4323, n4324, n4325, n4326, n4327, n4328, n4329, n4330, n4331, + n4332, n4333, n4334, n4335, n4336, n4337, n4338, n4339, n4340, n4341, + n4342, n4343, n4344, n4345, n4346, n4347, n4348, n4349, n4350, n4351, + n4352, n4353, n4354, n4355, n4356, n4357, n4358, n4359, n4360, n4361, + n4362, n4363, n4364, n4365, n4366, n4367, n4368, n4369, n4370, n4371, + n4372, n4373, n4374, n4375, n4376, n4377, n4378, n4379, n4380, n4381, + n4382, n4383, n4384, n4385, n4386, n4387, n4388, n4389, n4390, n4391, + n4392, n4393, n4394, n4395, n4396, n4397, n4398, n4399, n4400, n4401, + n4402, n4403, n4404, n4405, n4406, n4407, n4408, n4409, n4410, n4411, + n4412, n4413, n4414, n4415, n4416, n4417, n4418, n4419, n4420, n4421, + n4422, n4423, n4424, n4425, n4426, n4427, n4428, n4429, n4430, n4431, + n4432, n4433, n4434, n4435, n4436, n4437, n4438, n4439, n4440, n4441, + n4442, n4443, n4444, n4445, n4446, n4447, n4448, n4449, n4450, n4451, + n4452, n4453, n4454, n4455, n4456, n4457, n4458, n4459, n4460, n4461, + n4462, n4463, n4464, n4465, n4466, n4467, n4468, n4469, n4470, n4471, + n4472, n4473, n4474, n4475, n4476, n4477, n4478, n4479, n4480, n4481, + n4482, n4483, n4484, n4485, n4486, n4487, n4488, n4489, n4490, n4491, + n4492, n4493, n4494, n4495, n4496, n4497, n4498, n4499, n4500, n4501, + n4502, n4503, n4504, n4505, n4506, n4507, n4508, n4509, n4510, n4511, + n4512, n4513, n4514, n4515, n4516, n4517, n4518, n4519, n4520, n4521, + n4522, n4523, n4524, n4525, n4526, n4527, n4528, n4529, n4530, n4531, + n4532, n4533, n4534, n4535, n4536, n4537, n4538, n4539, n4540, n4541, + n4542, n4543, n4544, n4545, n4546, n4547, n4548, n4549, n4550, n4551, + n4552, n4553, n4554, n4555, n4556, n4557, n4558, n4559, n4560, n4561, + n4562, n4563, n4564, n4565, n4566, n4567, n4568, n4569, n4570, n4571, + n4572, n4573, n4574, n4575, n4576, n4577, n4578, n4579, n4580, n4581, + n4582, n4583, n4584, n4585, n4586, n4587, n4588, n4589, n4590, n4591, + n4592, n4593, n4594, n4595, n4596, n4597, n4598, n4599, n4600, n4601, + n4602, n4603, n4604, n4605, n4606, n4607, n4608, n4609, n4610, n4611, + n4612, n4613, n4614, n4615, n4616, n4617, n4618, n4619, n4620, n4621, + n4622, n4623, n4624, n4625, n4626, n4627, n4628, n4629, n4630, n4631, + n4632, n4633, n4634, n4635, n4636, n4637, n4638, n4639, n4640, n4641, + n4642, n4643, n4644, n4645, n4646, n4647, n4648, n4649, n4650, n4651, + n4652, n4653, n4654, n4655, n4656, n4657, n4658, n4659, n4660, n4661, + n4662, n4663, n4664, n4665, n4666, n4667, n4668, n4669, n4670, n4671, + n4672, n4673, n4674, n4675, n4676, n4677, n4678, n4679, n4680, n4681, + n4682, n4683, n4684, n4685, n4686, n4687, n4688, n4689, n4690, n4691, + n4692, n4693, n4694, n4695, n4696, n4697, n4698, n4699, n4700, n4701, + n4702, n4703, n4704, n4705, n4706, n4707, n4708, n4709, n4710, n4711, + n4712, n4713, n4714, n4715, n4716, n4717, n4718, n4719, n4720, n4721, + n4722, n4723, n4724, n4725, n4726, n4727, n4728, n4729, n4730, n4731, + n4732, n4733, n4734, n4735, n4736, n4737, n4738, n4739, n4740, n4741, + n4742, n4743, n4744, n4745, n4746, n4747, n4748, n4749, n4750, n4751, + n4752, n4753, n4754, n4755, n4756, n4757, n4758, n4759, n4760, n4761, + n4762, n4763, n4764, n4765, n4766, n4767, n4768, n4769, n4770, n4771, + n4772, n4773, n4774, n4775, n4776, n4777, n4778, n4779, n4780, n4781, + n4782, n4783, n4784, n4785, n4786, n4787, n4788, n4789, n4790, n4791, + n4792, n4793, n4794, n4795, n4796, n4797, n4798, n4799, n4800, n4801, + n4802, n4803, n4804, n4805, n4806, n4807, n4808, n4809, n4810, n4811, + n4812, n4813, n4814, n4815, n4816, n4817, n4818, n4819, n4820, n4821, + n4822, n4823, n4824, n4825, n4826, n4827, n4828, n4829, n4830, n4831, + n4832, n4833, n4834, n4835, n4836, n4837, n4838, n4839, n4840, n4841, + n4842, n4843, n4844, n4845, n4846, n4847, n4848, n4849, n4850, n4851, + n4852, n4853, n4854, n4855, n4856, n4857, n4858, n4859, n4860, n4861, + n4862, n4863, n4864, n4865, n4866, n4867, n4868, n4869, n4870, n4871, + n4872, n4873, n4874, n4875, n4876, n4877, n4878, n4879, n4880, n4881, + n4882, n4883, n4884, n4885, n4886, n4887, n4888, n4889, n4890, n4891, + n4892, n4893, n4894, n4895, n4896, n4897, n4898, n4899, n4900, n4901, + n4902, n4903, n4904, n4905, n4906, n4907, n4908, n4909, n4910, n4911, + n4912, n4913, n4914, n4915, n4916, n4917, n4918, n4919, n4920, n4921, + n4922, n4923, n4924, n4925, n4926, n4927, n4928, n4929, n4930, n4931, + n4932, n4933, n4934, n4935, n4936, n4937, n4938, n4939, n4940, n4941, + n4942, n4943, n4944, n4945, n4946, n4947, n4948, n4949, n4950, n4951, + n4952, n4953, n4954, n4955, n4956, n4957, n4958, n4959, n4960, n4961, + n4962, n4963, n4964, n4965, n4966, n4967, n4968, n4969, n4970, n4971, + n4972, n4973, n4974, n4975, n4976, n4977, n4978, n4979, n4980, n4981, + n4982, n4983, n4984, n4985, n4986, n4987, n4988, n4989, n4990, n4991, + n4992, n4993, n4994, n4995, n4996, n4997, n4998, n4999, n5000, n5001, + n5002, n5003, n5004, n5005, n5006, n5007, n5008, n5009, n5010, n5011, + n5012, n5013, n5014, n5015, n5016, n5017, n5018, n5019, n5020, n5021, + n5022, n5023, n5024, n5025, n5026, n5027, n5028, n5029, n5030, n5031, + n5032, n5033, n5034, n5035, n5036, n5037, n5038, n5039, n5040, n5041, + n5042, n5043, n5044, n5045, n5046, n5047, n5048, n5049, n5050, n5051, + n5052, n5053, n5054, n5055, n5056, n5057, n5058, n5059, n5060, n5061, + n5062, n5063, n5064, n5065, n5066, n5067, n5068, n5069, n5070, n5071, + n5072, n5073, n5074, n5075, n5076, n5077, n5078, n5079, n5080, n5081, + n5082, n5083, n5084, n5085, n5086, n5087, n5088, n5089, n5090, n5091, + n5092, n5093, n5094, n5095, n5096, n5097, n5098, n5099, n5100, n5101, + n5102, n5103, n5104, n5105, n5106, n5107, n5108, n5109, n5110, n5111, + n5112, n5113, n5114, n5115, n5116, n5117, n5118, n5119, n5120, n5121, + n5122, n5123, n5124, n5125, n5126, n5127, n5128, n5129, n5130, n5131, + n5132, n5133, n5134, n5135, n5136, n5137, n5138, n5139, n5140, n5141, + n5142, n5143, n5144, n5145, n5146, n5147, n5148, n5149, n5150, n5151, + n5152, n5153, n5154, n5155, n5156, n5157, n5158, n5159, n5160, n5161, + n5162, n5163, n5164, n5165, n5166, n5167, n5168, n5169, n5170, n5171, + n5172, n5173, n5174, n5175, n5176, n5177, n5178, n5179, n5180, n5181, + n5182, n5183, n5184, n5185, n5186, n5187, n5188, n5189, n5190, n5191, + n5192, n5193, n5194, n5195, n5196, n5197, n5198, n5199, n5200, n5201, + n5202, n5203, n5204, n5205, n5206, n5207, n5208, n5209, n5210, n5211, + n5212, n5213, n5214, n5215, n5216, n5217, n5218, n5219, n5220, n5221, + n5222, n5223, n5224, n5225, n5226, n5227, n5228, n5229, n5230, n5231, + n5232, n5233, n5234, n5235, n5236, n5237, n5238, n5239, n5240, n5241, + n5242, n5243, n5244, n5245, n5246, n5247, n5248, n5249, n5250, n5251, + n5252, n5253, n5254, n5255, n5256, n5257, n5258, n5259, n5260, n5261, + n5262, n5263, n5264, n5265, n5266, n5267, n5268, n5269, n5270, n5271, + n5272, n5273, n5274, n5275, n5276, n5277, n5278, n5279, n5280, n5281, + n5282, n5283, n5284, n5285, n5286, n5287, n5288, n5289, n5290, n5291, + n5292, n5293, n5294, n5295, n5296, n5297, n5298, n5299, n5300, n5301, + n5302, n5303, n5304, n5305, n5306, n5307, n5308, n5309, n5310, n5311, + n5312, n5313, n5314, n5315, n5316, n5317, n5318, n5319, n5320, n5321, + n5322, n5323, n5324, n5325, n5326, n5327, n5328, n5329, n5330, n5331, + n5332, n5333, n5334, n5335, n5336, n5337, n5338, n5339, n5340, n5341, + n5342, n5343, n5344, n5345, n5346, n5347, n5348, n5349, n5350, n5351, + n5352, n5353, n5354, n5355, n5356, n5357, n5358, n5359, n5360, n5361, + n5362, n5363, n5364, n5365, n5366, n5367, n5368, n5369, n5370, n5371, + n5372, n5373, n5374, n5375, n5376, n5377, n5378, n5379, n5380, n5381, + n5382, n5383, n5384, n5385, n5386, n5387, n5388, n5389, n5390, n5391, + n5392, n5393, n5394, n5395, n5396, n5397, n5398, n5399, n5400, n5401, + n5402, n5403, n5404, n5405, n5406, n5407, n5408, n5409, n5410, n5411, + n5412, n5413, n5414, n5415, n5416, n5417, n5418, n5419, n5420, n5421, + n5422, n5423, n5424, n5425, n5426, n5427, n5428, n5429, n5430, n5431, + n5432, n5433, n5434, n5435, n5436, n5437, n5438, n5439, n5440, n5441, + n5442, n5443, n5444, n5445, n5446, n5447, n5448, n5449, n5450, n5451, + n5452, n5453, n5454, n5455, n5456, n5457, n5458, n5459, n5460, n5461, + n5462, n5463, n5464, n5465, n5466, n5467, n5468, n5469, n5470, n5471, + n5472, n5473, n5474, n5475, n5476, n5477, n5478, n5479, n5480, n5481, + n5482, n5483, n5484, n5485, n5486, n5487, n5488, n5489, n5490, n5491, + n5492, n5493, n5494, n5495, n5496, n5497, n5498, n5499, n5500, n5501, + n5502, n5503, n5504, n5505, n5506, n5507, n5508, n5509, n5510, n5511, + n5512, n5513, n5514, n5515, n5516, n5517, n5518, n5519, n5520, n5521, + n5522, n5523, n5524, n5525, n5526, n5527, n5528, n5529, n5530, n5531, + n5532, n5533, n5534, n5535, n5536, n5537, n5538, n5539, n5540, n5541, + n5542, n5543, n5544, n5545, n5546, n5547, n5548, n5549, n5550, n5551, + n5552, n5553, n5554, n5555, n5556, n5557, n5558, n5559, n5560, n5561, + n5562, n5563, n5564, n5565, n5566, n5567, n5568, n5569, n5570, n5571, + n5572, n5573, n5574, n5575, n5576, n5577, n5578, n5579, n5580, n5581, + n5582, n5583, n5584, n5585, n5586, n5587, n5588, n5589, n5590, n5591, + n5592, n5593, n5594, n5595, n5596, n5597, n5598, n5599, n5600, n5601, + n5602, n5603, n5604, n5605, n5606, n5607, n5608, n5609, n5610, n5611, + n5612, n5613, n5614, n5615, n5616, n5617, n5618, n5619, n5620, n5621, + n5622, n5623, n5624, n5625, n5626, n5627, n5628, n5629, n5630, n5631, + n5632, n5633, n5634, n5635, n5636, n5637, n5638, n5639, n5640, n5641, + n5642, n5643, n5644, n5645, n5646, n5647, n5648, n5649, n5650, n5651, + n5652, n5653, n5654, n5655, n5656, n5657, n5658, n5659, n5660, n5661, + n5662, n5663, n5664, n5665, n5666, n5667, n5668, n5669, n5670, n5671, + n5672, n5673, n5674, n5675, n5676, n5677, n5678, n5679, n5680, n5681, + n5682, n5683, n5684, n5685, n5686, n5687, n5688, n5689, n5690, n5691, + n5692, n5693, n5694, n5695, n5696, n5697, n5698, n5699, n5700, n5701, + n5702, n5703, n5704, n5705, n5706, n5707, n5708, n5709, n5710, n5711, + n5712, n5713, n5714, n5715, n5716, n5717, n5718, n5719, n5720, n5721, + n5722, n5723, n5724, n5725, n5726, n5727, n5728, n5729, n5730, n5731, + n5732, n5733, n5734, n5735, n5736, n5737, n5738, n5739, n5740, n5741, + n5742, n5743, n5744, n5745, n5746, n5747, n5748, n5749, n5750, n5751, + n5752, n5753, n5754, n5755, n5756, n5757, n5758, n5759, n5760, n5761, + n5762, n5763, n5764, n5765, n5766, n5767, n5768, n5769, n5770, n5771, + n5772, n5773, n5774, n5775, n5776, n5777, n5778, n5779, n5780, n5781, + n5782, n5783, n5784, n5785, n5786, n5787, n5788, n5789, n5790, n5791, + n5792, n5793, n5794, n5795, n5796, n5797, n5798, n5799, n5800, n5801, + n5802, n5803, n5804, n5805, n5806, n5807, n5808, n5809, n5810, n5811, + n5812, n5813, n5814, n5815, n5816, n5817, n5818, n5819, n5820, n5821, + n5822, n5823, n5824, n5825, n5826, n5827, n5828, n5829, n5830, n5831, + n5832, n5833, n5834, n5835, n5836, n5837, n5838, n5839, n5840, n5841, + n5842, n5843, n5844, n5845, n5846, n5847, n5848, n5849, n5850, n5851, + n5852, n5853, n5854, n5855, n5856, n5857, n5858, n5859, n5860, n5861, + n5862, n5863, n5864, n5865, n5866, n5867, n5868, n5869, n5870, n5871, + n5872, n5873, n5874, n5875, n5876, n5877, n5878, n5879, n5880, n5881, + n5882, n5883, n5884, n5885, n5886, n5887, n5888, n5889, n5890, n5891, + n5892, n5893, n5894, n5895, n5896, n5897, n5898, n5899, n5900, n5901, + n5902, n5903, n5904, n5905, n5906, n5907, n5908, n5909, n5910, n5911, + n5912, n5913, n5914, n5915, n5916, n5917, n5918, n5919, n5920, n5921, + n5922, n5923, n5924, n5925, n5926, n5927, n5928, n5929, n5930, n5931, + n5932, n5933, n5934, n5935, n5936, n5937, n5938, n5939, n5940, n5941, + n5942, n5943, n5944, n5945, n5946, n5947, n5948, n5949, n5950, n5951, + n5952, n5953, n5954, n5955, n5956, n5957, n5958, n5959, n5960, n5961, + n5962, n5963, n5964, n5965, n5966, n5967, n5968, n5969, n5970, n5971, + n5972, n5973, n5974, n5975, n5976, n5977, n5978, n5979, n5980, n5981, + n5982, n5983, n5984, n5985, n5986, n5987, n5988, n5989, n5990, n5991, + n5992, n5993, n5994, n5995, n5996, n5997, n5998, n5999, n6000, n6001, + n6002, n6003, n6004, n6005, n6006, n6007, n6008, n6009, n6010, n6011, + n6012, n6013, n6014, n6015, n6016, n6017, n6018, n6019, n6020, n6021, + n6022, n6023, n6024, n6025, n6026, n6027, n6028, n6029, n6030, n6031, + n6032, n6033, n6034, n6035, n6036, n6037, n6038, n6039, n6040, n6041, + n6042, n6043, n6044, n6045, n6046, n6047, n6048, n6049, n6050, n6051, + n6052, n6053, n6054, n6055, n6056, n6057, n6058, n6059, n6060, n6061, + n6062, n6063, n6064, n6065, n6066, n6067, n6068, n6069, n6070, n6071, + n6072, n6073, n6074, n6075, n6076, n6077, n6078, n6079, n6080, n6081, + n6082, n6083, n6084, n6085, n6086, n6087, n6088, n6089, n6090, n6091, + n6092, n6093, n6094, n6095, n6096, n6097, n6098, n6099, n6100, n6101, + n6102, n6103, n6104, n6105, n6106, n6107, n6108, n6109, n6110, n6111, + n6112, n6113, n6114, n6115, n6116, n6117, n6118, n6119, n6120, n6121, + n6122, n6123, n6124, n6125, n6126, n6127, n6128, n6129, n6130, n6131, + n6132, n6133, n6134, n6135, n6136, n6137, n6138, n6139, n6140, n6141, + n6142, n6143, n6144, n6145, n6146, n6147, n6148, n6149, n6150, n6151, + n6152, n6153, n6154, n6155, n6156, n6157, n6158, n6159, n6160, n6161, + n6162, n6163, n6164, n6165, n6166, n6167, n6168, n6169, n6170, n6171, + n6172, n6173, n6174, n6175, n6176, n6177, n6178, n6179, n6180, n6181, + n6182, n6183, n6184, n6185, n6186, n6187, n6188, n6189, n6190, n6191, + n6192, n6193, n6194, n6195, n6196, n6197, n6198, n6199, n6200, n6201, + n6202, n6203, n6204, n6205, n6206, n6207, n6208, n6209, n6210, n6211, + n6212, n6213, n6214, n6215, n6216, n6217, n6218, n6219, n6220, n6221, + n6222, n6223, n6224, n6225, n6226, n6227, n6228, n6229, n6230, n6231, + n6232, n6233, n6234, n6235, n6236, n6237, n6238, n6239, n6240, n6241, + n6242, n6243, n6244, n6245, n6246, n6247, n6248, n6249, n6250, n6251, + n6252, n6253, n6254, n6255, n6256, n6257, n6258, n6259, n6260, n6261, + n6262, n6263, n6264, n6265, n6266, n6267, n6268, n6269, n6270, n6271, + n6272, n6273, n6274, n6275, n6276, n6277, n6278, n6279, n6280, n6281, + n6282, n6283, n6284, n6285, n6286, n6287, n6288, n6289, n6290, n6291, + n6292, n6293, n6294, n6295, n6296, n6297, n6298, n6299, n6300, n6301, + n6302, n6303, n6304, n6305, n6306, n6307, n6308, n6309, n6310, n6311, + n6312, n6313, n6314, n6315, n6316, n6317, n6318, n6319, n6320, n6321, + n6322, n6323, n6324, n6325, n6326, n6327, n6328, n6329, n6330, n6331, + n6332, n6333, n6334, n6335, n6336, n6337, n6338, n6339, n6340, n6341, + n6342, n6343, n6344, n6345, n6346, n6347, n6348, n6349, n6350, n6351, + n6352, n6353, n6354, n6355, n6356, n6357, n6358, n6359, n6360, n6361, + n6362, n6363, n6364, n6365, n6366, n6367, n6368, n6369, n6370, n6371, + n6372, n6373, n6374, n6375, n6376, n6377, n6378, n6379, n6380, n6381, + n6382, n6383, n6384, n6385, n6386, n6387, n6388, n6389, n6390, n6391, + n6392, n6393, n6394, n6395, n6396, n6397, n6398, n6399, n6400, n6401, + n6402, n6403, n6404, n6405; + +assign po001 = pi187; + +assign po015 = po003; + +assign po004 = pi106; + +assign po009 = pi136; + +assign po010 = pi022; + +assign po011 = pi112; + +assign po005 = po012; + +assign po013 = pi062; + +assign po014 = pi123; + +assign po101 = po023; + +assign po067 = po023; + +assign po066 = po023; + +assign po023 = pi119; + +assign po024 = pi152; + +assign po025 = pi125; + +assign po027 = pi102; + +assign po028 = pi031; + +assign po031 = pi155; + +assign po065 = po034; + +assign po035 = pi182; + +assign po036 = pi023; + +assign po038 = pi071; + +assign po039 = pi015; + +assign po040 = pi132; + +assign po044 = pi044; + +assign po052 = pi048; + +assign po057 = pi117; + +assign po059 = pi091; + +assign po063 = pi000; + +assign po064 = pi194; + +assign po069 = pi147; + +assign po070 = pi002; + +assign po071 = pi080; + +assign po072 = pi188; + +assign po018 = po074; + +assign po021 = po074; + +assign po079 = pi084; + +assign po082 = pi144; + +assign po084 = pi199; + +assign po085 = pi066; + +assign po091 = pi008; + +assign po092 = pi154; + +assign po099 = pi042; + +assign po102 = pi179; + +assign po103 = pi145; + +assign po104 = pi127; + +assign po106 = pi105; + +assign po107 = pi029; + +assign po020 = po041; + +assign po032 = po007; + +assign po089 = po076; + +assign po054 = po076; + + OR2 U2865 ( .A(n2822), .B(n2823), .Z(po100)); + AN2 U2866 ( .A(n2824), .B(pi192), .Z(n2823)); + OR2 U2867 ( .A(n2825), .B(n2826), .Z(n2824)); + AN2 U2868 ( .A(n2827), .B(n2828), .Z(n2826)); + IV2 U2869 ( .A(n2829), .Z(n2825)); + OR2 U2870 ( .A(n2828), .B(n2827), .Z(n2829)); + OR2 U2871 ( .A(n2830), .B(n2831), .Z(n2827)); + AN2 U2872 ( .A(n2832), .B(n2833), .Z(n2831)); + AN2 U2873 ( .A(n2834), .B(n2835), .Z(n2830)); + AN2 U2874 ( .A(n2836), .B(n2837), .Z(n2822)); + OR2 U2875 ( .A(n2838), .B(n2839), .Z(n2836)); + AN2 U2876 ( .A(n2840), .B(n2828), .Z(n2839)); + IV2 U2877 ( .A(n2841), .Z(n2838)); + OR2 U2878 ( .A(n2828), .B(n2840), .Z(n2841)); + OR2 U2879 ( .A(n2842), .B(n2843), .Z(n2840)); + AN2 U2880 ( .A(n2844), .B(n2845), .Z(n2843)); + AN2 U2881 ( .A(n2846), .B(n2847), .Z(n2842)); + AN2 U2882 ( .A(n2848), .B(n2849), .Z(n2828)); + IV2 U2883 ( .A(n2850), .Z(n2849)); + AN2 U2884 ( .A(n2851), .B(n2852), .Z(n2850)); + OR2 U2885 ( .A(n2852), .B(n2851), .Z(n2848)); + OR2 U2886 ( .A(n2853), .B(n2854), .Z(n2851)); + AN2 U2887 ( .A(n2855), .B(n2856), .Z(n2854)); + IV2 U2888 ( .A(n2857), .Z(n2853)); + OR2 U2889 ( .A(n2856), .B(n2855), .Z(n2857)); + IV2 U2890 ( .A(n2858), .Z(n2855)); + OR2 U2891 ( .A(n2859), .B(n2860), .Z(n2858)); + AN2 U2892 ( .A(n2861), .B(n2862), .Z(n2859)); + OR2 U2893 ( .A(n2863), .B(n2864), .Z(n2856)); + OR2 U2894 ( .A(n2865), .B(n2866), .Z(n2864)); + AN2 U2895 ( .A(pi192), .B(n2867), .Z(n2866)); + OR2 U2896 ( .A(n2868), .B(n2869), .Z(n2867)); + OR2 U2897 ( .A(n2870), .B(n2871), .Z(n2869)); + AN2 U2898 ( .A(n2872), .B(n2873), .Z(n2871)); + AN2 U2899 ( .A(n2862), .B(n2874), .Z(n2872)); + OR2 U2900 ( .A(n2875), .B(n2876), .Z(n2874)); + AN2 U2901 ( .A(n2877), .B(n2878), .Z(n2875)); + AN2 U2902 ( .A(n2879), .B(n2880), .Z(n2870)); + AN2 U2903 ( .A(n2881), .B(n2882), .Z(n2868)); + OR2 U2904 ( .A(n2883), .B(n2884), .Z(n2881)); + AN2 U2905 ( .A(n2885), .B(n2886), .Z(n2884)); + AN2 U2906 ( .A(n2879), .B(n2887), .Z(n2883)); + IV2 U2907 ( .A(n2873), .Z(n2879)); + OR2 U2908 ( .A(n2888), .B(n2889), .Z(n2873)); + AN2 U2909 ( .A(n2890), .B(n2891), .Z(n2889)); + AN2 U2910 ( .A(n2892), .B(n2886), .Z(n2888)); + AN2 U2911 ( .A(n2893), .B(n2837), .Z(n2865)); + OR2 U2912 ( .A(n2894), .B(n2895), .Z(n2893)); + OR2 U2913 ( .A(n2896), .B(n2897), .Z(n2895)); + OR2 U2914 ( .A(n2898), .B(n2899), .Z(n2897)); + AN2 U2915 ( .A(n2900), .B(n2901), .Z(n2899)); + AN2 U2916 ( .A(n2902), .B(n2903), .Z(n2900)); + OR2 U2917 ( .A(n2904), .B(n2905), .Z(n2903)); + OR2 U2918 ( .A(n2906), .B(n2907), .Z(n2905)); + AN2 U2919 ( .A(n2890), .B(n2908), .Z(n2907)); + AN2 U2920 ( .A(n2909), .B(n2886), .Z(n2906)); + AN2 U2921 ( .A(n2910), .B(pi082), .Z(n2909)); + AN2 U2922 ( .A(pi200), .B(n2911), .Z(n2898)); + OR2 U2923 ( .A(n2912), .B(n2913), .Z(n2911)); + OR2 U2924 ( .A(n2914), .B(n2915), .Z(n2913)); + AN2 U2925 ( .A(n2916), .B(n2890), .Z(n2915)); + AN2 U2926 ( .A(n2910), .B(n2917), .Z(n2916)); + OR2 U2927 ( .A(n2918), .B(n2919), .Z(n2917)); + AN2 U2928 ( .A(n2920), .B(n2886), .Z(n2914)); + OR2 U2929 ( .A(n2921), .B(n2922), .Z(n2920)); + OR2 U2930 ( .A(n2923), .B(n2924), .Z(n2922)); + AN2 U2931 ( .A(n2918), .B(n2860), .Z(n2924)); + AN2 U2932 ( .A(n2925), .B(n2926), .Z(n2923)); + OR2 U2933 ( .A(n2927), .B(n2928), .Z(n2926)); + AN2 U2934 ( .A(n2844), .B(n2929), .Z(n2928)); + AN2 U2935 ( .A(n2930), .B(n2931), .Z(n2927)); + OR2 U2936 ( .A(n2932), .B(n2933), .Z(n2921)); + AN2 U2937 ( .A(n2934), .B(n2935), .Z(n2933)); + AN2 U2938 ( .A(n2936), .B(n2910), .Z(n2934)); + AN2 U2939 ( .A(n2937), .B(n2938), .Z(n2932)); + AN2 U2940 ( .A(n2929), .B(n2939), .Z(n2937)); + AN2 U2941 ( .A(n2935), .B(n2940), .Z(n2912)); + OR2 U2942 ( .A(n2904), .B(n2941), .Z(n2940)); + AN2 U2943 ( .A(n2890), .B(n2942), .Z(n2941)); + AN2 U2944 ( .A(n2943), .B(n2944), .Z(n2942)); + OR2 U2945 ( .A(n2945), .B(n2946), .Z(n2944)); + IV2 U2946 ( .A(n2910), .Z(n2945)); + OR2 U2947 ( .A(n2936), .B(n2947), .Z(n2943)); + IV2 U2948 ( .A(n2948), .Z(n2904)); + OR2 U2949 ( .A(n2949), .B(n2939), .Z(n2948)); + AN2 U2950 ( .A(n2950), .B(n2951), .Z(n2949)); + OR2 U2951 ( .A(n2890), .B(n2947), .Z(n2951)); + OR2 U2952 ( .A(n2929), .B(n2886), .Z(n2950)); + AN2 U2953 ( .A(n2952), .B(n2953), .Z(n2896)); + AN2 U2954 ( .A(n2954), .B(n2955), .Z(n2953)); + OR2 U2955 ( .A(n2956), .B(n2957), .Z(n2955)); + AN2 U2956 ( .A(n2890), .B(n2958), .Z(n2956)); + OR2 U2957 ( .A(n2959), .B(n2960), .Z(n2958)); + OR2 U2958 ( .A(pi082), .B(n2961), .Z(n2954)); + AN2 U2959 ( .A(n2901), .B(n2886), .Z(n2961)); + AN2 U2960 ( .A(n2910), .B(n2962), .Z(n2952)); + AN2 U2961 ( .A(n2947), .B(n2862), .Z(n2910)); + OR2 U2962 ( .A(n2963), .B(n2964), .Z(n2894)); + AN2 U2963 ( .A(n2965), .B(n2890), .Z(n2964)); + AN2 U2964 ( .A(n2929), .B(n2966), .Z(n2965)); + OR2 U2965 ( .A(n2967), .B(n2968), .Z(n2966)); + AN2 U2966 ( .A(n2969), .B(n2901), .Z(n2967)); + OR2 U2967 ( .A(n2970), .B(n2971), .Z(n2969)); + OR2 U2968 ( .A(n2972), .B(n2973), .Z(n2971)); + AN2 U2969 ( .A(n2902), .B(n2962), .Z(n2973)); + AN2 U2970 ( .A(n2974), .B(n2957), .Z(n2972)); + AN2 U2971 ( .A(pi082), .B(po031), .Z(n2970)); + AN2 U2972 ( .A(n2975), .B(n2886), .Z(n2963)); + OR2 U2973 ( .A(n2976), .B(n2977), .Z(n2975)); + AN2 U2974 ( .A(n2968), .B(n2947), .Z(n2977)); + AN2 U2975 ( .A(n2978), .B(n2979), .Z(n2968)); + OR2 U2976 ( .A(n2980), .B(n2981), .Z(n2979)); + AN2 U2977 ( .A(n2982), .B(n2931), .Z(n2980)); + AN2 U2978 ( .A(n2983), .B(n2929), .Z(n2976)); + AN2 U2979 ( .A(n2984), .B(n2936), .Z(n2983)); + AN2 U2980 ( .A(n2974), .B(n2901), .Z(n2984)); + OR2 U2981 ( .A(n2985), .B(n2986), .Z(n2863)); + AN2 U2982 ( .A(n2987), .B(n2890), .Z(n2986)); + AN2 U2983 ( .A(n2988), .B(n2989), .Z(n2987)); + AN2 U2984 ( .A(n2990), .B(n2962), .Z(n2989)); + AN2 U2985 ( .A(n2862), .B(n2877), .Z(n2988)); + AN2 U2986 ( .A(n2991), .B(n2886), .Z(n2985)); + OR2 U2987 ( .A(n2992), .B(n2993), .Z(n2991)); + AN2 U2988 ( .A(n2994), .B(n2995), .Z(n2993)); + AN2 U2989 ( .A(n2990), .B(n2901), .Z(n2995)); + AN2 U2990 ( .A(n2996), .B(n2974), .Z(n2994)); + OR2 U2991 ( .A(po031), .B(n2930), .Z(n2996)); + AN2 U2992 ( .A(n2997), .B(n2860), .Z(n2992)); + AN2 U2993 ( .A(n2998), .B(n2930), .Z(n2860)); + AN2 U2994 ( .A(n2877), .B(n2999), .Z(n2997)); + IV2 U2995 ( .A(n2882), .Z(n2877)); + AN2 U2996 ( .A(n3000), .B(n3001), .Z(n2852)); + OR2 U2997 ( .A(n3002), .B(n2925), .Z(n3001)); + OR2 U2998 ( .A(n3003), .B(n3004), .Z(n3000)); + IV2 U2999 ( .A(n3002), .Z(n3004)); + OR2 U3000 ( .A(n3005), .B(n3006), .Z(n3002)); + AN2 U3001 ( .A(n3007), .B(n3008), .Z(n3006)); + OR2 U3002 ( .A(n3009), .B(n3010), .Z(n3008)); + AN2 U3003 ( .A(n3011), .B(n3012), .Z(n3009)); + AN2 U3004 ( .A(n3013), .B(n3014), .Z(n3007)); + OR2 U3005 ( .A(n3015), .B(n3016), .Z(n3014)); + IV2 U3006 ( .A(n3017), .Z(n3016)); + OR2 U3007 ( .A(n3017), .B(n3018), .Z(n3013)); + OR2 U3008 ( .A(n3019), .B(n3020), .Z(n3017)); + AN2 U3009 ( .A(n3021), .B(n3022), .Z(n3020)); + OR2 U3010 ( .A(n3023), .B(n3024), .Z(n3022)); + OR2 U3011 ( .A(n3025), .B(n3026), .Z(n3024)); + OR2 U3012 ( .A(n3027), .B(n3028), .Z(n3026)); + AN2 U3013 ( .A(po010), .B(n3029), .Z(n3028)); + AN2 U3014 ( .A(n3030), .B(pi192), .Z(n3027)); + AN2 U3015 ( .A(n3031), .B(n3032), .Z(n3030)); + OR2 U3016 ( .A(n3033), .B(n3034), .Z(n3032)); + AN2 U3017 ( .A(n3035), .B(n3036), .Z(n3034)); + AN2 U3018 ( .A(n3015), .B(n3037), .Z(n3035)); + OR2 U3019 ( .A(n3038), .B(n3039), .Z(n3031)); + OR2 U3020 ( .A(n3040), .B(n3041), .Z(n3039)); + AN2 U3021 ( .A(po010), .B(n3042), .Z(n3040)); + OR2 U3022 ( .A(n3043), .B(n3044), .Z(n3025)); + AN2 U3023 ( .A(n3015), .B(n3045), .Z(n3044)); + OR2 U3024 ( .A(n3046), .B(n3047), .Z(n3045)); + AN2 U3025 ( .A(n3048), .B(n3049), .Z(n3047)); + OR2 U3026 ( .A(n3050), .B(n3051), .Z(n3048)); + AN2 U3027 ( .A(n3052), .B(po070), .Z(n3051)); + AN2 U3028 ( .A(n3053), .B(po099), .Z(n3050)); + AN2 U3029 ( .A(n3054), .B(n3055), .Z(n3046)); + AN2 U3030 ( .A(n3018), .B(n3056), .Z(n3043)); + OR2 U3031 ( .A(n3057), .B(n3058), .Z(n3056)); + OR2 U3032 ( .A(n3059), .B(n3060), .Z(n3058)); + AN2 U3033 ( .A(n3061), .B(n3042), .Z(n3060)); + AN2 U3034 ( .A(n3062), .B(n3063), .Z(n3059)); + AN2 U3035 ( .A(n3064), .B(n3049), .Z(n3062)); + AN2 U3036 ( .A(n3065), .B(n3066), .Z(n3057)); + OR2 U3037 ( .A(n3067), .B(n3068), .Z(n3023)); + OR2 U3038 ( .A(n3069), .B(n3070), .Z(n3068)); + AN2 U3039 ( .A(n3071), .B(n3072), .Z(n3070)); + AN2 U3040 ( .A(n3053), .B(n3073), .Z(n3069)); + OR2 U3041 ( .A(n3074), .B(n3075), .Z(n3067)); + AN2 U3042 ( .A(n3076), .B(n3077), .Z(n3075)); + OR2 U3043 ( .A(n3078), .B(n3079), .Z(n3077)); + AN2 U3044 ( .A(n3080), .B(n3066), .Z(n3078)); + AN2 U3045 ( .A(n3081), .B(n3061), .Z(n3074)); + AN2 U3046 ( .A(n3082), .B(n3038), .Z(n3081)); + AN2 U3047 ( .A(n3083), .B(n3084), .Z(n3019)); + OR2 U3048 ( .A(n3085), .B(n3086), .Z(n3084)); + OR2 U3049 ( .A(n3087), .B(n3088), .Z(n3086)); + OR2 U3050 ( .A(n3089), .B(n3090), .Z(n3088)); + AN2 U3051 ( .A(n3091), .B(n3049), .Z(n3089)); + OR2 U3052 ( .A(n3092), .B(n3093), .Z(n3087)); + AN2 U3053 ( .A(n3015), .B(n3094), .Z(n3093)); + OR2 U3054 ( .A(n3095), .B(n3096), .Z(n3094)); + OR2 U3055 ( .A(n3097), .B(n3098), .Z(n3096)); + AN2 U3056 ( .A(n3099), .B(n3100), .Z(n3098)); + AN2 U3057 ( .A(po010), .B(po070), .Z(n3099)); + AN2 U3058 ( .A(n3101), .B(pi192), .Z(n3097)); + AN2 U3059 ( .A(n3041), .B(n3038), .Z(n3101)); + OR2 U3060 ( .A(n3102), .B(n3103), .Z(n3041)); + AN2 U3061 ( .A(n3104), .B(pi166), .Z(n3103)); + AN2 U3062 ( .A(n3037), .B(n3049), .Z(n3104)); + AN2 U3063 ( .A(n3105), .B(n3106), .Z(n3102)); + OR2 U3064 ( .A(n3107), .B(n3042), .Z(n3105)); + AN2 U3065 ( .A(po010), .B(n3108), .Z(n3107)); + AN2 U3066 ( .A(n3079), .B(n3109), .Z(n3095)); + OR2 U3067 ( .A(n3110), .B(n3111), .Z(n3079)); + AN2 U3068 ( .A(n3071), .B(n3112), .Z(n3111)); + AN2 U3069 ( .A(n3065), .B(n3113), .Z(n3110)); + OR2 U3070 ( .A(n3114), .B(n3066), .Z(n3113)); + AN2 U3071 ( .A(po010), .B(n3115), .Z(n3114)); + AN2 U3072 ( .A(n3018), .B(n3116), .Z(n3092)); + OR2 U3073 ( .A(n3117), .B(n3118), .Z(n3116)); + AN2 U3074 ( .A(n3065), .B(n3119), .Z(n3118)); + AN2 U3075 ( .A(n3063), .B(n3120), .Z(n3117)); + OR2 U3076 ( .A(n3121), .B(n3122), .Z(n3085)); + OR2 U3077 ( .A(n3123), .B(n3124), .Z(n3122)); + AN2 U3078 ( .A(n3125), .B(n3055), .Z(n3124)); + AN2 U3079 ( .A(n3076), .B(n3112), .Z(n3125)); + AN2 U3080 ( .A(n3126), .B(n3127), .Z(n3123)); + AN2 U3081 ( .A(n3033), .B(n3037), .Z(n3126)); + AN2 U3082 ( .A(n3080), .B(n3072), .Z(n3121)); + AN2 U3083 ( .A(n3119), .B(n3018), .Z(n3072)); + AN2 U3084 ( .A(n3128), .B(n3129), .Z(n3005)); + OR2 U3085 ( .A(n3130), .B(n3131), .Z(n3129)); + OR2 U3086 ( .A(n3132), .B(n3133), .Z(n3131)); + AN2 U3087 ( .A(n3134), .B(pi192), .Z(n3133)); + AN2 U3088 ( .A(n3135), .B(n3136), .Z(n3134)); + OR2 U3089 ( .A(n3137), .B(po044), .Z(n3135)); + AN2 U3090 ( .A(n3138), .B(n2886), .Z(n3137)); + AN2 U3091 ( .A(n3139), .B(n2837), .Z(n3132)); + AN2 U3092 ( .A(n3140), .B(n3141), .Z(n3139)); + OR2 U3093 ( .A(n3142), .B(po044), .Z(n3140)); + AN2 U3094 ( .A(n3143), .B(n2886), .Z(n3142)); + AN2 U3095 ( .A(n3144), .B(n3145), .Z(n3130)); + OR2 U3096 ( .A(n3146), .B(n3147), .Z(n3145)); + IV2 U3097 ( .A(n3012), .Z(n3144)); + AN2 U3098 ( .A(n3148), .B(n3149), .Z(n3128)); + OR2 U3099 ( .A(n3150), .B(n3018), .Z(n3149)); + OR2 U3100 ( .A(n3015), .B(n3151), .Z(n3148)); + IV2 U3101 ( .A(n3150), .Z(n3151)); + OR2 U3102 ( .A(n3152), .B(n3153), .Z(n3150)); + AN2 U3103 ( .A(n3021), .B(n3154), .Z(n3153)); + AN2 U3104 ( .A(n3155), .B(n3083), .Z(n3152)); + IV2 U3105 ( .A(n3154), .Z(n3155)); + OR2 U3106 ( .A(n3156), .B(n3157), .Z(n3154)); + OR2 U3107 ( .A(n3091), .B(n3158), .Z(n3157)); + OR2 U3108 ( .A(n3159), .B(n3160), .Z(n3158)); + AN2 U3109 ( .A(n3161), .B(n3049), .Z(n3160)); + OR2 U3110 ( .A(n3162), .B(n3029), .Z(n3161)); + OR2 U3111 ( .A(n3163), .B(n3164), .Z(n3029)); + AN2 U3112 ( .A(po099), .B(n3165), .Z(n3164)); + OR2 U3113 ( .A(n3166), .B(n3167), .Z(n3165)); + OR2 U3114 ( .A(n3168), .B(n3169), .Z(n3167)); + AN2 U3115 ( .A(n3170), .B(pi192), .Z(n3169)); + AN2 U3116 ( .A(n3038), .B(n3171), .Z(n3170)); + AN2 U3117 ( .A(n3054), .B(n2837), .Z(n3168)); + AN2 U3118 ( .A(n3172), .B(n3173), .Z(n3166)); + AN2 U3119 ( .A(n3015), .B(n3109), .Z(n3172)); + AN2 U3120 ( .A(n3100), .B(n3018), .Z(n3163)); + AN2 U3121 ( .A(n3015), .B(n3174), .Z(n3162)); + OR2 U3122 ( .A(n3175), .B(n3176), .Z(n3174)); + AN2 U3123 ( .A(n3054), .B(n3173), .Z(n3176)); + AN2 U3124 ( .A(n3177), .B(n3109), .Z(n3054)); + AN2 U3125 ( .A(n3178), .B(n3064), .Z(n3175)); + AN2 U3126 ( .A(n3038), .B(n3037), .Z(n3178)); + AN2 U3127 ( .A(po010), .B(n3179), .Z(n3159)); + OR2 U3128 ( .A(n3090), .B(n3180), .Z(n3179)); + OR2 U3129 ( .A(n3181), .B(n3182), .Z(n3180)); + AN2 U3130 ( .A(n3053), .B(n3183), .Z(n3182)); + OR2 U3131 ( .A(n3184), .B(n3119), .Z(n3183)); + AN2 U3132 ( .A(pi141), .B(n3015), .Z(n3184)); + AN2 U3133 ( .A(n3109), .B(n3065), .Z(n3053)); + AN2 U3134 ( .A(n3185), .B(n3061), .Z(n3181)); + AN2 U3135 ( .A(n3186), .B(n3038), .Z(n3185)); + OR2 U3136 ( .A(n3187), .B(n3063), .Z(n3186)); + AN2 U3137 ( .A(pi033), .B(n3015), .Z(n3187)); + OR2 U3138 ( .A(n3188), .B(n3189), .Z(n3090)); + AN2 U3139 ( .A(n3190), .B(n3065), .Z(n3189)); + AN2 U3140 ( .A(n3073), .B(n3076), .Z(n3190)); + AN2 U3141 ( .A(n3191), .B(n3061), .Z(n3188)); + AN2 U3142 ( .A(n3082), .B(n3033), .Z(n3191)); + OR2 U3143 ( .A(n3192), .B(n3193), .Z(n3091)); + OR2 U3144 ( .A(n3194), .B(n3195), .Z(n3193)); + AN2 U3145 ( .A(n3052), .B(n3018), .Z(n3195)); + AN2 U3146 ( .A(n3064), .B(n3196), .Z(n3194)); + OR2 U3147 ( .A(n3197), .B(n3198), .Z(n3196)); + AN2 U3148 ( .A(n3082), .B(n3199), .Z(n3198)); + AN2 U3149 ( .A(n3042), .B(n3033), .Z(n3197)); + OR2 U3150 ( .A(n3200), .B(n3201), .Z(n3192)); + AN2 U3151 ( .A(n3173), .B(n3202), .Z(n3201)); + OR2 U3152 ( .A(n3203), .B(n3204), .Z(n3202)); + AN2 U3153 ( .A(n3073), .B(n3205), .Z(n3204)); + AN2 U3154 ( .A(n3076), .B(n3066), .Z(n3203)); + AN2 U3155 ( .A(n3206), .B(po099), .Z(n3200)); + AN2 U3156 ( .A(po070), .B(n3207), .Z(n3206)); + OR2 U3157 ( .A(n3208), .B(n3209), .Z(n3156)); + AN2 U3158 ( .A(n3210), .B(n3055), .Z(n3209)); + AN2 U3159 ( .A(n3076), .B(n3115), .Z(n3210)); + AN2 U3160 ( .A(n3211), .B(n3127), .Z(n3208)); + AN2 U3161 ( .A(n3033), .B(n3108), .Z(n3211)); + OR2 U3162 ( .A(n3212), .B(n3213), .Z(po098)); + AN2 U3163 ( .A(n3214), .B(pi192), .Z(n3213)); + OR2 U3164 ( .A(n3215), .B(n3216), .Z(n3214)); + AN2 U3165 ( .A(n2832), .B(n2901), .Z(n3216)); + IV2 U3166 ( .A(n2835), .Z(n2832)); + AN2 U3167 ( .A(pi200), .B(n2835), .Z(n3215)); + OR2 U3168 ( .A(n2880), .B(n2876), .Z(n2835)); + AN2 U3169 ( .A(n3217), .B(n2837), .Z(n3212)); + OR2 U3170 ( .A(n3218), .B(n3219), .Z(n3217)); + AN2 U3171 ( .A(n3220), .B(n2847), .Z(n3219)); + OR2 U3172 ( .A(n3221), .B(n2902), .Z(n3220)); + AN2 U3173 ( .A(pi200), .B(n2938), .Z(n3221)); + AN2 U3174 ( .A(n2844), .B(n3222), .Z(n3218)); + IV2 U3175 ( .A(n2847), .Z(n2844)); + OR2 U3176 ( .A(n2936), .B(n2978), .Z(n2847)); + OR2 U3177 ( .A(n3223), .B(n3224), .Z(po097)); + AN2 U3178 ( .A(n3225), .B(n3226), .Z(n3224)); + OR2 U3179 ( .A(n3227), .B(n3228), .Z(n3226)); + OR2 U3180 ( .A(n3229), .B(n3230), .Z(n3228)); + OR2 U3181 ( .A(n3231), .B(n3232), .Z(n3230)); + AN2 U3182 ( .A(n3233), .B(n3234), .Z(n3232)); + AN2 U3183 ( .A(n3235), .B(n3236), .Z(n3231)); + OR2 U3184 ( .A(n3237), .B(n3238), .Z(n3227)); + OR2 U3185 ( .A(n3239), .B(n3240), .Z(n3238)); + AN2 U3186 ( .A(n3241), .B(n3242), .Z(n3240)); + AN2 U3187 ( .A(n3243), .B(n3244), .Z(n3239)); + AN2 U3188 ( .A(po082), .B(n3245), .Z(n3237)); + AN2 U3189 ( .A(n3246), .B(n3247), .Z(n3223)); + OR2 U3190 ( .A(n3248), .B(n3249), .Z(n3246)); + AN2 U3191 ( .A(n3250), .B(n3251), .Z(n3249)); + OR2 U3192 ( .A(n3252), .B(n3253), .Z(po096)); + AN2 U3193 ( .A(n3254), .B(n3255), .Z(n3252)); + OR2 U3194 ( .A(n3256), .B(n3257), .Z(n3254)); + AN2 U3195 ( .A(n3258), .B(n3259), .Z(n3256)); + AN2 U3196 ( .A(n3260), .B(n3261), .Z(n3258)); + OR2 U3197 ( .A(n3262), .B(n3263), .Z(n3260)); + AN2 U3198 ( .A(n3264), .B(n3265), .Z(n3262)); + OR2 U3199 ( .A(n3266), .B(n3267), .Z(po095)); + OR2 U3200 ( .A(n3268), .B(n3269), .Z(n3267)); + AN2 U3201 ( .A(n3270), .B(n3119), .Z(n3269)); + OR2 U3202 ( .A(n3271), .B(n3272), .Z(n3270)); + AN2 U3203 ( .A(n3273), .B(n2837), .Z(n3272)); + AN2 U3204 ( .A(n3065), .B(n3274), .Z(n3271)); + AN2 U3205 ( .A(n3063), .B(n3275), .Z(n3268)); + OR2 U3206 ( .A(n3276), .B(n3277), .Z(n3275)); + AN2 U3207 ( .A(n3273), .B(pi192), .Z(n3277)); + AN2 U3208 ( .A(n3061), .B(n3274), .Z(n3276)); + OR2 U3209 ( .A(n3278), .B(n3279), .Z(n3266)); + AN2 U3210 ( .A(n3280), .B(n3281), .Z(n3279)); + OR2 U3211 ( .A(n3282), .B(n3100), .Z(n3281)); + AN2 U3212 ( .A(n3283), .B(n3284), .Z(n3278)); + OR2 U3213 ( .A(n3285), .B(n3052), .Z(n3283)); + AN2 U3214 ( .A(n3286), .B(n3287), .Z(n3052)); + AN2 U3215 ( .A(po099), .B(n3207), .Z(n3285)); + AN2 U3216 ( .A(n3288), .B(n3289), .Z(po094)); + OR2 U3217 ( .A(n3290), .B(n3291), .Z(n3289)); + OR2 U3218 ( .A(n3234), .B(n3292), .Z(n3288)); + OR2 U3219 ( .A(n3293), .B(n3294), .Z(po093)); + AN2 U3220 ( .A(n3295), .B(n3296), .Z(n3294)); + OR2 U3221 ( .A(n3297), .B(n3298), .Z(n3296)); + AN2 U3222 ( .A(n3299), .B(n3300), .Z(n3297)); + AN2 U3223 ( .A(n3301), .B(n3302), .Z(n3293)); + IV2 U3224 ( .A(n3303), .Z(n3302)); + AN2 U3225 ( .A(n3304), .B(n3299), .Z(n3303)); + OR2 U3226 ( .A(n3305), .B(n3306), .Z(n3299)); + OR2 U3227 ( .A(n3300), .B(n3298), .Z(n3304)); + AN2 U3228 ( .A(n3305), .B(n3306), .Z(n3298)); + OR2 U3229 ( .A(n3307), .B(n3308), .Z(po090)); + OR2 U3230 ( .A(n3309), .B(n3310), .Z(n3308)); + AN2 U3231 ( .A(n3311), .B(n3312), .Z(n3310)); + AN2 U3232 ( .A(n3313), .B(n3314), .Z(n3309)); + OR2 U3233 ( .A(n3315), .B(n3316), .Z(n3313)); + OR2 U3234 ( .A(n3317), .B(n3318), .Z(n3316)); + AN2 U3235 ( .A(n3319), .B(n3320), .Z(n3315)); + OR2 U3236 ( .A(n3321), .B(n3322), .Z(n3319)); + OR2 U3237 ( .A(n3323), .B(n3324), .Z(n3307)); + AN2 U3238 ( .A(n3325), .B(n3326), .Z(n3324)); + AN2 U3239 ( .A(n3327), .B(n3328), .Z(n3323)); + OR2 U3240 ( .A(n3329), .B(n3330), .Z(n3327)); + OR2 U3241 ( .A(n3331), .B(n3332), .Z(po088)); + IV2 U3242 ( .A(n3333), .Z(n3332)); + OR2 U3243 ( .A(n3334), .B(n3335), .Z(n3333)); + AN2 U3244 ( .A(n3335), .B(n3334), .Z(n3331)); + AN2 U3245 ( .A(n3336), .B(n3337), .Z(n3334)); + OR2 U3246 ( .A(n3338), .B(n3339), .Z(n3337)); + IV2 U3247 ( .A(n3340), .Z(n3338)); + OR2 U3248 ( .A(n3341), .B(n3340), .Z(n3336)); + OR2 U3249 ( .A(n3342), .B(n3343), .Z(n3340)); + AN2 U3250 ( .A(n3291), .B(n3250), .Z(n3343)); + AN2 U3251 ( .A(n3344), .B(n3292), .Z(n3342)); + OR2 U3252 ( .A(n3345), .B(n3346), .Z(n3335)); + IV2 U3253 ( .A(n3347), .Z(n3346)); + OR2 U3254 ( .A(n3348), .B(n3349), .Z(n3347)); + AN2 U3255 ( .A(n3349), .B(n3348), .Z(n3345)); + AN2 U3256 ( .A(n3350), .B(n3351), .Z(n3348)); + OR2 U3257 ( .A(n3352), .B(n3353), .Z(n3351)); + IV2 U3258 ( .A(n3354), .Z(n3353)); + OR2 U3259 ( .A(n3354), .B(n3355), .Z(n3350)); + OR2 U3260 ( .A(n3356), .B(n3357), .Z(n3354)); + OR2 U3261 ( .A(n3358), .B(n3359), .Z(n3357)); + AN2 U3262 ( .A(n3360), .B(n3361), .Z(n3359)); + AN2 U3263 ( .A(n3362), .B(n3363), .Z(n3360)); + OR2 U3264 ( .A(n3364), .B(n3365), .Z(n3362)); + OR2 U3265 ( .A(n3366), .B(n3367), .Z(n3365)); + AN2 U3266 ( .A(n3368), .B(n3369), .Z(n3367)); + AN2 U3267 ( .A(n3370), .B(n3371), .Z(n3368)); + AN2 U3268 ( .A(n3372), .B(n3373), .Z(n3366)); + AN2 U3269 ( .A(n3374), .B(n3375), .Z(n3372)); + OR2 U3270 ( .A(n3376), .B(n3377), .Z(n3374)); + OR2 U3271 ( .A(n3378), .B(n3379), .Z(n3377)); + AN2 U3272 ( .A(n2837), .B(n3380), .Z(n3379)); + AN2 U3273 ( .A(pi060), .B(n3381), .Z(n3378)); + AN2 U3274 ( .A(n3382), .B(n3369), .Z(n3364)); + AN2 U3275 ( .A(n3369), .B(n3383), .Z(n3358)); + OR2 U3276 ( .A(n3384), .B(n3385), .Z(n3383)); + OR2 U3277 ( .A(n3386), .B(n3387), .Z(n3385)); + AN2 U3278 ( .A(n3370), .B(n3388), .Z(n3387)); + OR2 U3279 ( .A(n3389), .B(n3390), .Z(n3388)); + AN2 U3280 ( .A(n3391), .B(n3392), .Z(n3390)); + AN2 U3281 ( .A(n3393), .B(n3394), .Z(n3386)); + OR2 U3282 ( .A(n3395), .B(n3396), .Z(n3394)); + OR2 U3283 ( .A(n3321), .B(n3397), .Z(n3396)); + AN2 U3284 ( .A(n3398), .B(n3399), .Z(n3397)); + AN2 U3285 ( .A(n3400), .B(n3401), .Z(n3395)); + OR2 U3286 ( .A(n3402), .B(n3371), .Z(n3400)); + AN2 U3287 ( .A(n3403), .B(n3404), .Z(n3402)); + AN2 U3288 ( .A(pi060), .B(po071), .Z(n3403)); + OR2 U3289 ( .A(n3405), .B(n3406), .Z(n3384)); + AN2 U3290 ( .A(n3407), .B(n3363), .Z(n3406)); + AN2 U3291 ( .A(n3408), .B(n3401), .Z(n3407)); + OR2 U3292 ( .A(n3409), .B(n3410), .Z(n3408)); + AN2 U3293 ( .A(n3411), .B(n3412), .Z(n3409)); + OR2 U3294 ( .A(n3413), .B(n3414), .Z(n3411)); + AN2 U3295 ( .A(n3415), .B(n3370), .Z(n3414)); + OR2 U3296 ( .A(n3416), .B(n3417), .Z(n3415)); + AN2 U3297 ( .A(n3418), .B(n3381), .Z(n3413)); + AN2 U3298 ( .A(n3393), .B(n3419), .Z(n3418)); + AN2 U3299 ( .A(n3420), .B(po027), .Z(n3405)); + OR2 U3300 ( .A(n3421), .B(n3422), .Z(n3420)); + OR2 U3301 ( .A(n3423), .B(n3424), .Z(n3422)); + AN2 U3302 ( .A(n3410), .B(n3361), .Z(n3424)); + IV2 U3303 ( .A(n3425), .Z(n3410)); + AN2 U3304 ( .A(n3426), .B(n3401), .Z(n3423)); + OR2 U3305 ( .A(n3427), .B(n3382), .Z(n3426)); + IV2 U3306 ( .A(n3428), .Z(n3382)); + OR2 U3307 ( .A(n3429), .B(n3430), .Z(n3421)); + AN2 U3308 ( .A(n3431), .B(n3370), .Z(n3430)); + AN2 U3309 ( .A(n3393), .B(n3432), .Z(n3429)); + OR2 U3310 ( .A(n3433), .B(n3434), .Z(n3432)); + AN2 U3311 ( .A(po071), .B(n3419), .Z(n3434)); + IV2 U3312 ( .A(n3435), .Z(n3369)); + OR2 U3313 ( .A(n3436), .B(n3375), .Z(n3435)); + AN2 U3314 ( .A(n3437), .B(n3438), .Z(n3436)); + AN2 U3315 ( .A(n3439), .B(n3440), .Z(n3438)); + OR2 U3316 ( .A(n3361), .B(n3441), .Z(n3440)); + AN2 U3317 ( .A(n3442), .B(n3443), .Z(n3441)); + OR2 U3318 ( .A(n3444), .B(n3445), .Z(n3443)); + OR2 U3319 ( .A(n3370), .B(n3446), .Z(n3445)); + OR2 U3320 ( .A(n3371), .B(n3380), .Z(n3444)); + AN2 U3321 ( .A(n3447), .B(n3448), .Z(n3442)); + OR2 U3322 ( .A(po027), .B(n3449), .Z(n3448)); + AN2 U3323 ( .A(n3450), .B(n3428), .Z(n3449)); + OR2 U3324 ( .A(n3371), .B(n3451), .Z(n3428)); + OR2 U3325 ( .A(n3370), .B(n3412), .Z(n3450)); + OR2 U3326 ( .A(n3363), .B(n3425), .Z(n3447)); + OR2 U3327 ( .A(n3427), .B(n3452), .Z(n3425)); + AN2 U3328 ( .A(n3453), .B(n3412), .Z(n3452)); + AN2 U3329 ( .A(n3371), .B(n3451), .Z(n3427)); + OR2 U3330 ( .A(n3401), .B(n3454), .Z(n3439)); + IV2 U3331 ( .A(n3455), .Z(n3454)); + AN2 U3332 ( .A(n3412), .B(n3456), .Z(n3455)); + OR2 U3333 ( .A(n3457), .B(n3458), .Z(n3456)); + AN2 U3334 ( .A(n3392), .B(n3393), .Z(n3458)); + AN2 U3335 ( .A(n3459), .B(n3460), .Z(n3437)); + OR2 U3336 ( .A(n3457), .B(n3461), .Z(n3460)); + AN2 U3337 ( .A(n3462), .B(n3463), .Z(n3459)); + OR2 U3338 ( .A(n3464), .B(n3370), .Z(n3463)); + IV2 U3339 ( .A(n3465), .Z(n3464)); + OR2 U3340 ( .A(n3389), .B(n3431), .Z(n3465)); + AN2 U3341 ( .A(n3466), .B(n3419), .Z(n3431)); + AN2 U3342 ( .A(n3467), .B(n3468), .Z(n3389)); + IV2 U3343 ( .A(n3469), .Z(n3468)); + OR2 U3344 ( .A(n3417), .B(n3470), .Z(n3469)); + AN2 U3345 ( .A(n3471), .B(n3399), .Z(n3470)); + OR2 U3346 ( .A(n3371), .B(n3446), .Z(n3471)); + OR2 U3347 ( .A(n3472), .B(n3393), .Z(n3462)); + AN2 U3348 ( .A(n3473), .B(n3474), .Z(n3472)); + AN2 U3349 ( .A(n3475), .B(n3476), .Z(n3474)); + OR2 U3350 ( .A(n3412), .B(n3477), .Z(n3476)); + AN2 U3351 ( .A(n3478), .B(n3261), .Z(n3475)); + IV2 U3352 ( .A(n3479), .Z(n3478)); + AN2 U3353 ( .A(n3361), .B(n3480), .Z(n3479)); + AN2 U3354 ( .A(n3481), .B(n3482), .Z(n3473)); + OR2 U3355 ( .A(n3483), .B(n3380), .Z(n3482)); + AN2 U3356 ( .A(n3484), .B(n3461), .Z(n3483)); + OR2 U3357 ( .A(n3371), .B(n3485), .Z(n3484)); + IV2 U3358 ( .A(n3433), .Z(n3481)); + OR2 U3359 ( .A(n3486), .B(n3417), .Z(n3433)); + AN2 U3360 ( .A(n3404), .B(n3381), .Z(n3417)); + AN2 U3361 ( .A(n3487), .B(n3380), .Z(n3486)); + AN2 U3362 ( .A(n3488), .B(n3375), .Z(n3356)); + OR2 U3363 ( .A(n3489), .B(n3490), .Z(n3375)); + AN2 U3364 ( .A(n3225), .B(n3491), .Z(n3489)); + OR2 U3365 ( .A(n3492), .B(n3493), .Z(n3491)); + AN2 U3366 ( .A(n3494), .B(n3495), .Z(n3492)); + OR2 U3367 ( .A(n3496), .B(n3497), .Z(n3488)); + OR2 U3368 ( .A(n3498), .B(n3499), .Z(n3497)); + AN2 U3369 ( .A(n3371), .B(n3500), .Z(n3499)); + OR2 U3370 ( .A(n3501), .B(n3502), .Z(n3500)); + OR2 U3371 ( .A(n3503), .B(n3504), .Z(n3502)); + AN2 U3372 ( .A(n3505), .B(n3401), .Z(n3504)); + OR2 U3373 ( .A(n3506), .B(n3507), .Z(n3505)); + AN2 U3374 ( .A(n3508), .B(n3509), .Z(n3507)); + OR2 U3375 ( .A(n3510), .B(n3511), .Z(n3509)); + OR2 U3376 ( .A(n3398), .B(n3487), .Z(n3511)); + AN2 U3377 ( .A(n3512), .B(n2837), .Z(n3487)); + AN2 U3378 ( .A(n3404), .B(pi060), .Z(n3510)); + AN2 U3379 ( .A(n3513), .B(po027), .Z(n3506)); + AN2 U3380 ( .A(n3514), .B(n3515), .Z(n3513)); + OR2 U3381 ( .A(n3516), .B(n3517), .Z(n3515)); + OR2 U3382 ( .A(n3321), .B(n3373), .Z(n3514)); + AN2 U3383 ( .A(n3518), .B(n3467), .Z(n3503)); + AN2 U3384 ( .A(n3404), .B(n3519), .Z(n3518)); + OR2 U3385 ( .A(n3520), .B(n3521), .Z(n3501)); + AN2 U3386 ( .A(n3522), .B(n3517), .Z(n3521)); + AN2 U3387 ( .A(n3516), .B(n3363), .Z(n3522)); + AN2 U3388 ( .A(n3373), .B(n3523), .Z(n3520)); + OR2 U3389 ( .A(n3524), .B(n3525), .Z(n3523)); + AN2 U3390 ( .A(n3361), .B(n3526), .Z(n3524)); + OR2 U3391 ( .A(n3527), .B(n3321), .Z(n3526)); + AN2 U3392 ( .A(n3519), .B(n3528), .Z(n3498)); + OR2 U3393 ( .A(n3529), .B(n3530), .Z(n3528)); + OR2 U3394 ( .A(n3392), .B(n3531), .Z(n3530)); + AN2 U3395 ( .A(n3376), .B(n3412), .Z(n3531)); + OR2 U3396 ( .A(n3532), .B(n3321), .Z(n3376)); + AN2 U3397 ( .A(n3533), .B(n3399), .Z(n3532)); + AN2 U3398 ( .A(n3534), .B(n3381), .Z(n3529)); + OR2 U3399 ( .A(n3535), .B(n3536), .Z(n3519)); + AN2 U3400 ( .A(n3537), .B(po027), .Z(n3536)); + AN2 U3401 ( .A(n3538), .B(n3363), .Z(n3535)); + AN2 U3402 ( .A(n3517), .B(n3401), .Z(n3538)); + OR2 U3403 ( .A(n3539), .B(n3540), .Z(n3496)); + AN2 U3404 ( .A(n3541), .B(n3542), .Z(n3540)); + OR2 U3405 ( .A(n3543), .B(n3544), .Z(n3542)); + AN2 U3406 ( .A(n3453), .B(po071), .Z(n3544)); + AN2 U3407 ( .A(n3480), .B(n3534), .Z(n3543)); + AN2 U3408 ( .A(n3545), .B(n3546), .Z(n3541)); + OR2 U3409 ( .A(n3361), .B(n3547), .Z(n3546)); + AN2 U3410 ( .A(n3508), .B(n3412), .Z(n3547)); + OR2 U3411 ( .A(n3401), .B(n3548), .Z(n3545)); + IV2 U3412 ( .A(n3508), .Z(n3548)); + OR2 U3413 ( .A(n3549), .B(n3550), .Z(n3508)); + AN2 U3414 ( .A(n3373), .B(n3363), .Z(n3550)); + AN2 U3415 ( .A(n3517), .B(po027), .Z(n3549)); + AN2 U3416 ( .A(n3551), .B(n3467), .Z(n3539)); + AN2 U3417 ( .A(n3552), .B(n3381), .Z(n3551)); + OR2 U3418 ( .A(n3553), .B(n3554), .Z(n3552)); + AN2 U3419 ( .A(n3537), .B(n3363), .Z(n3554)); + AN2 U3420 ( .A(n3555), .B(po027), .Z(n3553)); + IV2 U3421 ( .A(n3537), .Z(n3555)); + OR2 U3422 ( .A(n3556), .B(n3557), .Z(n3537)); + AN2 U3423 ( .A(n3373), .B(n3401), .Z(n3557)); + AN2 U3424 ( .A(n3517), .B(n3361), .Z(n3556)); + IV2 U3425 ( .A(n3373), .Z(n3517)); + OR2 U3426 ( .A(n3558), .B(n3559), .Z(n3373)); + AN2 U3427 ( .A(n3370), .B(n3560), .Z(n3559)); + OR2 U3428 ( .A(n3561), .B(n3562), .Z(n3560)); + OR2 U3429 ( .A(n3361), .B(n3563), .Z(n3562)); + AN2 U3430 ( .A(n3321), .B(po027), .Z(n3563)); + OR2 U3431 ( .A(n3564), .B(n3565), .Z(n3561)); + OR2 U3432 ( .A(n3527), .B(n3566), .Z(n3565)); + AN2 U3433 ( .A(po027), .B(n3398), .Z(n3527)); + AN2 U3434 ( .A(n3404), .B(n3567), .Z(n3564)); + AN2 U3435 ( .A(n3393), .B(n3568), .Z(n3558)); + OR2 U3436 ( .A(n3569), .B(n3570), .Z(n3568)); + AN2 U3437 ( .A(n3571), .B(n3401), .Z(n3570)); + OR2 U3438 ( .A(n3516), .B(n3572), .Z(n3571)); + AN2 U3439 ( .A(n3419), .B(n3363), .Z(n3572)); + AN2 U3440 ( .A(po104), .B(n3453), .Z(n3516)); + AN2 U3441 ( .A(n3573), .B(n3467), .Z(n3569)); + AN2 U3442 ( .A(n3261), .B(n3533), .Z(n3467)); + AN2 U3443 ( .A(n3404), .B(po027), .Z(n3573)); + OR2 U3444 ( .A(n3574), .B(n3575), .Z(n3349)); + AN2 U3445 ( .A(n3576), .B(n3247), .Z(n3575)); + IV2 U3446 ( .A(n3577), .Z(n3576)); + AN2 U3447 ( .A(n3225), .B(n3577), .Z(n3574)); + OR2 U3448 ( .A(n3578), .B(n3579), .Z(n3577)); + OR2 U3449 ( .A(n3580), .B(n3581), .Z(n3579)); + OR2 U3450 ( .A(n3582), .B(n3583), .Z(n3581)); + AN2 U3451 ( .A(n3584), .B(n2837), .Z(n3583)); + OR2 U3452 ( .A(n3585), .B(n3586), .Z(n3584)); + OR2 U3453 ( .A(n3587), .B(n3588), .Z(n3586)); + AN2 U3454 ( .A(n3589), .B(n3590), .Z(n3588)); + AN2 U3455 ( .A(n3591), .B(n3592), .Z(n3589)); + OR2 U3456 ( .A(n3593), .B(n3355), .Z(n3592)); + AN2 U3457 ( .A(n3594), .B(n3595), .Z(n3591)); + OR2 U3458 ( .A(n3596), .B(n3597), .Z(n3595)); + OR2 U3459 ( .A(pi003), .B(n3598), .Z(n3594)); + AN2 U3460 ( .A(n3599), .B(n3600), .Z(n3587)); + OR2 U3461 ( .A(n3601), .B(n3602), .Z(n3600)); + OR2 U3462 ( .A(n3603), .B(n3604), .Z(n3602)); + AN2 U3463 ( .A(n3605), .B(n3593), .Z(n3604)); + AN2 U3464 ( .A(n3606), .B(n3607), .Z(n3603)); + OR2 U3465 ( .A(n3608), .B(n3609), .Z(n3601)); + AN2 U3466 ( .A(n3610), .B(pi003), .Z(n3609)); + AN2 U3467 ( .A(n3611), .B(n3612), .Z(n3610)); + AN2 U3468 ( .A(n3613), .B(n3597), .Z(n3608)); + OR2 U3469 ( .A(n3614), .B(n3615), .Z(n3613)); + AN2 U3470 ( .A(n3598), .B(n3616), .Z(n3615)); + AN2 U3471 ( .A(n3617), .B(n3593), .Z(n3614)); + AN2 U3472 ( .A(n3618), .B(n3619), .Z(n3585)); + AN2 U3473 ( .A(n3620), .B(n3590), .Z(n3618)); + OR2 U3474 ( .A(n3621), .B(n3622), .Z(n3620)); + OR2 U3475 ( .A(n3494), .B(n3606), .Z(n3622)); + AN2 U3476 ( .A(pi003), .B(n3623), .Z(n3606)); + AN2 U3477 ( .A(n3616), .B(po011), .Z(n3623)); + AN2 U3478 ( .A(n3624), .B(n3625), .Z(n3621)); + AN2 U3479 ( .A(n3616), .B(n3597), .Z(n3624)); + AN2 U3480 ( .A(pi192), .B(n3626), .Z(n3582)); + OR2 U3481 ( .A(n3627), .B(n3628), .Z(n3626)); + OR2 U3482 ( .A(n3629), .B(n3630), .Z(n3628)); + AN2 U3483 ( .A(n3631), .B(n3632), .Z(n3630)); + AN2 U3484 ( .A(n3633), .B(n3634), .Z(n3631)); + OR2 U3485 ( .A(n3635), .B(n3355), .Z(n3634)); + AN2 U3486 ( .A(n3636), .B(n3637), .Z(n3633)); + OR2 U3487 ( .A(n3596), .B(n3638), .Z(n3637)); + AN2 U3488 ( .A(n3639), .B(n3339), .Z(n3596)); + OR2 U3489 ( .A(pi098), .B(n3598), .Z(n3636)); + AN2 U3490 ( .A(n3640), .B(n3641), .Z(n3629)); + OR2 U3491 ( .A(n3642), .B(n3643), .Z(n3641)); + OR2 U3492 ( .A(n3644), .B(n3645), .Z(n3643)); + AN2 U3493 ( .A(n3605), .B(n3635), .Z(n3645)); + OR2 U3494 ( .A(n3646), .B(n3647), .Z(n3605)); + AN2 U3495 ( .A(n3648), .B(n3493), .Z(n3647)); + AN2 U3496 ( .A(n3341), .B(n3639), .Z(n3646)); + AN2 U3497 ( .A(n3649), .B(n3607), .Z(n3644)); + OR2 U3498 ( .A(n3650), .B(n3651), .Z(n3642)); + AN2 U3499 ( .A(n3652), .B(pi098), .Z(n3651)); + AN2 U3500 ( .A(n3653), .B(n3612), .Z(n3652)); + AN2 U3501 ( .A(n3654), .B(n3638), .Z(n3650)); + OR2 U3502 ( .A(n3655), .B(n3656), .Z(n3654)); + AN2 U3503 ( .A(n3598), .B(n3657), .Z(n3656)); + OR2 U3504 ( .A(n3658), .B(n3659), .Z(n3598)); + AN2 U3505 ( .A(n3660), .B(n3493), .Z(n3659)); + AN2 U3506 ( .A(po011), .B(n3612), .Z(n3660)); + AN2 U3507 ( .A(n3625), .B(n3661), .Z(n3658)); + AN2 U3508 ( .A(n3617), .B(n3635), .Z(n3655)); + AN2 U3509 ( .A(n3625), .B(n3493), .Z(n3617)); + AN2 U3510 ( .A(n3662), .B(n3619), .Z(n3627)); + AN2 U3511 ( .A(n3663), .B(n3632), .Z(n3662)); + OR2 U3512 ( .A(n3664), .B(n3665), .Z(n3663)); + OR2 U3513 ( .A(n3494), .B(n3649), .Z(n3665)); + AN2 U3514 ( .A(pi098), .B(n3666), .Z(n3649)); + AN2 U3515 ( .A(n3657), .B(po011), .Z(n3666)); + AN2 U3516 ( .A(n3612), .B(n3667), .Z(n3494)); + AN2 U3517 ( .A(n3668), .B(n3625), .Z(n3664)); + AN2 U3518 ( .A(n3657), .B(n3638), .Z(n3668)); + OR2 U3519 ( .A(n3669), .B(n3670), .Z(n3580)); + AN2 U3520 ( .A(n3671), .B(po011), .Z(n3670)); + AN2 U3521 ( .A(n3672), .B(n3612), .Z(n3671)); + AN2 U3522 ( .A(n3673), .B(n3674), .Z(n3672)); + OR2 U3523 ( .A(pi192), .B(n3675), .Z(n3674)); + AN2 U3524 ( .A(n3676), .B(n3597), .Z(n3675)); + OR2 U3525 ( .A(n3677), .B(n3678), .Z(n3676)); + AN2 U3526 ( .A(n3679), .B(n3680), .Z(n3677)); + AN2 U3527 ( .A(n3599), .B(n3661), .Z(n3679)); + OR2 U3528 ( .A(n2837), .B(n3681), .Z(n3673)); + AN2 U3529 ( .A(n3682), .B(n3638), .Z(n3681)); + OR2 U3530 ( .A(n3683), .B(n3684), .Z(n3682)); + AN2 U3531 ( .A(n3685), .B(n3686), .Z(n3683)); + AN2 U3532 ( .A(n3640), .B(n3661), .Z(n3685)); + AN2 U3533 ( .A(n3687), .B(n3688), .Z(n3669)); + OR2 U3534 ( .A(n3689), .B(n3690), .Z(n3687)); + OR2 U3535 ( .A(n3691), .B(n3692), .Z(n3690)); + AN2 U3536 ( .A(n3693), .B(n3612), .Z(n3692)); + AN2 U3537 ( .A(n3619), .B(n3694), .Z(n3693)); + OR2 U3538 ( .A(n3695), .B(n3696), .Z(n3694)); + OR2 U3539 ( .A(n3697), .B(n3698), .Z(n3696)); + AN2 U3540 ( .A(n3699), .B(n3616), .Z(n3698)); + AN2 U3541 ( .A(n3700), .B(n3657), .Z(n3697)); + AN2 U3542 ( .A(n3341), .B(n3701), .Z(n3695)); + AN2 U3543 ( .A(n3493), .B(n3352), .Z(n3619)); + AN2 U3544 ( .A(n3625), .B(n3702), .Z(n3691)); + OR2 U3545 ( .A(n3703), .B(n3704), .Z(n3702)); + AN2 U3546 ( .A(n3705), .B(n3706), .Z(n3704)); + OR2 U3547 ( .A(n3684), .B(n3707), .Z(n3706)); + OR2 U3548 ( .A(n3708), .B(n3709), .Z(n3707)); + AN2 U3549 ( .A(n3710), .B(n3640), .Z(n3709)); + OR2 U3550 ( .A(n3711), .B(n3712), .Z(n3710)); + AN2 U3551 ( .A(n3607), .B(n3686), .Z(n3712)); + AN2 U3552 ( .A(n3493), .B(n3713), .Z(n3711)); + IV2 U3553 ( .A(n3686), .Z(n3713)); + AN2 U3554 ( .A(n3653), .B(n3632), .Z(n3708)); + OR2 U3555 ( .A(n3714), .B(n3715), .Z(n3653)); + AN2 U3556 ( .A(n3686), .B(n3493), .Z(n3714)); + AN2 U3557 ( .A(n3635), .B(n3339), .Z(n3686)); + AN2 U3558 ( .A(n3607), .B(n3716), .Z(n3684)); + AN2 U3559 ( .A(n3632), .B(n3657), .Z(n3716)); + AN2 U3560 ( .A(n3717), .B(n3718), .Z(n3703)); + OR2 U3561 ( .A(n3678), .B(n3719), .Z(n3718)); + OR2 U3562 ( .A(n3720), .B(n3721), .Z(n3719)); + AN2 U3563 ( .A(n3722), .B(n3599), .Z(n3721)); + OR2 U3564 ( .A(n3723), .B(n3724), .Z(n3722)); + AN2 U3565 ( .A(n3680), .B(n3607), .Z(n3724)); + AN2 U3566 ( .A(n3493), .B(n3725), .Z(n3723)); + IV2 U3567 ( .A(n3680), .Z(n3725)); + AN2 U3568 ( .A(n3611), .B(n3590), .Z(n3720)); + OR2 U3569 ( .A(n3726), .B(n3715), .Z(n3611)); + AN2 U3570 ( .A(n3341), .B(n3607), .Z(n3715)); + AN2 U3571 ( .A(n3680), .B(n3493), .Z(n3726)); + AN2 U3572 ( .A(n3593), .B(n3339), .Z(n3680)); + AN2 U3573 ( .A(n3607), .B(n3727), .Z(n3678)); + AN2 U3574 ( .A(n3590), .B(n3616), .Z(n3727)); + AN2 U3575 ( .A(n3661), .B(n3352), .Z(n3607)); + AN2 U3576 ( .A(n3639), .B(n3728), .Z(n3689)); + OR2 U3577 ( .A(n3729), .B(n3730), .Z(n3728)); + OR2 U3578 ( .A(n3731), .B(n3732), .Z(n3730)); + AN2 U3579 ( .A(n3733), .B(pi192), .Z(n3732)); + AN2 U3580 ( .A(n3640), .B(n3657), .Z(n3733)); + AN2 U3581 ( .A(n3734), .B(n2837), .Z(n3731)); + AN2 U3582 ( .A(n3599), .B(n3616), .Z(n3734)); + IV2 U3583 ( .A(n3590), .Z(n3599)); + AN2 U3584 ( .A(n3735), .B(n3355), .Z(n3729)); + OR2 U3585 ( .A(n3339), .B(n3736), .Z(n3735)); + OR2 U3586 ( .A(n3737), .B(n3738), .Z(n3578)); + AN2 U3587 ( .A(n3355), .B(n3739), .Z(n3738)); + OR2 U3588 ( .A(n3740), .B(n3741), .Z(n3739)); + AN2 U3589 ( .A(n3742), .B(n3625), .Z(n3741)); + AN2 U3590 ( .A(n3743), .B(n3744), .Z(n3742)); + OR2 U3591 ( .A(n3661), .B(n3688), .Z(n3744)); + OR2 U3592 ( .A(po011), .B(n3745), .Z(n3743)); + AN2 U3593 ( .A(n3493), .B(n3746), .Z(n3745)); + AN2 U3594 ( .A(n3747), .B(n3639), .Z(n3740)); + AN2 U3595 ( .A(n3661), .B(n3612), .Z(n3639)); + AN2 U3596 ( .A(n3736), .B(n3746), .Z(n3747)); + OR2 U3597 ( .A(n3292), .B(n3344), .Z(n3736)); + IV2 U3598 ( .A(n3291), .Z(n3292)); + AN2 U3599 ( .A(n3748), .B(n3648), .Z(n3737)); + OR2 U3600 ( .A(n3749), .B(n3750), .Z(n3648)); + AN2 U3601 ( .A(n3625), .B(po011), .Z(n3750)); + IV2 U3602 ( .A(n3612), .Z(n3625)); + AN2 U3603 ( .A(n3751), .B(n3688), .Z(n3749)); + AN2 U3604 ( .A(n3339), .B(n3612), .Z(n3751)); + OR2 U3605 ( .A(n3752), .B(n3753), .Z(n3612)); + OR2 U3606 ( .A(n3754), .B(n3755), .Z(n3753)); + AN2 U3607 ( .A(n3756), .B(n3757), .Z(n3755)); + OR2 U3608 ( .A(n3758), .B(n3759), .Z(n3752)); + AN2 U3609 ( .A(n3760), .B(n3761), .Z(n3759)); + AN2 U3610 ( .A(n3661), .B(n3762), .Z(n3748)); + OR2 U3611 ( .A(n3763), .B(n3764), .Z(po087)); + OR2 U3612 ( .A(po042), .B(n3765), .Z(n3764)); + OR2 U3613 ( .A(po029), .B(po022), .Z(n3765)); + OR2 U3614 ( .A(n3766), .B(n3767), .Z(n3763)); + OR2 U3615 ( .A(po080), .B(po056), .Z(n3767)); + OR2 U3616 ( .A(po105), .B(po083), .Z(n3766)); + IV2 U3617 ( .A(n3768), .Z(po105)); + AN2 U3618 ( .A(n3769), .B(n3770), .Z(n3768)); + AN2 U3619 ( .A(pi034), .B(pi007), .Z(n3770)); + AN2 U3620 ( .A(pi139), .B(pi120), .Z(n3769)); + OR2 U3621 ( .A(n3771), .B(n3772), .Z(po086)); + AN2 U3622 ( .A(n3773), .B(n3774), .Z(n3772)); + OR2 U3623 ( .A(n3775), .B(n3776), .Z(n3774)); + AN2 U3624 ( .A(n3777), .B(n3778), .Z(n3771)); + AN2 U3625 ( .A(n3779), .B(n3780), .Z(n3777)); + OR2 U3626 ( .A(po025), .B(n3781), .Z(n3779)); + IV2 U3627 ( .A(n3782), .Z(po083)); + AN2 U3628 ( .A(n3783), .B(n3784), .Z(n3782)); + AN2 U3629 ( .A(pi067), .B(pi041), .Z(n3784)); + AN2 U3630 ( .A(pi104), .B(pi070), .Z(n3783)); + OR2 U3631 ( .A(n3785), .B(n3786), .Z(po081)); + AN2 U3632 ( .A(n2862), .B(n3787), .Z(n3786)); + AN2 U3633 ( .A(n2930), .B(n3788), .Z(n3785)); + OR2 U3634 ( .A(n3146), .B(n3789), .Z(n3788)); + OR2 U3635 ( .A(n3790), .B(n3791), .Z(n3789)); + AN2 U3636 ( .A(n3792), .B(n2901), .Z(n3791)); + OR2 U3637 ( .A(n3793), .B(n3794), .Z(n3792)); + AN2 U3638 ( .A(pi192), .B(n2887), .Z(n3794)); + AN2 U3639 ( .A(n3795), .B(n2974), .Z(n3793)); + OR2 U3640 ( .A(n3796), .B(po031), .Z(n3795)); + AN2 U3641 ( .A(n2957), .B(n2837), .Z(n3796)); + AN2 U3642 ( .A(n3797), .B(n2935), .Z(n3790)); + AN2 U3643 ( .A(n2946), .B(n2837), .Z(n3797)); + OR2 U3644 ( .A(n3798), .B(n3799), .Z(po080)); + OR2 U3645 ( .A(n3800), .B(n3801), .Z(n3799)); + OR2 U3646 ( .A(n3802), .B(n3803), .Z(n3801)); + AN2 U3647 ( .A(n3804), .B(n2837), .Z(n3803)); + OR2 U3648 ( .A(n3805), .B(n3806), .Z(n3804)); + OR2 U3649 ( .A(n3807), .B(n3808), .Z(n3806)); + AN2 U3650 ( .A(n3809), .B(n3810), .Z(n3808)); + AN2 U3651 ( .A(n3811), .B(n3812), .Z(n3807)); + AN2 U3652 ( .A(n3813), .B(n3814), .Z(n3805)); + OR2 U3653 ( .A(n3815), .B(n3816), .Z(n3814)); + OR2 U3654 ( .A(n3817), .B(n3818), .Z(n3813)); + AN2 U3655 ( .A(pi192), .B(n3819), .Z(n3802)); + OR2 U3656 ( .A(n3820), .B(n3821), .Z(n3819)); + OR2 U3657 ( .A(n3822), .B(n3823), .Z(n3821)); + AN2 U3658 ( .A(n3824), .B(n3825), .Z(n3823)); + OR2 U3659 ( .A(n3826), .B(n3827), .Z(n3825)); + IV2 U3660 ( .A(n3828), .Z(n3824)); + AN2 U3661 ( .A(n3827), .B(n3826), .Z(n3828)); + OR2 U3662 ( .A(n3829), .B(n3830), .Z(n3826)); + AN2 U3663 ( .A(n3817), .B(n3171), .Z(n3830)); + AN2 U3664 ( .A(n3816), .B(pi033), .Z(n3829)); + IV2 U3665 ( .A(n3817), .Z(n3816)); + OR2 U3666 ( .A(n3831), .B(n3832), .Z(n3817)); + AN2 U3667 ( .A(n3833), .B(pi192), .Z(n3832)); + OR2 U3668 ( .A(n3834), .B(n3835), .Z(n3833)); + IV2 U3669 ( .A(n3836), .Z(n3835)); + OR2 U3670 ( .A(n3837), .B(n3838), .Z(n3836)); + AN2 U3671 ( .A(n3838), .B(n3837), .Z(n3834)); + AN2 U3672 ( .A(n3839), .B(n3840), .Z(n3837)); + OR2 U3673 ( .A(n3841), .B(pi013), .Z(n3840)); + IV2 U3674 ( .A(n3842), .Z(n3841)); + OR2 U3675 ( .A(n3843), .B(n3842), .Z(n3839)); + OR2 U3676 ( .A(n3844), .B(n3845), .Z(n3842)); + AN2 U3677 ( .A(pi026), .B(n3846), .Z(n3845)); + AN2 U3678 ( .A(pi077), .B(n3847), .Z(n3844)); + IV2 U3679 ( .A(pi013), .Z(n3843)); + OR2 U3680 ( .A(n3848), .B(n3849), .Z(n3838)); + AN2 U3681 ( .A(n3850), .B(n3136), .Z(n3849)); + AN2 U3682 ( .A(n3851), .B(pi088), .Z(n3848)); + IV2 U3683 ( .A(n3850), .Z(n3851)); + OR2 U3684 ( .A(n3852), .B(n3853), .Z(n3850)); + IV2 U3685 ( .A(n3854), .Z(n3853)); + OR2 U3686 ( .A(n3855), .B(pi157), .Z(n3854)); + AN2 U3687 ( .A(pi157), .B(n3855), .Z(n3852)); + IV2 U3688 ( .A(pi137), .Z(n3855)); + AN2 U3689 ( .A(n3856), .B(n3857), .Z(n3827)); + OR2 U3690 ( .A(n3858), .B(pi096), .Z(n3857)); + IV2 U3691 ( .A(n3859), .Z(n3858)); + OR2 U3692 ( .A(n3859), .B(n3199), .Z(n3856)); + OR2 U3693 ( .A(n3860), .B(n3861), .Z(n3859)); + AN2 U3694 ( .A(pi166), .B(n3862), .Z(n3861)); + IV2 U3695 ( .A(pi175), .Z(n3862)); + AN2 U3696 ( .A(pi175), .B(n3106), .Z(n3860)); + OR2 U3697 ( .A(n3863), .B(n3864), .Z(n3822)); + AN2 U3698 ( .A(n3865), .B(n3866), .Z(n3864)); + IV2 U3699 ( .A(n3867), .Z(n3863)); + OR2 U3700 ( .A(n3866), .B(n3865), .Z(n3867)); + OR2 U3701 ( .A(n3868), .B(n3869), .Z(n3865)); + AN2 U3702 ( .A(n3811), .B(n3870), .Z(n3869)); + IV2 U3703 ( .A(n3810), .Z(n3811)); + AN2 U3704 ( .A(pi016), .B(n3810), .Z(n3868)); + OR2 U3705 ( .A(n3871), .B(n3872), .Z(n3810)); + OR2 U3706 ( .A(n3873), .B(n3874), .Z(n3872)); + AN2 U3707 ( .A(n3875), .B(pi148), .Z(n3874)); + OR2 U3708 ( .A(n3876), .B(n3877), .Z(n3875)); + AN2 U3709 ( .A(n3878), .B(pi135), .Z(n3877)); + AN2 U3710 ( .A(n3879), .B(n3880), .Z(n3876)); + AN2 U3711 ( .A(n3881), .B(n3882), .Z(n3873)); + IV2 U3712 ( .A(pi148), .Z(n3882)); + OR2 U3713 ( .A(n3883), .B(n3884), .Z(n3881)); + AN2 U3714 ( .A(n3879), .B(pi135), .Z(n3884)); + OR2 U3715 ( .A(n3885), .B(n3886), .Z(n3879)); + AN2 U3716 ( .A(n3887), .B(n3888), .Z(n3886)); + AN2 U3717 ( .A(n3889), .B(n3890), .Z(n3885)); + AN2 U3718 ( .A(n3878), .B(n3880), .Z(n3883)); + OR2 U3719 ( .A(n3891), .B(n3892), .Z(n3878)); + AN2 U3720 ( .A(n3887), .B(n3890), .Z(n3892)); + AN2 U3721 ( .A(n3889), .B(n3888), .Z(n3891)); + IV2 U3722 ( .A(n3887), .Z(n3889)); + OR2 U3723 ( .A(n3893), .B(n3894), .Z(n3887)); + AN2 U3724 ( .A(n3895), .B(n3896), .Z(n3894)); + AN2 U3725 ( .A(n3897), .B(pi005), .Z(n3893)); + IV2 U3726 ( .A(n3895), .Z(n3897)); + OR2 U3727 ( .A(n3898), .B(n3899), .Z(n3895)); + AN2 U3728 ( .A(pi069), .B(n3900), .Z(n3899)); + IV2 U3729 ( .A(pi072), .Z(n3900)); + AN2 U3730 ( .A(pi072), .B(n3901), .Z(n3898)); + AN2 U3731 ( .A(n3902), .B(n3903), .Z(n3866)); + OR2 U3732 ( .A(n3904), .B(pi045), .Z(n3903)); + IV2 U3733 ( .A(n3905), .Z(n3902)); + AN2 U3734 ( .A(n3904), .B(pi045), .Z(n3905)); + AN2 U3735 ( .A(n3906), .B(n3907), .Z(n3904)); + OR2 U3736 ( .A(n3908), .B(pi158), .Z(n3907)); + OR2 U3737 ( .A(n3909), .B(pi079), .Z(n3906)); + OR2 U3738 ( .A(n3910), .B(n3911), .Z(n3820)); + AN2 U3739 ( .A(n3912), .B(n3913), .Z(n3911)); + AN2 U3740 ( .A(n3534), .B(n3914), .Z(n3912)); + AN2 U3741 ( .A(pi118), .B(n3915), .Z(n3910)); + OR2 U3742 ( .A(n3916), .B(n3917), .Z(n3915)); + AN2 U3743 ( .A(n3918), .B(pi060), .Z(n3917)); + AN2 U3744 ( .A(n3919), .B(n3534), .Z(n3916)); + AN2 U3745 ( .A(n3920), .B(n3921), .Z(n3919)); + AN2 U3746 ( .A(n3922), .B(n3923), .Z(n3800)); + OR2 U3747 ( .A(n3924), .B(n3925), .Z(n3923)); + OR2 U3748 ( .A(n3321), .B(n3926), .Z(n3925)); + AN2 U3749 ( .A(n3927), .B(n3928), .Z(n3924)); + AN2 U3750 ( .A(pi050), .B(pi118), .Z(n3928)); + AN2 U3751 ( .A(pi196), .B(n3534), .Z(n3927)); + OR2 U3752 ( .A(n3929), .B(n3930), .Z(n3798)); + OR2 U3753 ( .A(n3931), .B(n3932), .Z(n3930)); + AN2 U3754 ( .A(n3933), .B(n3533), .Z(n3932)); + AN2 U3755 ( .A(n3934), .B(n3913), .Z(n3933)); + OR2 U3756 ( .A(n3918), .B(n3935), .Z(n3934)); + OR2 U3757 ( .A(n3936), .B(n3937), .Z(n3935)); + AN2 U3758 ( .A(n3938), .B(n3921), .Z(n3937)); + AN2 U3759 ( .A(n3920), .B(n3261), .Z(n3938)); + AN2 U3760 ( .A(n3939), .B(n3922), .Z(n3936)); + AN2 U3761 ( .A(pi196), .B(n3940), .Z(n3939)); + AN2 U3762 ( .A(n3922), .B(n3941), .Z(n3918)); + AN2 U3763 ( .A(n3942), .B(n3943), .Z(n3941)); + AN2 U3764 ( .A(n3944), .B(n3945), .Z(n3931)); + AN2 U3765 ( .A(pi204), .B(n3946), .Z(n3944)); + IV2 U3766 ( .A(n3947), .Z(n3946)); + AN2 U3767 ( .A(n3948), .B(n3261), .Z(n3929)); + OR2 U3768 ( .A(n3949), .B(n3950), .Z(n3948)); + AN2 U3769 ( .A(n3947), .B(n3951), .Z(n3950)); + AN2 U3770 ( .A(n3952), .B(n3953), .Z(n3947)); + IV2 U3771 ( .A(n3954), .Z(n3953)); + AN2 U3772 ( .A(n3955), .B(n3956), .Z(n3954)); + OR2 U3773 ( .A(n3956), .B(n3955), .Z(n3952)); + OR2 U3774 ( .A(n3957), .B(n3958), .Z(n3955)); + AN2 U3775 ( .A(n3959), .B(n3960), .Z(n3958)); + IV2 U3776 ( .A(pi009), .Z(n3960)); + AN2 U3777 ( .A(pi009), .B(n3961), .Z(n3957)); + AN2 U3778 ( .A(n3962), .B(n3963), .Z(n3956)); + OR2 U3779 ( .A(n3964), .B(pi129), .Z(n3963)); + IV2 U3780 ( .A(n3965), .Z(n3964)); + OR2 U3781 ( .A(n3965), .B(n3966), .Z(n3962)); + OR2 U3782 ( .A(n3967), .B(n3968), .Z(n3965)); + AN2 U3783 ( .A(pi138), .B(n3969), .Z(n3968)); + AN2 U3784 ( .A(pi169), .B(n3970), .Z(n3967)); + IV2 U3785 ( .A(pi138), .Z(n3970)); + AN2 U3786 ( .A(n3971), .B(n3533), .Z(n3949)); + AN2 U3787 ( .A(n3914), .B(pi118), .Z(n3971)); + OR2 U3788 ( .A(n3972), .B(n3973), .Z(n3914)); + AN2 U3789 ( .A(n3920), .B(n3922), .Z(n3973)); + IV2 U3790 ( .A(n3921), .Z(n3922)); + AN2 U3791 ( .A(n3921), .B(n3974), .Z(n3972)); + IV2 U3792 ( .A(n3920), .Z(n3974)); + OR2 U3793 ( .A(n3975), .B(n3976), .Z(n3920)); + AN2 U3794 ( .A(pi050), .B(n3942), .Z(n3976)); + AN2 U3795 ( .A(pi196), .B(n3943), .Z(n3975)); + OR2 U3796 ( .A(n3977), .B(n3978), .Z(n3921)); + AN2 U3797 ( .A(n3979), .B(pi192), .Z(n3978)); + OR2 U3798 ( .A(n3980), .B(n3981), .Z(n3979)); + AN2 U3799 ( .A(n3982), .B(n3983), .Z(n3981)); + IV2 U3800 ( .A(n3984), .Z(n3980)); + OR2 U3801 ( .A(n3983), .B(n3982), .Z(n3984)); + OR2 U3802 ( .A(n3985), .B(n3986), .Z(n3982)); + IV2 U3803 ( .A(n3987), .Z(n3986)); + OR2 U3804 ( .A(n3988), .B(n3989), .Z(n3987)); + AN2 U3805 ( .A(n3988), .B(n3989), .Z(n3985)); + AN2 U3806 ( .A(n3990), .B(n3991), .Z(n3988)); + OR2 U3807 ( .A(n3992), .B(pi039), .Z(n3991)); + OR2 U3808 ( .A(n3993), .B(pi004), .Z(n3990)); + IV2 U3809 ( .A(pi039), .Z(n3993)); + AN2 U3810 ( .A(n3994), .B(n3995), .Z(n3983)); + OR2 U3811 ( .A(n3996), .B(pi068), .Z(n3995)); + IV2 U3812 ( .A(n3997), .Z(n3996)); + OR2 U3813 ( .A(n3997), .B(n3998), .Z(n3994)); + OR2 U3814 ( .A(n3999), .B(n4000), .Z(n3997)); + AN2 U3815 ( .A(pi098), .B(n4001), .Z(n4000)); + AN2 U3816 ( .A(pi171), .B(n3638), .Z(n3999)); + OR2 U3817 ( .A(pi037), .B(pi043), .Z(po078)); + OR2 U3818 ( .A(n4002), .B(n4003), .Z(po077)); + AN2 U3819 ( .A(n4004), .B(n4005), .Z(n4003)); + AN2 U3820 ( .A(n4006), .B(n4007), .Z(n4002)); + OR2 U3821 ( .A(n4008), .B(n4009), .Z(n4007)); + IV2 U3822 ( .A(pi090), .Z(po076)); + OR2 U3823 ( .A(n4010), .B(n4011), .Z(po075)); + AN2 U3824 ( .A(n4012), .B(n4013), .Z(n4011)); + OR2 U3825 ( .A(n4014), .B(n4015), .Z(n4012)); + OR2 U3826 ( .A(n4016), .B(n4017), .Z(n4015)); + OR2 U3827 ( .A(n4018), .B(n4019), .Z(n4014)); + AN2 U3828 ( .A(n4020), .B(n3314), .Z(n4019)); + OR2 U3829 ( .A(n4021), .B(n4022), .Z(n4020)); + AN2 U3830 ( .A(n3926), .B(n3328), .Z(n4022)); + OR2 U3831 ( .A(n3265), .B(n3326), .Z(n3328)); + AN2 U3832 ( .A(n4023), .B(pi204), .Z(n4021)); + AN2 U3833 ( .A(n3312), .B(n3261), .Z(n4023)); + OR2 U3834 ( .A(n4024), .B(n4025), .Z(n3312)); + AN2 U3835 ( .A(n3326), .B(n4026), .Z(n4024)); + AN2 U3836 ( .A(n4027), .B(n3320), .Z(n4018)); + OR2 U3837 ( .A(n4028), .B(n3321), .Z(n4027)); + AN2 U3838 ( .A(po040), .B(n4029), .Z(n4010)); + OR2 U3839 ( .A(n4030), .B(n4031), .Z(n4029)); + OR2 U3840 ( .A(n4032), .B(n4033), .Z(n4031)); + AN2 U3841 ( .A(n4034), .B(n3261), .Z(n4032)); + OR2 U3842 ( .A(n4035), .B(n4036), .Z(n4034)); + OR2 U3843 ( .A(n4037), .B(n4038), .Z(n4036)); + AN2 U3844 ( .A(n4039), .B(n3951), .Z(n4038)); + AN2 U3845 ( .A(n4025), .B(n3314), .Z(n4039)); + AN2 U3846 ( .A(n4040), .B(n4041), .Z(n4037)); + AN2 U3847 ( .A(n4042), .B(n3320), .Z(n4035)); + OR2 U3848 ( .A(n4043), .B(n4044), .Z(n4042)); + AN2 U3849 ( .A(pi204), .B(n4045), .Z(n4044)); + AN2 U3850 ( .A(n4046), .B(n4041), .Z(n4043)); + OR2 U3851 ( .A(n4047), .B(n4048), .Z(n4030)); + AN2 U3852 ( .A(n4049), .B(n4050), .Z(n4048)); + OR2 U3853 ( .A(n4051), .B(n4052), .Z(n4050)); + AN2 U3854 ( .A(n3321), .B(n3265), .Z(n4052)); + AN2 U3855 ( .A(n3951), .B(n4053), .Z(n4051)); + AN2 U3856 ( .A(n3326), .B(n3314), .Z(n4049)); + AN2 U3857 ( .A(n4054), .B(n4055), .Z(n4047)); + AN2 U3858 ( .A(n4056), .B(n4057), .Z(n4055)); + AN2 U3859 ( .A(n3945), .B(pi204), .Z(n4054)); + OR2 U3860 ( .A(n4058), .B(n4059), .Z(po073)); + AN2 U3861 ( .A(n4060), .B(n4061), .Z(n4059)); + AN2 U3862 ( .A(n3457), .B(n4062), .Z(n4058)); + OR2 U3863 ( .A(n4063), .B(n4064), .Z(po068)); + AN2 U3864 ( .A(n4065), .B(n4066), .Z(n4064)); + OR2 U3865 ( .A(n4067), .B(n4068), .Z(n4065)); + OR2 U3866 ( .A(n4069), .B(n4070), .Z(n4068)); + AN2 U3867 ( .A(n4071), .B(n4072), .Z(n4070)); + OR2 U3868 ( .A(n4073), .B(po059), .Z(n4071)); + AN2 U3869 ( .A(n4074), .B(n2837), .Z(n4073)); + AN2 U3870 ( .A(n4075), .B(n4076), .Z(n4069)); + AN2 U3871 ( .A(n3780), .B(n4077), .Z(n4075)); + AN2 U3872 ( .A(n4078), .B(n4079), .Z(n4063)); + OR2 U3873 ( .A(n4080), .B(n4081), .Z(n4079)); + OR2 U3874 ( .A(n4082), .B(n4083), .Z(n4081)); + AN2 U3875 ( .A(n4084), .B(n2837), .Z(n4083)); + AN2 U3876 ( .A(n4085), .B(pi192), .Z(n4082)); + AN2 U3877 ( .A(n3778), .B(n3776), .Z(n4080)); + OR2 U3878 ( .A(n4086), .B(n4087), .Z(n3776)); + OR2 U3879 ( .A(n4088), .B(n4089), .Z(po061)); + AN2 U3880 ( .A(n2861), .B(n4090), .Z(n4089)); + OR2 U3881 ( .A(n4091), .B(n4092), .Z(n4090)); + OR2 U3882 ( .A(n4093), .B(n4094), .Z(n4092)); + AN2 U3883 ( .A(n4095), .B(n2901), .Z(n4094)); + AN2 U3884 ( .A(n2862), .B(n2990), .Z(n4093)); + OR2 U3885 ( .A(n4096), .B(n4097), .Z(n4091)); + AN2 U3886 ( .A(n2880), .B(n4098), .Z(n4097)); + AN2 U3887 ( .A(n4099), .B(n4100), .Z(n4096)); + OR2 U3888 ( .A(n4101), .B(n4102), .Z(n4100)); + OR2 U3889 ( .A(n2978), .B(n4103), .Z(n4102)); + AN2 U3890 ( .A(n3222), .B(n2957), .Z(n4103)); + AN2 U3891 ( .A(n2935), .B(po031), .Z(n4101)); + IV2 U3892 ( .A(n2938), .Z(n2935)); + AN2 U3893 ( .A(n4104), .B(n2998), .Z(n4088)); + OR2 U3894 ( .A(n4105), .B(n4106), .Z(n4104)); + OR2 U3895 ( .A(n4107), .B(n4108), .Z(n4106)); + AN2 U3896 ( .A(n2892), .B(pi192), .Z(n4108)); + AN2 U3897 ( .A(n2929), .B(n2837), .Z(n4107)); + AN2 U3898 ( .A(n2930), .B(n3787), .Z(n4105)); + OR2 U3899 ( .A(n4109), .B(n4110), .Z(n3787)); + OR2 U3900 ( .A(n4111), .B(n4112), .Z(n4110)); + AN2 U3901 ( .A(n4113), .B(n2837), .Z(n4112)); + AN2 U3902 ( .A(n2876), .B(pi192), .Z(n4111)); + AN2 U3903 ( .A(pi200), .B(n4114), .Z(n4109)); + OR2 U3904 ( .A(n4115), .B(n2999), .Z(n4114)); + OR2 U3905 ( .A(n4116), .B(n4117), .Z(n2999)); + AN2 U3906 ( .A(n2938), .B(n2962), .Z(n4117)); + AN2 U3907 ( .A(pi192), .B(n2878), .Z(n4116)); + AN2 U3908 ( .A(n2918), .B(n2837), .Z(n4115)); + AN2 U3909 ( .A(n2938), .B(pi082), .Z(n2918)); + OR2 U3910 ( .A(n4118), .B(n4119), .Z(po060)); + OR2 U3911 ( .A(n4120), .B(n4121), .Z(n4119)); + AN2 U3912 ( .A(n4122), .B(n4123), .Z(n4121)); + OR2 U3913 ( .A(n4124), .B(n4125), .Z(n4123)); + AN2 U3914 ( .A(n4126), .B(n4127), .Z(n4124)); + OR2 U3915 ( .A(n4128), .B(n4129), .Z(n4126)); + AN2 U3916 ( .A(n4130), .B(n4131), .Z(n4122)); + OR2 U3917 ( .A(n4132), .B(n4133), .Z(n4131)); + AN2 U3918 ( .A(pi052), .B(n4134), .Z(n4132)); + OR2 U3919 ( .A(po064), .B(n4135), .Z(n4130)); + AN2 U3920 ( .A(n4136), .B(n4137), .Z(n4120)); + OR2 U3921 ( .A(n4138), .B(n4139), .Z(n4137)); + AN2 U3922 ( .A(n4140), .B(n4141), .Z(n4139)); + OR2 U3923 ( .A(n4142), .B(n4143), .Z(n4140)); + OR2 U3924 ( .A(n4144), .B(n4145), .Z(n4143)); + AN2 U3925 ( .A(po040), .B(n3321), .Z(n4145)); + AN2 U3926 ( .A(po103), .B(n4146), .Z(n4144)); + OR2 U3927 ( .A(n4147), .B(n3321), .Z(n4146)); + AN2 U3928 ( .A(n4148), .B(n4149), .Z(n4147)); + OR2 U3929 ( .A(po004), .B(n4150), .Z(n4149)); + OR2 U3930 ( .A(n4151), .B(n4152), .Z(n4142)); + OR2 U3931 ( .A(n4153), .B(n4154), .Z(n4152)); + IV2 U3932 ( .A(n4155), .Z(n4154)); + OR2 U3933 ( .A(n4156), .B(n4157), .Z(n4155)); + AN2 U3934 ( .A(n4158), .B(n4159), .Z(n4153)); + AN2 U3935 ( .A(n4160), .B(po004), .Z(n4151)); + AN2 U3936 ( .A(n3951), .B(po040), .Z(n4160)); + AN2 U3937 ( .A(po004), .B(n4161), .Z(n4138)); + AN2 U3938 ( .A(n4162), .B(n4163), .Z(n4136)); + OR2 U3939 ( .A(n4164), .B(n4134), .Z(n4163)); + AN2 U3940 ( .A(n4165), .B(po064), .Z(n4164)); + IV2 U3941 ( .A(n4166), .Z(n4165)); + OR2 U3942 ( .A(n4167), .B(n4125), .Z(n4166)); + OR2 U3943 ( .A(n4135), .B(n4168), .Z(n4162)); + OR2 U3944 ( .A(n4169), .B(n4170), .Z(n4168)); + AN2 U3945 ( .A(pi054), .B(n4167), .Z(n4170)); + AN2 U3946 ( .A(n4171), .B(n4133), .Z(n4169)); + IV2 U3947 ( .A(n4172), .Z(n4171)); + OR2 U3948 ( .A(n4173), .B(n4174), .Z(n4118)); + AN2 U3949 ( .A(n4175), .B(pi054), .Z(n4174)); + AN2 U3950 ( .A(n4176), .B(n4177), .Z(n4175)); + OR2 U3951 ( .A(n4178), .B(n4179), .Z(n4176)); + AN2 U3952 ( .A(n4134), .B(n4133), .Z(n4179)); + AN2 U3953 ( .A(n4135), .B(po064), .Z(n4178)); + AN2 U3954 ( .A(n4180), .B(n4135), .Z(n4173)); + IV2 U3955 ( .A(n4134), .Z(n4135)); + OR2 U3956 ( .A(n4181), .B(n4182), .Z(n4134)); + AN2 U3957 ( .A(n4183), .B(n4184), .Z(n4182)); + OR2 U3958 ( .A(n4185), .B(n4186), .Z(n4183)); + AN2 U3959 ( .A(n4187), .B(n4188), .Z(n4186)); + IV2 U3960 ( .A(n4189), .Z(n4188)); + OR2 U3961 ( .A(n4190), .B(n4191), .Z(n4187)); + AN2 U3962 ( .A(n4192), .B(n4193), .Z(n4191)); + OR2 U3963 ( .A(n4194), .B(n4195), .Z(n4193)); + AN2 U3964 ( .A(n4196), .B(n3261), .Z(n4195)); + OR2 U3965 ( .A(n4197), .B(n4198), .Z(n4196)); + AN2 U3966 ( .A(n4199), .B(po103), .Z(n4198)); + AN2 U3967 ( .A(n4200), .B(n4201), .Z(n4199)); + AN2 U3968 ( .A(n4202), .B(n4025), .Z(n4197)); + AN2 U3969 ( .A(n4203), .B(po091), .Z(n4202)); + AN2 U3970 ( .A(n4204), .B(n4205), .Z(n4190)); + OR2 U3971 ( .A(n3317), .B(n4206), .Z(n4204)); + OR2 U3972 ( .A(n4207), .B(n4208), .Z(n4206)); + AN2 U3973 ( .A(n4209), .B(n3314), .Z(n4208)); + IV2 U3974 ( .A(n4210), .Z(n4209)); + AN2 U3975 ( .A(n4210), .B(n4057), .Z(n4207)); + AN2 U3976 ( .A(po103), .B(n4148), .Z(n4210)); + AN2 U3977 ( .A(n4189), .B(n4211), .Z(n4185)); + OR2 U3978 ( .A(n4212), .B(n4213), .Z(n4211)); + AN2 U3979 ( .A(n4214), .B(n4205), .Z(n4213)); + OR2 U3980 ( .A(n3325), .B(n4215), .Z(n4214)); + OR2 U3981 ( .A(n4216), .B(n3329), .Z(n4215)); + AN2 U3982 ( .A(n4040), .B(n4217), .Z(n3329)); + AN2 U3983 ( .A(n4192), .B(n4218), .Z(n4212)); + OR2 U3984 ( .A(n4219), .B(n4220), .Z(n4218)); + OR2 U3985 ( .A(n4221), .B(n4222), .Z(n4220)); + AN2 U3986 ( .A(n4025), .B(n4201), .Z(n4222)); + IV2 U3987 ( .A(n4223), .Z(n4025)); + AN2 U3988 ( .A(po091), .B(n4200), .Z(n4221)); + OR2 U3989 ( .A(n4224), .B(n4225), .Z(n4189)); + AN2 U3990 ( .A(n4226), .B(n4227), .Z(n4225)); + IV2 U3991 ( .A(n4228), .Z(n4224)); + OR2 U3992 ( .A(n4227), .B(n4226), .Z(n4228)); + AN2 U3993 ( .A(n4229), .B(n4230), .Z(n4181)); + OR2 U3994 ( .A(n4231), .B(n4232), .Z(n4230)); + OR2 U3995 ( .A(n4233), .B(n4234), .Z(n4232)); + AN2 U3996 ( .A(n4235), .B(n4236), .Z(n4234)); + OR2 U3997 ( .A(n4237), .B(n4238), .Z(n4235)); + AN2 U3998 ( .A(n4239), .B(n4240), .Z(n4238)); + OR2 U3999 ( .A(n3325), .B(n4216), .Z(n4240)); + AN2 U4000 ( .A(po103), .B(n4194), .Z(n4216)); + AN2 U4001 ( .A(n3265), .B(n4040), .Z(n3325)); + AN2 U4002 ( .A(n4241), .B(n4242), .Z(n4237)); + OR2 U4003 ( .A(n4243), .B(n4219), .Z(n4241)); + OR2 U4004 ( .A(n4244), .B(n3321), .Z(n4219)); + AN2 U4005 ( .A(n4203), .B(n4223), .Z(n4244)); + AN2 U4006 ( .A(n4245), .B(n3314), .Z(n4243)); + OR2 U4007 ( .A(n4246), .B(po091), .Z(n4245)); + AN2 U4008 ( .A(n4247), .B(n3265), .Z(n4246)); + AN2 U4009 ( .A(n4248), .B(n4249), .Z(n4233)); + OR2 U4010 ( .A(n4250), .B(n4251), .Z(n4249)); + OR2 U4011 ( .A(n4252), .B(n4253), .Z(n4251)); + AN2 U4012 ( .A(po103), .B(n4254), .Z(n4253)); + OR2 U4013 ( .A(n4255), .B(n3330), .Z(n4254)); + AN2 U4014 ( .A(n3311), .B(n4242), .Z(n4255)); + AN2 U4015 ( .A(n4194), .B(n3265), .Z(n4252)); + AN2 U4016 ( .A(n4148), .B(n3311), .Z(n4194)); + AN2 U4017 ( .A(n4239), .B(n4256), .Z(n4250)); + OR2 U4018 ( .A(n4257), .B(n4258), .Z(n4256)); + OR2 U4019 ( .A(n3926), .B(n4259), .Z(n4258)); + AN2 U4020 ( .A(n4260), .B(n3314), .Z(n4259)); + OR2 U4021 ( .A(n4261), .B(n4262), .Z(n4257)); + AN2 U4022 ( .A(pi058), .B(pi129), .Z(n4262)); + AN2 U4023 ( .A(n4056), .B(n3966), .Z(n4261)); + AN2 U4024 ( .A(n4263), .B(n4264), .Z(n4231)); + AN2 U4025 ( .A(n4265), .B(n4266), .Z(n4264)); + OR2 U4026 ( .A(n4267), .B(n4236), .Z(n4266)); + AN2 U4027 ( .A(n4268), .B(pi058), .Z(n4267)); + AN2 U4028 ( .A(n4242), .B(n3265), .Z(n4268)); + OR2 U4029 ( .A(n4269), .B(n4248), .Z(n4265)); + IV2 U4030 ( .A(n4236), .Z(n4248)); + AN2 U4031 ( .A(n4270), .B(n4271), .Z(n4236)); + IV2 U4032 ( .A(n4272), .Z(n4271)); + AN2 U4033 ( .A(n4226), .B(n4141), .Z(n4272)); + OR2 U4034 ( .A(n4226), .B(n4141), .Z(n4270)); + OR2 U4035 ( .A(n4273), .B(n4274), .Z(n4226)); + IV2 U4036 ( .A(n4275), .Z(n4274)); + OR2 U4037 ( .A(n4276), .B(n4277), .Z(n4275)); + AN2 U4038 ( .A(n4277), .B(n4276), .Z(n4273)); + AN2 U4039 ( .A(n4278), .B(n4279), .Z(n4276)); + OR2 U4040 ( .A(n4280), .B(n4281), .Z(n4279)); + IV2 U4041 ( .A(n4282), .Z(n4280)); + OR2 U4042 ( .A(n4283), .B(n4282), .Z(n4278)); + OR2 U4043 ( .A(n4284), .B(n4285), .Z(n4282)); + AN2 U4044 ( .A(po040), .B(n4286), .Z(n4285)); + OR2 U4045 ( .A(n4017), .B(n4287), .Z(n4286)); + OR2 U4046 ( .A(n4288), .B(n4289), .Z(n4287)); + AN2 U4047 ( .A(n4290), .B(n3314), .Z(n4289)); + OR2 U4048 ( .A(n4291), .B(n3321), .Z(n4290)); + IV2 U4049 ( .A(n4292), .Z(n4288)); + OR2 U4050 ( .A(n4293), .B(n3314), .Z(n4292)); + OR2 U4051 ( .A(n4294), .B(n3330), .Z(n4017)); + AN2 U4052 ( .A(n4201), .B(n3926), .Z(n3330)); + AN2 U4053 ( .A(n3311), .B(pi204), .Z(n4294)); + AN2 U4054 ( .A(n4295), .B(n4013), .Z(n4284)); + OR2 U4055 ( .A(n4033), .B(n4296), .Z(n4295)); + OR2 U4056 ( .A(n4297), .B(n4298), .Z(n4296)); + AN2 U4057 ( .A(n4299), .B(n3314), .Z(n4298)); + AN2 U4058 ( .A(n4300), .B(n3261), .Z(n4299)); + OR2 U4059 ( .A(n4301), .B(n4302), .Z(n4300)); + AN2 U4060 ( .A(pi204), .B(n4203), .Z(n4302)); + AN2 U4061 ( .A(po091), .B(n4041), .Z(n4301)); + AN2 U4062 ( .A(n4040), .B(n4293), .Z(n4297)); + AN2 U4063 ( .A(n3311), .B(n3951), .Z(n4033)); + IV2 U4064 ( .A(n4260), .Z(n3311)); + OR2 U4065 ( .A(n3321), .B(n4057), .Z(n4260)); + IV2 U4066 ( .A(n4281), .Z(n4283)); + OR2 U4067 ( .A(n4303), .B(n4304), .Z(n4281)); + AN2 U4068 ( .A(n4305), .B(n4306), .Z(n4304)); + AN2 U4069 ( .A(n4307), .B(n3261), .Z(n4305)); + OR2 U4070 ( .A(n4308), .B(n4309), .Z(n4307)); + AN2 U4071 ( .A(pi065), .B(n4148), .Z(n4309)); + AN2 U4072 ( .A(n4310), .B(pi058), .Z(n4308)); + AN2 U4073 ( .A(n4311), .B(n4312), .Z(n4303)); + OR2 U4074 ( .A(n3961), .B(n4313), .Z(n4311)); + IV2 U4075 ( .A(n3959), .Z(n3961)); + OR2 U4076 ( .A(n4314), .B(n4315), .Z(n3959)); + AN2 U4077 ( .A(pi058), .B(n4150), .Z(n4315)); + AN2 U4078 ( .A(pi065), .B(n4316), .Z(n4314)); + OR2 U4079 ( .A(n4317), .B(n4318), .Z(n4277)); + AN2 U4080 ( .A(po004), .B(n3265), .Z(n4318)); + AN2 U4081 ( .A(po103), .B(n4319), .Z(n4317)); + AN2 U4082 ( .A(n4239), .B(pi058), .Z(n4269)); + AN2 U4083 ( .A(n4040), .B(n3261), .Z(n4263)); + AN2 U4084 ( .A(n4320), .B(n4167), .Z(n4180)); + OR2 U4085 ( .A(n4129), .B(n4127), .Z(n4320)); + AN2 U4086 ( .A(po023), .B(pi183), .Z(po058)); + IV2 U4087 ( .A(n4321), .Z(po056)); + AN2 U4088 ( .A(n4322), .B(n4323), .Z(n4321)); + AN2 U4089 ( .A(pi063), .B(pi010), .Z(n4323)); + AN2 U4090 ( .A(pi203), .B(pi073), .Z(n4322)); + OR2 U4091 ( .A(n4324), .B(n4325), .Z(po055)); + AN2 U4092 ( .A(n3015), .B(n4326), .Z(n4325)); + AN2 U4093 ( .A(n3018), .B(n4327), .Z(n4324)); + AN2 U4094 ( .A(n4328), .B(n4329), .Z(po053)); + OR2 U4095 ( .A(n4306), .B(n4330), .Z(n4329)); + IV2 U4096 ( .A(n4312), .Z(n4306)); + OR2 U4097 ( .A(n4331), .B(n4312), .Z(n4328)); + AN2 U4098 ( .A(n4332), .B(n3780), .Z(po051)); + OR2 U4099 ( .A(n4333), .B(n4334), .Z(n4332)); + OR2 U4100 ( .A(n4335), .B(n4336), .Z(po050)); + AN2 U4101 ( .A(n4337), .B(n4338), .Z(n4336)); + OR2 U4102 ( .A(n4339), .B(n4340), .Z(n4338)); + AN2 U4103 ( .A(n4087), .B(n4341), .Z(n4339)); + AN2 U4104 ( .A(n4342), .B(n4343), .Z(n4335)); + OR2 U4105 ( .A(n4344), .B(n4345), .Z(n4343)); + OR2 U4106 ( .A(n4346), .B(n4347), .Z(n4345)); + AN2 U4107 ( .A(n4067), .B(n4348), .Z(n4346)); + OR2 U4108 ( .A(n4349), .B(n4350), .Z(n4067)); + AN2 U4109 ( .A(n4351), .B(pi192), .Z(n4350)); + AN2 U4110 ( .A(n4072), .B(n3880), .Z(n4351)); + AN2 U4111 ( .A(n4352), .B(n3890), .Z(n4349)); + AN2 U4112 ( .A(n4353), .B(n3780), .Z(n4352)); + OR2 U4113 ( .A(n4354), .B(n4355), .Z(n4344)); + AN2 U4114 ( .A(n4356), .B(n3780), .Z(n4355)); + AN2 U4115 ( .A(n4357), .B(n4076), .Z(n4354)); + AN2 U4116 ( .A(n4072), .B(n4358), .Z(n4357)); + OR2 U4117 ( .A(n4359), .B(n4360), .Z(po049)); + AN2 U4118 ( .A(n3361), .B(n4361), .Z(n4360)); + OR2 U4119 ( .A(n4362), .B(n4363), .Z(n4361)); + AN2 U4120 ( .A(n4364), .B(n3451), .Z(n4362)); + IV2 U4121 ( .A(n3401), .Z(n3361)); + AN2 U4122 ( .A(n4365), .B(n3401), .Z(n4359)); + OR2 U4123 ( .A(n4366), .B(n4367), .Z(n4365)); + AN2 U4124 ( .A(n3453), .B(n4368), .Z(n4366)); + OR2 U4125 ( .A(n4369), .B(n3253), .Z(po048)); + AN2 U4126 ( .A(n4370), .B(n3255), .Z(n4369)); + OR2 U4127 ( .A(n4371), .B(n4372), .Z(n4370)); + AN2 U4128 ( .A(n4373), .B(n3259), .Z(n4371)); + AN2 U4129 ( .A(n4374), .B(n4242), .Z(n4373)); + OR2 U4130 ( .A(n4375), .B(n4376), .Z(po047)); + OR2 U4131 ( .A(n4377), .B(n4378), .Z(n4376)); + AN2 U4132 ( .A(n4379), .B(n4380), .Z(n4378)); + AN2 U4133 ( .A(n4381), .B(n3259), .Z(n4377)); + AN2 U4134 ( .A(n4382), .B(n4383), .Z(n4381)); + OR2 U4135 ( .A(po004), .B(n4384), .Z(n4383)); + OR2 U4136 ( .A(n3926), .B(n4385), .Z(n4384)); + AN2 U4137 ( .A(n4386), .B(pi065), .Z(n4385)); + AN2 U4138 ( .A(n4387), .B(n4157), .Z(n4386)); + OR2 U4139 ( .A(n4319), .B(n4388), .Z(n4382)); + OR2 U4140 ( .A(n4389), .B(n4390), .Z(n4388)); + AN2 U4141 ( .A(n4391), .B(n3321), .Z(n4390)); + AN2 U4142 ( .A(n4392), .B(n4310), .Z(n4389)); + AN2 U4143 ( .A(pi204), .B(n4387), .Z(n4392)); + OR2 U4144 ( .A(n4393), .B(n4394), .Z(n4375)); + AN2 U4145 ( .A(n4395), .B(n4319), .Z(n4394)); + OR2 U4146 ( .A(n4396), .B(n4397), .Z(n4395)); + AN2 U4147 ( .A(n4227), .B(n4161), .Z(n4397)); + AN2 U4148 ( .A(po004), .B(n4398), .Z(n4393)); + OR2 U4149 ( .A(n4399), .B(n4400), .Z(n4398)); + OR2 U4150 ( .A(n4401), .B(n4402), .Z(n4400)); + AN2 U4151 ( .A(n4310), .B(n4403), .Z(n4402)); + AN2 U4152 ( .A(n4404), .B(n4227), .Z(n4401)); + AN2 U4153 ( .A(n4405), .B(n4406), .Z(n4399)); + AN2 U4154 ( .A(n4407), .B(n3261), .Z(n4405)); + OR2 U4155 ( .A(n4408), .B(n4409), .Z(n4407)); + AN2 U4156 ( .A(n4028), .B(pi065), .Z(n4409)); + AN2 U4157 ( .A(po040), .B(n4410), .Z(n4408)); + OR2 U4158 ( .A(n4411), .B(n4412), .Z(n4410)); + AN2 U4159 ( .A(pi065), .B(n4045), .Z(n4412)); + AN2 U4160 ( .A(n4046), .B(n4156), .Z(n4411)); + OR2 U4161 ( .A(n4413), .B(n4414), .Z(po046)); + OR2 U4162 ( .A(n4415), .B(n4416), .Z(n4414)); + AN2 U4163 ( .A(n3339), .B(n4417), .Z(n4416)); + AN2 U4164 ( .A(n3341), .B(n4418), .Z(n4415)); + OR2 U4165 ( .A(n4419), .B(n4420), .Z(n4413)); + AN2 U4166 ( .A(n4421), .B(pi192), .Z(n4420)); + OR2 U4167 ( .A(n4422), .B(n4423), .Z(n4421)); + AN2 U4168 ( .A(n4424), .B(pi098), .Z(n4423)); + AN2 U4169 ( .A(n4425), .B(n4426), .Z(n4424)); + OR2 U4170 ( .A(n3242), .B(n3657), .Z(n4425)); + IV2 U4171 ( .A(n3635), .Z(n3657)); + AN2 U4172 ( .A(n4427), .B(n3638), .Z(n4422)); + OR2 U4173 ( .A(n4428), .B(n4429), .Z(n4427)); + AN2 U4174 ( .A(n4430), .B(pi171), .Z(n4429)); + AN2 U4175 ( .A(n4431), .B(n4001), .Z(n4428)); + AN2 U4176 ( .A(n4432), .B(n2837), .Z(n4419)); + OR2 U4177 ( .A(n4433), .B(n4434), .Z(n4432)); + AN2 U4178 ( .A(n4435), .B(pi003), .Z(n4434)); + AN2 U4179 ( .A(n4436), .B(n4426), .Z(n4435)); + OR2 U4180 ( .A(n3244), .B(n3616), .Z(n4436)); + IV2 U4181 ( .A(n3593), .Z(n3616)); + AN2 U4182 ( .A(n4437), .B(n3597), .Z(n4433)); + OR2 U4183 ( .A(n4438), .B(n4439), .Z(n4437)); + AN2 U4184 ( .A(n4430), .B(pi142), .Z(n4439)); + AN2 U4185 ( .A(n4431), .B(n4440), .Z(n4438)); + AN2 U4186 ( .A(n3236), .B(n4441), .Z(n4431)); + OR2 U4187 ( .A(n4442), .B(n4443), .Z(po045)); + AN2 U4188 ( .A(n4444), .B(n4445), .Z(n4443)); + OR2 U4189 ( .A(n4446), .B(n3756), .Z(n4445)); + AN2 U4190 ( .A(n4006), .B(n4005), .Z(n4446)); + OR2 U4191 ( .A(n4447), .B(n4448), .Z(n4005)); + AN2 U4192 ( .A(n4449), .B(n3301), .Z(n4447)); + AN2 U4193 ( .A(n4450), .B(n3306), .Z(n4449)); + AN2 U4194 ( .A(n3757), .B(n4451), .Z(n4442)); + OR2 U4195 ( .A(n4452), .B(n4453), .Z(n4451)); + OR2 U4196 ( .A(n4454), .B(n4455), .Z(n4453)); + AN2 U4197 ( .A(po092), .B(n4456), .Z(n4455)); + OR2 U4198 ( .A(n4008), .B(n4004), .Z(n4456)); + AN2 U4199 ( .A(n4457), .B(n4458), .Z(n4008)); + AN2 U4200 ( .A(n4458), .B(n4459), .Z(n4454)); + OR2 U4201 ( .A(n4460), .B(n4461), .Z(n4459)); + AN2 U4202 ( .A(n4462), .B(n4463), .Z(n4461)); + AN2 U4203 ( .A(n4464), .B(n4465), .Z(n4462)); + AN2 U4204 ( .A(n4466), .B(n4467), .Z(n4460)); + AN2 U4205 ( .A(n4468), .B(n4469), .Z(n4466)); + OR2 U4206 ( .A(n4470), .B(n4471), .Z(po043)); + OR2 U4207 ( .A(n4472), .B(n4473), .Z(n4471)); + AN2 U4208 ( .A(n4474), .B(n4475), .Z(n4473)); + AN2 U4209 ( .A(n4476), .B(n4337), .Z(n4474)); + AN2 U4210 ( .A(n4477), .B(n4478), .Z(n4472)); + OR2 U4211 ( .A(n4479), .B(n4480), .Z(n4477)); + AN2 U4212 ( .A(n4337), .B(n4481), .Z(n4480)); + IV2 U4213 ( .A(n4342), .Z(n4337)); + AN2 U4214 ( .A(n4342), .B(n4476), .Z(n4479)); + IV2 U4215 ( .A(n4481), .Z(n4476)); + AN2 U4216 ( .A(n4482), .B(n4481), .Z(n4470)); + OR2 U4217 ( .A(n4483), .B(n4484), .Z(n4481)); + OR2 U4218 ( .A(n4485), .B(n4486), .Z(n4484)); + AN2 U4219 ( .A(n4487), .B(n4078), .Z(n4486)); + OR2 U4220 ( .A(n4488), .B(n4489), .Z(n4487)); + AN2 U4221 ( .A(n4490), .B(n3778), .Z(n4489)); + AN2 U4222 ( .A(n3773), .B(n4491), .Z(n4488)); + AN2 U4223 ( .A(n4492), .B(n4066), .Z(n4485)); + AN2 U4224 ( .A(n4490), .B(n3773), .Z(n4492)); + AN2 U4225 ( .A(n4491), .B(n4341), .Z(n4483)); + IV2 U4226 ( .A(n4490), .Z(n4491)); + OR2 U4227 ( .A(n4493), .B(n4494), .Z(n4490)); + IV2 U4228 ( .A(n4495), .Z(n4494)); + OR2 U4229 ( .A(n4496), .B(n4497), .Z(n4495)); + AN2 U4230 ( .A(n4497), .B(n4496), .Z(n4493)); + AN2 U4231 ( .A(n4498), .B(n4499), .Z(n4496)); + IV2 U4232 ( .A(n4500), .Z(n4499)); + AN2 U4233 ( .A(n4333), .B(n4501), .Z(n4500)); + OR2 U4234 ( .A(n4501), .B(n4333), .Z(n4498)); + OR2 U4235 ( .A(n4502), .B(n4503), .Z(n4501)); + OR2 U4236 ( .A(n4504), .B(n4505), .Z(n4503)); + OR2 U4237 ( .A(n4506), .B(n4507), .Z(n4505)); + AN2 U4238 ( .A(n4508), .B(n4509), .Z(n4507)); + AN2 U4239 ( .A(n4510), .B(n4511), .Z(n4506)); + AN2 U4240 ( .A(n4512), .B(n4513), .Z(n4504)); + OR2 U4241 ( .A(n4514), .B(n4515), .Z(n4502)); + AN2 U4242 ( .A(n4516), .B(n4517), .Z(n4515)); + AN2 U4243 ( .A(po102), .B(n4518), .Z(n4514)); + OR2 U4244 ( .A(n4519), .B(n4520), .Z(n4518)); + AN2 U4245 ( .A(n4513), .B(n4521), .Z(n4520)); + OR2 U4246 ( .A(n4522), .B(n4523), .Z(n4513)); + AN2 U4247 ( .A(n4508), .B(n3773), .Z(n4523)); + AN2 U4248 ( .A(po025), .B(n4516), .Z(n4522)); + AN2 U4249 ( .A(n4508), .B(n4524), .Z(n4519)); + IV2 U4250 ( .A(n4510), .Z(n4508)); + OR2 U4251 ( .A(n4525), .B(n4526), .Z(n4510)); + AN2 U4252 ( .A(n4527), .B(n4528), .Z(n4526)); + OR2 U4253 ( .A(n4529), .B(n4530), .Z(n4527)); + OR2 U4254 ( .A(n4531), .B(n4532), .Z(n4530)); + AN2 U4255 ( .A(po025), .B(n4533), .Z(n4532)); + OR2 U4256 ( .A(n4534), .B(n4535), .Z(n4533)); + OR2 U4257 ( .A(n4356), .B(n4536), .Z(n4535)); + AN2 U4258 ( .A(n4537), .B(n4538), .Z(n4536)); + AN2 U4259 ( .A(n4539), .B(n4540), .Z(n4537)); + AN2 U4260 ( .A(n3890), .B(n4541), .Z(n4534)); + OR2 U4261 ( .A(n4542), .B(n4543), .Z(n4541)); + AN2 U4262 ( .A(n4085), .B(n4544), .Z(n4542)); + AN2 U4263 ( .A(n3778), .B(n4545), .Z(n4531)); + OR2 U4264 ( .A(n4546), .B(n4547), .Z(n4545)); + OR2 U4265 ( .A(n4548), .B(n4549), .Z(n4547)); + AN2 U4266 ( .A(n3775), .B(n4550), .Z(n4549)); + AN2 U4267 ( .A(n4524), .B(n4333), .Z(n4548)); + AN2 U4268 ( .A(n4086), .B(n4551), .Z(n4546)); + OR2 U4269 ( .A(n4552), .B(n4553), .Z(n4529)); + AN2 U4270 ( .A(n4554), .B(n4555), .Z(n4553)); + OR2 U4271 ( .A(n4556), .B(n3888), .Z(n4555)); + AN2 U4272 ( .A(pi192), .B(n4557), .Z(n4556)); + AN2 U4273 ( .A(n4558), .B(n4559), .Z(n4554)); + OR2 U4274 ( .A(n4085), .B(n4560), .Z(n4558)); + AN2 U4275 ( .A(n4561), .B(n3773), .Z(n4560)); + AN2 U4276 ( .A(n4562), .B(n4563), .Z(n4552)); + OR2 U4277 ( .A(n4564), .B(n4565), .Z(n4563)); + AN2 U4278 ( .A(n4566), .B(n4567), .Z(n4564)); + AN2 U4279 ( .A(n3773), .B(n4076), .Z(n4566)); + OR2 U4280 ( .A(pi076), .B(n4557), .Z(n4562)); + AN2 U4281 ( .A(n4516), .B(n4568), .Z(n4525)); + OR2 U4282 ( .A(n4569), .B(n4570), .Z(n4568)); + OR2 U4283 ( .A(n4571), .B(n4572), .Z(n4570)); + OR2 U4284 ( .A(n4573), .B(n4574), .Z(n4572)); + AN2 U4285 ( .A(n4575), .B(n4576), .Z(n4574)); + AN2 U4286 ( .A(n4577), .B(n4076), .Z(n4575)); + AN2 U4287 ( .A(n3773), .B(n4578), .Z(n4577)); + AN2 U4288 ( .A(n4579), .B(n4580), .Z(n4573)); + OR2 U4289 ( .A(n4581), .B(n4565), .Z(n4579)); + AN2 U4290 ( .A(n4539), .B(n4582), .Z(n4565)); + AN2 U4291 ( .A(n2837), .B(n4550), .Z(n4582)); + IV2 U4292 ( .A(n4540), .Z(n4550)); + AN2 U4293 ( .A(n4567), .B(n4076), .Z(n4581)); + AN2 U4294 ( .A(n4086), .B(n4583), .Z(n4571)); + OR2 U4295 ( .A(n4584), .B(n4585), .Z(n4583)); + AN2 U4296 ( .A(n4543), .B(n3773), .Z(n4585)); + AN2 U4297 ( .A(n4586), .B(n4544), .Z(n4584)); + OR2 U4298 ( .A(n4085), .B(n3778), .Z(n4586)); + AN2 U4299 ( .A(n4557), .B(n3888), .Z(n4086)); + OR2 U4300 ( .A(n4587), .B(n4588), .Z(n4569)); + AN2 U4301 ( .A(n4589), .B(n4590), .Z(n4588)); + OR2 U4302 ( .A(n4591), .B(n3890), .Z(n4590)); + AN2 U4303 ( .A(po025), .B(pi192), .Z(n4591)); + AN2 U4304 ( .A(n4559), .B(n4592), .Z(n4589)); + OR2 U4305 ( .A(n4561), .B(n4085), .Z(n4592)); + OR2 U4306 ( .A(n4551), .B(n4353), .Z(n4559)); + AN2 U4307 ( .A(n4593), .B(n3775), .Z(n4587)); + AN2 U4308 ( .A(n2837), .B(n4576), .Z(n3775)); + AN2 U4309 ( .A(n4594), .B(n4540), .Z(n4593)); + OR2 U4310 ( .A(n4567), .B(n4066), .Z(n4540)); + OR2 U4311 ( .A(n4539), .B(n3778), .Z(n4594)); + IV2 U4312 ( .A(n4528), .Z(n4516)); + OR2 U4313 ( .A(n4595), .B(n4596), .Z(n4497)); + AN2 U4314 ( .A(n4597), .B(n4598), .Z(n4596)); + OR2 U4315 ( .A(n4599), .B(n4600), .Z(n4598)); + AN2 U4316 ( .A(n4601), .B(n4602), .Z(n4600)); + IV2 U4317 ( .A(n4603), .Z(n4599)); + OR2 U4318 ( .A(n4602), .B(n4601), .Z(n4603)); + OR2 U4319 ( .A(n4604), .B(n4605), .Z(n4601)); + AN2 U4320 ( .A(n3295), .B(n4606), .Z(n4605)); + AN2 U4321 ( .A(n4450), .B(n3301), .Z(n4604)); + AN2 U4322 ( .A(n4607), .B(n4608), .Z(n4602)); + OR2 U4323 ( .A(n4609), .B(n4610), .Z(n4608)); + IV2 U4324 ( .A(n4611), .Z(n4610)); + OR2 U4325 ( .A(n4611), .B(n4612), .Z(n4607)); + IV2 U4326 ( .A(n4609), .Z(n4612)); + OR2 U4327 ( .A(n4613), .B(n4614), .Z(n4609)); + OR2 U4328 ( .A(n4615), .B(n4616), .Z(n4614)); + AN2 U4329 ( .A(n4004), .B(n4617), .Z(n4616)); + OR2 U4330 ( .A(n4618), .B(n4619), .Z(n4617)); + AN2 U4331 ( .A(n4620), .B(n4463), .Z(n4619)); + AN2 U4332 ( .A(n4621), .B(n4464), .Z(n4620)); + AN2 U4333 ( .A(n4622), .B(n4467), .Z(n4618)); + AN2 U4334 ( .A(n4623), .B(n4468), .Z(n4622)); + AN2 U4335 ( .A(n4006), .B(n4624), .Z(n4615)); + OR2 U4336 ( .A(n4625), .B(n4626), .Z(n4613)); + AN2 U4337 ( .A(n4627), .B(n4628), .Z(n4626)); + OR2 U4338 ( .A(n4629), .B(n4630), .Z(n4627)); + AN2 U4339 ( .A(n4631), .B(pi192), .Z(n4630)); + AN2 U4340 ( .A(n4632), .B(pi158), .Z(n4631)); + AN2 U4341 ( .A(n4633), .B(n4634), .Z(n4632)); + OR2 U4342 ( .A(n4635), .B(n4465), .Z(n4634)); + OR2 U4343 ( .A(n4464), .B(n4636), .Z(n4633)); + OR2 U4344 ( .A(n4621), .B(n3301), .Z(n4636)); + AN2 U4345 ( .A(n4637), .B(n2837), .Z(n4629)); + AN2 U4346 ( .A(n4638), .B(pi151), .Z(n4637)); + AN2 U4347 ( .A(n4639), .B(n4640), .Z(n4638)); + OR2 U4348 ( .A(n4641), .B(n4469), .Z(n4640)); + OR2 U4349 ( .A(n4468), .B(n4642), .Z(n4639)); + OR2 U4350 ( .A(n4623), .B(n3301), .Z(n4642)); + AN2 U4351 ( .A(n4643), .B(po092), .Z(n4625)); + AN2 U4352 ( .A(n4644), .B(n3295), .Z(n4643)); + AN2 U4353 ( .A(n4645), .B(n4646), .Z(n4644)); + OR2 U4354 ( .A(pi192), .B(n4647), .Z(n4646)); + AN2 U4355 ( .A(n4641), .B(n4648), .Z(n4647)); + OR2 U4356 ( .A(n2837), .B(n4649), .Z(n4645)); + AN2 U4357 ( .A(n4635), .B(n3908), .Z(n4649)); + IV2 U4358 ( .A(n3761), .Z(n4597)); + AN2 U4359 ( .A(n4650), .B(n3761), .Z(n4595)); + OR2 U4360 ( .A(n4651), .B(n4652), .Z(n3761)); + AN2 U4361 ( .A(n4653), .B(n4654), .Z(n4651)); + AN2 U4362 ( .A(n4333), .B(n4528), .Z(n4654)); + OR2 U4363 ( .A(n4655), .B(n4656), .Z(n4528)); + AN2 U4364 ( .A(n4657), .B(n3083), .Z(n4655)); + AN2 U4365 ( .A(n3018), .B(n4658), .Z(n4657)); + OR2 U4366 ( .A(n4659), .B(n4660), .Z(n4658)); + OR2 U4367 ( .A(n4661), .B(n4662), .Z(n4660)); + AN2 U4368 ( .A(n4663), .B(n3010), .Z(n4662)); + AN2 U4369 ( .A(n4664), .B(n3049), .Z(n4661)); + OR2 U4370 ( .A(n4665), .B(n4663), .Z(n4664)); + AN2 U4371 ( .A(n4666), .B(n3010), .Z(n4665)); + OR2 U4372 ( .A(n3100), .B(n4667), .Z(n4659)); + AN2 U4373 ( .A(n4668), .B(n3012), .Z(n4667)); + OR2 U4374 ( .A(n4669), .B(n4670), .Z(n4668)); + AN2 U4375 ( .A(n4671), .B(pi201), .Z(n4670)); + AN2 U4376 ( .A(n4672), .B(n3115), .Z(n4671)); + OR2 U4377 ( .A(n4673), .B(n3173), .Z(n4672)); + AN2 U4378 ( .A(n2837), .B(n3049), .Z(n4673)); + AN2 U4379 ( .A(n4674), .B(pi088), .Z(n4669)); + AN2 U4380 ( .A(n4675), .B(n3108), .Z(n4674)); + OR2 U4381 ( .A(n4676), .B(n3064), .Z(n4675)); + AN2 U4382 ( .A(pi192), .B(n3049), .Z(n4676)); + AN2 U4383 ( .A(n4341), .B(n4482), .Z(n4653)); + OR2 U4384 ( .A(n4677), .B(n4678), .Z(n4650)); + OR2 U4385 ( .A(n4679), .B(n4680), .Z(n4678)); + OR2 U4386 ( .A(n4681), .B(n4682), .Z(n4680)); + AN2 U4387 ( .A(n4683), .B(n4684), .Z(n4682)); + AN2 U4388 ( .A(n4685), .B(n4648), .Z(n4683)); + AN2 U4389 ( .A(n4686), .B(n4687), .Z(n4681)); + OR2 U4390 ( .A(n4688), .B(n4689), .Z(n4687)); + OR2 U4391 ( .A(n4690), .B(n4691), .Z(n4689)); + AN2 U4392 ( .A(n4692), .B(n4621), .Z(n4691)); + AN2 U4393 ( .A(n4685), .B(n4623), .Z(n4690)); + AN2 U4394 ( .A(n4693), .B(n3300), .Z(n4688)); + IV2 U4395 ( .A(n4684), .Z(n4686)); + OR2 U4396 ( .A(n4694), .B(n4695), .Z(n4679)); + AN2 U4397 ( .A(n4696), .B(n3300), .Z(n4695)); + AN2 U4398 ( .A(po014), .B(n4684), .Z(n4696)); + OR2 U4399 ( .A(n4697), .B(n4698), .Z(n4684)); + AN2 U4400 ( .A(n4699), .B(n4450), .Z(n4697)); + AN2 U4401 ( .A(po039), .B(n4700), .Z(n4694)); + OR2 U4402 ( .A(n4701), .B(n4702), .Z(n4700)); + AN2 U4403 ( .A(n4698), .B(n4703), .Z(n4702)); + AN2 U4404 ( .A(n4704), .B(n4606), .Z(n4698)); + AN2 U4405 ( .A(n4705), .B(n4706), .Z(n4701)); + AN2 U4406 ( .A(n4707), .B(n4708), .Z(n4705)); + OR2 U4407 ( .A(n4704), .B(n3295), .Z(n4708)); + OR2 U4408 ( .A(n4699), .B(n3301), .Z(n4707)); + OR2 U4409 ( .A(n4709), .B(n4710), .Z(n4677)); + AN2 U4410 ( .A(n4711), .B(n4699), .Z(n4710)); + IV2 U4411 ( .A(n4704), .Z(n4699)); + AN2 U4412 ( .A(n4450), .B(n4712), .Z(n4711)); + OR2 U4413 ( .A(po014), .B(n3301), .Z(n4712)); + AN2 U4414 ( .A(n4713), .B(n4704), .Z(n4709)); + OR2 U4415 ( .A(n4714), .B(n4715), .Z(n4704)); + OR2 U4416 ( .A(n4716), .B(n4717), .Z(n4715)); + OR2 U4417 ( .A(n4718), .B(n4719), .Z(n4717)); + IV2 U4418 ( .A(n4720), .Z(n4719)); + OR2 U4419 ( .A(n4721), .B(n4006), .Z(n4720)); + OR2 U4420 ( .A(n4444), .B(n3756), .Z(n4721)); + AN2 U4421 ( .A(n4722), .B(n4006), .Z(n4718)); + AN2 U4422 ( .A(n4444), .B(n4452), .Z(n4722)); + OR2 U4423 ( .A(n4723), .B(n4724), .Z(n4452)); + OR2 U4424 ( .A(n4725), .B(n4726), .Z(n4724)); + AN2 U4425 ( .A(po092), .B(n4009), .Z(n4726)); + OR2 U4426 ( .A(n4727), .B(n4728), .Z(n4009)); + AN2 U4427 ( .A(n3295), .B(n4729), .Z(n4728)); + AN2 U4428 ( .A(n4606), .B(n4457), .Z(n4727)); + OR2 U4429 ( .A(n4730), .B(n4706), .Z(n4457)); + OR2 U4430 ( .A(n4731), .B(n4732), .Z(n4706)); + AN2 U4431 ( .A(n4733), .B(pi192), .Z(n4732)); + AN2 U4432 ( .A(n4465), .B(n3870), .Z(n4733)); + AN2 U4433 ( .A(n4734), .B(n2837), .Z(n4731)); + AN2 U4434 ( .A(n4469), .B(n4735), .Z(n4734)); + AN2 U4435 ( .A(po039), .B(n4729), .Z(n4730)); + OR2 U4436 ( .A(po014), .B(n4736), .Z(n4729)); + AN2 U4437 ( .A(n4737), .B(n4606), .Z(n4725)); + AN2 U4438 ( .A(po014), .B(n4738), .Z(n4737)); + OR2 U4439 ( .A(n4739), .B(n4740), .Z(n4738)); + AN2 U4440 ( .A(n4463), .B(n4464), .Z(n4740)); + AN2 U4441 ( .A(n4467), .B(n4468), .Z(n4739)); + OR2 U4442 ( .A(n4741), .B(n4742), .Z(n4723)); + AN2 U4443 ( .A(n4743), .B(n4463), .Z(n4742)); + AN2 U4444 ( .A(n3909), .B(pi192), .Z(n4463)); + IV2 U4445 ( .A(pi158), .Z(n3909)); + AN2 U4446 ( .A(n4744), .B(n3908), .Z(n4743)); + OR2 U4447 ( .A(n4745), .B(n3295), .Z(n4744)); + AN2 U4448 ( .A(n4606), .B(n4464), .Z(n4745)); + AN2 U4449 ( .A(n4746), .B(n4467), .Z(n4741)); + AN2 U4450 ( .A(n2837), .B(n4747), .Z(n4467)); + AN2 U4451 ( .A(n4748), .B(n4648), .Z(n4746)); + OR2 U4452 ( .A(n4749), .B(n3295), .Z(n4748)); + AN2 U4453 ( .A(n4606), .B(n4468), .Z(n4749)); + AN2 U4454 ( .A(n3756), .B(n4611), .Z(n4716)); + OR2 U4455 ( .A(n4750), .B(n4751), .Z(n4611)); + AN2 U4456 ( .A(n4004), .B(n4444), .Z(n4750)); + IV2 U4457 ( .A(n4006), .Z(n4004)); + AN2 U4458 ( .A(n4628), .B(n4752), .Z(n3756)); + OR2 U4459 ( .A(n3758), .B(n3760), .Z(n4714)); + AN2 U4460 ( .A(n4448), .B(n4751), .Z(n3758)); + OR2 U4461 ( .A(n4713), .B(n4693), .Z(n4448)); + OR2 U4462 ( .A(n4753), .B(n4754), .Z(n4693)); + AN2 U4463 ( .A(n4621), .B(pi192), .Z(n4754)); + AN2 U4464 ( .A(n4623), .B(n2837), .Z(n4753)); + AN2 U4465 ( .A(n3301), .B(n4624), .Z(n4713)); + OR2 U4466 ( .A(n4755), .B(n4756), .Z(po042)); + AN2 U4467 ( .A(n4757), .B(n2837), .Z(n4756)); + OR2 U4468 ( .A(n4758), .B(n4759), .Z(n4757)); + OR2 U4469 ( .A(n4760), .B(n4761), .Z(n4759)); + AN2 U4470 ( .A(n4762), .B(n4763), .Z(n4761)); + OR2 U4471 ( .A(n4764), .B(n4765), .Z(n4763)); + IV2 U4472 ( .A(n4766), .Z(n4762)); + AN2 U4473 ( .A(n4765), .B(n4764), .Z(n4766)); + OR2 U4474 ( .A(n4767), .B(n4768), .Z(n4764)); + AN2 U4475 ( .A(n4769), .B(n4770), .Z(n4768)); + AN2 U4476 ( .A(n4771), .B(pi028), .Z(n4767)); + AN2 U4477 ( .A(n4772), .B(n4773), .Z(n4765)); + OR2 U4478 ( .A(n4774), .B(pi094), .Z(n4773)); + IV2 U4479 ( .A(n4775), .Z(n4772)); + AN2 U4480 ( .A(n4774), .B(pi094), .Z(n4775)); + AN2 U4481 ( .A(n4776), .B(n4777), .Z(n4774)); + OR2 U4482 ( .A(n4778), .B(pi173), .Z(n4777)); + OR2 U4483 ( .A(n4779), .B(pi163), .Z(n4776)); + IV2 U4484 ( .A(pi173), .Z(n4779)); + AN2 U4485 ( .A(n4780), .B(n4781), .Z(n4760)); + IV2 U4486 ( .A(n4782), .Z(n4781)); + AN2 U4487 ( .A(n4783), .B(n4784), .Z(n4782)); + OR2 U4488 ( .A(n4784), .B(n4783), .Z(n4780)); + AN2 U4489 ( .A(n4785), .B(n4786), .Z(n4783)); + IV2 U4490 ( .A(n4787), .Z(n4786)); + AN2 U4491 ( .A(n4788), .B(n4789), .Z(n4787)); + OR2 U4492 ( .A(n4789), .B(n4788), .Z(n4785)); + OR2 U4493 ( .A(n4790), .B(n4791), .Z(n4788)); + AN2 U4494 ( .A(pi025), .B(n4792), .Z(n4791)); + IV2 U4495 ( .A(n4793), .Z(n4790)); + OR2 U4496 ( .A(n4792), .B(pi025), .Z(n4793)); + IV2 U4497 ( .A(pi035), .Z(n4792)); + AN2 U4498 ( .A(n4794), .B(n4795), .Z(n4789)); + IV2 U4499 ( .A(n4796), .Z(n4795)); + AN2 U4500 ( .A(pi056), .B(n4797), .Z(n4796)); + OR2 U4501 ( .A(n4797), .B(pi056), .Z(n4794)); + IV2 U4502 ( .A(pi100), .Z(n4797)); + OR2 U4503 ( .A(n4798), .B(n4799), .Z(n4784)); + IV2 U4504 ( .A(n4800), .Z(n4799)); + OR2 U4505 ( .A(n4801), .B(n4802), .Z(n4800)); + AN2 U4506 ( .A(n4802), .B(n4801), .Z(n4798)); + AN2 U4507 ( .A(n4803), .B(n4804), .Z(n4801)); + IV2 U4508 ( .A(n4805), .Z(n4804)); + AN2 U4509 ( .A(pi126), .B(n4806), .Z(n4805)); + OR2 U4510 ( .A(n4806), .B(pi126), .Z(n4803)); + IV2 U4511 ( .A(pi146), .Z(n4806)); + OR2 U4512 ( .A(n4807), .B(n4808), .Z(n4802)); + AN2 U4513 ( .A(pi190), .B(n4809), .Z(n4808)); + IV2 U4514 ( .A(pi202), .Z(n4809)); + AN2 U4515 ( .A(pi202), .B(n4810), .Z(n4807)); + IV2 U4516 ( .A(pi190), .Z(n4810)); + OR2 U4517 ( .A(n4811), .B(n4812), .Z(n4758)); + AN2 U4518 ( .A(n4813), .B(n4814), .Z(n4812)); + IV2 U4519 ( .A(n4815), .Z(n4814)); + AN2 U4520 ( .A(n4816), .B(n4817), .Z(n4815)); + OR2 U4521 ( .A(n4817), .B(n4816), .Z(n4813)); + AN2 U4522 ( .A(n4818), .B(n4819), .Z(n4816)); + OR2 U4523 ( .A(n4820), .B(pi019), .Z(n4819)); + OR2 U4524 ( .A(n4821), .B(n4822), .Z(n4818)); + IV2 U4525 ( .A(pi019), .Z(n4822)); + OR2 U4526 ( .A(n4823), .B(n4824), .Z(n4817)); + IV2 U4527 ( .A(n4825), .Z(n4824)); + OR2 U4528 ( .A(n4826), .B(pi085), .Z(n4825)); + AN2 U4529 ( .A(n4826), .B(pi085), .Z(n4823)); + AN2 U4530 ( .A(n4827), .B(n4828), .Z(n4826)); + OR2 U4531 ( .A(n4829), .B(pi167), .Z(n4828)); + IV2 U4532 ( .A(pi110), .Z(n4829)); + OR2 U4533 ( .A(n4830), .B(pi110), .Z(n4827)); + IV2 U4534 ( .A(pi167), .Z(n4830)); + AN2 U4535 ( .A(n4831), .B(n4832), .Z(n4811)); + IV2 U4536 ( .A(n4833), .Z(n4832)); + AN2 U4537 ( .A(n4834), .B(n4835), .Z(n4833)); + OR2 U4538 ( .A(n4835), .B(n4834), .Z(n4831)); + AN2 U4539 ( .A(n4836), .B(n4837), .Z(n4834)); + OR2 U4540 ( .A(n4838), .B(pi020), .Z(n4837)); + OR2 U4541 ( .A(n4839), .B(n4840), .Z(n4836)); + IV2 U4542 ( .A(pi020), .Z(n4840)); + OR2 U4543 ( .A(n4841), .B(n4842), .Z(n4835)); + IV2 U4544 ( .A(n4843), .Z(n4842)); + OR2 U4545 ( .A(n4844), .B(pi047), .Z(n4843)); + AN2 U4546 ( .A(n4844), .B(pi047), .Z(n4841)); + AN2 U4547 ( .A(n4845), .B(n4846), .Z(n4844)); + OR2 U4548 ( .A(n4847), .B(pi153), .Z(n4846)); + IV2 U4549 ( .A(pi075), .Z(n4847)); + OR2 U4550 ( .A(n4848), .B(pi075), .Z(n4845)); + IV2 U4551 ( .A(pi153), .Z(n4848)); + AN2 U4552 ( .A(pi192), .B(n4849), .Z(n4755)); + OR2 U4553 ( .A(n4850), .B(n4851), .Z(n4849)); + OR2 U4554 ( .A(n4852), .B(n4853), .Z(n4851)); + AN2 U4555 ( .A(n4854), .B(n4855), .Z(n4853)); + OR2 U4556 ( .A(n4856), .B(n4857), .Z(n4855)); + IV2 U4557 ( .A(n4858), .Z(n4854)); + AN2 U4558 ( .A(n4857), .B(n4856), .Z(n4858)); + OR2 U4559 ( .A(n4859), .B(n4860), .Z(n4856)); + AN2 U4560 ( .A(n4839), .B(n4861), .Z(n4860)); + AN2 U4561 ( .A(n4838), .B(po014), .Z(n4859)); + IV2 U4562 ( .A(n4839), .Z(n4838)); + OR2 U4563 ( .A(n4862), .B(n4863), .Z(n4839)); + AN2 U4564 ( .A(n4864), .B(pi192), .Z(n4863)); + OR2 U4565 ( .A(n4865), .B(n4866), .Z(n4864)); + IV2 U4566 ( .A(n4867), .Z(n4866)); + OR2 U4567 ( .A(n4868), .B(n4869), .Z(n4867)); + AN2 U4568 ( .A(n4869), .B(n4868), .Z(n4865)); + AN2 U4569 ( .A(n4870), .B(n4871), .Z(n4868)); + OR2 U4570 ( .A(n4872), .B(po024), .Z(n4871)); + IV2 U4571 ( .A(n4873), .Z(n4872)); + OR2 U4572 ( .A(n4873), .B(n4874), .Z(n4870)); + OR2 U4573 ( .A(n4875), .B(n4876), .Z(n4873)); + AN2 U4574 ( .A(po025), .B(n4877), .Z(n4876)); + AN2 U4575 ( .A(po059), .B(n4557), .Z(n4875)); + OR2 U4576 ( .A(n4878), .B(n4879), .Z(n4869)); + AN2 U4577 ( .A(n4880), .B(n4881), .Z(n4879)); + AN2 U4578 ( .A(n4882), .B(po072), .Z(n4878)); + IV2 U4579 ( .A(n4880), .Z(n4882)); + OR2 U4580 ( .A(n4883), .B(n4884), .Z(n4880)); + AN2 U4581 ( .A(po084), .B(n4885), .Z(n4884)); + AN2 U4582 ( .A(po102), .B(n4886), .Z(n4883)); + IV2 U4583 ( .A(po084), .Z(n4886)); + AN2 U4584 ( .A(n4887), .B(n2837), .Z(n4862)); + OR2 U4585 ( .A(n4888), .B(n4889), .Z(n4887)); + AN2 U4586 ( .A(n4890), .B(n4891), .Z(n4889)); + IV2 U4587 ( .A(n4892), .Z(n4888)); + OR2 U4588 ( .A(n4891), .B(n4890), .Z(n4892)); + OR2 U4589 ( .A(n4893), .B(n4894), .Z(n4890)); + IV2 U4590 ( .A(n4895), .Z(n4894)); + OR2 U4591 ( .A(n4896), .B(pi014), .Z(n4895)); + AN2 U4592 ( .A(n4896), .B(pi014), .Z(n4893)); + AN2 U4593 ( .A(n4897), .B(n4898), .Z(n4896)); + OR2 U4594 ( .A(n4899), .B(pi111), .Z(n4898)); + OR2 U4595 ( .A(n4900), .B(pi097), .Z(n4897)); + IV2 U4596 ( .A(pi111), .Z(n4900)); + AN2 U4597 ( .A(n4901), .B(n4902), .Z(n4891)); + OR2 U4598 ( .A(n4903), .B(pi143), .Z(n4902)); + IV2 U4599 ( .A(n4904), .Z(n4901)); + AN2 U4600 ( .A(n4903), .B(pi143), .Z(n4904)); + AN2 U4601 ( .A(n4905), .B(n4906), .Z(n4903)); + OR2 U4602 ( .A(n4907), .B(pi189), .Z(n4906)); + IV2 U4603 ( .A(n4908), .Z(n4905)); + AN2 U4604 ( .A(pi189), .B(n4907), .Z(n4908)); + IV2 U4605 ( .A(pi176), .Z(n4907)); + AN2 U4606 ( .A(n4909), .B(n4910), .Z(n4857)); + OR2 U4607 ( .A(n4911), .B(po039), .Z(n4910)); + IV2 U4608 ( .A(n4912), .Z(n4911)); + OR2 U4609 ( .A(n4912), .B(n3300), .Z(n4909)); + OR2 U4610 ( .A(n4913), .B(n4914), .Z(n4912)); + AN2 U4611 ( .A(po063), .B(n4628), .Z(n4914)); + AN2 U4612 ( .A(po092), .B(n4915), .Z(n4913)); + AN2 U4613 ( .A(n4916), .B(n4917), .Z(n4852)); + IV2 U4614 ( .A(n4918), .Z(n4917)); + AN2 U4615 ( .A(n4919), .B(n4920), .Z(n4918)); + OR2 U4616 ( .A(n4920), .B(n4919), .Z(n4916)); + IV2 U4617 ( .A(n4921), .Z(n4919)); + OR2 U4618 ( .A(n4922), .B(n4923), .Z(n4921)); + AN2 U4619 ( .A(n4821), .B(n3391), .Z(n4923)); + IV2 U4620 ( .A(n4924), .Z(n3391)); + AN2 U4621 ( .A(n4924), .B(n4820), .Z(n4922)); + IV2 U4622 ( .A(n4821), .Z(n4820)); + OR2 U4623 ( .A(n4925), .B(n4926), .Z(n4821)); + AN2 U4624 ( .A(n4927), .B(pi192), .Z(n4926)); + OR2 U4625 ( .A(n4928), .B(n4929), .Z(n4927)); + IV2 U4626 ( .A(n4930), .Z(n4929)); + OR2 U4627 ( .A(n4931), .B(n4932), .Z(n4930)); + AN2 U4628 ( .A(n4932), .B(n4931), .Z(n4928)); + AN2 U4629 ( .A(n4933), .B(n4934), .Z(n4931)); + OR2 U4630 ( .A(n4935), .B(po001), .Z(n4934)); + IV2 U4631 ( .A(n4936), .Z(n4935)); + OR2 U4632 ( .A(n4936), .B(n4937), .Z(n4933)); + OR2 U4633 ( .A(n4938), .B(n4939), .Z(n4936)); + AN2 U4634 ( .A(po011), .B(n4441), .Z(n4939)); + AN2 U4635 ( .A(po036), .B(n3688), .Z(n4938)); + OR2 U4636 ( .A(n4940), .B(n4941), .Z(n4932)); + AN2 U4637 ( .A(n4942), .B(n4943), .Z(n4941)); + AN2 U4638 ( .A(n4944), .B(po057), .Z(n4940)); + IV2 U4639 ( .A(n4942), .Z(n4944)); + OR2 U4640 ( .A(n4945), .B(n4946), .Z(n4942)); + AN2 U4641 ( .A(po069), .B(n4947), .Z(n4946)); + AN2 U4642 ( .A(po082), .B(n4948), .Z(n4945)); + IV2 U4643 ( .A(po069), .Z(n4948)); + AN2 U4644 ( .A(n4949), .B(n2837), .Z(n4925)); + OR2 U4645 ( .A(n4950), .B(n4951), .Z(n4949)); + AN2 U4646 ( .A(n4952), .B(n4953), .Z(n4951)); + IV2 U4647 ( .A(n4954), .Z(n4950)); + OR2 U4648 ( .A(n4953), .B(n4952), .Z(n4954)); + OR2 U4649 ( .A(n4955), .B(n4956), .Z(n4952)); + IV2 U4650 ( .A(n4957), .Z(n4956)); + OR2 U4651 ( .A(n4958), .B(pi024), .Z(n4957)); + AN2 U4652 ( .A(n4958), .B(pi024), .Z(n4955)); + AN2 U4653 ( .A(n4959), .B(n4960), .Z(n4958)); + OR2 U4654 ( .A(n4961), .B(pi078), .Z(n4960)); + OR2 U4655 ( .A(n4962), .B(pi030), .Z(n4959)); + IV2 U4656 ( .A(pi078), .Z(n4962)); + AN2 U4657 ( .A(n4963), .B(n4964), .Z(n4953)); + OR2 U4658 ( .A(n4965), .B(pi087), .Z(n4964)); + IV2 U4659 ( .A(n4966), .Z(n4963)); + AN2 U4660 ( .A(n4965), .B(pi087), .Z(n4966)); + AN2 U4661 ( .A(n4967), .B(n4968), .Z(n4965)); + OR2 U4662 ( .A(n4969), .B(pi164), .Z(n4968)); + OR2 U4663 ( .A(n4970), .B(pi159), .Z(n4967)); + IV2 U4664 ( .A(pi164), .Z(n4970)); + OR2 U4665 ( .A(n4971), .B(n4972), .Z(n4924)); + AN2 U4666 ( .A(po027), .B(n3512), .Z(n4972)); + AN2 U4667 ( .A(po104), .B(n3363), .Z(n4971)); + OR2 U4668 ( .A(n4973), .B(n4974), .Z(n4920)); + AN2 U4669 ( .A(po038), .B(n3380), .Z(n4974)); + AN2 U4670 ( .A(po071), .B(n4975), .Z(n4973)); + OR2 U4671 ( .A(n4976), .B(n4977), .Z(n4850)); + AN2 U4672 ( .A(n4978), .B(n4979), .Z(n4977)); + OR2 U4673 ( .A(n4980), .B(n4981), .Z(n4979)); + IV2 U4674 ( .A(n4982), .Z(n4978)); + AN2 U4675 ( .A(n4981), .B(n4980), .Z(n4982)); + OR2 U4676 ( .A(n4983), .B(n4984), .Z(n4980)); + AN2 U4677 ( .A(n4769), .B(n3049), .Z(n4984)); + AN2 U4678 ( .A(n4771), .B(po010), .Z(n4983)); + IV2 U4679 ( .A(n4769), .Z(n4771)); + OR2 U4680 ( .A(n4985), .B(n4986), .Z(n4769)); + AN2 U4681 ( .A(n4987), .B(pi192), .Z(n4986)); + OR2 U4682 ( .A(n4988), .B(n4989), .Z(n4987)); + IV2 U4683 ( .A(n4990), .Z(n4989)); + OR2 U4684 ( .A(n4991), .B(n4992), .Z(n4990)); + AN2 U4685 ( .A(n4992), .B(n4991), .Z(n4988)); + AN2 U4686 ( .A(n4993), .B(n4994), .Z(n4991)); + OR2 U4687 ( .A(n4995), .B(po031), .Z(n4994)); + IV2 U4688 ( .A(n4996), .Z(n4995)); + OR2 U4689 ( .A(n4996), .B(n2962), .Z(n4993)); + OR2 U4690 ( .A(n4997), .B(n4998), .Z(n4996)); + AN2 U4691 ( .A(po044), .B(n4999), .Z(n4998)); + IV2 U4692 ( .A(po052), .Z(n4999)); + AN2 U4693 ( .A(po052), .B(n5000), .Z(n4997)); + OR2 U4694 ( .A(n5001), .B(n5002), .Z(n4992)); + AN2 U4695 ( .A(n5003), .B(n5004), .Z(n5002)); + AN2 U4696 ( .A(n5005), .B(po079), .Z(n5001)); + IV2 U4697 ( .A(n5003), .Z(n5005)); + OR2 U4698 ( .A(n5006), .B(n5007), .Z(n5003)); + AN2 U4699 ( .A(po106), .B(n2931), .Z(n5007)); + AN2 U4700 ( .A(po107), .B(n5008), .Z(n5006)); + AN2 U4701 ( .A(n5009), .B(n2837), .Z(n4985)); + OR2 U4702 ( .A(n5010), .B(n5011), .Z(n5009)); + IV2 U4703 ( .A(n5012), .Z(n5011)); + OR2 U4704 ( .A(n5013), .B(n5014), .Z(n5012)); + AN2 U4705 ( .A(n5014), .B(n5013), .Z(n5010)); + AN2 U4706 ( .A(n5015), .B(n5016), .Z(n5013)); + OR2 U4707 ( .A(n5017), .B(pi011), .Z(n5016)); + IV2 U4708 ( .A(n5018), .Z(n5017)); + OR2 U4709 ( .A(n5018), .B(n5019), .Z(n5015)); + OR2 U4710 ( .A(n5020), .B(n5021), .Z(n5018)); + AN2 U4711 ( .A(pi021), .B(n5022), .Z(n5021)); + AN2 U4712 ( .A(pi032), .B(n5023), .Z(n5020)); + IV2 U4713 ( .A(pi021), .Z(n5023)); + OR2 U4714 ( .A(n5024), .B(n5025), .Z(n5014)); + AN2 U4715 ( .A(n5026), .B(n5027), .Z(n5025)); + AN2 U4716 ( .A(n5028), .B(pi086), .Z(n5024)); + IV2 U4717 ( .A(n5026), .Z(n5028)); + OR2 U4718 ( .A(n5029), .B(n5030), .Z(n5026)); + AN2 U4719 ( .A(pi115), .B(n5031), .Z(n5030)); + AN2 U4720 ( .A(pi165), .B(n5032), .Z(n5029)); + IV2 U4721 ( .A(pi115), .Z(n5032)); + AN2 U4722 ( .A(n5033), .B(n5034), .Z(n4981)); + OR2 U4723 ( .A(n5035), .B(po035), .Z(n5034)); + IV2 U4724 ( .A(n5036), .Z(n5035)); + OR2 U4725 ( .A(n5036), .B(n5037), .Z(n5033)); + OR2 U4726 ( .A(n5038), .B(n5039), .Z(n5036)); + AN2 U4727 ( .A(po070), .B(n3286), .Z(n5039)); + AN2 U4728 ( .A(po099), .B(n5040), .Z(n5038)); + AN2 U4729 ( .A(n5041), .B(n5042), .Z(n4976)); + OR2 U4730 ( .A(n5043), .B(n5044), .Z(n5042)); + IV2 U4731 ( .A(n5045), .Z(n5041)); + AN2 U4732 ( .A(n5044), .B(n5043), .Z(n5045)); + OR2 U4733 ( .A(n5046), .B(n5047), .Z(n5043)); + IV2 U4734 ( .A(n5048), .Z(n5047)); + OR2 U4735 ( .A(n5049), .B(n5050), .Z(n5048)); + AN2 U4736 ( .A(n5050), .B(n5049), .Z(n5046)); + AN2 U4737 ( .A(n5051), .B(n5052), .Z(n5049)); + OR2 U4738 ( .A(n4319), .B(po013), .Z(n5052)); + OR2 U4739 ( .A(n5053), .B(po004), .Z(n5051)); + IV2 U4740 ( .A(po013), .Z(n5053)); + OR2 U4741 ( .A(n5054), .B(n5055), .Z(n5050)); + AN2 U4742 ( .A(po028), .B(n4013), .Z(n5055)); + AN2 U4743 ( .A(po040), .B(n5056), .Z(n5054)); + AN2 U4744 ( .A(n5057), .B(n5058), .Z(n5044)); + OR2 U4745 ( .A(n5059), .B(n5060), .Z(n5058)); + IV2 U4746 ( .A(n5061), .Z(n5059)); + OR2 U4747 ( .A(n5062), .B(n5061), .Z(n5057)); + OR2 U4748 ( .A(n5063), .B(n5064), .Z(n5061)); + AN2 U4749 ( .A(po064), .B(n4128), .Z(n5064)); + AN2 U4750 ( .A(po085), .B(n4133), .Z(n5063)); + IV2 U4751 ( .A(n5060), .Z(n5062)); + OR2 U4752 ( .A(n5065), .B(n5066), .Z(n5060)); + AN2 U4753 ( .A(po091), .B(n3265), .Z(n5066)); + AN2 U4754 ( .A(po103), .B(n4201), .Z(n5065)); + IV2 U4755 ( .A(n5067), .Z(po041)); + AN2 U4756 ( .A(n5068), .B(pi193), .Z(n5067)); + AN2 U4757 ( .A(pi057), .B(n5069), .Z(n5068)); + IV2 U4758 ( .A(pi037), .Z(n5069)); + OR2 U4759 ( .A(n5070), .B(n5071), .Z(po037)); + AN2 U4760 ( .A(n3021), .B(n5072), .Z(n5071)); + OR2 U4761 ( .A(n5073), .B(n5074), .Z(n5072)); + AN2 U4762 ( .A(n3018), .B(n5075), .Z(n5073)); + OR2 U4763 ( .A(n5076), .B(n5077), .Z(n5075)); + AN2 U4764 ( .A(n4663), .B(n3284), .Z(n5076)); + OR2 U4765 ( .A(n5078), .B(n5079), .Z(n4663)); + AN2 U4766 ( .A(n3064), .B(n3108), .Z(n5079)); + AN2 U4767 ( .A(n3173), .B(n3115), .Z(n5078)); + AN2 U4768 ( .A(n3083), .B(n5080), .Z(n5070)); + OR2 U4769 ( .A(n5081), .B(n5082), .Z(n5080)); + OR2 U4770 ( .A(n5083), .B(n5084), .Z(n5082)); + AN2 U4771 ( .A(po070), .B(n4327), .Z(n5084)); + OR2 U4772 ( .A(n5085), .B(n5086), .Z(n4327)); + OR2 U4773 ( .A(n5087), .B(n5088), .Z(n5086)); + AN2 U4774 ( .A(n3273), .B(n5089), .Z(n5088)); + IV2 U4775 ( .A(n3284), .Z(n3273)); + AN2 U4776 ( .A(n5090), .B(n3274), .Z(n5087)); + OR2 U4777 ( .A(n5091), .B(n3282), .Z(n5085)); + IV2 U4778 ( .A(n4666), .Z(n3282)); + AN2 U4779 ( .A(n5092), .B(n3199), .Z(n5083)); + OR2 U4780 ( .A(n5093), .B(n5094), .Z(n5092)); + OR2 U4781 ( .A(n5095), .B(n5096), .Z(n5094)); + AN2 U4782 ( .A(n5090), .B(po010), .Z(n5096)); + AN2 U4783 ( .A(n3037), .B(n3061), .Z(n5090)); + AN2 U4784 ( .A(n5097), .B(n5098), .Z(n5095)); + AN2 U4785 ( .A(n3037), .B(n5099), .Z(n5098)); + AN2 U4786 ( .A(n5100), .B(n3120), .Z(n5097)); + OR2 U4787 ( .A(n5101), .B(n3061), .Z(n3120)); + AN2 U4788 ( .A(n3106), .B(pi192), .Z(n3061)); + AN2 U4789 ( .A(po010), .B(pi192), .Z(n5101)); + AN2 U4790 ( .A(n3082), .B(pi192), .Z(n5093)); + IV2 U4791 ( .A(n3108), .Z(n3082)); + OR2 U4792 ( .A(pi033), .B(n3286), .Z(n3108)); + OR2 U4793 ( .A(n5102), .B(n5103), .Z(n5081)); + AN2 U4794 ( .A(n3015), .B(n5104), .Z(n5103)); + IV2 U4795 ( .A(n3018), .Z(n3015)); + AN2 U4796 ( .A(n5105), .B(n3205), .Z(n5102)); + OR2 U4797 ( .A(n5106), .B(n5107), .Z(n5105)); + OR2 U4798 ( .A(n5091), .B(n5108), .Z(n5107)); + AN2 U4799 ( .A(n5100), .B(n5109), .Z(n5108)); + OR2 U4800 ( .A(n5110), .B(n5111), .Z(n5109)); + AN2 U4801 ( .A(n5112), .B(n2925), .Z(n5111)); + AN2 U4802 ( .A(po010), .B(n5089), .Z(n5112)); + IV2 U4803 ( .A(n3100), .Z(n5089)); + AN2 U4804 ( .A(n5113), .B(n3080), .Z(n5110)); + AN2 U4805 ( .A(n2837), .B(po010), .Z(n3080)); + AN2 U4806 ( .A(n3112), .B(n5099), .Z(n5113)); + AN2 U4807 ( .A(n3065), .B(n5114), .Z(n5091)); + AN2 U4808 ( .A(n3112), .B(n3274), .Z(n5114)); + IV2 U4809 ( .A(n3280), .Z(n3274)); + AN2 U4810 ( .A(n2837), .B(n5115), .Z(n3065)); + AN2 U4811 ( .A(n3073), .B(n2837), .Z(n5106)); + IV2 U4812 ( .A(n3115), .Z(n3073)); + OR2 U4813 ( .A(pi141), .B(n3286), .Z(n3115)); + OR2 U4814 ( .A(n5116), .B(n5117), .Z(po034)); + OR2 U4815 ( .A(n5118), .B(n5119), .Z(n5117)); + AN2 U4816 ( .A(pi054), .B(n5120), .Z(n5119)); + AN2 U4817 ( .A(n5121), .B(n5122), .Z(n5118)); + AN2 U4818 ( .A(n5123), .B(n5124), .Z(n5122)); + OR2 U4819 ( .A(po085), .B(po064), .Z(n5124)); + OR2 U4820 ( .A(n4125), .B(n4133), .Z(n5123)); + AN2 U4821 ( .A(n4129), .B(n4128), .Z(n4125)); + AN2 U4822 ( .A(pi052), .B(n5125), .Z(n5121)); + AN2 U4823 ( .A(n5126), .B(n5127), .Z(n5116)); + OR2 U4824 ( .A(n4167), .B(n5128), .Z(n5127)); + OR2 U4825 ( .A(n5129), .B(n5130), .Z(n5128)); + AN2 U4826 ( .A(n5131), .B(po064), .Z(n5130)); + AN2 U4827 ( .A(pi054), .B(po085), .Z(n5131)); + AN2 U4828 ( .A(n4129), .B(n4133), .Z(n5129)); + OR2 U4829 ( .A(n5132), .B(n5133), .Z(po033)); + AN2 U4830 ( .A(n3371), .B(n5134), .Z(n5133)); + OR2 U4831 ( .A(n5135), .B(n5136), .Z(n5134)); + AN2 U4832 ( .A(n5137), .B(n3412), .Z(n5132)); + OR2 U4833 ( .A(n5138), .B(n5139), .Z(n5137)); + OR2 U4834 ( .A(n5140), .B(n5141), .Z(po030)); + AN2 U4835 ( .A(n5142), .B(pi192), .Z(n5141)); + OR2 U4836 ( .A(n5143), .B(n5144), .Z(n5142)); + AN2 U4837 ( .A(n2834), .B(n5145), .Z(n5144)); + OR2 U4838 ( .A(n5146), .B(n5147), .Z(n5145)); + AN2 U4839 ( .A(n2998), .B(n3847), .Z(n5147)); + AN2 U4840 ( .A(n5148), .B(n5149), .Z(n5146)); + OR2 U4841 ( .A(n2862), .B(n5150), .Z(n5149)); + OR2 U4842 ( .A(n2880), .B(n5151), .Z(n5150)); + AN2 U4843 ( .A(n2887), .B(n2901), .Z(n5151)); + AN2 U4844 ( .A(n2891), .B(n5152), .Z(n5148)); + IV2 U4845 ( .A(n5153), .Z(n5152)); + AN2 U4846 ( .A(n5154), .B(n2833), .Z(n5143)); + OR2 U4847 ( .A(n5155), .B(n5156), .Z(n5154)); + AN2 U4848 ( .A(n5157), .B(n5158), .Z(n5155)); + AN2 U4849 ( .A(n2930), .B(n2878), .Z(n5158)); + AN2 U4850 ( .A(n5159), .B(n2837), .Z(n5140)); + OR2 U4851 ( .A(n5160), .B(n5161), .Z(n5159)); + AN2 U4852 ( .A(n2846), .B(n5162), .Z(n5161)); + OR2 U4853 ( .A(n5163), .B(n5164), .Z(n5162)); + AN2 U4854 ( .A(n2998), .B(n5165), .Z(n5164)); + AN2 U4855 ( .A(n5166), .B(n5167), .Z(n5163)); + OR2 U4856 ( .A(n5168), .B(n5169), .Z(n5167)); + AN2 U4857 ( .A(n3222), .B(n2946), .Z(n5168)); + OR2 U4858 ( .A(n5170), .B(n2959), .Z(n3222)); + AN2 U4859 ( .A(n2982), .B(n2901), .Z(n2959)); + AN2 U4860 ( .A(po107), .B(n5171), .Z(n5170)); + IV2 U4861 ( .A(n2981), .Z(n5171)); + AN2 U4862 ( .A(pi081), .B(pi200), .Z(n2981)); + AN2 U4863 ( .A(n2947), .B(n5172), .Z(n5166)); + AN2 U4864 ( .A(n5173), .B(n2845), .Z(n5160)); + OR2 U4865 ( .A(n5174), .B(n5175), .Z(n5173)); + AN2 U4866 ( .A(n5157), .B(n5176), .Z(n5174)); + AN2 U4867 ( .A(n2908), .B(n2938), .Z(n5176)); + OR2 U4868 ( .A(pi081), .B(n2931), .Z(n2938)); + IV2 U4869 ( .A(n5169), .Z(n2908)); + AN2 U4870 ( .A(n2861), .B(pi200), .Z(n5157)); + OR2 U4871 ( .A(n5177), .B(n5178), .Z(po029)); + OR2 U4872 ( .A(n5179), .B(n5180), .Z(n5178)); + AN2 U4873 ( .A(n5181), .B(n4313), .Z(n5180)); + AN2 U4874 ( .A(n3945), .B(n5182), .Z(n5179)); + OR2 U4875 ( .A(n5183), .B(n5184), .Z(n5182)); + OR2 U4876 ( .A(n5185), .B(n5186), .Z(n5184)); + AN2 U4877 ( .A(n5187), .B(pi186), .Z(n5186)); + OR2 U4878 ( .A(n5188), .B(n5189), .Z(n5187)); + AN2 U4879 ( .A(n5190), .B(n5191), .Z(n5189)); + AN2 U4880 ( .A(n5192), .B(pi124), .Z(n5188)); + IV2 U4881 ( .A(n5190), .Z(n5192)); + AN2 U4882 ( .A(n5193), .B(n5194), .Z(n5185)); + OR2 U4883 ( .A(n5195), .B(n5196), .Z(n5193)); + AN2 U4884 ( .A(n5190), .B(pi124), .Z(n5196)); + OR2 U4885 ( .A(n5197), .B(n5198), .Z(n5190)); + AN2 U4886 ( .A(n5199), .B(n5200), .Z(n5198)); + AN2 U4887 ( .A(n5201), .B(pi109), .Z(n5197)); + IV2 U4888 ( .A(n5199), .Z(n5201)); + AN2 U4889 ( .A(n5202), .B(n5191), .Z(n5195)); + OR2 U4890 ( .A(n5203), .B(n5204), .Z(n5202)); + AN2 U4891 ( .A(n5199), .B(pi109), .Z(n5204)); + OR2 U4892 ( .A(n5205), .B(n5206), .Z(n5199)); + AN2 U4893 ( .A(n5207), .B(n5208), .Z(n5206)); + AN2 U4894 ( .A(n5181), .B(pi055), .Z(n5205)); + AN2 U4895 ( .A(n5209), .B(n5200), .Z(n5203)); + AN2 U4896 ( .A(pi055), .B(n5207), .Z(n5209)); + AN2 U4897 ( .A(n5210), .B(n5211), .Z(n5183)); + OR2 U4898 ( .A(n5212), .B(n5213), .Z(n5211)); + IV2 U4899 ( .A(n5214), .Z(n5210)); + AN2 U4900 ( .A(n5213), .B(n5212), .Z(n5214)); + OR2 U4901 ( .A(n5215), .B(n5216), .Z(n5212)); + IV2 U4902 ( .A(n5217), .Z(n5216)); + OR2 U4903 ( .A(n5218), .B(pi006), .Z(n5217)); + AN2 U4904 ( .A(n5218), .B(pi006), .Z(n5215)); + AN2 U4905 ( .A(n5219), .B(n5220), .Z(n5218)); + OR2 U4906 ( .A(n5221), .B(pi061), .Z(n5220)); + IV2 U4907 ( .A(pi051), .Z(n5221)); + OR2 U4908 ( .A(n5222), .B(pi051), .Z(n5219)); + IV2 U4909 ( .A(pi061), .Z(n5222)); + AN2 U4910 ( .A(n5223), .B(n5224), .Z(n5213)); + IV2 U4911 ( .A(n5225), .Z(n5224)); + AN2 U4912 ( .A(n5226), .B(n5227), .Z(n5225)); + OR2 U4913 ( .A(n5227), .B(n5226), .Z(n5223)); + OR2 U4914 ( .A(n5228), .B(n5229), .Z(n5226)); + AN2 U4915 ( .A(pi093), .B(n5230), .Z(n5229)); + IV2 U4916 ( .A(n5231), .Z(n5228)); + OR2 U4917 ( .A(n5230), .B(pi093), .Z(n5231)); + IV2 U4918 ( .A(pi122), .Z(n5230)); + AN2 U4919 ( .A(n5232), .B(n5233), .Z(n5227)); + IV2 U4920 ( .A(n5234), .Z(n5233)); + AN2 U4921 ( .A(pi134), .B(n5235), .Z(n5234)); + OR2 U4922 ( .A(n5235), .B(pi134), .Z(n5232)); + IV2 U4923 ( .A(pi198), .Z(n5235)); + OR2 U4924 ( .A(n5236), .B(n5237), .Z(n5177)); + AN2 U4925 ( .A(n5238), .B(n2837), .Z(n5237)); + OR2 U4926 ( .A(n5239), .B(n5240), .Z(n5238)); + AN2 U4927 ( .A(n5241), .B(n5242), .Z(n5240)); + OR2 U4928 ( .A(n3809), .B(n5243), .Z(n5242)); + IV2 U4929 ( .A(n3812), .Z(n3809)); + OR2 U4930 ( .A(n5244), .B(n3812), .Z(n5241)); + AN2 U4931 ( .A(n5245), .B(n5246), .Z(n3812)); + IV2 U4932 ( .A(n5247), .Z(n5246)); + AN2 U4933 ( .A(n5248), .B(n5249), .Z(n5247)); + OR2 U4934 ( .A(n5249), .B(n5248), .Z(n5245)); + OR2 U4935 ( .A(n5250), .B(n5251), .Z(n5248)); + AN2 U4936 ( .A(pi040), .B(n5252), .Z(n5251)); + IV2 U4937 ( .A(pi095), .Z(n5252)); + AN2 U4938 ( .A(pi095), .B(n4735), .Z(n5250)); + AN2 U4939 ( .A(n5253), .B(n5254), .Z(n5249)); + OR2 U4940 ( .A(n4747), .B(pi156), .Z(n5254)); + IV2 U4941 ( .A(pi151), .Z(n4747)); + OR2 U4942 ( .A(n4648), .B(pi151), .Z(n5253)); + AN2 U4943 ( .A(n5255), .B(n5256), .Z(n5239)); + OR2 U4944 ( .A(n3815), .B(n5257), .Z(n5256)); + OR2 U4945 ( .A(n5258), .B(n3818), .Z(n5255)); + IV2 U4946 ( .A(n3815), .Z(n3818)); + OR2 U4947 ( .A(n5259), .B(n5260), .Z(n3815)); + AN2 U4948 ( .A(n5261), .B(n5262), .Z(n5260)); + IV2 U4949 ( .A(n5263), .Z(n5259)); + OR2 U4950 ( .A(n5262), .B(n5261), .Z(n5263)); + OR2 U4951 ( .A(n5264), .B(n5265), .Z(n5261)); + AN2 U4952 ( .A(pi128), .B(n3177), .Z(n5265)); + AN2 U4953 ( .A(pi141), .B(n3205), .Z(n5264)); + AN2 U4954 ( .A(n5266), .B(n5267), .Z(n5262)); + OR2 U4955 ( .A(n5115), .B(pi185), .Z(n5267)); + OR2 U4956 ( .A(n5268), .B(pi174), .Z(n5266)); + IV2 U4957 ( .A(pi185), .Z(n5268)); + AN2 U4958 ( .A(pi192), .B(n5269), .Z(n5236)); + OR2 U4959 ( .A(n5270), .B(n5271), .Z(n5269)); + OR2 U4960 ( .A(n5272), .B(n5273), .Z(n5271)); + AN2 U4961 ( .A(n5274), .B(n5275), .Z(n5273)); + IV2 U4962 ( .A(n5276), .Z(n5275)); + AN2 U4963 ( .A(n5277), .B(n5278), .Z(n5276)); + OR2 U4964 ( .A(n5278), .B(n5277), .Z(n5274)); + AN2 U4965 ( .A(n5279), .B(n5280), .Z(n5277)); + OR2 U4966 ( .A(n5243), .B(pi036), .Z(n5280)); + IV2 U4967 ( .A(n5244), .Z(n5243)); + OR2 U4968 ( .A(n5244), .B(n5281), .Z(n5279)); + IV2 U4969 ( .A(pi036), .Z(n5281)); + OR2 U4970 ( .A(n3871), .B(n5282), .Z(n5244)); + AN2 U4971 ( .A(n5283), .B(pi192), .Z(n5282)); + OR2 U4972 ( .A(n5284), .B(n5285), .Z(n5283)); + AN2 U4973 ( .A(n5286), .B(n5287), .Z(n5285)); + IV2 U4974 ( .A(n5288), .Z(n5284)); + OR2 U4975 ( .A(n5287), .B(n5286), .Z(n5288)); + OR2 U4976 ( .A(n5289), .B(n5290), .Z(n5286)); + IV2 U4977 ( .A(n5291), .Z(n5290)); + OR2 U4978 ( .A(n5292), .B(pi017), .Z(n5291)); + AN2 U4979 ( .A(pi017), .B(n5292), .Z(n5289)); + AN2 U4980 ( .A(n5293), .B(n5294), .Z(n5292)); + OR2 U4981 ( .A(n5295), .B(pi083), .Z(n5294)); + IV2 U4982 ( .A(pi027), .Z(n5295)); + OR2 U4983 ( .A(n5296), .B(pi027), .Z(n5293)); + IV2 U4984 ( .A(pi083), .Z(n5296)); + AN2 U4985 ( .A(n5297), .B(n5298), .Z(n5287)); + OR2 U4986 ( .A(n5299), .B(pi089), .Z(n5298)); + IV2 U4987 ( .A(n5300), .Z(n5297)); + AN2 U4988 ( .A(n5299), .B(pi089), .Z(n5300)); + AN2 U4989 ( .A(n5301), .B(n5302), .Z(n5299)); + OR2 U4990 ( .A(n5303), .B(pi162), .Z(n5302)); + OR2 U4991 ( .A(n5304), .B(pi140), .Z(n5301)); + IV2 U4992 ( .A(pi162), .Z(n5304)); + AN2 U4993 ( .A(n5305), .B(n2837), .Z(n3871)); + OR2 U4994 ( .A(n5306), .B(n5307), .Z(n5305)); + AN2 U4995 ( .A(n5308), .B(n5309), .Z(n5307)); + IV2 U4996 ( .A(n5310), .Z(n5306)); + OR2 U4997 ( .A(n5309), .B(n5308), .Z(n5310)); + OR2 U4998 ( .A(n5311), .B(n5312), .Z(n5308)); + IV2 U4999 ( .A(n5313), .Z(n5312)); + OR2 U5000 ( .A(n5314), .B(pi064), .Z(n5313)); + AN2 U5001 ( .A(pi064), .B(n5314), .Z(n5311)); + AN2 U5002 ( .A(n5315), .B(n5316), .Z(n5314)); + OR2 U5003 ( .A(n4077), .B(pi108), .Z(n5316)); + OR2 U5004 ( .A(n5317), .B(pi076), .Z(n5315)); + IV2 U5005 ( .A(pi108), .Z(n5317)); + AN2 U5006 ( .A(n5318), .B(n5319), .Z(n5309)); + OR2 U5007 ( .A(n5320), .B(pi114), .Z(n5319)); + IV2 U5008 ( .A(n5321), .Z(n5320)); + OR2 U5009 ( .A(n5321), .B(n5322), .Z(n5318)); + OR2 U5010 ( .A(n5323), .B(n5324), .Z(n5321)); + AN2 U5011 ( .A(pi160), .B(n4074), .Z(n5324)); + AN2 U5012 ( .A(pi170), .B(n4358), .Z(n5323)); + OR2 U5013 ( .A(n5325), .B(n5326), .Z(n5278)); + IV2 U5014 ( .A(n5327), .Z(n5326)); + OR2 U5015 ( .A(n5328), .B(pi101), .Z(n5327)); + AN2 U5016 ( .A(n5328), .B(pi101), .Z(n5325)); + AN2 U5017 ( .A(n5329), .B(n5330), .Z(n5328)); + OR2 U5018 ( .A(n5331), .B(pi205), .Z(n5330)); + OR2 U5019 ( .A(n5332), .B(pi168), .Z(n5329)); + IV2 U5020 ( .A(pi205), .Z(n5332)); + AN2 U5021 ( .A(n5333), .B(n5334), .Z(n5272)); + AN2 U5022 ( .A(n5335), .B(n5200), .Z(n5334)); + IV2 U5023 ( .A(pi109), .Z(n5200)); + AN2 U5024 ( .A(n5191), .B(n5194), .Z(n5335)); + IV2 U5025 ( .A(pi186), .Z(n5194)); + IV2 U5026 ( .A(pi124), .Z(n5191)); + AN2 U5027 ( .A(n5181), .B(n5208), .Z(n5333)); + IV2 U5028 ( .A(pi055), .Z(n5208)); + IV2 U5029 ( .A(n5207), .Z(n5181)); + OR2 U5030 ( .A(n3977), .B(n5336), .Z(n5207)); + AN2 U5031 ( .A(n5337), .B(pi192), .Z(n5336)); + OR2 U5032 ( .A(n5338), .B(n5339), .Z(n5337)); + AN2 U5033 ( .A(n5340), .B(n5341), .Z(n5339)); + IV2 U5034 ( .A(n5342), .Z(n5338)); + OR2 U5035 ( .A(n5341), .B(n5340), .Z(n5342)); + OR2 U5036 ( .A(n5343), .B(n5344), .Z(n5340)); + IV2 U5037 ( .A(n5345), .Z(n5344)); + OR2 U5038 ( .A(n5346), .B(n5347), .Z(n5345)); + AN2 U5039 ( .A(n5346), .B(n5347), .Z(n5343)); + AN2 U5040 ( .A(n5348), .B(n5349), .Z(n5346)); + OR2 U5041 ( .A(n5350), .B(pi099), .Z(n5349)); + OR2 U5042 ( .A(n5351), .B(pi018), .Z(n5348)); + IV2 U5043 ( .A(pi099), .Z(n5351)); + AN2 U5044 ( .A(n5352), .B(n5353), .Z(n5341)); + OR2 U5045 ( .A(n5354), .B(pi150), .Z(n5353)); + IV2 U5046 ( .A(n5355), .Z(n5352)); + AN2 U5047 ( .A(pi150), .B(n5354), .Z(n5355)); + AN2 U5048 ( .A(n5356), .B(n5357), .Z(n5354)); + OR2 U5049 ( .A(n5358), .B(pi180), .Z(n5357)); + OR2 U5050 ( .A(n5359), .B(pi172), .Z(n5356)); + IV2 U5051 ( .A(pi180), .Z(n5359)); + AN2 U5052 ( .A(n5360), .B(n2837), .Z(n3977)); + AN2 U5053 ( .A(n5361), .B(n5362), .Z(n5360)); + IV2 U5054 ( .A(n5363), .Z(n5362)); + AN2 U5055 ( .A(n5364), .B(n5365), .Z(n5363)); + OR2 U5056 ( .A(n5365), .B(n5364), .Z(n5361)); + OR2 U5057 ( .A(n5366), .B(n5367), .Z(n5364)); + AN2 U5058 ( .A(n5368), .B(n3261), .Z(n5367)); + AN2 U5059 ( .A(n5369), .B(n3321), .Z(n5366)); + IV2 U5060 ( .A(n5368), .Z(n5369)); + OR2 U5061 ( .A(n5370), .B(n5371), .Z(n5368)); + AN2 U5062 ( .A(pi003), .B(n5372), .Z(n5371)); + AN2 U5063 ( .A(pi130), .B(n3597), .Z(n5370)); + AN2 U5064 ( .A(n5373), .B(n5374), .Z(n5365)); + OR2 U5065 ( .A(n5375), .B(pi142), .Z(n5374)); + IV2 U5066 ( .A(n5376), .Z(n5375)); + OR2 U5067 ( .A(n5376), .B(n4440), .Z(n5373)); + OR2 U5068 ( .A(n5377), .B(n5378), .Z(n5376)); + AN2 U5069 ( .A(pi177), .B(n5379), .Z(n5378)); + AN2 U5070 ( .A(pi195), .B(n5380), .Z(n5377)); + IV2 U5071 ( .A(pi177), .Z(n5380)); + AN2 U5072 ( .A(n5381), .B(n5382), .Z(n5270)); + IV2 U5073 ( .A(n5383), .Z(n5382)); + AN2 U5074 ( .A(n5384), .B(n5385), .Z(n5383)); + OR2 U5075 ( .A(n5385), .B(n5384), .Z(n5381)); + AN2 U5076 ( .A(n5386), .B(n5387), .Z(n5384)); + OR2 U5077 ( .A(n5257), .B(pi001), .Z(n5387)); + IV2 U5078 ( .A(n5258), .Z(n5257)); + OR2 U5079 ( .A(n5258), .B(n5388), .Z(n5386)); + IV2 U5080 ( .A(pi001), .Z(n5388)); + OR2 U5081 ( .A(n3831), .B(n5389), .Z(n5258)); + AN2 U5082 ( .A(n5390), .B(pi192), .Z(n5389)); + OR2 U5083 ( .A(n5391), .B(n5392), .Z(n5390)); + AN2 U5084 ( .A(n5393), .B(n5394), .Z(n5392)); + IV2 U5085 ( .A(n5395), .Z(n5391)); + OR2 U5086 ( .A(n5394), .B(n5393), .Z(n5395)); + OR2 U5087 ( .A(n5396), .B(n5397), .Z(n5393)); + IV2 U5088 ( .A(n5398), .Z(n5397)); + OR2 U5089 ( .A(n5399), .B(pi038), .Z(n5398)); + AN2 U5090 ( .A(pi038), .B(n5399), .Z(n5396)); + AN2 U5091 ( .A(n5400), .B(n5401), .Z(n5399)); + OR2 U5092 ( .A(n5402), .B(pi103), .Z(n5401)); + IV2 U5093 ( .A(pi053), .Z(n5402)); + OR2 U5094 ( .A(n5403), .B(pi053), .Z(n5400)); + IV2 U5095 ( .A(pi103), .Z(n5403)); + AN2 U5096 ( .A(n5404), .B(n5405), .Z(n5394)); + OR2 U5097 ( .A(n5406), .B(pi121), .Z(n5405)); + IV2 U5098 ( .A(n5407), .Z(n5404)); + AN2 U5099 ( .A(n5406), .B(pi121), .Z(n5407)); + AN2 U5100 ( .A(n5408), .B(n5409), .Z(n5406)); + OR2 U5101 ( .A(n5410), .B(pi184), .Z(n5409)); + IV2 U5102 ( .A(pi149), .Z(n5410)); + OR2 U5103 ( .A(n5411), .B(pi149), .Z(n5408)); + IV2 U5104 ( .A(pi184), .Z(n5411)); + AN2 U5105 ( .A(n5412), .B(n2837), .Z(n3831)); + OR2 U5106 ( .A(n5413), .B(n5414), .Z(n5412)); + IV2 U5107 ( .A(n5415), .Z(n5414)); + OR2 U5108 ( .A(n5416), .B(n5417), .Z(n5415)); + AN2 U5109 ( .A(n5417), .B(n5416), .Z(n5413)); + AN2 U5110 ( .A(n5418), .B(n5419), .Z(n5416)); + OR2 U5111 ( .A(n5420), .B(pi081), .Z(n5419)); + IV2 U5112 ( .A(n5421), .Z(n5420)); + OR2 U5113 ( .A(n5421), .B(n2982), .Z(n5418)); + OR2 U5114 ( .A(n5422), .B(n5423), .Z(n5421)); + AN2 U5115 ( .A(pi082), .B(n5424), .Z(n5423)); + IV2 U5116 ( .A(pi092), .Z(n5424)); + AN2 U5117 ( .A(pi092), .B(n2957), .Z(n5422)); + OR2 U5118 ( .A(n5425), .B(n5426), .Z(n5417)); + AN2 U5119 ( .A(n5427), .B(n5165), .Z(n5426)); + AN2 U5120 ( .A(n5428), .B(pi107), .Z(n5425)); + IV2 U5121 ( .A(n5427), .Z(n5428)); + OR2 U5122 ( .A(n5429), .B(n5430), .Z(n5427)); + AN2 U5123 ( .A(pi201), .B(n5431), .Z(n5430)); + AN2 U5124 ( .A(pi206), .B(n3141), .Z(n5429)); + OR2 U5125 ( .A(n5432), .B(n5433), .Z(n5385)); + IV2 U5126 ( .A(n5434), .Z(n5433)); + OR2 U5127 ( .A(n5435), .B(pi059), .Z(n5434)); + AN2 U5128 ( .A(n5435), .B(pi059), .Z(n5432)); + AN2 U5129 ( .A(n5436), .B(n5437), .Z(n5435)); + OR2 U5130 ( .A(n5438), .B(pi197), .Z(n5437)); + IV2 U5131 ( .A(pi131), .Z(n5438)); + OR2 U5132 ( .A(n5439), .B(pi131), .Z(n5436)); + OR2 U5133 ( .A(n5440), .B(n5441), .Z(po026)); + AN2 U5134 ( .A(n3355), .B(n5442), .Z(n5441)); + OR2 U5135 ( .A(n5443), .B(n5444), .Z(n5442)); + OR2 U5136 ( .A(n5445), .B(n5446), .Z(n5444)); + AN2 U5137 ( .A(po036), .B(n5447), .Z(n5446)); + OR2 U5138 ( .A(n4418), .B(n5448), .Z(n5447)); + AN2 U5139 ( .A(n5449), .B(n3236), .Z(n5445)); + AN2 U5140 ( .A(n5450), .B(n5451), .Z(n5449)); + OR2 U5141 ( .A(n5452), .B(n2837), .Z(n5451)); + OR2 U5142 ( .A(pi192), .B(n5453), .Z(n5450)); + AN2 U5143 ( .A(n4418), .B(n5448), .Z(n5443)); + AN2 U5144 ( .A(n3352), .B(n5454), .Z(n5440)); + IV2 U5145 ( .A(n5455), .Z(po022)); + AN2 U5146 ( .A(n5456), .B(n5457), .Z(n5455)); + AN2 U5147 ( .A(pi074), .B(pi046), .Z(n5457)); + AN2 U5148 ( .A(pi178), .B(pi113), .Z(n5456)); + OR2 U5149 ( .A(n5458), .B(n5459), .Z(po019)); + AN2 U5150 ( .A(n4478), .B(n5460), .Z(n5459)); + OR2 U5151 ( .A(n5461), .B(n4511), .Z(n5460)); + OR2 U5152 ( .A(n5462), .B(n5463), .Z(n4511)); + OR2 U5153 ( .A(n5464), .B(n5465), .Z(n5463)); + AN2 U5154 ( .A(n5466), .B(pi192), .Z(n5465)); + AN2 U5155 ( .A(n5467), .B(n2837), .Z(n5464)); + AN2 U5156 ( .A(n4475), .B(n5468), .Z(n5458)); + OR2 U5157 ( .A(n5469), .B(n5470), .Z(n5468)); + OR2 U5158 ( .A(n5471), .B(n4509), .Z(n5470)); + OR2 U5159 ( .A(n5472), .B(n5473), .Z(n4509)); + OR2 U5160 ( .A(n5474), .B(n5475), .Z(n5473)); + AN2 U5161 ( .A(n5476), .B(n4078), .Z(n5475)); + AN2 U5162 ( .A(n5477), .B(n5322), .Z(n5476)); + AN2 U5163 ( .A(n5478), .B(n4551), .Z(n5472)); + IV2 U5164 ( .A(n4544), .Z(n4551)); + OR2 U5165 ( .A(n4561), .B(n4066), .Z(n4544)); + AN2 U5166 ( .A(pi192), .B(n3901), .Z(n5478)); + AN2 U5167 ( .A(po102), .B(n4347), .Z(n5471)); + OR2 U5168 ( .A(n5479), .B(n4524), .Z(n4347)); + AN2 U5169 ( .A(n5480), .B(n4078), .Z(n4524)); + IV2 U5170 ( .A(n4066), .Z(n4078)); + AN2 U5171 ( .A(n4521), .B(n4072), .Z(n5479)); + OR2 U5172 ( .A(n5481), .B(n5482), .Z(n4521)); + AN2 U5173 ( .A(po059), .B(n5480), .Z(n5482)); + OR2 U5174 ( .A(n5483), .B(po024), .Z(n5480)); + AN2 U5175 ( .A(n5477), .B(n4074), .Z(n5481)); + OR2 U5176 ( .A(n5484), .B(n5485), .Z(n5469)); + AN2 U5177 ( .A(n4517), .B(n3780), .Z(n5485)); + OR2 U5178 ( .A(n5486), .B(n5487), .Z(n4517)); + AN2 U5179 ( .A(n4356), .B(n5488), .Z(n5487)); + AN2 U5180 ( .A(n4076), .B(n5489), .Z(n4356)); + AN2 U5181 ( .A(n4077), .B(n4578), .Z(n5489)); + AN2 U5182 ( .A(n5490), .B(n4543), .Z(n5486)); + AN2 U5183 ( .A(n4348), .B(n4353), .Z(n4543)); + AN2 U5184 ( .A(n3890), .B(n5491), .Z(n5490)); + AN2 U5185 ( .A(n4512), .B(n4072), .Z(n5484)); + OR2 U5186 ( .A(n5492), .B(n3773), .Z(n4072)); + AN2 U5187 ( .A(po025), .B(n3780), .Z(n5492)); + IV2 U5188 ( .A(n4087), .Z(n3780)); + OR2 U5189 ( .A(n5493), .B(n5494), .Z(n4512)); + OR2 U5190 ( .A(n5495), .B(n5496), .Z(n5494)); + AN2 U5191 ( .A(n5497), .B(pi192), .Z(n5496)); + AN2 U5192 ( .A(n5498), .B(n4348), .Z(n5497)); + OR2 U5193 ( .A(n5499), .B(n5500), .Z(n5498)); + AN2 U5194 ( .A(po102), .B(n3880), .Z(n5500)); + AN2 U5195 ( .A(n4353), .B(n3901), .Z(n5499)); + AN2 U5196 ( .A(n5501), .B(n4076), .Z(n5495)); + AN2 U5197 ( .A(n5488), .B(n4358), .Z(n5501)); + AN2 U5198 ( .A(n5502), .B(n4076), .Z(n5493)); + AN2 U5199 ( .A(n2837), .B(n5503), .Z(n4076)); + AN2 U5200 ( .A(po024), .B(n5322), .Z(n5502)); + OR2 U5201 ( .A(n5504), .B(n5505), .Z(po074)); + AN2 U5202 ( .A(n5506), .B(n5507), .Z(n5505)); + OR2 U5203 ( .A(n4167), .B(n5508), .Z(n5507)); + OR2 U5204 ( .A(pi054), .B(n5509), .Z(n5508)); + AN2 U5205 ( .A(pi025), .B(pi146), .Z(n5509)); + OR2 U5206 ( .A(n5510), .B(n5511), .Z(n5506)); + OR2 U5207 ( .A(n5512), .B(n5513), .Z(n5511)); + AN2 U5208 ( .A(n3926), .B(n5514), .Z(n5513)); + OR2 U5209 ( .A(n5515), .B(n5516), .Z(n5514)); + OR2 U5210 ( .A(n5517), .B(n5518), .Z(n5516)); + OR2 U5211 ( .A(pi056), .B(pi035), .Z(n5518)); + OR2 U5212 ( .A(pi100), .B(n5519), .Z(n5515)); + OR2 U5213 ( .A(pi190), .B(pi126), .Z(n5519)); + AN2 U5214 ( .A(n5520), .B(n4319), .Z(n5512)); + OR2 U5215 ( .A(n5521), .B(n5522), .Z(n5520)); + AN2 U5216 ( .A(pi198), .B(n3945), .Z(n5522)); + AN2 U5217 ( .A(n5523), .B(n5524), .Z(n5521)); + AN2 U5218 ( .A(n5525), .B(n5517), .Z(n5524)); + AN2 U5219 ( .A(n5056), .B(n4201), .Z(n5525)); + AN2 U5220 ( .A(n4391), .B(pi192), .Z(n5523)); + OR2 U5221 ( .A(n5526), .B(n5527), .Z(n5510)); + AN2 U5222 ( .A(n5528), .B(n5529), .Z(n5527)); + OR2 U5223 ( .A(pi198), .B(n4319), .Z(n5529)); + OR2 U5224 ( .A(n5530), .B(n5531), .Z(n5528)); + AN2 U5225 ( .A(n3945), .B(n5532), .Z(n5531)); + OR2 U5226 ( .A(n5533), .B(n5534), .Z(n5532)); + AN2 U5227 ( .A(pi061), .B(n4013), .Z(n5534)); + AN2 U5228 ( .A(n5535), .B(pi134), .Z(n5533)); + AN2 U5229 ( .A(n5536), .B(n4201), .Z(n5535)); + AN2 U5230 ( .A(n5537), .B(n5538), .Z(n5530)); + AN2 U5231 ( .A(n5536), .B(n3261), .Z(n5538)); + OR2 U5232 ( .A(pi061), .B(n4013), .Z(n5536)); + AN2 U5233 ( .A(n5539), .B(n5540), .Z(n5537)); + OR2 U5234 ( .A(pi134), .B(n4201), .Z(n5540)); + OR2 U5235 ( .A(n5541), .B(n5542), .Z(n5539)); + AN2 U5236 ( .A(n5543), .B(n5517), .Z(n5542)); + AN2 U5237 ( .A(pi192), .B(n5544), .Z(n5541)); + OR2 U5238 ( .A(n5545), .B(n5546), .Z(n5544)); + AN2 U5239 ( .A(pi006), .B(n3265), .Z(n5546)); + AN2 U5240 ( .A(n5543), .B(n5056), .Z(n5545)); + OR2 U5241 ( .A(pi006), .B(n3265), .Z(n5543)); + AN2 U5242 ( .A(n5547), .B(n5548), .Z(n5526)); + AN2 U5243 ( .A(n5549), .B(n5550), .Z(n5548)); + AN2 U5244 ( .A(n5517), .B(n2837), .Z(n5550)); + OR2 U5245 ( .A(n5551), .B(n5552), .Z(n5517)); + OR2 U5246 ( .A(n5553), .B(n5554), .Z(n5552)); + AN2 U5247 ( .A(n3945), .B(n5555), .Z(n5554)); + OR2 U5248 ( .A(n5556), .B(n5557), .Z(n5555)); + OR2 U5249 ( .A(n5558), .B(n5559), .Z(n5557)); + AN2 U5250 ( .A(n5560), .B(n3380), .Z(n5559)); + AN2 U5251 ( .A(n5561), .B(n5562), .Z(n5558)); + OR2 U5252 ( .A(n5563), .B(n5564), .Z(n5561)); + AN2 U5253 ( .A(pi055), .B(n3512), .Z(n5564)); + AN2 U5254 ( .A(n5565), .B(pi124), .Z(n5563)); + AN2 U5255 ( .A(n5566), .B(n3363), .Z(n5565)); + AN2 U5256 ( .A(pi109), .B(n4975), .Z(n5556)); + AN2 U5257 ( .A(n5567), .B(n5568), .Z(n5553)); + OR2 U5258 ( .A(n5569), .B(n5570), .Z(n5568)); + OR2 U5259 ( .A(n5571), .B(n5572), .Z(n5570)); + AN2 U5260 ( .A(n5573), .B(n5566), .Z(n5572)); + OR2 U5261 ( .A(pi055), .B(n3512), .Z(n5566)); + AN2 U5262 ( .A(n5574), .B(n3261), .Z(n5573)); + OR2 U5263 ( .A(n5575), .B(n5576), .Z(n5574)); + AN2 U5264 ( .A(pi124), .B(n5562), .Z(n5576)); + OR2 U5265 ( .A(n5577), .B(n5560), .Z(n5562)); + AN2 U5266 ( .A(n5578), .B(n3380), .Z(n5577)); + AN2 U5267 ( .A(n5579), .B(n3363), .Z(n5575)); + OR2 U5268 ( .A(n5580), .B(n5560), .Z(n5579)); + AN2 U5269 ( .A(n5578), .B(pi186), .Z(n5560)); + OR2 U5270 ( .A(pi109), .B(n4975), .Z(n5578)); + AN2 U5271 ( .A(pi109), .B(n3380), .Z(n5580)); + AN2 U5272 ( .A(n5581), .B(n5582), .Z(n5571)); + AN2 U5273 ( .A(n4975), .B(n3380), .Z(n5582)); + AN2 U5274 ( .A(n5583), .B(n3363), .Z(n5581)); + OR2 U5275 ( .A(n5584), .B(n5585), .Z(n5583)); + AN2 U5276 ( .A(pi192), .B(n3512), .Z(n5585)); + AN2 U5277 ( .A(pi055), .B(n3261), .Z(n5584)); + OR2 U5278 ( .A(n3926), .B(n5586), .Z(n5569)); + AN2 U5279 ( .A(n5587), .B(n5588), .Z(n5586)); + AN2 U5280 ( .A(n5589), .B(pi110), .Z(n5588)); + AN2 U5281 ( .A(pi167), .B(n2837), .Z(n5589)); + AN2 U5282 ( .A(pi019), .B(pi085), .Z(n5587)); + AN2 U5283 ( .A(n5590), .B(n5591), .Z(n5567)); + OR2 U5284 ( .A(pi192), .B(n5592), .Z(n5591)); + AN2 U5285 ( .A(n5593), .B(n5594), .Z(n5592)); + OR2 U5286 ( .A(pi164), .B(n3261), .Z(n5594)); + OR2 U5287 ( .A(n5595), .B(n5596), .Z(n5593)); + OR2 U5288 ( .A(n5597), .B(n5598), .Z(n5596)); + AN2 U5289 ( .A(n5599), .B(pi024), .Z(n5598)); + AN2 U5290 ( .A(pi195), .B(n5600), .Z(n5597)); + OR2 U5291 ( .A(n5601), .B(n5599), .Z(n5600)); + AN2 U5292 ( .A(n5602), .B(n5603), .Z(n5599)); + IV2 U5293 ( .A(n5604), .Z(n5603)); + AN2 U5294 ( .A(n5605), .B(n5606), .Z(n5604)); + OR2 U5295 ( .A(n5607), .B(n5608), .Z(n5606)); + AN2 U5296 ( .A(n5609), .B(n5610), .Z(n5608)); + OR2 U5297 ( .A(n5611), .B(n4961), .Z(n5610)); + IV2 U5298 ( .A(pi030), .Z(n4961)); + AN2 U5299 ( .A(n5612), .B(n3597), .Z(n5611)); + OR2 U5300 ( .A(n5612), .B(n3597), .Z(n5609)); + AN2 U5301 ( .A(n4969), .B(n4440), .Z(n5607)); + OR2 U5302 ( .A(n4440), .B(n4969), .Z(n5605)); + IV2 U5303 ( .A(pi159), .Z(n4969)); + AN2 U5304 ( .A(pi024), .B(n5602), .Z(n5601)); + OR2 U5305 ( .A(pi087), .B(pi130), .Z(n5602)); + AN2 U5306 ( .A(pi087), .B(pi130), .Z(n5595)); + OR2 U5307 ( .A(n2837), .B(n5613), .Z(n5590)); + OR2 U5308 ( .A(n5614), .B(n5615), .Z(n5613)); + AN2 U5309 ( .A(n5347), .B(n5616), .Z(n5615)); + AN2 U5310 ( .A(n5617), .B(n4943), .Z(n5614)); + OR2 U5311 ( .A(n5347), .B(n5616), .Z(n5617)); + OR2 U5312 ( .A(n5618), .B(n5619), .Z(n5616)); + OR2 U5313 ( .A(n5620), .B(n5621), .Z(n5619)); + AN2 U5314 ( .A(n5622), .B(pi099), .Z(n5621)); + AN2 U5315 ( .A(n5623), .B(n4937), .Z(n5620)); + OR2 U5316 ( .A(n5624), .B(n5622), .Z(n5623)); + AN2 U5317 ( .A(n5625), .B(n5626), .Z(n5622)); + IV2 U5318 ( .A(n5627), .Z(n5626)); + AN2 U5319 ( .A(n5628), .B(n5629), .Z(n5627)); + OR2 U5320 ( .A(n5630), .B(n5631), .Z(n5629)); + AN2 U5321 ( .A(n5632), .B(n5633), .Z(n5631)); + OR2 U5322 ( .A(po011), .B(n5634), .Z(n5633)); + AN2 U5323 ( .A(n5612), .B(n5358), .Z(n5634)); + OR2 U5324 ( .A(n5612), .B(n5358), .Z(n5632)); + IV2 U5325 ( .A(pi172), .Z(n5358)); + IV2 U5326 ( .A(po062), .Z(n5612)); + OR2 U5327 ( .A(n5635), .B(n5636), .Z(po062)); + AN2 U5328 ( .A(n5637), .B(n2837), .Z(n5636)); + OR2 U5329 ( .A(n5638), .B(n5639), .Z(n5637)); + AN2 U5330 ( .A(pi020), .B(pi095), .Z(n5639)); + AN2 U5331 ( .A(n5640), .B(n5641), .Z(n5638)); + OR2 U5332 ( .A(pi020), .B(pi095), .Z(n5641)); + OR2 U5333 ( .A(n5642), .B(n5643), .Z(n5640)); + AN2 U5334 ( .A(pi151), .B(n5644), .Z(n5643)); + AN2 U5335 ( .A(pi153), .B(n5645), .Z(n5642)); + OR2 U5336 ( .A(pi151), .B(n5644), .Z(n5645)); + OR2 U5337 ( .A(n5646), .B(n5647), .Z(n5644)); + AN2 U5338 ( .A(pi075), .B(pi156), .Z(n5647)); + AN2 U5339 ( .A(n5648), .B(n5649), .Z(n5646)); + OR2 U5340 ( .A(pi075), .B(pi156), .Z(n5649)); + OR2 U5341 ( .A(n5650), .B(n5651), .Z(n5648)); + AN2 U5342 ( .A(pi040), .B(n5652), .Z(n5651)); + AN2 U5343 ( .A(pi047), .B(n5653), .Z(n5650)); + OR2 U5344 ( .A(pi040), .B(n5652), .Z(n5653)); + AN2 U5345 ( .A(pi192), .B(n5654), .Z(n5635)); + OR2 U5346 ( .A(n5655), .B(n5656), .Z(n5654)); + OR2 U5347 ( .A(n5657), .B(n5658), .Z(n5656)); + AN2 U5348 ( .A(pi101), .B(n5659), .Z(n5658)); + AN2 U5349 ( .A(n5660), .B(n4628), .Z(n5657)); + OR2 U5350 ( .A(n5661), .B(n5659), .Z(n5660)); + OR2 U5351 ( .A(n5662), .B(n5663), .Z(n5659)); + AN2 U5352 ( .A(n5664), .B(pi036), .Z(n5663)); + AN2 U5353 ( .A(n5665), .B(n4861), .Z(n5662)); + OR2 U5354 ( .A(n5666), .B(n5664), .Z(n5665)); + AN2 U5355 ( .A(n5667), .B(n5668), .Z(n5664)); + IV2 U5356 ( .A(n5669), .Z(n5668)); + AN2 U5357 ( .A(n5670), .B(n5671), .Z(n5669)); + OR2 U5358 ( .A(po039), .B(n5672), .Z(n5671)); + AN2 U5359 ( .A(n5673), .B(n5331), .Z(n5672)); + OR2 U5360 ( .A(n5673), .B(n5331), .Z(n5670)); + IV2 U5361 ( .A(pi168), .Z(n5331)); + IV2 U5362 ( .A(n5652), .Z(n5673)); + OR2 U5363 ( .A(n5674), .B(n5675), .Z(n5652)); + AN2 U5364 ( .A(n5676), .B(n2837), .Z(n5675)); + OR2 U5365 ( .A(n5677), .B(n5678), .Z(n5676)); + AN2 U5366 ( .A(pi143), .B(pi108), .Z(n5678)); + AN2 U5367 ( .A(n5679), .B(n5680), .Z(n5677)); + OR2 U5368 ( .A(pi108), .B(pi143), .Z(n5680)); + OR2 U5369 ( .A(n5681), .B(n5682), .Z(n5679)); + AN2 U5370 ( .A(pi014), .B(pi114), .Z(n5682)); + AN2 U5371 ( .A(n5683), .B(n5684), .Z(n5681)); + OR2 U5372 ( .A(pi014), .B(pi114), .Z(n5684)); + OR2 U5373 ( .A(n5685), .B(n5686), .Z(n5683)); + OR2 U5374 ( .A(n5687), .B(n5688), .Z(n5686)); + AN2 U5375 ( .A(n5689), .B(pi170), .Z(n5688)); + AN2 U5376 ( .A(pi176), .B(n5690), .Z(n5687)); + OR2 U5377 ( .A(n5691), .B(n5689), .Z(n5690)); + AN2 U5378 ( .A(n5692), .B(n5693), .Z(n5689)); + IV2 U5379 ( .A(n5694), .Z(n5693)); + AN2 U5380 ( .A(n5695), .B(n5696), .Z(n5694)); + OR2 U5381 ( .A(n5697), .B(n4899), .Z(n5696)); + IV2 U5382 ( .A(pi097), .Z(n4899)); + AN2 U5383 ( .A(n5698), .B(n4077), .Z(n5697)); + OR2 U5384 ( .A(n5698), .B(n4077), .Z(n5695)); + AN2 U5385 ( .A(pi170), .B(n5692), .Z(n5691)); + OR2 U5386 ( .A(pi160), .B(pi189), .Z(n5692)); + AN2 U5387 ( .A(pi189), .B(pi160), .Z(n5685)); + AN2 U5388 ( .A(pi192), .B(n5699), .Z(n5674)); + OR2 U5389 ( .A(n5700), .B(n5701), .Z(n5699)); + AN2 U5390 ( .A(pi089), .B(n4881), .Z(n5701)); + AN2 U5391 ( .A(n5702), .B(n5703), .Z(n5700)); + OR2 U5392 ( .A(pi089), .B(n4881), .Z(n5703)); + OR2 U5393 ( .A(n5704), .B(n5705), .Z(n5702)); + AN2 U5394 ( .A(pi027), .B(n4885), .Z(n5705)); + AN2 U5395 ( .A(n5706), .B(n5707), .Z(n5704)); + OR2 U5396 ( .A(pi027), .B(n4885), .Z(n5707)); + OR2 U5397 ( .A(n5708), .B(n5709), .Z(n5706)); + OR2 U5398 ( .A(n5710), .B(n5711), .Z(n5709)); + AN2 U5399 ( .A(n5712), .B(pi083), .Z(n5711)); + AN2 U5400 ( .A(n5713), .B(n4877), .Z(n5710)); + OR2 U5401 ( .A(n5714), .B(n5712), .Z(n5713)); + AN2 U5402 ( .A(n5715), .B(n5716), .Z(n5712)); + IV2 U5403 ( .A(n5717), .Z(n5716)); + AN2 U5404 ( .A(n5718), .B(n5719), .Z(n5717)); + OR2 U5405 ( .A(po025), .B(n5720), .Z(n5719)); + AN2 U5406 ( .A(n5698), .B(n5303), .Z(n5720)); + OR2 U5407 ( .A(n5698), .B(n5303), .Z(n5718)); + IV2 U5408 ( .A(pi140), .Z(n5303)); + IV2 U5409 ( .A(n5721), .Z(n5698)); + OR2 U5410 ( .A(n5722), .B(n5723), .Z(n5721)); + AN2 U5411 ( .A(n5724), .B(n2837), .Z(n5723)); + OR2 U5412 ( .A(n5725), .B(n5726), .Z(n5724)); + OR2 U5413 ( .A(n5727), .B(n5728), .Z(n5726)); + AN2 U5414 ( .A(n5729), .B(pi094), .Z(n5728)); + AN2 U5415 ( .A(pi128), .B(n5730), .Z(n5727)); + OR2 U5416 ( .A(n5731), .B(n5729), .Z(n5730)); + AN2 U5417 ( .A(n5732), .B(n5733), .Z(n5729)); + IV2 U5418 ( .A(n5734), .Z(n5733)); + AN2 U5419 ( .A(n5735), .B(n5736), .Z(n5734)); + OR2 U5420 ( .A(n5737), .B(n5738), .Z(n5736)); + AN2 U5421 ( .A(n5739), .B(n5740), .Z(n5738)); + OR2 U5422 ( .A(n5741), .B(n5115), .Z(n5740)); + AN2 U5423 ( .A(n5742), .B(n4778), .Z(n5741)); + OR2 U5424 ( .A(n5742), .B(n4778), .Z(n5739)); + IV2 U5425 ( .A(pi163), .Z(n4778)); + AN2 U5426 ( .A(n3177), .B(n4770), .Z(n5737)); + OR2 U5427 ( .A(n3177), .B(n4770), .Z(n5735)); + IV2 U5428 ( .A(pi028), .Z(n4770)); + AN2 U5429 ( .A(pi094), .B(n5732), .Z(n5731)); + OR2 U5430 ( .A(pi173), .B(pi185), .Z(n5732)); + AN2 U5431 ( .A(pi173), .B(pi185), .Z(n5725)); + AN2 U5432 ( .A(pi192), .B(n5743), .Z(n5722)); + OR2 U5433 ( .A(n5744), .B(n5745), .Z(n5743)); + OR2 U5434 ( .A(n5746), .B(n5747), .Z(n5745)); + AN2 U5435 ( .A(pi131), .B(n5748), .Z(n5747)); + AN2 U5436 ( .A(n5749), .B(n5040), .Z(n5746)); + OR2 U5437 ( .A(n5750), .B(n5748), .Z(n5749)); + OR2 U5438 ( .A(n5751), .B(n5752), .Z(n5748)); + AN2 U5439 ( .A(n5753), .B(pi059), .Z(n5752)); + AN2 U5440 ( .A(n5754), .B(n3286), .Z(n5751)); + OR2 U5441 ( .A(n5755), .B(n5753), .Z(n5754)); + AN2 U5442 ( .A(n5756), .B(n5757), .Z(n5753)); + IV2 U5443 ( .A(n5758), .Z(n5757)); + AN2 U5444 ( .A(n5759), .B(n5760), .Z(n5758)); + OR2 U5445 ( .A(po010), .B(n5761), .Z(n5760)); + AN2 U5446 ( .A(n5742), .B(n5439), .Z(n5761)); + OR2 U5447 ( .A(n5742), .B(n5439), .Z(n5759)); + IV2 U5448 ( .A(pi197), .Z(n5439)); + IV2 U5449 ( .A(n5762), .Z(n5742)); + OR2 U5450 ( .A(n5763), .B(n5764), .Z(n5762)); + AN2 U5451 ( .A(n5765), .B(n2837), .Z(n5764)); + OR2 U5452 ( .A(n5766), .B(n5767), .Z(n5765)); + AN2 U5453 ( .A(pi021), .B(pi201), .Z(n5767)); + AN2 U5454 ( .A(n5768), .B(n5769), .Z(n5766)); + OR2 U5455 ( .A(pi021), .B(pi201), .Z(n5769)); + OR2 U5456 ( .A(n5770), .B(n5771), .Z(n5768)); + IV2 U5457 ( .A(n5772), .Z(n5771)); + AN2 U5458 ( .A(n5773), .B(n5774), .Z(n5772)); + OR2 U5459 ( .A(n5775), .B(n5022), .Z(n5774)); + OR2 U5460 ( .A(n5431), .B(n5776), .Z(n5773)); + AN2 U5461 ( .A(n5777), .B(n5775), .Z(n5776)); + OR2 U5462 ( .A(n5778), .B(n5779), .Z(n5775)); + AN2 U5463 ( .A(n5780), .B(n5781), .Z(n5779)); + OR2 U5464 ( .A(n5782), .B(n5783), .Z(n5781)); + AN2 U5465 ( .A(n5784), .B(n5785), .Z(n5783)); + OR2 U5466 ( .A(n5786), .B(n5787), .Z(n5785)); + IV2 U5467 ( .A(pi181), .Z(n5787)); + AN2 U5468 ( .A(n2982), .B(n5019), .Z(n5786)); + OR2 U5469 ( .A(n2982), .B(n5019), .Z(n5784)); + IV2 U5470 ( .A(pi011), .Z(n5019)); + AN2 U5471 ( .A(n5027), .B(n2957), .Z(n5782)); + OR2 U5472 ( .A(n2957), .B(n5027), .Z(n5780)); + IV2 U5473 ( .A(pi086), .Z(n5027)); + OR2 U5474 ( .A(n5022), .B(n5778), .Z(n5777)); + AN2 U5475 ( .A(n5165), .B(n5031), .Z(n5778)); + IV2 U5476 ( .A(pi165), .Z(n5031)); + IV2 U5477 ( .A(pi032), .Z(n5022)); + AN2 U5478 ( .A(pi165), .B(pi107), .Z(n5770)); + AN2 U5479 ( .A(pi192), .B(n5788), .Z(n5763)); + OR2 U5480 ( .A(n5789), .B(n5790), .Z(n5788)); + AN2 U5481 ( .A(pi121), .B(n5000), .Z(n5790)); + AN2 U5482 ( .A(n5791), .B(n5792), .Z(n5789)); + OR2 U5483 ( .A(pi121), .B(n5000), .Z(n5792)); + OR2 U5484 ( .A(n5793), .B(n5794), .Z(n5791)); + AN2 U5485 ( .A(pi053), .B(n5795), .Z(n5794)); + AN2 U5486 ( .A(n5796), .B(n5004), .Z(n5793)); + OR2 U5487 ( .A(pi053), .B(n5795), .Z(n5796)); + OR2 U5488 ( .A(n5797), .B(n5798), .Z(n5795)); + AN2 U5489 ( .A(pi184), .B(n5008), .Z(n5798)); + AN2 U5490 ( .A(n5799), .B(n5800), .Z(n5797)); + OR2 U5491 ( .A(pi184), .B(n5008), .Z(n5800)); + OR2 U5492 ( .A(n5801), .B(n5802), .Z(n5799)); + AN2 U5493 ( .A(pi181), .B(pi103), .Z(n5802)); + AN2 U5494 ( .A(n5803), .B(n2962), .Z(n5801)); + OR2 U5495 ( .A(pi103), .B(pi181), .Z(n5803)); + AN2 U5496 ( .A(pi059), .B(n5756), .Z(n5755)); + AN2 U5497 ( .A(pi131), .B(n5756), .Z(n5750)); + OR2 U5498 ( .A(pi001), .B(n5037), .Z(n5756)); + AN2 U5499 ( .A(pi001), .B(n5037), .Z(n5744)); + AN2 U5500 ( .A(pi083), .B(n5715), .Z(n5714)); + OR2 U5501 ( .A(pi162), .B(n4874), .Z(n5715)); + AN2 U5502 ( .A(pi162), .B(n4874), .Z(n5708)); + AN2 U5503 ( .A(pi036), .B(n5667), .Z(n5666)); + AN2 U5504 ( .A(pi101), .B(n5667), .Z(n5661)); + OR2 U5505 ( .A(pi205), .B(n4915), .Z(n5667)); + AN2 U5506 ( .A(pi205), .B(n4915), .Z(n5655)); + AN2 U5507 ( .A(po036), .B(n5350), .Z(n5630)); + OR2 U5508 ( .A(po036), .B(n5350), .Z(n5628)); + IV2 U5509 ( .A(pi018), .Z(n5350)); + AN2 U5510 ( .A(pi099), .B(n5625), .Z(n5624)); + OR2 U5511 ( .A(pi180), .B(n4947), .Z(n5625)); + AN2 U5512 ( .A(pi180), .B(n4947), .Z(n5618)); + AN2 U5513 ( .A(n3261), .B(pi049), .Z(n5347)); + AN2 U5514 ( .A(n3926), .B(n5804), .Z(n5551)); + OR2 U5515 ( .A(n5805), .B(n5806), .Z(n5804)); + OR2 U5516 ( .A(pi085), .B(pi019), .Z(n5806)); + OR2 U5517 ( .A(pi110), .B(n5807), .Z(n5805)); + OR2 U5518 ( .A(pi167), .B(pi164), .Z(n5807)); + AN2 U5519 ( .A(pi126), .B(pi190), .Z(n5549)); + AN2 U5520 ( .A(n5808), .B(pi035), .Z(n5547)); + AN2 U5521 ( .A(pi056), .B(pi100), .Z(n5808)); + AN2 U5522 ( .A(pi054), .B(n5809), .Z(n5504)); + OR2 U5523 ( .A(n4167), .B(n5810), .Z(n5809)); + OR2 U5524 ( .A(pi146), .B(pi025), .Z(n5810)); + AN2 U5525 ( .A(n5811), .B(n2882), .Z(po017)); + OR2 U5526 ( .A(pi200), .B(n3003), .Z(n5811)); + OR2 U5527 ( .A(n5812), .B(n5813), .Z(po016)); + OR2 U5528 ( .A(n5814), .B(n5815), .Z(n5813)); + AN2 U5529 ( .A(n3280), .B(n3064), .Z(n5815)); + AN2 U5530 ( .A(n5100), .B(n5816), .Z(n5814)); + OR2 U5531 ( .A(n5817), .B(n5818), .Z(n5816)); + AN2 U5532 ( .A(n3055), .B(n5819), .Z(n5818)); + OR2 U5533 ( .A(n2845), .B(n3143), .Z(n5819)); + OR2 U5534 ( .A(n2978), .B(n3147), .Z(n3143)); + AN2 U5535 ( .A(n3127), .B(n5820), .Z(n5817)); + OR2 U5536 ( .A(n2833), .B(n3138), .Z(n5820)); + OR2 U5537 ( .A(n2880), .B(n3147), .Z(n3138)); + OR2 U5538 ( .A(n2998), .B(n5821), .Z(n3147)); + OR2 U5539 ( .A(n2882), .B(n2862), .Z(n5821)); + OR2 U5540 ( .A(n2925), .B(n2901), .Z(n2882)); + AN2 U5541 ( .A(pi192), .B(n3036), .Z(n3127)); + IV2 U5542 ( .A(n5822), .Z(n5100)); + OR2 U5543 ( .A(n5823), .B(n5824), .Z(n5812)); + AN2 U5544 ( .A(n5825), .B(pi192), .Z(n5824)); + AN2 U5545 ( .A(n5826), .B(po010), .Z(n5825)); + AN2 U5546 ( .A(n5827), .B(n2837), .Z(n5823)); + AN2 U5547 ( .A(n5828), .B(n5829), .Z(n5827)); + OR2 U5548 ( .A(n5830), .B(n5831), .Z(po008)); + AN2 U5549 ( .A(n3344), .B(n3251), .Z(n5831)); + OR2 U5550 ( .A(n5832), .B(n5833), .Z(n3251)); + AN2 U5551 ( .A(n3355), .B(n5454), .Z(n5832)); + OR2 U5552 ( .A(n5834), .B(n5835), .Z(n5454)); + AN2 U5553 ( .A(n5836), .B(n4426), .Z(n5834)); + IV2 U5554 ( .A(n3352), .Z(n3355)); + AN2 U5555 ( .A(n3250), .B(n5837), .Z(n5830)); + OR2 U5556 ( .A(n5838), .B(n5839), .Z(n5837)); + OR2 U5557 ( .A(n5840), .B(n3245), .Z(n5839)); + OR2 U5558 ( .A(n5841), .B(n5842), .Z(n3245)); + AN2 U5559 ( .A(n3244), .B(n3699), .Z(n5842)); + AN2 U5560 ( .A(n3242), .B(n3700), .Z(n5841)); + IV2 U5561 ( .A(n5843), .Z(n3242)); + AN2 U5562 ( .A(n3352), .B(n3701), .Z(n5840)); + OR2 U5563 ( .A(n5844), .B(n5845), .Z(n5838)); + AN2 U5564 ( .A(n4418), .B(n3762), .Z(n5845)); + AN2 U5565 ( .A(n5846), .B(n3236), .Z(n5844)); + OR2 U5566 ( .A(pi037), .B(n5847), .Z(po007)); + IV2 U5567 ( .A(pi116), .Z(n5847)); + OR2 U5568 ( .A(n5848), .B(n5849), .Z(po006)); + AN2 U5569 ( .A(n3370), .B(n5850), .Z(n5849)); + OR2 U5570 ( .A(n5851), .B(n5852), .Z(n5850)); + OR2 U5571 ( .A(n5853), .B(n5138), .Z(n5852)); + OR2 U5572 ( .A(n5854), .B(n3566), .Z(n5138)); + AN2 U5573 ( .A(n3512), .B(n3926), .Z(n3566)); + AN2 U5574 ( .A(n3926), .B(n4368), .Z(n5854)); + OR2 U5575 ( .A(n5855), .B(n5856), .Z(n5851)); + AN2 U5576 ( .A(n5139), .B(n3380), .Z(n5856)); + OR2 U5577 ( .A(n5857), .B(n5858), .Z(n5139)); + AN2 U5578 ( .A(n5859), .B(n3261), .Z(n5857)); + AN2 U5579 ( .A(n5860), .B(pi118), .Z(n5855)); + AN2 U5580 ( .A(n5861), .B(n3261), .Z(n5860)); + OR2 U5581 ( .A(n5858), .B(n5859), .Z(n5861)); + OR2 U5582 ( .A(n5862), .B(n5863), .Z(n5859)); + OR2 U5583 ( .A(n3404), .B(n5864), .Z(n5863)); + AN2 U5584 ( .A(n5865), .B(pi060), .Z(n5864)); + AN2 U5585 ( .A(n4368), .B(n5866), .Z(n5865)); + IV2 U5586 ( .A(n3446), .Z(n3404)); + AN2 U5587 ( .A(n4367), .B(pi196), .Z(n5862)); + IV2 U5588 ( .A(n4364), .Z(n4367)); + IV2 U5589 ( .A(n5867), .Z(n5858)); + IV2 U5590 ( .A(n3393), .Z(n3370)); + AN2 U5591 ( .A(n3393), .B(n5868), .Z(n5848)); + OR2 U5592 ( .A(n5869), .B(n5870), .Z(n5868)); + OR2 U5593 ( .A(n5135), .B(n5871), .Z(n5870)); + AN2 U5594 ( .A(n3480), .B(n5872), .Z(n5871)); + AN2 U5595 ( .A(n5867), .B(n3321), .Z(n5135)); + OR2 U5596 ( .A(po104), .B(n4364), .Z(n5867)); + OR2 U5597 ( .A(n5873), .B(n5874), .Z(n5869)); + OR2 U5598 ( .A(n5875), .B(n5876), .Z(n5874)); + AN2 U5599 ( .A(n5877), .B(po071), .Z(n5876)); + OR2 U5600 ( .A(n5136), .B(n5878), .Z(n5877)); + OR2 U5601 ( .A(n5879), .B(n5880), .Z(n5136)); + OR2 U5602 ( .A(n5881), .B(n5882), .Z(n5880)); + AN2 U5603 ( .A(n3398), .B(n4364), .Z(n5882)); + AN2 U5604 ( .A(n4363), .B(n3419), .Z(n5879)); + AN2 U5605 ( .A(n5883), .B(n3398), .Z(n5875)); + AN2 U5606 ( .A(n3446), .B(n3533), .Z(n3398)); + AN2 U5607 ( .A(n4364), .B(n3913), .Z(n5883)); + OR2 U5608 ( .A(n4062), .B(po027), .Z(n4364)); + IV2 U5609 ( .A(n4061), .Z(n4062)); + AN2 U5610 ( .A(n3416), .B(n4363), .Z(n5873)); + IV2 U5611 ( .A(n4368), .Z(n4363)); + OR2 U5612 ( .A(n3363), .B(n4061), .Z(n4368)); + OR2 U5613 ( .A(n5884), .B(n5885), .Z(n4061)); + OR2 U5614 ( .A(n5886), .B(n5887), .Z(n5885)); + AN2 U5615 ( .A(n5888), .B(n4943), .Z(n5887)); + AN2 U5616 ( .A(n5889), .B(n3989), .Z(n5886)); + OR2 U5617 ( .A(n5890), .B(n5891), .Z(n5884)); + OR2 U5618 ( .A(n5892), .B(n3490), .Z(n5891)); + AN2 U5619 ( .A(n5893), .B(n5894), .Z(n5890)); + AN2 U5620 ( .A(n3495), .B(n5895), .Z(n5893)); + AN2 U5621 ( .A(n3446), .B(n3480), .Z(n3416)); + OR2 U5622 ( .A(po104), .B(n3942), .Z(n3446)); + OR2 U5623 ( .A(n5896), .B(n3253), .Z(po012)); + AN2 U5624 ( .A(n5897), .B(pi054), .Z(n3253)); + OR2 U5625 ( .A(n4133), .B(n5898), .Z(n5897)); + AN2 U5626 ( .A(n4127), .B(n3255), .Z(n5896)); + OR2 U5627 ( .A(pi054), .B(n5120), .Z(n3255)); + OR2 U5628 ( .A(n5899), .B(n4167), .Z(n5120)); + AN2 U5629 ( .A(n4133), .B(n4128), .Z(n5899)); + IV2 U5630 ( .A(po064), .Z(n4133)); + OR2 U5631 ( .A(n5900), .B(n4372), .Z(n4127)); + OR2 U5632 ( .A(n5901), .B(n5902), .Z(n4372)); + AN2 U5633 ( .A(n4380), .B(n4319), .Z(n5901)); + AN2 U5634 ( .A(n5903), .B(n4374), .Z(n5900)); + OR2 U5635 ( .A(n5904), .B(n5905), .Z(n4374)); + AN2 U5636 ( .A(n5906), .B(n3261), .Z(n5905)); + OR2 U5637 ( .A(n5907), .B(n5908), .Z(n5906)); + OR2 U5638 ( .A(n5909), .B(n5910), .Z(n5908)); + AN2 U5639 ( .A(n5911), .B(po103), .Z(n5910)); + AN2 U5640 ( .A(n5912), .B(pi058), .Z(n5911)); + AN2 U5641 ( .A(n5913), .B(n5914), .Z(n5912)); + AN2 U5642 ( .A(n5915), .B(n5916), .Z(n5913)); + OR2 U5643 ( .A(pi204), .B(n5917), .Z(n5916)); + AN2 U5644 ( .A(n4057), .B(n4013), .Z(n5917)); + OR2 U5645 ( .A(n5918), .B(n5919), .Z(n5915)); + AN2 U5646 ( .A(po040), .B(n3966), .Z(n5918)); + AN2 U5647 ( .A(n5920), .B(n3265), .Z(n5909)); + AN2 U5648 ( .A(n5921), .B(n5922), .Z(n5920)); + AN2 U5649 ( .A(n4057), .B(n4316), .Z(n5922)); + AN2 U5650 ( .A(n4157), .B(n5914), .Z(n5921)); + OR2 U5651 ( .A(n5923), .B(n4319), .Z(n5914)); + AN2 U5652 ( .A(pi065), .B(pi192), .Z(n5923)); + OR2 U5653 ( .A(pi204), .B(n4013), .Z(n4157)); + AN2 U5654 ( .A(n5924), .B(n4046), .Z(n5907)); + AN2 U5655 ( .A(po040), .B(n3263), .Z(n5924)); + OR2 U5656 ( .A(n5925), .B(n2837), .Z(n3263)); + AN2 U5657 ( .A(n3264), .B(pi058), .Z(n5925)); + AN2 U5658 ( .A(n4391), .B(n4319), .Z(n5904)); + AN2 U5659 ( .A(n3265), .B(n4013), .Z(n4391)); + IV2 U5660 ( .A(n4158), .Z(n5903)); + OR2 U5661 ( .A(n4312), .B(n5926), .Z(n4158)); + OR2 U5662 ( .A(n4040), .B(n4229), .Z(n5926)); + IV2 U5663 ( .A(n4184), .Z(n4229)); + OR2 U5664 ( .A(n5927), .B(n5928), .Z(n4184)); + OR2 U5665 ( .A(n5929), .B(n5930), .Z(n5928)); + AN2 U5666 ( .A(n5931), .B(n5932), .Z(n5929)); + OR2 U5667 ( .A(n5933), .B(n5934), .Z(n5932)); + AN2 U5668 ( .A(n3457), .B(n3493), .Z(n5934)); + IV2 U5669 ( .A(n3661), .Z(n3493)); + OR2 U5670 ( .A(n5935), .B(n5936), .Z(n3661)); + OR2 U5671 ( .A(n3229), .B(n3235), .Z(n5936)); + OR2 U5672 ( .A(n5937), .B(n5938), .Z(n3235)); + OR2 U5673 ( .A(n5939), .B(n5940), .Z(n5938)); + AN2 U5674 ( .A(n5452), .B(n3241), .Z(n5940)); + AN2 U5675 ( .A(n5453), .B(n3243), .Z(n5939)); + AN2 U5676 ( .A(po082), .B(n5846), .Z(n5937)); + OR2 U5677 ( .A(n5941), .B(n5942), .Z(n5846)); + AN2 U5678 ( .A(n5452), .B(n3700), .Z(n5942)); + AN2 U5679 ( .A(n3638), .B(n3635), .Z(n5452)); + IV2 U5680 ( .A(pi098), .Z(n3638)); + AN2 U5681 ( .A(n5453), .B(n3699), .Z(n5941)); + AN2 U5682 ( .A(n3597), .B(n3593), .Z(n5453)); + OR2 U5683 ( .A(n5943), .B(n5944), .Z(n3229)); + AN2 U5684 ( .A(po082), .B(n3344), .Z(n5944)); + AN2 U5685 ( .A(n3352), .B(n5945), .Z(n5943)); + OR2 U5686 ( .A(n5946), .B(n5947), .Z(n5945)); + OR2 U5687 ( .A(n3241), .B(n3243), .Z(n5947)); + AN2 U5688 ( .A(po082), .B(n3701), .Z(n5946)); + IV2 U5689 ( .A(n5833), .Z(n3701)); + OR2 U5690 ( .A(n3233), .B(n5948), .Z(n5935)); + AN2 U5691 ( .A(n3339), .B(n5949), .Z(n5948)); + AN2 U5692 ( .A(n5949), .B(po011), .Z(n3233)); + OR2 U5693 ( .A(n5950), .B(n5951), .Z(n5949)); + OR2 U5694 ( .A(n5952), .B(n5953), .Z(n5951)); + AN2 U5695 ( .A(n3241), .B(n3635), .Z(n5953)); + AN2 U5696 ( .A(n3992), .B(n3700), .Z(n3241)); + AN2 U5697 ( .A(n3632), .B(pi192), .Z(n3700)); + IV2 U5698 ( .A(pi004), .Z(n3992)); + AN2 U5699 ( .A(n3243), .B(n3593), .Z(n5952)); + OR2 U5700 ( .A(po036), .B(n4440), .Z(n3593)); + AN2 U5701 ( .A(n5372), .B(n3699), .Z(n3243)); + AN2 U5702 ( .A(po082), .B(n3762), .Z(n5950)); + OR2 U5703 ( .A(n5954), .B(n5955), .Z(n3762)); + OR2 U5704 ( .A(n5956), .B(n5957), .Z(n5955)); + AN2 U5705 ( .A(po036), .B(n5958), .Z(n5957)); + OR2 U5706 ( .A(n5959), .B(po001), .Z(n5958)); + AN2 U5707 ( .A(pi192), .B(n5960), .Z(n5956)); + OR2 U5708 ( .A(n5961), .B(n5962), .Z(n5960)); + AN2 U5709 ( .A(po001), .B(n4001), .Z(n5962)); + AN2 U5710 ( .A(n3635), .B(n3998), .Z(n5961)); + OR2 U5711 ( .A(po036), .B(n4001), .Z(n3635)); + IV2 U5712 ( .A(pi171), .Z(n4001)); + AN2 U5713 ( .A(n3699), .B(n4440), .Z(n5954)); + AN2 U5714 ( .A(n2837), .B(n3590), .Z(n3699)); + AN2 U5715 ( .A(n5963), .B(n3667), .Z(n5933)); + AN2 U5716 ( .A(n3291), .B(n3341), .Z(n3667)); + IV2 U5717 ( .A(n3339), .Z(n3341)); + OR2 U5718 ( .A(n5964), .B(n5965), .Z(n3339)); + AN2 U5719 ( .A(n5966), .B(n4441), .Z(n5965)); + AN2 U5720 ( .A(n5448), .B(po036), .Z(n5964)); + IV2 U5721 ( .A(n5966), .Z(n5448)); + OR2 U5722 ( .A(n5967), .B(n5968), .Z(n3291)); + AN2 U5723 ( .A(n5969), .B(n3688), .Z(n5968)); + IV2 U5724 ( .A(n3746), .Z(n5969)); + AN2 U5725 ( .A(po011), .B(n3746), .Z(n5967)); + OR2 U5726 ( .A(n3717), .B(n3705), .Z(n3746)); + AN2 U5727 ( .A(n3495), .B(n5970), .Z(n5963)); + OR2 U5728 ( .A(n5971), .B(n5972), .Z(n5970)); + OR2 U5729 ( .A(n5973), .B(n5974), .Z(n5972)); + AN2 U5730 ( .A(n5975), .B(pi192), .Z(n5974)); + AN2 U5731 ( .A(n3567), .B(n5976), .Z(n5975)); + OR2 U5732 ( .A(n5977), .B(n5978), .Z(n5976)); + OR2 U5733 ( .A(n5979), .B(n5980), .Z(n5978)); + AN2 U5734 ( .A(n4635), .B(n5981), .Z(n5979)); + IV2 U5735 ( .A(n4464), .Z(n4635)); + OR2 U5736 ( .A(po039), .B(n3870), .Z(n4464)); + IV2 U5737 ( .A(pi016), .Z(n3870)); + OR2 U5738 ( .A(n5982), .B(n5983), .Z(n5977)); + AN2 U5739 ( .A(n5984), .B(n3033), .Z(n5983)); + IV2 U5740 ( .A(n3038), .Z(n3033)); + OR2 U5741 ( .A(po070), .B(n3199), .Z(n3038)); + IV2 U5742 ( .A(pi096), .Z(n3199)); + AN2 U5743 ( .A(n5985), .B(n5986), .Z(n5982)); + OR2 U5744 ( .A(n5987), .B(n3042), .Z(n5986)); + IV2 U5745 ( .A(n3037), .Z(n3042)); + OR2 U5746 ( .A(po099), .B(n3171), .Z(n3037)); + AN2 U5747 ( .A(n3063), .B(n5988), .Z(n5987)); + OR2 U5748 ( .A(n5989), .B(n5990), .Z(n5988)); + AN2 U5749 ( .A(n3036), .B(n5991), .Z(n5989)); + OR2 U5750 ( .A(n5992), .B(n3010), .Z(n5991)); + OR2 U5751 ( .A(n5993), .B(n5994), .Z(n3010)); + AN2 U5752 ( .A(pi088), .B(n3012), .Z(n5992)); + IV2 U5753 ( .A(n5995), .Z(n3036)); + OR2 U5754 ( .A(n5996), .B(n5990), .Z(n5995)); + AN2 U5755 ( .A(pi166), .B(n3049), .Z(n5990)); + AN2 U5756 ( .A(po010), .B(n3106), .Z(n5996)); + OR2 U5757 ( .A(n3534), .B(n3363), .Z(n3567)); + AN2 U5758 ( .A(n5997), .B(n3457), .Z(n5973)); + IV2 U5759 ( .A(n4060), .Z(n3457)); + OR2 U5760 ( .A(n5998), .B(n5999), .Z(n4060)); + AN2 U5761 ( .A(po027), .B(n3451), .Z(n5998)); + AN2 U5762 ( .A(n3760), .B(n4652), .Z(n5997)); + AN2 U5763 ( .A(n6000), .B(n6001), .Z(n5971)); + OR2 U5764 ( .A(n3261), .B(n3525), .Z(n6001)); + AN2 U5765 ( .A(n3363), .B(n3321), .Z(n3525)); + OR2 U5766 ( .A(n6002), .B(n6003), .Z(n6000)); + AN2 U5767 ( .A(n6004), .B(n2837), .Z(n6003)); + OR2 U5768 ( .A(n6005), .B(n6006), .Z(n6004)); + OR2 U5769 ( .A(n6007), .B(n6008), .Z(n6006)); + AN2 U5770 ( .A(n4641), .B(n5981), .Z(n6007)); + IV2 U5771 ( .A(n4468), .Z(n4641)); + OR2 U5772 ( .A(po039), .B(n4735), .Z(n4468)); + IV2 U5773 ( .A(pi040), .Z(n4735)); + OR2 U5774 ( .A(n6009), .B(n6010), .Z(n6005)); + AN2 U5775 ( .A(n5985), .B(n6011), .Z(n6010)); + OR2 U5776 ( .A(n6012), .B(n6013), .Z(n6011)); + OR2 U5777 ( .A(n3066), .B(n6014), .Z(n6013)); + AN2 U5778 ( .A(n6015), .B(n6016), .Z(n6014)); + IV2 U5779 ( .A(n3112), .Z(n3066)); + OR2 U5780 ( .A(po099), .B(n3177), .Z(n3112)); + AN2 U5781 ( .A(n6017), .B(n6018), .Z(n6012)); + OR2 U5782 ( .A(n6019), .B(n6020), .Z(n6018)); + AN2 U5783 ( .A(n6021), .B(n3119), .Z(n6019)); + OR2 U5784 ( .A(n6022), .B(n5993), .Z(n6021)); + AN2 U5785 ( .A(n2890), .B(n5000), .Z(n5993)); + AN2 U5786 ( .A(pi201), .B(n3012), .Z(n6022)); + OR2 U5787 ( .A(n2890), .B(n5000), .Z(n3012)); + IV2 U5788 ( .A(n2886), .Z(n2890)); + OR2 U5789 ( .A(n6023), .B(n6024), .Z(n2886)); + OR2 U5790 ( .A(n6025), .B(n6026), .Z(n6024)); + AN2 U5791 ( .A(n6027), .B(n2998), .Z(n6026)); + AN2 U5792 ( .A(po079), .B(n6028), .Z(n6025)); + OR2 U5793 ( .A(n6029), .B(n4095), .Z(n6028)); + OR2 U5794 ( .A(n6030), .B(n6031), .Z(n4095)); + AN2 U5795 ( .A(n4098), .B(n2887), .Z(n6031)); + AN2 U5796 ( .A(n6032), .B(po031), .Z(n6030)); + AN2 U5797 ( .A(n2990), .B(n2974), .Z(n6032)); + AN2 U5798 ( .A(n2990), .B(n5169), .Z(n6029)); + OR2 U5799 ( .A(n6033), .B(po106), .Z(n2990)); + AN2 U5800 ( .A(n2837), .B(n5431), .Z(n6033)); + OR2 U5801 ( .A(n6034), .B(n6035), .Z(n6023)); + AN2 U5802 ( .A(n4099), .B(n6036), .Z(n6035)); + OR2 U5803 ( .A(n6037), .B(n6038), .Z(n6036)); + AN2 U5804 ( .A(n6039), .B(n5165), .Z(n6038)); + OR2 U5805 ( .A(n6040), .B(n5169), .Z(n6039)); + OR2 U5806 ( .A(n2978), .B(n2862), .Z(n5169)); + AN2 U5807 ( .A(po031), .B(n2974), .Z(n6040)); + AN2 U5808 ( .A(n6041), .B(n2974), .Z(n6037)); + AN2 U5809 ( .A(n5172), .B(n2957), .Z(n6041)); + AN2 U5810 ( .A(n2947), .B(n2837), .Z(n4099)); + AN2 U5811 ( .A(n6042), .B(n4098), .Z(n6034)); + AN2 U5812 ( .A(n2891), .B(pi192), .Z(n4098)); + IV2 U5813 ( .A(n2892), .Z(n2891)); + AN2 U5814 ( .A(n6043), .B(n3847), .Z(n6042)); + IV2 U5815 ( .A(n2885), .Z(n6043)); + AN2 U5816 ( .A(n5984), .B(n3076), .Z(n6009)); + IV2 U5817 ( .A(n3109), .Z(n3076)); + OR2 U5818 ( .A(po070), .B(n3205), .Z(n3109)); + IV2 U5819 ( .A(pi128), .Z(n3205)); + AN2 U5820 ( .A(n5985), .B(n6044), .Z(n6002)); + OR2 U5821 ( .A(n6045), .B(n6046), .Z(n5927)); + AN2 U5822 ( .A(n6047), .B(n3945), .Z(n6046)); + IV2 U5823 ( .A(n4313), .Z(n3945)); + OR2 U5824 ( .A(n3321), .B(n2837), .Z(n4313)); + AN2 U5825 ( .A(pi050), .B(n4975), .Z(n6047)); + AN2 U5826 ( .A(n6048), .B(n6049), .Z(n6045)); + AN2 U5827 ( .A(n6050), .B(n6051), .Z(n6049)); + OR2 U5828 ( .A(n3363), .B(n6052), .Z(n6051)); + OR2 U5829 ( .A(po027), .B(n3989), .Z(n6050)); + AN2 U5830 ( .A(n6053), .B(n4943), .Z(n6048)); + OR2 U5831 ( .A(n4239), .B(n4192), .Z(n4312)); + OR2 U5832 ( .A(n6054), .B(n6055), .Z(po003)); + OR2 U5833 ( .A(n6056), .B(n6057), .Z(n6055)); + AN2 U5834 ( .A(n6058), .B(pi054), .Z(n6057)); + OR2 U5835 ( .A(n6059), .B(n6060), .Z(n6058)); + AN2 U5836 ( .A(n4177), .B(n5126), .Z(n6060)); + AN2 U5837 ( .A(n5125), .B(n5898), .Z(n6059)); + AN2 U5838 ( .A(n6061), .B(n4129), .Z(n6056)); + AN2 U5839 ( .A(n4177), .B(n5125), .Z(n6061)); + OR2 U5840 ( .A(n6062), .B(n3257), .Z(n5125)); + OR2 U5841 ( .A(n5902), .B(n6063), .Z(n3257)); + OR2 U5842 ( .A(n6064), .B(n6065), .Z(n6063)); + AN2 U5843 ( .A(n4403), .B(n4319), .Z(n6065)); + IV2 U5844 ( .A(po004), .Z(n4319)); + OR2 U5845 ( .A(n6066), .B(n4380), .Z(n4403)); + AN2 U5846 ( .A(n6067), .B(n3259), .Z(n6066)); + AN2 U5847 ( .A(n4053), .B(n4013), .Z(n6067)); + OR2 U5848 ( .A(n6068), .B(n3265), .Z(n4053)); + AN2 U5849 ( .A(pi058), .B(n3261), .Z(n6068)); + AN2 U5850 ( .A(n6069), .B(n6070), .Z(n6064)); + AN2 U5851 ( .A(n4387), .B(n4013), .Z(n6070)); + IV2 U5852 ( .A(po040), .Z(n4013)); + AN2 U5853 ( .A(pi065), .B(n3259), .Z(n6069)); + OR2 U5854 ( .A(n6071), .B(n4379), .Z(n5902)); + IV2 U5855 ( .A(n4159), .Z(n4379)); + AN2 U5856 ( .A(n4380), .B(n4404), .Z(n6071)); + IV2 U5857 ( .A(n4161), .Z(n4404)); + IV2 U5858 ( .A(n4141), .Z(n4380)); + OR2 U5859 ( .A(n4016), .B(n6072), .Z(n4141)); + OR2 U5860 ( .A(n6073), .B(n6074), .Z(n6072)); + AN2 U5861 ( .A(n6075), .B(n4242), .Z(n6074)); + OR2 U5862 ( .A(n6076), .B(n6077), .Z(n4016)); + AN2 U5863 ( .A(n4040), .B(n3951), .Z(n6076)); + AN2 U5864 ( .A(n3259), .B(n6078), .Z(n6062)); + OR2 U5865 ( .A(n6079), .B(n3926), .Z(n6078)); + AN2 U5866 ( .A(n3264), .B(n4387), .Z(n6079)); + AN2 U5867 ( .A(n3261), .B(n4026), .Z(n4387)); + IV2 U5868 ( .A(n6080), .Z(n3264)); + OR2 U5869 ( .A(n6081), .B(n5919), .Z(n6080)); + AN2 U5870 ( .A(n4330), .B(n6082), .Z(n3259)); + AN2 U5871 ( .A(n4205), .B(n3314), .Z(n6082)); + IV2 U5872 ( .A(n5898), .Z(n4177)); + AN2 U5873 ( .A(n4172), .B(n5126), .Z(n6054)); + OR2 U5874 ( .A(n6083), .B(n6084), .Z(n5126)); + OR2 U5875 ( .A(n6085), .B(n6086), .Z(n6084)); + AN2 U5876 ( .A(n4227), .B(n4159), .Z(n6086)); + OR2 U5877 ( .A(po004), .B(n4161), .Z(n4159)); + OR2 U5878 ( .A(n4310), .B(n3321), .Z(n4161)); + OR2 U5879 ( .A(n6087), .B(n6088), .Z(n4227)); + OR2 U5880 ( .A(n6089), .B(n6077), .Z(n6088)); + OR2 U5881 ( .A(n6090), .B(n6091), .Z(n6077)); + OR2 U5882 ( .A(n3317), .B(n6092), .Z(n6091)); + AN2 U5883 ( .A(n4291), .B(n4056), .Z(n6092)); + AN2 U5884 ( .A(n3321), .B(po103), .Z(n3317)); + AN2 U5885 ( .A(n4040), .B(n3321), .Z(n6090)); + AN2 U5886 ( .A(n4192), .B(n6075), .Z(n6089)); + OR2 U5887 ( .A(n3321), .B(n6093), .Z(n6075)); + OR2 U5888 ( .A(n6073), .B(n6094), .Z(n6087)); + AN2 U5889 ( .A(n4040), .B(n4291), .Z(n6094)); + AN2 U5890 ( .A(po040), .B(n6095), .Z(n6073)); + OR2 U5891 ( .A(n4040), .B(n6096), .Z(n6095)); + OR2 U5892 ( .A(n3318), .B(n4293), .Z(n6096)); + OR2 U5893 ( .A(n3951), .B(n3321), .Z(n4293)); + AN2 U5894 ( .A(n4056), .B(n6097), .Z(n3318)); + AN2 U5895 ( .A(n4057), .B(pi192), .Z(n6097)); + IV2 U5896 ( .A(n4026), .Z(n4056)); + OR2 U5897 ( .A(pi058), .B(n3265), .Z(n4026)); + IV2 U5898 ( .A(n3314), .Z(n4040)); + OR2 U5899 ( .A(n6098), .B(n4201), .Z(n3314)); + IV2 U5900 ( .A(po091), .Z(n4201)); + AN2 U5901 ( .A(n3261), .B(n4200), .Z(n6098)); + AN2 U5902 ( .A(n6081), .B(pi192), .Z(n6085)); + AN2 U5903 ( .A(n4150), .B(po004), .Z(n6081)); + IV2 U5904 ( .A(pi065), .Z(n4150)); + OR2 U5905 ( .A(n6099), .B(n4396), .Z(n6083)); + AN2 U5906 ( .A(n4406), .B(n6100), .Z(n4396)); + OR2 U5907 ( .A(n6101), .B(n3321), .Z(n6100)); + AN2 U5908 ( .A(n6102), .B(n6103), .Z(n6101)); + AN2 U5909 ( .A(n4223), .B(n4057), .Z(n6103)); + AN2 U5910 ( .A(n4310), .B(n6104), .Z(n6102)); + OR2 U5911 ( .A(po040), .B(n5919), .Z(n6104)); + IV2 U5912 ( .A(pi204), .Z(n5919)); + IV2 U5913 ( .A(n4156), .Z(n4310)); + OR2 U5914 ( .A(pi065), .B(n2837), .Z(n4156)); + AN2 U5915 ( .A(po004), .B(n6105), .Z(n6099)); + OR2 U5916 ( .A(n6106), .B(n3321), .Z(n6105)); + AN2 U5917 ( .A(n4406), .B(n6093), .Z(n6106)); + OR2 U5918 ( .A(n6107), .B(n4028), .Z(n6093)); + AN2 U5919 ( .A(n4223), .B(n4291), .Z(n4028)); + AN2 U5920 ( .A(n4057), .B(n3951), .Z(n4291)); + IV2 U5921 ( .A(n4041), .Z(n3951)); + OR2 U5922 ( .A(pi204), .B(n2837), .Z(n4041)); + OR2 U5923 ( .A(po103), .B(n4316), .Z(n4223)); + IV2 U5924 ( .A(pi058), .Z(n4316)); + AN2 U5925 ( .A(po040), .B(n3322), .Z(n6107)); + OR2 U5926 ( .A(n4046), .B(n4045), .Z(n3322)); + OR2 U5927 ( .A(n6108), .B(n6109), .Z(n4045)); + AN2 U5928 ( .A(n4148), .B(n4057), .Z(n6109)); + OR2 U5929 ( .A(po091), .B(n3966), .Z(n4057)); + IV2 U5930 ( .A(pi129), .Z(n3966)); + AN2 U5931 ( .A(n4203), .B(po103), .Z(n6108)); + IV2 U5932 ( .A(n4200), .Z(n4203)); + OR2 U5933 ( .A(pi129), .B(n2837), .Z(n4200)); + AN2 U5934 ( .A(po103), .B(po091), .Z(n4046)); + AN2 U5935 ( .A(n5898), .B(n4129), .Z(n4172)); + IV2 U5936 ( .A(pi054), .Z(n4129)); + OR2 U5937 ( .A(n4167), .B(n4128), .Z(n5898)); + IV2 U5938 ( .A(po085), .Z(n4128)); + IV2 U5939 ( .A(pi052), .Z(n4167)); + AN2 U5940 ( .A(n6110), .B(n6111), .Z(po002)); + OR2 U5941 ( .A(n3306), .B(n4450), .Z(n6111)); + OR2 U5942 ( .A(n4458), .B(n4606), .Z(n6110)); + IV2 U5943 ( .A(n3306), .Z(n4458)); + OR2 U5944 ( .A(n6112), .B(n4652), .Z(n3306)); + OR2 U5945 ( .A(n6113), .B(n6114), .Z(n4652)); + AN2 U5946 ( .A(n6115), .B(n2837), .Z(n6114)); + OR2 U5947 ( .A(n6116), .B(n6117), .Z(n6115)); + AN2 U5948 ( .A(pi108), .B(n4881), .Z(n6117)); + AN2 U5949 ( .A(n4475), .B(n6118), .Z(n6116)); + OR2 U5950 ( .A(n5467), .B(n6119), .Z(n6118)); + IV2 U5951 ( .A(n5488), .Z(n6119)); + OR2 U5952 ( .A(po102), .B(n5322), .Z(n5488)); + IV2 U5953 ( .A(pi114), .Z(n5322)); + AN2 U5954 ( .A(n6120), .B(n6121), .Z(n5467)); + OR2 U5955 ( .A(pi114), .B(n4885), .Z(n6120)); + AN2 U5956 ( .A(pi192), .B(n6122), .Z(n6113)); + OR2 U5957 ( .A(n6123), .B(n6124), .Z(n6122)); + AN2 U5958 ( .A(pi148), .B(n4881), .Z(n6124)); + AN2 U5959 ( .A(n4475), .B(n6125), .Z(n6123)); + OR2 U5960 ( .A(n5466), .B(n6126), .Z(n6125)); + IV2 U5961 ( .A(n5491), .Z(n6126)); + OR2 U5962 ( .A(po102), .B(n3901), .Z(n5491)); + IV2 U5963 ( .A(pi069), .Z(n3901)); + AN2 U5964 ( .A(n6127), .B(n6128), .Z(n5466)); + OR2 U5965 ( .A(pi069), .B(n4885), .Z(n6127)); + AN2 U5966 ( .A(n5461), .B(n4475), .Z(n6112)); + AN2 U5967 ( .A(n4087), .B(n6129), .Z(n5461)); + AN2 U5968 ( .A(n6130), .B(n4341), .Z(n6129)); + IV2 U5969 ( .A(n5474), .Z(n6130)); + AN2 U5970 ( .A(n6131), .B(po102), .Z(n5474)); + AN2 U5971 ( .A(n4334), .B(n4333), .Z(n4087)); + OR2 U5972 ( .A(n6132), .B(n4656), .Z(n4334)); + OR2 U5973 ( .A(n6133), .B(n6134), .Z(n4656)); + AN2 U5974 ( .A(n5074), .B(n3083), .Z(n6133)); + AN2 U5975 ( .A(n6135), .B(n3083), .Z(n6132)); + AN2 U5976 ( .A(n3018), .B(n4326), .Z(n6135)); + OR2 U5977 ( .A(n6136), .B(n5077), .Z(n4326)); + OR2 U5978 ( .A(n6137), .B(n3100), .Z(n5077)); + AN2 U5979 ( .A(n3280), .B(n4666), .Z(n6137)); + OR2 U5980 ( .A(n3286), .B(n6138), .Z(n4666)); + AN2 U5981 ( .A(n5829), .B(n3049), .Z(n3280)); + AN2 U5982 ( .A(n6139), .B(n3284), .Z(n6136)); + OR2 U5983 ( .A(n3049), .B(n5829), .Z(n3284)); + OR2 U5984 ( .A(n6140), .B(n3287), .Z(n6139)); + OR2 U5985 ( .A(n6141), .B(n6142), .Z(n3287)); + AN2 U5986 ( .A(pi141), .B(n3173), .Z(n6141)); + AN2 U5987 ( .A(n6143), .B(n3286), .Z(n6140)); + OR2 U5988 ( .A(n3173), .B(n3064), .Z(n6143)); + OR2 U5989 ( .A(n6144), .B(n6145), .Z(po000)); + AN2 U5990 ( .A(n6146), .B(po103), .Z(n6145)); + OR2 U5991 ( .A(n6147), .B(n6148), .Z(n6146)); + AN2 U5992 ( .A(n6149), .B(n3326), .Z(n6148)); + AN2 U5993 ( .A(n4217), .B(n3320), .Z(n6147)); + AN2 U5994 ( .A(n6150), .B(n3265), .Z(n6144)); + IV2 U5995 ( .A(po103), .Z(n3265)); + OR2 U5996 ( .A(n6151), .B(n6152), .Z(n6150)); + AN2 U5997 ( .A(n6149), .B(n3320), .Z(n6152)); + OR2 U5998 ( .A(n4406), .B(n4192), .Z(n3320)); + IV2 U5999 ( .A(n4205), .Z(n4192)); + AN2 U6000 ( .A(n4242), .B(n4331), .Z(n4406)); + IV2 U6001 ( .A(n4330), .Z(n4331)); + AN2 U6002 ( .A(n4217), .B(n3326), .Z(n6151)); + OR2 U6003 ( .A(n6153), .B(n4239), .Z(n3326)); + IV2 U6004 ( .A(n4242), .Z(n4239)); + OR2 U6005 ( .A(po028), .B(n6154), .Z(n4242)); + AN2 U6006 ( .A(n6155), .B(n6156), .Z(n6154)); + OR2 U6007 ( .A(n3321), .B(n3969), .Z(n6156)); + IV2 U6008 ( .A(pi169), .Z(n3969)); + AN2 U6009 ( .A(n4330), .B(n4205), .Z(n6153)); + OR2 U6010 ( .A(n6157), .B(n5056), .Z(n4205)); + IV2 U6011 ( .A(po028), .Z(n5056)); + AN2 U6012 ( .A(n3261), .B(n6158), .Z(n6157)); + OR2 U6013 ( .A(pi169), .B(n2837), .Z(n6158)); + OR2 U6014 ( .A(n6159), .B(n6160), .Z(n4330)); + OR2 U6015 ( .A(n6161), .B(n5930), .Z(n6160)); + OR2 U6016 ( .A(n6162), .B(n6163), .Z(n5930)); + OR2 U6017 ( .A(n6164), .B(n6165), .Z(n6163)); + AN2 U6018 ( .A(n3393), .B(n6166), .Z(n6165)); + OR2 U6019 ( .A(n6167), .B(n5853), .Z(n6166)); + AN2 U6020 ( .A(n6168), .B(n3371), .Z(n6167)); + IV2 U6021 ( .A(n3412), .Z(n3371)); + AN2 U6022 ( .A(n5999), .B(n6053), .Z(n6164)); + AN2 U6023 ( .A(n3453), .B(n3363), .Z(n5999)); + AN2 U6024 ( .A(n3926), .B(n4975), .Z(n6162)); + AN2 U6025 ( .A(n3940), .B(n4975), .Z(n6161)); + OR2 U6026 ( .A(n6169), .B(n6170), .Z(n6159)); + AN2 U6027 ( .A(n5931), .B(n6171), .Z(n6170)); + OR2 U6028 ( .A(n6172), .B(n6173), .Z(n6171)); + OR2 U6029 ( .A(n6174), .B(n6175), .Z(n6173)); + AN2 U6030 ( .A(n5888), .B(n3363), .Z(n6175)); + OR2 U6031 ( .A(n6176), .B(n6177), .Z(n5888)); + OR2 U6032 ( .A(n3248), .B(n6178), .Z(n6177)); + AN2 U6033 ( .A(n5833), .B(n3250), .Z(n6178)); + AN2 U6034 ( .A(n4937), .B(n6179), .Z(n5833)); + OR2 U6035 ( .A(n6180), .B(n6181), .Z(n6176)); + AN2 U6036 ( .A(n3495), .B(n5835), .Z(n6181)); + OR2 U6037 ( .A(n6182), .B(n5894), .Z(n5835)); + AN2 U6038 ( .A(n5966), .B(n6183), .Z(n6182)); + OR2 U6039 ( .A(n6184), .B(n6185), .Z(n5966)); + AN2 U6040 ( .A(pi171), .B(pi192), .Z(n6185)); + AN2 U6041 ( .A(pi142), .B(n2837), .Z(n6184)); + AN2 U6042 ( .A(n6186), .B(n5836), .Z(n6180)); + OR2 U6043 ( .A(n6187), .B(n6188), .Z(n5836)); + AN2 U6044 ( .A(n3705), .B(n5843), .Z(n6188)); + AN2 U6045 ( .A(pi192), .B(pi098), .Z(n3705)); + AN2 U6046 ( .A(n3717), .B(n6189), .Z(n6187)); + AN2 U6047 ( .A(n2837), .B(pi003), .Z(n3717)); + AN2 U6048 ( .A(n5889), .B(n3534), .Z(n6174)); + AN2 U6049 ( .A(n3261), .B(pi060), .Z(n3534)); + AN2 U6050 ( .A(pi192), .B(n6190), .Z(n5889)); + OR2 U6051 ( .A(n6191), .B(n6192), .Z(n6190)); + OR2 U6052 ( .A(n6193), .B(n6194), .Z(n6192)); + AN2 U6053 ( .A(pi004), .B(n4947), .Z(n6194)); + AN2 U6054 ( .A(n3640), .B(n3250), .Z(n6193)); + IV2 U6055 ( .A(n3344), .Z(n3250)); + IV2 U6056 ( .A(n3632), .Z(n3640)); + OR2 U6057 ( .A(po001), .B(n3998), .Z(n3632)); + OR2 U6058 ( .A(n6195), .B(n6196), .Z(n6191)); + AN2 U6059 ( .A(n6197), .B(n3495), .Z(n6196)); + AN2 U6060 ( .A(pi171), .B(n6183), .Z(n6197)); + AN2 U6061 ( .A(n6198), .B(n6186), .Z(n6195)); + IV2 U6062 ( .A(n6199), .Z(n6186)); + AN2 U6063 ( .A(pi098), .B(n5843), .Z(n6198)); + OR2 U6064 ( .A(pi171), .B(n4441), .Z(n5843)); + OR2 U6065 ( .A(n5892), .B(n6200), .Z(n6172)); + AN2 U6066 ( .A(n6201), .B(n5894), .Z(n6200)); + AN2 U6067 ( .A(n4441), .B(n4417), .Z(n5894)); + AN2 U6068 ( .A(n3495), .B(n3453), .Z(n6201)); + IV2 U6069 ( .A(n3451), .Z(n3453)); + OR2 U6070 ( .A(n3533), .B(n3321), .Z(n3451)); + IV2 U6071 ( .A(n3477), .Z(n3533)); + OR2 U6072 ( .A(pi060), .B(n2837), .Z(n3477)); + IV2 U6073 ( .A(n6202), .Z(n3495)); + IV2 U6074 ( .A(n6203), .Z(n5892)); + OR2 U6075 ( .A(n6204), .B(n6155), .Z(n6203)); + AN2 U6076 ( .A(n6205), .B(n6206), .Z(n6204)); + AN2 U6077 ( .A(n6207), .B(n6208), .Z(n6206)); + OR2 U6078 ( .A(n6199), .B(n6209), .Z(n6208)); + OR2 U6079 ( .A(n3244), .B(n3597), .Z(n6209)); + IV2 U6080 ( .A(pi003), .Z(n3597)); + IV2 U6081 ( .A(n6189), .Z(n3244)); + OR2 U6082 ( .A(pi142), .B(n4441), .Z(n6189)); + OR2 U6083 ( .A(n4418), .B(n6202), .Z(n6199)); + IV2 U6084 ( .A(n4426), .Z(n4418)); + OR2 U6085 ( .A(n3688), .B(n3290), .Z(n4426)); + IV2 U6086 ( .A(po011), .Z(n3688)); + OR2 U6087 ( .A(n6202), .B(n6210), .Z(n6207)); + OR2 U6088 ( .A(n4430), .B(n4440), .Z(n6210)); + IV2 U6089 ( .A(pi142), .Z(n4440)); + IV2 U6090 ( .A(n6183), .Z(n4430)); + OR2 U6091 ( .A(n4417), .B(n4441), .Z(n6183)); + IV2 U6092 ( .A(po036), .Z(n4441)); + IV2 U6093 ( .A(n3236), .Z(n4417)); + OR2 U6094 ( .A(n3234), .B(po011), .Z(n3236)); + IV2 U6095 ( .A(n3290), .Z(n3234)); + OR2 U6096 ( .A(n6211), .B(n6212), .Z(n3290)); + OR2 U6097 ( .A(n6213), .B(n6214), .Z(n6212)); + OR2 U6098 ( .A(n6215), .B(n6216), .Z(n6214)); + AN2 U6099 ( .A(pi192), .B(n5980), .Z(n6216)); + OR2 U6100 ( .A(n6217), .B(n6218), .Z(n5980)); + OR2 U6101 ( .A(n6219), .B(n6220), .Z(n6218)); + AN2 U6102 ( .A(pi045), .B(n4915), .Z(n6220)); + AN2 U6103 ( .A(n4621), .B(n4751), .Z(n6219)); + IV2 U6104 ( .A(n4465), .Z(n4621)); + OR2 U6105 ( .A(po014), .B(n3908), .Z(n4465)); + IV2 U6106 ( .A(pi079), .Z(n3908)); + OR2 U6107 ( .A(n6221), .B(n6222), .Z(n6217)); + AN2 U6108 ( .A(n6223), .B(n3757), .Z(n6222)); + AN2 U6109 ( .A(pi158), .B(n4628), .Z(n6223)); + AN2 U6110 ( .A(n6224), .B(n6225), .Z(n6221)); + AN2 U6111 ( .A(pi175), .B(n5037), .Z(n6224)); + AN2 U6112 ( .A(n6008), .B(n2837), .Z(n6215)); + OR2 U6113 ( .A(n6226), .B(n6227), .Z(n6008)); + OR2 U6114 ( .A(n6228), .B(n6229), .Z(n6227)); + AN2 U6115 ( .A(pi095), .B(n4915), .Z(n6229)); + AN2 U6116 ( .A(n4623), .B(n4751), .Z(n6228)); + IV2 U6117 ( .A(n4469), .Z(n4623)); + OR2 U6118 ( .A(po014), .B(n4648), .Z(n4469)); + IV2 U6119 ( .A(pi156), .Z(n4648)); + OR2 U6120 ( .A(n6230), .B(n6231), .Z(n6226)); + AN2 U6121 ( .A(n6232), .B(n3757), .Z(n6231)); + AN2 U6122 ( .A(pi151), .B(n4628), .Z(n6232)); + AN2 U6123 ( .A(n6233), .B(n6225), .Z(n6230)); + AN2 U6124 ( .A(pi185), .B(n5037), .Z(n6233)); + AN2 U6125 ( .A(n5985), .B(n6234), .Z(n6213)); + OR2 U6126 ( .A(n6235), .B(n6236), .Z(n6234)); + OR2 U6127 ( .A(n6237), .B(n6238), .Z(n6236)); + OR2 U6128 ( .A(n6239), .B(n6240), .Z(n6238)); + AN2 U6129 ( .A(n6241), .B(n3055), .Z(n6240)); + AN2 U6130 ( .A(n5822), .B(n3119), .Z(n6241)); + OR2 U6131 ( .A(n6242), .B(n6016), .Z(n3119)); + AN2 U6132 ( .A(pi141), .B(po099), .Z(n6242)); + AN2 U6133 ( .A(n5994), .B(n6243), .Z(n6239)); + AN2 U6134 ( .A(n6244), .B(n3049), .Z(n6237)); + OR2 U6135 ( .A(n6243), .B(n6245), .Z(n6244)); + OR2 U6136 ( .A(n6020), .B(n6246), .Z(n6245)); + AN2 U6137 ( .A(n6247), .B(n5826), .Z(n6246)); + AN2 U6138 ( .A(n3106), .B(n5829), .Z(n5826)); + OR2 U6139 ( .A(n5994), .B(n5822), .Z(n5829)); + IV2 U6140 ( .A(pi166), .Z(n3106)); + AN2 U6141 ( .A(n3063), .B(pi192), .Z(n6247)); + AN2 U6142 ( .A(n6016), .B(n5994), .Z(n6020)); + AN2 U6143 ( .A(n3286), .B(n3177), .Z(n6016)); + OR2 U6144 ( .A(n6142), .B(n6248), .Z(n6243)); + AN2 U6145 ( .A(n3207), .B(n3286), .Z(n6248)); + OR2 U6146 ( .A(n6249), .B(n6250), .Z(n3207)); + AN2 U6147 ( .A(n3064), .B(n3171), .Z(n6250)); + AN2 U6148 ( .A(n3173), .B(n3177), .Z(n6249)); + IV2 U6149 ( .A(pi141), .Z(n3177)); + AN2 U6150 ( .A(pi033), .B(n3064), .Z(n6142)); + OR2 U6151 ( .A(n6251), .B(n6252), .Z(n6235)); + OR2 U6152 ( .A(n6044), .B(n3100), .Z(n6252)); + AN2 U6153 ( .A(n3286), .B(n6138), .Z(n3100)); + OR2 U6154 ( .A(n6253), .B(n6254), .Z(n6138)); + AN2 U6155 ( .A(pi033), .B(pi192), .Z(n6254)); + AN2 U6156 ( .A(pi141), .B(n2837), .Z(n6253)); + AN2 U6157 ( .A(n6255), .B(pi141), .Z(n6044)); + OR2 U6158 ( .A(n3071), .B(n6256), .Z(n6255)); + AN2 U6159 ( .A(n3055), .B(n5994), .Z(n6256)); + IV2 U6160 ( .A(n6257), .Z(n5994)); + OR2 U6161 ( .A(n2925), .B(n5099), .Z(n6257)); + OR2 U6162 ( .A(n6258), .B(n6259), .Z(n5099)); + OR2 U6163 ( .A(n3146), .B(n6260), .Z(n6259)); + OR2 U6164 ( .A(n6261), .B(n6262), .Z(n6260)); + AN2 U6165 ( .A(n2845), .B(n2837), .Z(n6262)); + IV2 U6166 ( .A(n2846), .Z(n2845)); + AN2 U6167 ( .A(n6263), .B(n6264), .Z(n2846)); + OR2 U6168 ( .A(n3141), .B(po044), .Z(n6263)); + IV2 U6169 ( .A(pi201), .Z(n3141)); + AN2 U6170 ( .A(pi192), .B(n2833), .Z(n6261)); + IV2 U6171 ( .A(n2834), .Z(n2833)); + AN2 U6172 ( .A(n6265), .B(n6266), .Z(n2834)); + OR2 U6173 ( .A(n3136), .B(po044), .Z(n6265)); + IV2 U6174 ( .A(pi088), .Z(n3136)); + OR2 U6175 ( .A(n6267), .B(n6268), .Z(n3146)); + AN2 U6176 ( .A(n2880), .B(pi192), .Z(n6268)); + IV2 U6177 ( .A(n2878), .Z(n2880)); + OR2 U6178 ( .A(pi077), .B(n2962), .Z(n2878)); + AN2 U6179 ( .A(n2978), .B(n2837), .Z(n6267)); + IV2 U6180 ( .A(n2939), .Z(n2978)); + OR2 U6181 ( .A(n2998), .B(n6269), .Z(n6258)); + OR2 U6182 ( .A(n2901), .B(n2862), .Z(n6269)); + IV2 U6183 ( .A(pi200), .Z(n2901)); + IV2 U6184 ( .A(n3003), .Z(n2925)); + OR2 U6185 ( .A(pi192), .B(n2960), .Z(n3003)); + IV2 U6186 ( .A(n6270), .Z(n2960)); + OR2 U6187 ( .A(n6271), .B(n6272), .Z(n6270)); + AN2 U6188 ( .A(pi081), .B(n2931), .Z(n6272)); + IV2 U6189 ( .A(po107), .Z(n2931)); + AN2 U6190 ( .A(po107), .B(n2982), .Z(n6271)); + AN2 U6191 ( .A(n2837), .B(n6017), .Z(n3055)); + IV2 U6192 ( .A(n5828), .Z(n6017)); + OR2 U6193 ( .A(n6273), .B(n6015), .Z(n5828)); + AN2 U6194 ( .A(pi174), .B(n3049), .Z(n6015)); + AN2 U6195 ( .A(po010), .B(n5115), .Z(n6273)); + IV2 U6196 ( .A(pi174), .Z(n5115)); + AN2 U6197 ( .A(n3049), .B(n3173), .Z(n3071)); + AN2 U6198 ( .A(n2837), .B(pi174), .Z(n3173)); + IV2 U6199 ( .A(po010), .Z(n3049)); + AN2 U6200 ( .A(n6274), .B(n3063), .Z(n6251)); + IV2 U6201 ( .A(n6275), .Z(n3063)); + OR2 U6202 ( .A(n6276), .B(n6277), .Z(n6275)); + AN2 U6203 ( .A(pi033), .B(n3286), .Z(n6277)); + IV2 U6204 ( .A(po099), .Z(n3286)); + AN2 U6205 ( .A(po099), .B(n3171), .Z(n6276)); + IV2 U6206 ( .A(pi033), .Z(n3171)); + AN2 U6207 ( .A(n3064), .B(n5822), .Z(n6274)); + OR2 U6208 ( .A(n6278), .B(n6279), .Z(n5822)); + OR2 U6209 ( .A(n6280), .B(n6281), .Z(n6279)); + AN2 U6210 ( .A(n6282), .B(pi192), .Z(n6281)); + AN2 U6211 ( .A(n5156), .B(n6266), .Z(n6282)); + OR2 U6212 ( .A(pi088), .B(n5000), .Z(n6266)); + OR2 U6213 ( .A(n6283), .B(n5153), .Z(n5156)); + AN2 U6214 ( .A(n2861), .B(n6284), .Z(n6283)); + OR2 U6215 ( .A(n2885), .B(n2892), .Z(n6284)); + AN2 U6216 ( .A(n5008), .B(pi157), .Z(n2892)); + AN2 U6217 ( .A(n2930), .B(n2876), .Z(n2885)); + IV2 U6218 ( .A(n2887), .Z(n2876)); + OR2 U6219 ( .A(po031), .B(n3846), .Z(n2887)); + IV2 U6220 ( .A(pi077), .Z(n3846)); + AN2 U6221 ( .A(n6285), .B(n2837), .Z(n6280)); + AN2 U6222 ( .A(n5175), .B(n6264), .Z(n6285)); + OR2 U6223 ( .A(pi201), .B(n5000), .Z(n6264)); + OR2 U6224 ( .A(n6286), .B(n6287), .Z(n5175)); + AN2 U6225 ( .A(n2861), .B(n6288), .Z(n6286)); + OR2 U6226 ( .A(n6289), .B(n2929), .Z(n6288)); + IV2 U6227 ( .A(n2947), .Z(n2929)); + OR2 U6228 ( .A(po106), .B(n5431), .Z(n2947)); + IV2 U6229 ( .A(pi206), .Z(n5431)); + AN2 U6230 ( .A(n2930), .B(n4113), .Z(n6289)); + OR2 U6231 ( .A(n2919), .B(n2936), .Z(n4113)); + IV2 U6232 ( .A(n2946), .Z(n2936)); + OR2 U6233 ( .A(po031), .B(n2957), .Z(n2946)); + IV2 U6234 ( .A(pi082), .Z(n2957)); + AN2 U6235 ( .A(n2902), .B(n2939), .Z(n2919)); + OR2 U6236 ( .A(pi082), .B(n2962), .Z(n2939)); + IV2 U6237 ( .A(po031), .Z(n2962)); + IV2 U6238 ( .A(n2974), .Z(n2902)); + OR2 U6239 ( .A(po107), .B(n2982), .Z(n2974)); + IV2 U6240 ( .A(pi081), .Z(n2982)); + IV2 U6241 ( .A(n2862), .Z(n2930)); + OR2 U6242 ( .A(n6290), .B(n6291), .Z(n2862)); + AN2 U6243 ( .A(n6292), .B(n5008), .Z(n6291)); + IV2 U6244 ( .A(po106), .Z(n5008)); + AN2 U6245 ( .A(n6293), .B(po106), .Z(n6290)); + IV2 U6246 ( .A(n6292), .Z(n6293)); + OR2 U6247 ( .A(n6294), .B(n6295), .Z(n6292)); + AN2 U6248 ( .A(pi192), .B(pi157), .Z(n6295)); + AN2 U6249 ( .A(pi206), .B(n2837), .Z(n6294)); + IV2 U6250 ( .A(n2998), .Z(n2861)); + OR2 U6251 ( .A(n6296), .B(n6297), .Z(n2998)); + OR2 U6252 ( .A(n6298), .B(n6299), .Z(n6297)); + AN2 U6253 ( .A(n5153), .B(pi192), .Z(n6299)); + AN2 U6254 ( .A(n5004), .B(pi026), .Z(n5153)); + IV2 U6255 ( .A(po079), .Z(n5004)); + AN2 U6256 ( .A(n6287), .B(n2837), .Z(n6298)); + IV2 U6257 ( .A(n5172), .Z(n6287)); + OR2 U6258 ( .A(po079), .B(n5165), .Z(n5172)); + AN2 U6259 ( .A(po079), .B(n6027), .Z(n6296)); + OR2 U6260 ( .A(n6300), .B(n6301), .Z(n6027)); + AN2 U6261 ( .A(pi192), .B(n3847), .Z(n6301)); + IV2 U6262 ( .A(pi026), .Z(n3847)); + AN2 U6263 ( .A(n5165), .B(n2837), .Z(n6300)); + IV2 U6264 ( .A(pi107), .Z(n5165)); + AN2 U6265 ( .A(n3011), .B(n5000), .Z(n6278)); + IV2 U6266 ( .A(po044), .Z(n5000)); + OR2 U6267 ( .A(n6302), .B(n6303), .Z(n3011)); + AN2 U6268 ( .A(pi088), .B(pi192), .Z(n6303)); + AN2 U6269 ( .A(pi201), .B(n2837), .Z(n6302)); + AN2 U6270 ( .A(pi192), .B(pi166), .Z(n3064)); + AN2 U6271 ( .A(n3018), .B(n5984), .Z(n5985)); + AN2 U6272 ( .A(n6304), .B(n5104), .Z(n3018)); + IV2 U6273 ( .A(n5074), .Z(n5104)); + OR2 U6274 ( .A(n6305), .B(n5040), .Z(n6304)); + OR2 U6275 ( .A(n6306), .B(n6307), .Z(n6211)); + OR2 U6276 ( .A(n6308), .B(n6309), .Z(n6307)); + AN2 U6277 ( .A(n4624), .B(n5981), .Z(n6309)); + AN2 U6278 ( .A(n3300), .B(n3305), .Z(n4624)); + AN2 U6279 ( .A(n5984), .B(n5074), .Z(n6308)); + AN2 U6280 ( .A(n5040), .B(n6305), .Z(n5074)); + OR2 U6281 ( .A(n6310), .B(n6311), .Z(n6305)); + AN2 U6282 ( .A(pi096), .B(pi192), .Z(n6311)); + AN2 U6283 ( .A(pi128), .B(n2837), .Z(n6310)); + IV2 U6284 ( .A(po070), .Z(n5040)); + AN2 U6285 ( .A(n3083), .B(n6225), .Z(n5984)); + AN2 U6286 ( .A(n6312), .B(n6313), .Z(n6225)); + AN2 U6287 ( .A(n3760), .B(n4341), .Z(n6313)); + AN2 U6288 ( .A(n4333), .B(n4482), .Z(n6312)); + OR2 U6289 ( .A(n6314), .B(n6315), .Z(n4333)); + AN2 U6290 ( .A(po025), .B(n6316), .Z(n6315)); + OR2 U6291 ( .A(n6317), .B(n3888), .Z(n6316)); + AN2 U6292 ( .A(pi192), .B(pi191), .Z(n3888)); + AN2 U6293 ( .A(pi076), .B(n2837), .Z(n6317)); + AN2 U6294 ( .A(n3781), .B(n4557), .Z(n6314)); + OR2 U6295 ( .A(n4538), .B(n3890), .Z(n3781)); + IV2 U6296 ( .A(n6318), .Z(n3890)); + OR2 U6297 ( .A(pi191), .B(n2837), .Z(n6318)); + AN2 U6298 ( .A(n4077), .B(n2837), .Z(n4538)); + IV2 U6299 ( .A(n3021), .Z(n3083)); + OR2 U6300 ( .A(n6319), .B(n6134), .Z(n3021)); + AN2 U6301 ( .A(n6320), .B(n5037), .Z(n6134)); + IV2 U6302 ( .A(po035), .Z(n5037)); + AN2 U6303 ( .A(n6321), .B(po035), .Z(n6319)); + IV2 U6304 ( .A(n6320), .Z(n6321)); + OR2 U6305 ( .A(n6322), .B(n6323), .Z(n6320)); + AN2 U6306 ( .A(pi175), .B(pi192), .Z(n6323)); + AN2 U6307 ( .A(pi185), .B(n2837), .Z(n6322)); + AN2 U6308 ( .A(n3760), .B(n6324), .Z(n6306)); + OR2 U6309 ( .A(n6325), .B(n6326), .Z(n6324)); + OR2 U6310 ( .A(n6327), .B(n6328), .Z(n6326)); + AN2 U6311 ( .A(n4482), .B(n4340), .Z(n6328)); + OR2 U6312 ( .A(n6329), .B(n6330), .Z(n4340)); + AN2 U6313 ( .A(n6121), .B(n2837), .Z(n6330)); + OR2 U6314 ( .A(n6331), .B(n4567), .Z(n6121)); + IV2 U6315 ( .A(n4578), .Z(n4567)); + OR2 U6316 ( .A(po024), .B(n4358), .Z(n4578)); + AN2 U6317 ( .A(n4084), .B(n4066), .Z(n6331)); + OR2 U6318 ( .A(n6332), .B(n4539), .Z(n4084)); + IV2 U6319 ( .A(n5503), .Z(n4539)); + OR2 U6320 ( .A(po059), .B(n4074), .Z(n5503)); + IV2 U6321 ( .A(pi170), .Z(n4074)); + AN2 U6322 ( .A(n4576), .B(n3778), .Z(n6332)); + IV2 U6323 ( .A(n4580), .Z(n4576)); + OR2 U6324 ( .A(po025), .B(n4077), .Z(n4580)); + IV2 U6325 ( .A(pi076), .Z(n4077)); + AN2 U6326 ( .A(pi192), .B(n6128), .Z(n6329)); + OR2 U6327 ( .A(n6333), .B(n6334), .Z(n6128)); + OR2 U6328 ( .A(n4561), .B(n6335), .Z(n6334)); + AN2 U6329 ( .A(n6336), .B(n4341), .Z(n6335)); + AN2 U6330 ( .A(n4066), .B(n3778), .Z(n4341)); + IV2 U6331 ( .A(n3773), .Z(n3778)); + OR2 U6332 ( .A(n6337), .B(n6338), .Z(n3773)); + AN2 U6333 ( .A(n6339), .B(n4877), .Z(n6338)); + IV2 U6334 ( .A(po059), .Z(n4877)); + AN2 U6335 ( .A(n6340), .B(po059), .Z(n6337)); + IV2 U6336 ( .A(n6339), .Z(n6340)); + OR2 U6337 ( .A(n6341), .B(n6342), .Z(n6339)); + AN2 U6338 ( .A(pi135), .B(pi192), .Z(n6342)); + AN2 U6339 ( .A(pi170), .B(n2837), .Z(n6341)); + AN2 U6340 ( .A(pi191), .B(n4557), .Z(n6336)); + IV2 U6341 ( .A(po025), .Z(n4557)); + IV2 U6342 ( .A(n4348), .Z(n4561)); + OR2 U6343 ( .A(po024), .B(n3896), .Z(n4348)); + AN2 U6344 ( .A(n4085), .B(n4066), .Z(n6333)); + OR2 U6345 ( .A(n6343), .B(n6344), .Z(n4066)); + OR2 U6346 ( .A(n6345), .B(n6346), .Z(n6344)); + AN2 U6347 ( .A(n6347), .B(po024), .Z(n6346)); + AN2 U6348 ( .A(pi005), .B(pi192), .Z(n6347)); + AN2 U6349 ( .A(n6348), .B(n4874), .Z(n6345)); + IV2 U6350 ( .A(po024), .Z(n4874)); + OR2 U6351 ( .A(n6349), .B(n5483), .Z(n6348)); + AN2 U6352 ( .A(pi192), .B(n3896), .Z(n5483)); + IV2 U6353 ( .A(pi005), .Z(n3896)); + AN2 U6354 ( .A(n4358), .B(n2837), .Z(n6349)); + IV2 U6355 ( .A(pi160), .Z(n4358)); + AN2 U6356 ( .A(pi160), .B(n5477), .Z(n6343)); + AN2 U6357 ( .A(n2837), .B(po024), .Z(n5477)); + IV2 U6358 ( .A(n4353), .Z(n4085)); + OR2 U6359 ( .A(po059), .B(n3880), .Z(n4353)); + IV2 U6360 ( .A(pi135), .Z(n3880)); + AN2 U6361 ( .A(n4342), .B(n4475), .Z(n4482)); + AN2 U6362 ( .A(n6350), .B(n6351), .Z(n4342)); + OR2 U6363 ( .A(n6131), .B(po102), .Z(n6351)); + IV2 U6364 ( .A(n6352), .Z(n6131)); + OR2 U6365 ( .A(n6352), .B(n4885), .Z(n6350)); + AN2 U6366 ( .A(n5462), .B(n4475), .Z(n6327)); + IV2 U6367 ( .A(n4478), .Z(n4475)); + OR2 U6368 ( .A(n6353), .B(n6325), .Z(n4478)); + AN2 U6369 ( .A(n6354), .B(po072), .Z(n6353)); + IV2 U6370 ( .A(n6355), .Z(n6354)); + AN2 U6371 ( .A(n6352), .B(n4885), .Z(n5462)); + IV2 U6372 ( .A(po102), .Z(n4885)); + OR2 U6373 ( .A(n6356), .B(n6357), .Z(n6352)); + AN2 U6374 ( .A(pi069), .B(pi192), .Z(n6357)); + AN2 U6375 ( .A(pi114), .B(n2837), .Z(n6356)); + AN2 U6376 ( .A(n6355), .B(n4881), .Z(n6325)); + IV2 U6377 ( .A(po072), .Z(n4881)); + OR2 U6378 ( .A(n6358), .B(n6359), .Z(n6355)); + AN2 U6379 ( .A(pi148), .B(pi192), .Z(n6359)); + AN2 U6380 ( .A(pi108), .B(n2837), .Z(n6358)); + AN2 U6381 ( .A(n4450), .B(n5981), .Z(n3760)); + AN2 U6382 ( .A(n3301), .B(n4751), .Z(n5981)); + AN2 U6383 ( .A(n4006), .B(n3757), .Z(n4751)); + IV2 U6384 ( .A(n4444), .Z(n3757)); + OR2 U6385 ( .A(n6360), .B(n3754), .Z(n4444)); + AN2 U6386 ( .A(n6361), .B(n4915), .Z(n3754)); + IV2 U6387 ( .A(po063), .Z(n4915)); + AN2 U6388 ( .A(n6362), .B(po063), .Z(n6360)); + IV2 U6389 ( .A(n6361), .Z(n6362)); + OR2 U6390 ( .A(n6363), .B(n6364), .Z(n6361)); + AN2 U6391 ( .A(pi045), .B(pi192), .Z(n6364)); + AN2 U6392 ( .A(pi095), .B(n2837), .Z(n6363)); + AN2 U6393 ( .A(n6365), .B(n6366), .Z(n4006)); + OR2 U6394 ( .A(n6367), .B(po092), .Z(n6366)); + IV2 U6395 ( .A(n4752), .Z(n6367)); + OR2 U6396 ( .A(n4752), .B(n4628), .Z(n6365)); + IV2 U6397 ( .A(po092), .Z(n4628)); + OR2 U6398 ( .A(n6368), .B(n6369), .Z(n4752)); + AN2 U6399 ( .A(pi158), .B(pi192), .Z(n6369)); + AN2 U6400 ( .A(pi151), .B(n2837), .Z(n6368)); + IV2 U6401 ( .A(n3295), .Z(n3301)); + OR2 U6402 ( .A(n6370), .B(n6371), .Z(n3295)); + AN2 U6403 ( .A(n4703), .B(n4861), .Z(n6371)); + IV2 U6404 ( .A(po014), .Z(n4861)); + AN2 U6405 ( .A(n4736), .B(po014), .Z(n6370)); + IV2 U6406 ( .A(n4703), .Z(n4736)); + OR2 U6407 ( .A(n6372), .B(n6373), .Z(n4703)); + AN2 U6408 ( .A(pi079), .B(pi192), .Z(n6373)); + AN2 U6409 ( .A(pi156), .B(n2837), .Z(n6372)); + IV2 U6410 ( .A(n4606), .Z(n4450)); + AN2 U6411 ( .A(n6374), .B(n6375), .Z(n4606)); + OR2 U6412 ( .A(n3305), .B(po039), .Z(n6375)); + OR2 U6413 ( .A(n3300), .B(n6376), .Z(n6374)); + IV2 U6414 ( .A(n3305), .Z(n6376)); + OR2 U6415 ( .A(n4685), .B(n4692), .Z(n3305)); + AN2 U6416 ( .A(pi192), .B(pi016), .Z(n4692)); + AN2 U6417 ( .A(n2837), .B(pi040), .Z(n4685)); + IV2 U6418 ( .A(po039), .Z(n3300)); + OR2 U6419 ( .A(n3352), .B(n3344), .Z(n6202)); + AN2 U6420 ( .A(n6377), .B(n6378), .Z(n3352)); + OR2 U6421 ( .A(n6179), .B(po001), .Z(n6378)); + IV2 U6422 ( .A(n6379), .Z(n6179)); + OR2 U6423 ( .A(n6379), .B(n4937), .Z(n6377)); + IV2 U6424 ( .A(po001), .Z(n4937)); + OR2 U6425 ( .A(n5959), .B(n6380), .Z(n6379)); + AN2 U6426 ( .A(pi192), .B(n3998), .Z(n6380)); + IV2 U6427 ( .A(pi068), .Z(n3998)); + AN2 U6428 ( .A(n2837), .B(n5379), .Z(n5959)); + AN2 U6429 ( .A(n6381), .B(n6382), .Z(n6205)); + OR2 U6430 ( .A(n3344), .B(n3590), .Z(n6382)); + OR2 U6431 ( .A(po001), .B(n5379), .Z(n3590)); + IV2 U6432 ( .A(pi195), .Z(n5379)); + OR2 U6433 ( .A(n6383), .B(n3248), .Z(n3344)); + AN2 U6434 ( .A(n6384), .B(n4947), .Z(n3248)); + IV2 U6435 ( .A(po082), .Z(n4947)); + AN2 U6436 ( .A(n6385), .B(po082), .Z(n6383)); + IV2 U6437 ( .A(n6384), .Z(n6385)); + OR2 U6438 ( .A(n6386), .B(n6387), .Z(n6384)); + AN2 U6439 ( .A(pi004), .B(pi192), .Z(n6387)); + AN2 U6440 ( .A(pi130), .B(n2837), .Z(n6386)); + OR2 U6441 ( .A(po082), .B(n5372), .Z(n6381)); + IV2 U6442 ( .A(pi130), .Z(n5372)); + AN2 U6443 ( .A(n3225), .B(n6053), .Z(n5931)); + IV2 U6444 ( .A(n3247), .Z(n3225)); + OR2 U6445 ( .A(n6388), .B(n3490), .Z(n3247)); + AN2 U6446 ( .A(n4943), .B(n5895), .Z(n3490)); + OR2 U6447 ( .A(n3926), .B(n3989), .Z(n5895)); + IV2 U6448 ( .A(n6389), .Z(n6388)); + OR2 U6449 ( .A(n4943), .B(n6390), .Z(n6389)); + AN2 U6450 ( .A(n6391), .B(n3261), .Z(n6390)); + OR2 U6451 ( .A(n2837), .B(pi133), .Z(n6391)); + AN2 U6452 ( .A(n6392), .B(n6053), .Z(n6169)); + AN2 U6453 ( .A(n3393), .B(n6393), .Z(n6053)); + IV2 U6454 ( .A(n3461), .Z(n6393)); + OR2 U6455 ( .A(n3401), .B(n3412), .Z(n3461)); + OR2 U6456 ( .A(n6394), .B(n5853), .Z(n3412)); + OR2 U6457 ( .A(n3466), .B(n3392), .Z(n5853)); + AN2 U6458 ( .A(n3380), .B(n3926), .Z(n3392)); + IV2 U6459 ( .A(po071), .Z(n3380)); + AN2 U6460 ( .A(n3261), .B(n3381), .Z(n3466)); + IV2 U6461 ( .A(n3399), .Z(n3381)); + OR2 U6462 ( .A(po071), .B(n3913), .Z(n3399)); + AN2 U6463 ( .A(po071), .B(n5878), .Z(n6394)); + OR2 U6464 ( .A(n3480), .B(n3321), .Z(n5878)); + AN2 U6465 ( .A(n3913), .B(pi192), .Z(n3480)); + IV2 U6466 ( .A(pi118), .Z(n3913)); + OR2 U6467 ( .A(n5881), .B(n6395), .Z(n3401)); + OR2 U6468 ( .A(n6168), .B(n6396), .Z(n6395)); + AN2 U6469 ( .A(po104), .B(n3321), .Z(n6396)); + AN2 U6470 ( .A(n3485), .B(n3261), .Z(n6168)); + IV2 U6471 ( .A(n3419), .Z(n3485)); + OR2 U6472 ( .A(n6397), .B(po104), .Z(n3419)); + AN2 U6473 ( .A(pi192), .B(n3942), .Z(n6397)); + IV2 U6474 ( .A(pi196), .Z(n3942)); + AN2 U6475 ( .A(pi192), .B(n5872), .Z(n5881)); + IV2 U6476 ( .A(n5866), .Z(n5872)); + OR2 U6477 ( .A(pi196), .B(n3512), .Z(n5866)); + IV2 U6478 ( .A(po104), .Z(n3512)); + OR2 U6479 ( .A(n6398), .B(n6399), .Z(n3393)); + AN2 U6480 ( .A(n6400), .B(n4975), .Z(n6399)); + IV2 U6481 ( .A(po038), .Z(n4975)); + OR2 U6482 ( .A(n6401), .B(n3321), .Z(n6400)); + AN2 U6483 ( .A(pi192), .B(n3943), .Z(n6401)); + IV2 U6484 ( .A(pi050), .Z(n3943)); + AN2 U6485 ( .A(po038), .B(n6402), .Z(n6398)); + OR2 U6486 ( .A(n3940), .B(n3926), .Z(n6402)); + AN2 U6487 ( .A(n3261), .B(pi050), .Z(n3940)); + AN2 U6488 ( .A(n6403), .B(n4943), .Z(n6392)); + IV2 U6489 ( .A(po057), .Z(n4943)); + OR2 U6490 ( .A(n6404), .B(n6052), .Z(n6403)); + OR2 U6491 ( .A(n6405), .B(n3926), .Z(n6052)); + IV2 U6492 ( .A(n6155), .Z(n3926)); + OR2 U6493 ( .A(pi192), .B(n3321), .Z(n6155)); + AN2 U6494 ( .A(pi060), .B(n3989), .Z(n6405)); + AN2 U6495 ( .A(n3989), .B(n3363), .Z(n6404)); + IV2 U6496 ( .A(po027), .Z(n3363)); + AN2 U6497 ( .A(n3261), .B(pi133), .Z(n3989)); + IV2 U6498 ( .A(n3321), .Z(n3261)); + IV2 U6499 ( .A(n6149), .Z(n4217)); + OR2 U6500 ( .A(n4148), .B(n3321), .Z(n6149)); + AN2 U6501 ( .A(pi161), .B(pi012), .Z(n3321)); + IV2 U6502 ( .A(n4247), .Z(n4148)); + OR2 U6503 ( .A(pi058), .B(n2837), .Z(n4247)); + IV2 U6504 ( .A(pi192), .Z(n2837)); + +endmodule + +module IV2(A, Z); + input A; + output Z; + + assign Z = ~A; +endmodule + +module AN2(A, B, Z); + input A, B; + output Z; + + assign Z = A & B; +endmodule + +module OR2(A, B, Z); + input A, B; + output Z; + + assign Z = A | B; +endmodule diff --git a/examples/smtbmc/glift/C7552.ys b/examples/smtbmc/glift/C7552.ys new file mode 100644 index 00000000000..a9a1f5dc26c --- /dev/null +++ b/examples/smtbmc/glift/C7552.ys @@ -0,0 +1,41 @@ +read_verilog C7552.v +techmap +flatten +select C7552_lev2 +glift -create-instrumented-model +techmap +opt +rename C7552_lev2 uut +cd .. +delete [AIONX][NVXR]2 +read_verilog C7552.v +techmap +flatten +select C7552_lev2 +glift -create-precise-model +techmap +opt +rename C7552_lev2 spec +cd .. +delete [AIONX][NVXR]2 + +design -push-copy +miter -equiv spec uut miter +flatten +delete uut spec +techmap +opt +stat miter +qbfsat -O2 -write-solution C7552.soln -solver yices -timeout 3600 -nocleanup -assume-outputs -assume-negative-polarity miter +design -pop +stat + +copy uut solved +qbfsat -specialize-from-file C7552.soln solved +opt solved +miter -equiv spec solved satmiter +flatten +sat -prove trigger 0 satmiter +delete satmiter +stat +shell diff --git a/examples/smtbmc/glift/C880.v b/examples/smtbmc/glift/C880.v new file mode 100755 index 00000000000..18dc24cc53c --- /dev/null +++ b/examples/smtbmc/glift/C880.v @@ -0,0 +1,451 @@ +module C880_lev2(pi00, pi01, pi02, pi03, pi04, pi05, pi06, pi07, pi08, pi09, + pi10, pi11, pi12, pi13, pi14, pi15, pi16, pi17, pi18, pi19, + pi20, pi21, pi22, pi23, pi24, pi25, pi26, pi27, pi28, pi29, + pi30, pi31, pi32, pi33, pi34, pi35, pi36, pi37, pi38, pi39, + pi40, pi41, pi42, pi43, pi44, pi45, pi46, pi47, pi48, pi49, + pi50, pi51, pi52, pi53, pi54, pi55, pi56, pi57, pi58, pi59, + po00, po01, po02, po03, po04, po05, po06, po07, po08, po09, + po10, po11, po12, po13, po14, po15, po16, po17, po18, po19, + po20, po21, po22, po23, po24, po25); + +input pi00, pi01, pi02, pi03, pi04, pi05, pi06, pi07, pi08, pi09, + pi10, pi11, pi12, pi13, pi14, pi15, pi16, pi17, pi18, pi19, + pi20, pi21, pi22, pi23, pi24, pi25, pi26, pi27, pi28, pi29, + pi30, pi31, pi32, pi33, pi34, pi35, pi36, pi37, pi38, pi39, + pi40, pi41, pi42, pi43, pi44, pi45, pi46, pi47, pi48, pi49, + pi50, pi51, pi52, pi53, pi54, pi55, pi56, pi57, pi58, pi59; + +output po00, po01, po02, po03, po04, po05, po06, po07, po08, po09, + po10, po11, po12, po13, po14, po15, po16, po17, po18, po19, + po20, po21, po22, po23, po24, po25; + +wire n137, n346, n364, n415, n295, n427, n351, n377, n454, n357, + n358, n359, n360, n361, n362, n363, n365, n366, n367, n368, + n369, n370, n371, n372, n373, n374, n375, n376, n378, n379, + n380, n381, n382, n383, n384, n385, n386, n387, n388, n389, + n390, n391, n392, n393, n394, n395, n396, n397, n398, n399, + n400, n401, n402, n403, n404, n405, n406, n407, n408, n409, + n410, n411, n412, n413, n414, n416, n417, n418, n419, n420, + n421, n422, n423, n424, n425, n426, n428, n429, n430, n431, + n432, n433, n434, n435, n436, n437, n438, n439, n440, n441, + n442, n443, n444, n445, n446, n447, n448, n449, n450, n451, + n452, n453, n455, n456, n457, n458, n459, n460, n461, n462, + n463, n464, n465, n466, n467, n468, n469, n470, n471, n472, + n473, n474, n475, n476, n477, n478, n479, n480, n481, n482, + n483, n484, n485, n486, n487, n488, n489, n490, n491, n492, + n493, n494, n495, n496, n497, n498, n499, n500, n501, n502, + n503, n504, n505, n506, n507, n508, n509, n510, n511, n512, + n513, n514, n515, n516, n517, n518, n519, n520, n521, n522, + n523, n524, n525, n526, n527, n528, n529, n530, n531, n532, + n533, n534, n535, n536, n537, n538, n539, n540, n541, n542, + n543, n544, n545, n546, n547, n548, n549, n550, n551, n552, + n553, n554, n555, n556, n557, n558, n559, n560, n561, n562, + n563, n564, n565, n566, n567, n568, n569, n570, n571, n572, + n573, n574, n575, n576, n577, n578, n579, n580, n581, n582, + n583, n584, n585, n586, n587, n588, n589, n590, n591, n592, + n593, n594, n595, n596, n597, n598, n599, n600, n601, n602, + n603, n604, n605, n606, n607, n608, n609, n610, n611, n612, + n613, n614, n615, n616, n617, n618, n619, n620, n621, n622, + n623, n624, n625, n626, n627, n628, n629, n630, n631, n632, + n633, n634, n635, n636, n637, n638, n639, n640, n641, n642, + n643, n644, n645, n646, n647, n648, n649, n650, n651, n652, + n653, n654, n655, n656, n657, n658, n659, n660, n661, n662, + n663, n664, n665, n666, n667, n668, n669, n670, n671, n672, + n673, n674, n675, n676, n677, n678, n679, n680, n681, n682, + n683, n684, n685, n686, n687, n688, n689, n690, n691, n692, + n693, n694, n695, n696; + + +assign po22 = n137; +assign po19 = n346; +assign po16 = n364; +assign po17 = n415; +assign po18 = n295; +assign po00 = n427; +assign po09 = n351; +assign po04 = n377; +assign po06 = n454; + AN2 U371 ( .A(pi11), .B(pi08), .Z(n357)); + AN2 U372 ( .A(pi28), .B(n357), .Z(n346)); + AN2 U373 ( .A(pi41), .B(pi25), .Z(n369)); + AN2 U374 ( .A(pi52), .B(n369), .Z(n361)); + AN2 U375 ( .A(pi51), .B(pi54), .Z(n359)); + AN2 U376 ( .A(pi28), .B(pi31), .Z(n604)); + AN2 U377 ( .A(n604), .B(pi55), .Z(n358)); + AN2 U378 ( .A(n359), .B(n358), .Z(n602)); + AN2 U379 ( .A(pi53), .B(n602), .Z(n360)); + AN2 U380 ( .A(n361), .B(n360), .Z(n577)); + IV2 U381 ( .A(pi20), .Z(n607)); + OR2 U382 ( .A(n607), .B(pi25), .Z(n362)); + IV2 U383 ( .A(n362), .Z(n365)); + AN2 U384 ( .A(pi25), .B(n607), .Z(n363)); + OR2 U385 ( .A(n365), .B(n363), .Z(n367)); + AN2 U386 ( .A(pi41), .B(pi24), .Z(n378)); + AN2 U387 ( .A(n346), .B(n378), .Z(n366)); + AN2 U388 ( .A(n367), .B(n366), .Z(n373)); + AN2 U389 ( .A(pi28), .B(pi54), .Z(n368)); + AN2 U390 ( .A(pi20), .B(n368), .Z(n603)); + AN2 U391 ( .A(pi08), .B(n603), .Z(n371)); + IV2 U392 ( .A(pi56), .Z(n694)); + IV2 U393 ( .A(n369), .Z(n692)); + OR2 U394 ( .A(n694), .B(n692), .Z(n370)); + AN2 U395 ( .A(n371), .B(n370), .Z(n372)); + OR2 U396 ( .A(n373), .B(n372), .Z(n424)); + AN2 U397 ( .A(pi14), .B(n424), .Z(n384)); + AN2 U398 ( .A(pi56), .B(pi48), .Z(n608)); + AN2 U399 ( .A(n608), .B(n346), .Z(n374)); + AN2 U400 ( .A(pi07), .B(n374), .Z(n376)); + IV2 U401 ( .A(pi43), .Z(n375)); + AN2 U402 ( .A(n376), .B(n375), .Z(n406)); + AN2 U403 ( .A(pi20), .B(n406), .Z(n403)); + IV2 U404 ( .A(n378), .Z(n379)); + AN2 U405 ( .A(n346), .B(n379), .Z(n407)); + AN2 U406 ( .A(pi55), .B(n407), .Z(n399)); + AN2 U407 ( .A(pi44), .B(n399), .Z(n381)); + AN2 U408 ( .A(pi37), .B(pi54), .Z(n380)); + OR2 U409 ( .A(n381), .B(n380), .Z(n382)); + OR2 U410 ( .A(n403), .B(n382), .Z(n383)); + OR2 U411 ( .A(n384), .B(n383), .Z(n436)); + AN2 U412 ( .A(pi26), .B(n436), .Z(n385)); + OR2 U413 ( .A(n577), .B(n385), .Z(n386)); + AN2 U414 ( .A(pi34), .B(n386), .Z(n448)); + AN2 U415 ( .A(pi43), .B(pi05), .Z(n388)); + AN2 U416 ( .A(pi32), .B(n436), .Z(n387)); + OR2 U417 ( .A(n388), .B(n387), .Z(n446)); + AN2 U418 ( .A(pi08), .B(pi37), .Z(n393)); + AN2 U419 ( .A(pi50), .B(n399), .Z(n390)); + AN2 U420 ( .A(pi18), .B(n424), .Z(n389)); + OR2 U421 ( .A(n390), .B(n389), .Z(n391)); + OR2 U422 ( .A(n403), .B(n391), .Z(n392)); + OR2 U423 ( .A(n393), .B(n392), .Z(n494)); + AN2 U424 ( .A(pi27), .B(n494), .Z(n497)); + OR2 U425 ( .A(pi27), .B(n494), .Z(n499)); + AN2 U426 ( .A(pi20), .B(pi37), .Z(n398)); + AN2 U427 ( .A(pi45), .B(n399), .Z(n395)); + AN2 U428 ( .A(pi22), .B(n424), .Z(n394)); + OR2 U429 ( .A(n395), .B(n394), .Z(n396)); + OR2 U430 ( .A(n403), .B(n396), .Z(n397)); + OR2 U431 ( .A(n398), .B(n397), .Z(n536)); + AN2 U432 ( .A(pi17), .B(n536), .Z(n539)); + OR2 U433 ( .A(pi17), .B(n536), .Z(n541)); + AN2 U434 ( .A(pi23), .B(n424), .Z(n405)); + AN2 U435 ( .A(pi30), .B(pi37), .Z(n401)); + AN2 U436 ( .A(pi29), .B(n399), .Z(n400)); + OR2 U437 ( .A(n401), .B(n400), .Z(n402)); + OR2 U438 ( .A(n403), .B(n402), .Z(n404)); + OR2 U439 ( .A(n405), .B(n404), .Z(n579)); + AN2 U440 ( .A(pi21), .B(n579), .Z(n582)); + OR2 U441 ( .A(pi21), .B(n579), .Z(n584)); + AN2 U442 ( .A(n406), .B(pi55), .Z(n429)); + IV2 U443 ( .A(pi28), .Z(n409)); + AN2 U444 ( .A(n407), .B(pi20), .Z(n408)); + OR2 U445 ( .A(n409), .B(n408), .Z(n423)); + AN2 U446 ( .A(pi50), .B(n423), .Z(n411)); + AN2 U447 ( .A(pi42), .B(n424), .Z(n410)); + OR2 U448 ( .A(n411), .B(n410), .Z(n412)); + OR2 U449 ( .A(n429), .B(n412), .Z(n514)); + AN2 U450 ( .A(pi15), .B(n514), .Z(n517)); + OR2 U451 ( .A(pi15), .B(n514), .Z(n519)); + AN2 U452 ( .A(pi45), .B(n423), .Z(n414)); + AN2 U453 ( .A(pi40), .B(n424), .Z(n413)); + OR2 U454 ( .A(n414), .B(n413), .Z(n416)); + OR2 U455 ( .A(n429), .B(n416), .Z(n556)); + AN2 U456 ( .A(pi03), .B(n556), .Z(n559)); + OR2 U457 ( .A(pi03), .B(n556), .Z(n561)); + AN2 U458 ( .A(pi29), .B(n423), .Z(n418)); + AN2 U459 ( .A(pi04), .B(n424), .Z(n417)); + OR2 U460 ( .A(n418), .B(n417), .Z(n419)); + OR2 U461 ( .A(n429), .B(n419), .Z(n471)); + AN2 U462 ( .A(pi10), .B(n471), .Z(n480)); + OR2 U463 ( .A(pi10), .B(n471), .Z(n482)); + AN2 U464 ( .A(pi46), .B(n482), .Z(n420)); + OR2 U465 ( .A(n480), .B(n420), .Z(n562)); + AN2 U466 ( .A(n561), .B(n562), .Z(n421)); + OR2 U467 ( .A(n559), .B(n421), .Z(n520)); + AN2 U468 ( .A(n519), .B(n520), .Z(n422)); + OR2 U469 ( .A(n517), .B(n422), .Z(n449)); + AN2 U470 ( .A(pi44), .B(n423), .Z(n426)); + AN2 U471 ( .A(pi49), .B(n424), .Z(n425)); + OR2 U472 ( .A(n426), .B(n425), .Z(n428)); + OR2 U473 ( .A(n429), .B(n428), .Z(n464)); + AN2 U474 ( .A(n449), .B(n464), .Z(n432)); + OR2 U475 ( .A(n449), .B(n464), .Z(n430)); + AN2 U476 ( .A(pi09), .B(n430), .Z(n431)); + OR2 U477 ( .A(n432), .B(n431), .Z(n585)); + AN2 U478 ( .A(n584), .B(n585), .Z(n433)); + OR2 U479 ( .A(n582), .B(n433), .Z(n542)); + AN2 U480 ( .A(n541), .B(n542), .Z(n434)); + OR2 U481 ( .A(n539), .B(n434), .Z(n500)); + AN2 U482 ( .A(n499), .B(n500), .Z(n435)); + OR2 U483 ( .A(n497), .B(n435), .Z(n597)); + OR2 U484 ( .A(pi34), .B(n436), .Z(n598)); + AN2 U485 ( .A(n436), .B(pi34), .Z(n600)); + IV2 U486 ( .A(n600), .Z(n437)); + AN2 U487 ( .A(n598), .B(n437), .Z(n442)); + OR2 U488 ( .A(n597), .B(n442), .Z(n440)); + AN2 U489 ( .A(n597), .B(n442), .Z(n438)); + IV2 U490 ( .A(n438), .Z(n439)); + AN2 U491 ( .A(n440), .B(n439), .Z(n441)); + AN2 U492 ( .A(pi12), .B(n441), .Z(n444)); + AN2 U493 ( .A(n442), .B(pi19), .Z(n443)); + OR2 U494 ( .A(n444), .B(n443), .Z(n445)); + OR2 U495 ( .A(n446), .B(n445), .Z(n447)); + OR2 U496 ( .A(n448), .B(n447), .Z(n137)); + IV2 U497 ( .A(n464), .Z(n459)); + AN2 U498 ( .A(pi12), .B(n449), .Z(n456)); + AN2 U499 ( .A(n459), .B(n456), .Z(n453)); + IV2 U500 ( .A(n449), .Z(n450)); + AN2 U501 ( .A(n450), .B(pi12), .Z(n451)); + OR2 U502 ( .A(pi19), .B(n451), .Z(n458)); + AN2 U503 ( .A(n464), .B(n458), .Z(n452)); + OR2 U504 ( .A(n453), .B(n452), .Z(n455)); + IV2 U505 ( .A(pi09), .Z(n612)); + AN2 U506 ( .A(n455), .B(n612), .Z(n470)); + OR2 U507 ( .A(pi26), .B(n456), .Z(n457)); + AN2 U508 ( .A(n464), .B(n457), .Z(n462)); + AN2 U509 ( .A(n459), .B(n458), .Z(n460)); + OR2 U510 ( .A(n577), .B(n460), .Z(n461)); + OR2 U511 ( .A(n462), .B(n461), .Z(n463)); + AN2 U512 ( .A(pi09), .B(n463), .Z(n468)); + AN2 U513 ( .A(pi23), .B(pi05), .Z(n466)); + AN2 U514 ( .A(pi32), .B(n464), .Z(n465)); + OR2 U515 ( .A(n466), .B(n465), .Z(n467)); + OR2 U516 ( .A(n468), .B(n467), .Z(n469)); + OR2 U517 ( .A(n470), .B(n469), .Z(n295)); + AN2 U518 ( .A(pi26), .B(n480), .Z(n479)); + AN2 U519 ( .A(pi40), .B(pi05), .Z(n473)); + AN2 U520 ( .A(pi32), .B(n471), .Z(n472)); + OR2 U521 ( .A(n473), .B(n472), .Z(n477)); + AN2 U522 ( .A(pi38), .B(pi36), .Z(n475)); + AN2 U523 ( .A(pi10), .B(n577), .Z(n474)); + OR2 U524 ( .A(n475), .B(n474), .Z(n476)); + OR2 U525 ( .A(n477), .B(n476), .Z(n478)); + OR2 U526 ( .A(n479), .B(n478), .Z(n491)); + IV2 U527 ( .A(n480), .Z(n481)); + AN2 U528 ( .A(n482), .B(n481), .Z(n487)); + OR2 U529 ( .A(pi46), .B(n487), .Z(n485)); + AN2 U530 ( .A(pi46), .B(n487), .Z(n483)); + IV2 U531 ( .A(n483), .Z(n484)); + AN2 U532 ( .A(n485), .B(n484), .Z(n486)); + AN2 U533 ( .A(pi12), .B(n486), .Z(n489)); + AN2 U534 ( .A(n487), .B(pi19), .Z(n488)); + OR2 U535 ( .A(n489), .B(n488), .Z(n490)); + OR2 U536 ( .A(n491), .B(n490), .Z(n351)); + AN2 U537 ( .A(pi26), .B(n494), .Z(n492)); + OR2 U538 ( .A(n577), .B(n492), .Z(n493)); + AN2 U539 ( .A(pi27), .B(n493), .Z(n511)); + AN2 U540 ( .A(pi14), .B(pi05), .Z(n496)); + AN2 U541 ( .A(pi32), .B(n494), .Z(n495)); + OR2 U542 ( .A(n496), .B(n495), .Z(n509)); + IV2 U543 ( .A(n497), .Z(n498)); + AN2 U544 ( .A(n499), .B(n498), .Z(n505)); + OR2 U545 ( .A(n500), .B(n505), .Z(n503)); + AN2 U546 ( .A(n500), .B(n505), .Z(n501)); + IV2 U547 ( .A(n501), .Z(n502)); + AN2 U548 ( .A(n503), .B(n502), .Z(n504)); + AN2 U549 ( .A(pi12), .B(n504), .Z(n507)); + AN2 U550 ( .A(n505), .B(pi19), .Z(n506)); + OR2 U551 ( .A(n507), .B(n506), .Z(n508)); + OR2 U552 ( .A(n509), .B(n508), .Z(n510)); + OR2 U553 ( .A(n511), .B(n510), .Z(n364)); + AN2 U554 ( .A(pi26), .B(n514), .Z(n512)); + OR2 U555 ( .A(n577), .B(n512), .Z(n513)); + AN2 U556 ( .A(pi15), .B(n513), .Z(n533)); + AN2 U557 ( .A(pi49), .B(pi05), .Z(n531)); + AN2 U558 ( .A(pi33), .B(pi36), .Z(n516)); + AN2 U559 ( .A(pi32), .B(n514), .Z(n515)); + OR2 U560 ( .A(n516), .B(n515), .Z(n529)); + IV2 U561 ( .A(n517), .Z(n518)); + AN2 U562 ( .A(n519), .B(n518), .Z(n525)); + OR2 U563 ( .A(n520), .B(n525), .Z(n523)); + AN2 U564 ( .A(n520), .B(n525), .Z(n521)); + IV2 U565 ( .A(n521), .Z(n522)); + AN2 U566 ( .A(n523), .B(n522), .Z(n524)); + AN2 U567 ( .A(pi12), .B(n524), .Z(n527)); + AN2 U568 ( .A(n525), .B(pi19), .Z(n526)); + OR2 U569 ( .A(n527), .B(n526), .Z(n528)); + OR2 U570 ( .A(n529), .B(n528), .Z(n530)); + OR2 U571 ( .A(n531), .B(n530), .Z(n532)); + OR2 U572 ( .A(n533), .B(n532), .Z(n377)); + AN2 U573 ( .A(pi26), .B(n536), .Z(n534)); + OR2 U574 ( .A(n577), .B(n534), .Z(n535)); + AN2 U575 ( .A(pi17), .B(n535), .Z(n553)); + AN2 U576 ( .A(pi18), .B(pi05), .Z(n538)); + AN2 U577 ( .A(pi32), .B(n536), .Z(n537)); + OR2 U578 ( .A(n538), .B(n537), .Z(n551)); + IV2 U579 ( .A(n539), .Z(n540)); + AN2 U580 ( .A(n541), .B(n540), .Z(n547)); + OR2 U581 ( .A(n542), .B(n547), .Z(n545)); + AN2 U582 ( .A(n542), .B(n547), .Z(n543)); + IV2 U583 ( .A(n543), .Z(n544)); + AN2 U584 ( .A(n545), .B(n544), .Z(n546)); + AN2 U585 ( .A(pi12), .B(n546), .Z(n549)); + AN2 U586 ( .A(n547), .B(pi19), .Z(n548)); + OR2 U587 ( .A(n549), .B(n548), .Z(n550)); + OR2 U588 ( .A(n551), .B(n550), .Z(n552)); + OR2 U589 ( .A(n553), .B(n552), .Z(n415)); + AN2 U590 ( .A(pi26), .B(n556), .Z(n554)); + OR2 U591 ( .A(n577), .B(n554), .Z(n555)); + AN2 U592 ( .A(pi03), .B(n555), .Z(n575)); + AN2 U593 ( .A(pi42), .B(pi05), .Z(n573)); + AN2 U594 ( .A(pi47), .B(pi36), .Z(n558)); + AN2 U595 ( .A(pi32), .B(n556), .Z(n557)); + OR2 U596 ( .A(n558), .B(n557), .Z(n571)); + IV2 U597 ( .A(n559), .Z(n560)); + AN2 U598 ( .A(n561), .B(n560), .Z(n567)); + OR2 U599 ( .A(n562), .B(n567), .Z(n565)); + AN2 U600 ( .A(n562), .B(n567), .Z(n563)); + IV2 U601 ( .A(n563), .Z(n564)); + AN2 U602 ( .A(n565), .B(n564), .Z(n566)); + AN2 U603 ( .A(pi12), .B(n566), .Z(n569)); + AN2 U604 ( .A(n567), .B(pi19), .Z(n568)); + OR2 U605 ( .A(n569), .B(n568), .Z(n570)); + OR2 U606 ( .A(n571), .B(n570), .Z(n572)); + OR2 U607 ( .A(n573), .B(n572), .Z(n574)); + OR2 U608 ( .A(n575), .B(n574), .Z(n427)); + AN2 U609 ( .A(pi26), .B(n579), .Z(n576)); + OR2 U610 ( .A(n577), .B(n576), .Z(n578)); + AN2 U611 ( .A(pi21), .B(n578), .Z(n596)); + AN2 U612 ( .A(pi22), .B(pi05), .Z(n581)); + AN2 U613 ( .A(pi32), .B(n579), .Z(n580)); + OR2 U614 ( .A(n581), .B(n580), .Z(n594)); + IV2 U615 ( .A(n582), .Z(n583)); + AN2 U616 ( .A(n584), .B(n583), .Z(n590)); + OR2 U617 ( .A(n585), .B(n590), .Z(n588)); + AN2 U618 ( .A(n585), .B(n590), .Z(n586)); + IV2 U619 ( .A(n586), .Z(n587)); + AN2 U620 ( .A(n588), .B(n587), .Z(n589)); + AN2 U621 ( .A(pi12), .B(n589), .Z(n592)); + AN2 U622 ( .A(n590), .B(pi19), .Z(n591)); + OR2 U623 ( .A(n592), .B(n591), .Z(n593)); + OR2 U624 ( .A(n594), .B(n593), .Z(n595)); + OR2 U625 ( .A(n596), .B(n595), .Z(n454)); + AN2 U626 ( .A(n598), .B(n597), .Z(n599)); + OR2 U627 ( .A(n600), .B(n599), .Z(po07)); + OR2 U628 ( .A(pi58), .B(pi00), .Z(n609)); + AN2 U629 ( .A(pi59), .B(n609), .Z(po24)); + AN2 U630 ( .A(n602), .B(pi57), .Z(n601)); + AN2 U631 ( .A(pi41), .B(n601), .Z(po13)); + AN2 U632 ( .A(pi48), .B(n602), .Z(po08)); + AN2 U633 ( .A(n603), .B(pi31), .Z(po03)); + AN2 U634 ( .A(pi48), .B(pi16), .Z(n610)); + AN2 U635 ( .A(pi25), .B(n610), .Z(po25)); + AN2 U636 ( .A(pi11), .B(n604), .Z(n605)); + IV2 U637 ( .A(n605), .Z(n606)); + OR2 U638 ( .A(n607), .B(n606), .Z(n691)); + OR2 U639 ( .A(po25), .B(n691), .Z(po02)); + AN2 U640 ( .A(n608), .B(pi25), .Z(po10)); + AN2 U641 ( .A(pi13), .B(n609), .Z(po12)); + AN2 U642 ( .A(pi07), .B(n610), .Z(po14)); + IV2 U643 ( .A(pi15), .Z(n611)); + AN2 U644 ( .A(pi09), .B(n611), .Z(n614)); + AN2 U645 ( .A(pi15), .B(n612), .Z(n613)); + OR2 U646 ( .A(n614), .B(n613), .Z(n618)); + IV2 U647 ( .A(pi35), .Z(n654)); + OR2 U648 ( .A(n654), .B(pi06), .Z(n617)); + IV2 U649 ( .A(pi06), .Z(n615)); + OR2 U650 ( .A(n615), .B(pi35), .Z(n616)); + AN2 U651 ( .A(n617), .B(n616), .Z(n619)); + OR2 U652 ( .A(n618), .B(n619), .Z(n622)); + AN2 U653 ( .A(n619), .B(n618), .Z(n620)); + IV2 U654 ( .A(n620), .Z(n621)); + AN2 U655 ( .A(n622), .B(n621), .Z(n628)); + IV2 U656 ( .A(pi10), .Z(n624)); + OR2 U657 ( .A(n624), .B(pi03), .Z(n623)); + IV2 U658 ( .A(n623), .Z(n626)); + AN2 U659 ( .A(pi03), .B(n624), .Z(n625)); + OR2 U660 ( .A(n626), .B(n625), .Z(n627)); + OR2 U661 ( .A(n628), .B(n627), .Z(n631)); + AN2 U662 ( .A(n628), .B(n627), .Z(n629)); + IV2 U663 ( .A(n629), .Z(n630)); + AN2 U664 ( .A(n631), .B(n630), .Z(n637)); + IV2 U665 ( .A(pi34), .Z(n632)); + AN2 U666 ( .A(pi27), .B(n632), .Z(n635)); + OR2 U667 ( .A(n632), .B(pi27), .Z(n633)); + IV2 U668 ( .A(n633), .Z(n634)); + OR2 U669 ( .A(n635), .B(n634), .Z(n636)); + OR2 U670 ( .A(n637), .B(n636), .Z(n640)); + AN2 U671 ( .A(n637), .B(n636), .Z(n638)); + IV2 U672 ( .A(n638), .Z(n639)); + AN2 U673 ( .A(n640), .B(n639), .Z(n647)); + IV2 U674 ( .A(pi21), .Z(n642)); + OR2 U675 ( .A(n642), .B(pi17), .Z(n641)); + IV2 U676 ( .A(n641), .Z(n644)); + AN2 U677 ( .A(pi17), .B(n642), .Z(n643)); + OR2 U678 ( .A(n644), .B(n643), .Z(n646)); + OR2 U679 ( .A(n647), .B(n646), .Z(n645)); + IV2 U680 ( .A(n645), .Z(n649)); + AN2 U681 ( .A(n647), .B(n646), .Z(n648)); + OR2 U682 ( .A(n649), .B(n648), .Z(po15)); + IV2 U683 ( .A(pi42), .Z(n650)); + AN2 U684 ( .A(pi49), .B(n650), .Z(n653)); + IV2 U685 ( .A(pi49), .Z(n651)); + AN2 U686 ( .A(pi42), .B(n651), .Z(n652)); + OR2 U687 ( .A(n653), .B(n652), .Z(n658)); + OR2 U688 ( .A(n654), .B(pi39), .Z(n657)); + IV2 U689 ( .A(pi39), .Z(n655)); + OR2 U690 ( .A(n655), .B(pi35), .Z(n656)); + AN2 U691 ( .A(n657), .B(n656), .Z(n659)); + OR2 U692 ( .A(n658), .B(n659), .Z(n662)); + AN2 U693 ( .A(n659), .B(n658), .Z(n660)); + IV2 U694 ( .A(n660), .Z(n661)); + AN2 U695 ( .A(n662), .B(n661), .Z(n668)); + IV2 U696 ( .A(pi04), .Z(n664)); + OR2 U697 ( .A(n664), .B(pi18), .Z(n663)); + IV2 U698 ( .A(n663), .Z(n666)); + AN2 U699 ( .A(pi18), .B(n664), .Z(n665)); + OR2 U700 ( .A(n666), .B(n665), .Z(n667)); + OR2 U701 ( .A(n668), .B(n667), .Z(n671)); + AN2 U702 ( .A(n668), .B(n667), .Z(n669)); + IV2 U703 ( .A(n669), .Z(n670)); + AN2 U704 ( .A(n671), .B(n670), .Z(n677)); + IV2 U705 ( .A(pi22), .Z(n673)); + OR2 U706 ( .A(n673), .B(pi14), .Z(n672)); + IV2 U707 ( .A(n672), .Z(n675)); + AN2 U708 ( .A(pi14), .B(n673), .Z(n674)); + OR2 U709 ( .A(n675), .B(n674), .Z(n676)); + OR2 U710 ( .A(n677), .B(n676), .Z(n680)); + AN2 U711 ( .A(n677), .B(n676), .Z(n678)); + IV2 U712 ( .A(n678), .Z(n679)); + AN2 U713 ( .A(n680), .B(n679), .Z(n687)); + IV2 U714 ( .A(pi40), .Z(n682)); + OR2 U715 ( .A(n682), .B(pi23), .Z(n681)); + IV2 U716 ( .A(n681), .Z(n684)); + AN2 U717 ( .A(pi23), .B(n682), .Z(n683)); + OR2 U718 ( .A(n684), .B(n683), .Z(n686)); + OR2 U719 ( .A(n687), .B(n686), .Z(n685)); + IV2 U720 ( .A(n685), .Z(n689)); + AN2 U721 ( .A(n687), .B(n686), .Z(n688)); + OR2 U722 ( .A(n689), .B(n688), .Z(po20)); + AN2 U723 ( .A(pi01), .B(pi02), .Z(po21)); + IV2 U724 ( .A(po25), .Z(n690)); + OR2 U725 ( .A(n691), .B(n690), .Z(po23)); + IV2 U726 ( .A(pi16), .Z(n696)); + OR2 U727 ( .A(n692), .B(n696), .Z(po11)); + AN2 U728 ( .A(pi07), .B(pi41), .Z(n693)); + IV2 U729 ( .A(n693), .Z(n695)); + OR2 U730 ( .A(n694), .B(n695), .Z(po01)); + OR2 U731 ( .A(n696), .B(n695), .Z(po05)); + +endmodule + +module IV2(A, Z); + input A; + output Z; + + assign Z = ~A; +endmodule + +module AN2(A, B, Z); + input A, B; + output Z; + + assign Z = A & B; +endmodule + +module OR2(A, B, Z); + input A, B; + output Z; + + assign Z = A | B; +endmodule diff --git a/examples/smtbmc/glift/C880.ys b/examples/smtbmc/glift/C880.ys new file mode 100644 index 00000000000..410768f21b5 --- /dev/null +++ b/examples/smtbmc/glift/C880.ys @@ -0,0 +1,41 @@ +read_verilog C880.v +techmap +flatten +select C880_lev2 +glift -create-instrumented-model +techmap +opt +rename C880_lev2 uut +cd .. +delete [AIONX][NVXR]2 +read_verilog C880.v +techmap +flatten +select C880_lev2 +glift -create-precise-model +techmap +opt +rename C880_lev2 spec +cd .. +delete [AIONX][NVXR]2 + +design -push-copy +miter -equiv spec uut miter +flatten +delete uut spec +techmap +opt +stat miter +qbfsat -O2 -write-solution C880.soln -solver yices -timeout 3600 -nocleanup -assume-outputs -assume-negative-polarity miter +design -pop +stat + +copy uut solved +qbfsat -specialize-from-file C880.soln solved +opt solved +miter -equiv spec solved satmiter +flatten +sat -prove trigger 0 satmiter +delete satmiter +stat +shell diff --git a/examples/smtbmc/glift/alu2.v b/examples/smtbmc/glift/alu2.v new file mode 100755 index 00000000000..6b6e3d7af24 --- /dev/null +++ b/examples/smtbmc/glift/alu2.v @@ -0,0 +1,400 @@ +module alu2_lev2(pi0, pi1, pi2, pi3, pi4, pi5, pi6, pi7, pi8, pi9, + po0, po1, po2, po3, po4, po5); + +input pi0, pi1, pi2, pi3, pi4, pi5, pi6, pi7, pi8, pi9; + +output po0, po1, po2, po3, po4, po5; + +wire n358, n359, n360, n361, n362, n363, n364, n365, n366, n367, + n368, n369, n370, n371, n372, n373, n374, n375, n376, n377, + n378, n379, n380, n381, n382, n383, n384, n385, n386, n387, + n388, n389, n390, n391, n392, n393, n394, n395, n396, n397, + n398, n399, n400, n401, n402, n403, n404, n405, n406, n407, + n408, n409, n410, n411, n412, n413, n414, n415, n416, n417, + n418, n419, n420, n421, n422, n423, n424, n425, n426, n427, + n428, n429, n430, n431, n432, n433, n434, n435, n436, n437, + n438, n439, n440, n441, n442, n443, n444, n445, n446, n447, + n448, n449, n450, n451, n452, n453, n454, n455, n456, n457, + n458, n459, n460, n461, n462, n463, n464, n465, n466, n467, + n468, n469, n470, n471, n472, n473, n474, n475, n476, n477, + n478, n479, n480, n481, n482, n483, n484, n485, n486, n487, + n488, n489, n490, n491, n492, n493, n494, n495, n496, n497, + n498, n499, n500, n501, n502, n503, n504, n505, n506, n507, + n508, n509, n510, n511, n512, n513, n514, n515, n516, n517, + n518, n519, n520, n521, n522, n523, n524, n525, n526, n527, + n528, n529, n530, n531, n532, n533, n534, n535, n536, n537, + n538, n539, n540, n541, n542, n543, n544, n545, n546, n547, + n548, n549, n550, n551, n552, n553, n554, n555, n556, n557, + n558, n559, n560, n561, n562, n563, n564, n565, n566, n567, + n568, n569, n570, n571, n572, n573, n574, n575, n576, n577, + n578, n579, n580, n581, n582, n583, n584, n585, n586, n587, + n588, n589, n590, n591, n592, n593, n594, n595, n596, n597, + n598, n599, n600, n601, n602, n603, n604, n605, n606, n607, + n608, n609, n610, n611, n612, n613, n614, n615, n616, n617, + n618, n619, n620, n621, n622, n623, n624, n625, n626, n627, + n628, n629, n630, n631, n632, n633, n634, n635, n636, n637, + n638, n639, n640, n641, n642, n643, n644, n645, n646, n647, + n648, n649, n650, n651, n652, n653, n654, n655, n656, n657, + n658, n659, n660, n661, n662, n663, n664, n665, n666, n667, + n668, n669, n670, n671, n672, n673, n674, n675, n676, n677, + n678, n679, n680, n681, n682, n683, n684, n685, n686, n687; + + AN2 U363 ( .A(n358), .B(po2), .Z(po5)); + OR2 U364 ( .A(n359), .B(n360), .Z(n358)); + AN2 U365 ( .A(n361), .B(n362), .Z(n359)); + AN2 U366 ( .A(pi9), .B(n363), .Z(po4)); + OR2 U367 ( .A(n364), .B(n365), .Z(n363)); + OR2 U368 ( .A(n366), .B(n367), .Z(n365)); + AN2 U369 ( .A(pi6), .B(n368), .Z(n367)); + OR2 U370 ( .A(n369), .B(n370), .Z(n368)); + OR2 U371 ( .A(n371), .B(n372), .Z(n370)); + OR2 U372 ( .A(n373), .B(n374), .Z(n372)); + AN2 U373 ( .A(n375), .B(n376), .Z(n374)); + AN2 U374 ( .A(n377), .B(n378), .Z(n375)); + OR2 U375 ( .A(n379), .B(n380), .Z(n377)); + OR2 U376 ( .A(n381), .B(n382), .Z(n380)); + OR2 U377 ( .A(n383), .B(n384), .Z(n379)); + AN2 U378 ( .A(n385), .B(pi5), .Z(n384)); + AN2 U379 ( .A(n386), .B(n387), .Z(n383)); + AN2 U380 ( .A(pi4), .B(n361), .Z(n386)); + AN2 U381 ( .A(n388), .B(n389), .Z(n373)); + OR2 U382 ( .A(n390), .B(n391), .Z(n388)); + AN2 U383 ( .A(pi1), .B(n392), .Z(n390)); + OR2 U384 ( .A(n393), .B(n394), .Z(n392)); + OR2 U385 ( .A(pi7), .B(n395), .Z(n394)); + AN2 U386 ( .A(n381), .B(n396), .Z(n395)); + OR2 U387 ( .A(n397), .B(n398), .Z(n369)); + AN2 U388 ( .A(n399), .B(n400), .Z(n398)); + AN2 U389 ( .A(n387), .B(n401), .Z(n399)); + AN2 U390 ( .A(n402), .B(n403), .Z(n397)); + AN2 U391 ( .A(pi0), .B(n404), .Z(n402)); + OR2 U392 ( .A(pi1), .B(n389), .Z(n404)); + AN2 U393 ( .A(n405), .B(n406), .Z(n366)); + OR2 U394 ( .A(n407), .B(n408), .Z(n406)); + AN2 U395 ( .A(n360), .B(n409), .Z(n408)); + OR2 U396 ( .A(n410), .B(n411), .Z(n409)); + OR2 U397 ( .A(n412), .B(n413), .Z(n411)); + AN2 U398 ( .A(n414), .B(pi3), .Z(n413)); + AN2 U399 ( .A(n389), .B(n415), .Z(n410)); + AN2 U400 ( .A(po3), .B(n416), .Z(n407)); + OR2 U401 ( .A(n417), .B(n414), .Z(n416)); + OR2 U402 ( .A(n418), .B(n419), .Z(n364)); + OR2 U403 ( .A(n420), .B(n421), .Z(n419)); + AN2 U404 ( .A(n422), .B(n382), .Z(n421)); + AN2 U405 ( .A(pi7), .B(n389), .Z(n422)); + AN2 U406 ( .A(n423), .B(n424), .Z(n418)); + AN2 U407 ( .A(n425), .B(n426), .Z(n423)); + OR2 U408 ( .A(n427), .B(po3), .Z(po2)); + AN2 U409 ( .A(n428), .B(n429), .Z(n427)); + OR2 U410 ( .A(n430), .B(n431), .Z(po1)); + AN2 U411 ( .A(pi9), .B(n432), .Z(n431)); + OR2 U412 ( .A(n433), .B(n434), .Z(n432)); + OR2 U413 ( .A(n435), .B(n436), .Z(n434)); + AN2 U414 ( .A(n437), .B(n438), .Z(n436)); + IV2 U415 ( .A(n425), .Z(n438)); + AN2 U416 ( .A(n424), .B(n426), .Z(n437)); + OR2 U417 ( .A(n439), .B(n440), .Z(n424)); + OR2 U418 ( .A(n441), .B(n442), .Z(n440)); + AN2 U419 ( .A(n381), .B(n443), .Z(n442)); + OR2 U420 ( .A(n444), .B(n445), .Z(n443)); + AN2 U421 ( .A(n446), .B(n447), .Z(n441)); + AN2 U422 ( .A(n387), .B(n361), .Z(n446)); + AN2 U423 ( .A(n448), .B(n425), .Z(n435)); + OR2 U424 ( .A(n449), .B(n450), .Z(n425)); + OR2 U425 ( .A(n420), .B(n451), .Z(n450)); + OR2 U426 ( .A(n452), .B(n453), .Z(n451)); + AN2 U427 ( .A(pi6), .B(n454), .Z(n453)); + OR2 U428 ( .A(n371), .B(n455), .Z(n454)); + AN2 U429 ( .A(n376), .B(n456), .Z(n455)); + OR2 U430 ( .A(n457), .B(n458), .Z(n456)); + OR2 U431 ( .A(n459), .B(n460), .Z(n458)); + AN2 U432 ( .A(n461), .B(n378), .Z(n460)); + OR2 U433 ( .A(n462), .B(n463), .Z(n461)); + AN2 U434 ( .A(n385), .B(n464), .Z(n462)); + OR2 U435 ( .A(n465), .B(pi5), .Z(n464)); + AN2 U436 ( .A(pi7), .B(n466), .Z(n459)); + OR2 U437 ( .A(n467), .B(n468), .Z(n466)); + OR2 U438 ( .A(n469), .B(n470), .Z(n468)); + AN2 U439 ( .A(n381), .B(pi1), .Z(n470)); + AN2 U440 ( .A(n471), .B(n428), .Z(n469)); + AN2 U441 ( .A(pi0), .B(n387), .Z(n471)); + AN2 U442 ( .A(n412), .B(n361), .Z(n467)); + AN2 U443 ( .A(n472), .B(n473), .Z(n457)); + AN2 U444 ( .A(n360), .B(n428), .Z(n472)); + AN2 U445 ( .A(n463), .B(n428), .Z(n371)); + AN2 U446 ( .A(n474), .B(n475), .Z(n452)); + OR2 U447 ( .A(n476), .B(n477), .Z(n474)); + OR2 U448 ( .A(n478), .B(n479), .Z(n477)); + AN2 U449 ( .A(n480), .B(n428), .Z(n479)); + AN2 U450 ( .A(n481), .B(n482), .Z(n480)); + OR2 U451 ( .A(n360), .B(n389), .Z(n482)); + OR2 U452 ( .A(n401), .B(n483), .Z(n481)); + AN2 U453 ( .A(pi7), .B(n484), .Z(n483)); + OR2 U454 ( .A(n393), .B(n485), .Z(n484)); + AN2 U455 ( .A(n376), .B(n415), .Z(n485)); + AN2 U456 ( .A(n414), .B(n429), .Z(n393)); + AN2 U457 ( .A(n486), .B(n378), .Z(n478)); + OR2 U458 ( .A(n412), .B(n389), .Z(n486)); + AN2 U459 ( .A(n487), .B(pi1), .Z(n412)); + OR2 U460 ( .A(n488), .B(n489), .Z(n476)); + AN2 U461 ( .A(n490), .B(n401), .Z(n488)); + AN2 U462 ( .A(pi1), .B(n429), .Z(n490)); + AN2 U463 ( .A(n385), .B(n491), .Z(n420)); + IV2 U464 ( .A(n492), .Z(n491)); + OR2 U465 ( .A(n493), .B(n487), .Z(n492)); + AN2 U466 ( .A(n494), .B(n495), .Z(n493)); + OR2 U467 ( .A(pi6), .B(n389), .Z(n495)); + OR2 U468 ( .A(pi7), .B(pi1), .Z(n494)); + OR2 U469 ( .A(n496), .B(n497), .Z(n449)); + AN2 U470 ( .A(n498), .B(n376), .Z(n497)); + AN2 U471 ( .A(n381), .B(n382), .Z(n498)); + AN2 U472 ( .A(n499), .B(n389), .Z(n496)); + OR2 U473 ( .A(n500), .B(n501), .Z(n499)); + OR2 U474 ( .A(n502), .B(n503), .Z(n501)); + AN2 U475 ( .A(n385), .B(n504), .Z(n503)); + OR2 U476 ( .A(n505), .B(n506), .Z(n504)); + AN2 U477 ( .A(po3), .B(n400), .Z(n506)); + AN2 U478 ( .A(n507), .B(n428), .Z(n505)); + AN2 U479 ( .A(n508), .B(n387), .Z(n502)); + OR2 U480 ( .A(n509), .B(n510), .Z(n508)); + OR2 U481 ( .A(n489), .B(n511), .Z(n510)); + OR2 U482 ( .A(n465), .B(n512), .Z(n511)); + AN2 U483 ( .A(n513), .B(pi1), .Z(n512)); + AN2 U484 ( .A(pi0), .B(n514), .Z(n513)); + OR2 U485 ( .A(n507), .B(n515), .Z(n514)); + AN2 U486 ( .A(n361), .B(n428), .Z(n465)); + AN2 U487 ( .A(po3), .B(n360), .Z(n489)); + OR2 U488 ( .A(n516), .B(n517), .Z(n509)); + OR2 U489 ( .A(n518), .B(n519), .Z(n517)); + AN2 U490 ( .A(n391), .B(n362), .Z(n519)); + AN2 U491 ( .A(n428), .B(n400), .Z(n391)); + AN2 U492 ( .A(n520), .B(n521), .Z(n518)); + OR2 U493 ( .A(n522), .B(n362), .Z(n521)); + AN2 U494 ( .A(n429), .B(n523), .Z(n520)); + AN2 U495 ( .A(n417), .B(n378), .Z(n516)); + AN2 U496 ( .A(n522), .B(n382), .Z(n500)); + AN2 U497 ( .A(pi1), .B(n396), .Z(n382)); + AN2 U498 ( .A(n361), .B(n378), .Z(n522)); + OR2 U499 ( .A(n524), .B(n525), .Z(n448)); + OR2 U500 ( .A(n526), .B(n527), .Z(n525)); + OR2 U501 ( .A(pi8), .B(n528), .Z(n524)); + AN2 U502 ( .A(n529), .B(n530), .Z(n430)); + OR2 U503 ( .A(n531), .B(n532), .Z(n529)); + OR2 U504 ( .A(n533), .B(n534), .Z(n532)); + OR2 U505 ( .A(n535), .B(n536), .Z(n534)); + AN2 U506 ( .A(n537), .B(n376), .Z(n536)); + IV2 U507 ( .A(n389), .Z(n376)); + AN2 U508 ( .A(n538), .B(n389), .Z(n535)); + OR2 U509 ( .A(n539), .B(n540), .Z(n389)); + OR2 U510 ( .A(n541), .B(n542), .Z(n540)); + OR2 U511 ( .A(n543), .B(n544), .Z(n542)); + AN2 U512 ( .A(pi1), .B(n545), .Z(n544)); + AN2 U513 ( .A(n546), .B(n428), .Z(n543)); + AN2 U514 ( .A(n547), .B(n548), .Z(n546)); + OR2 U515 ( .A(pi3), .B(n396), .Z(n548)); + AN2 U516 ( .A(pi9), .B(n549), .Z(n541)); + OR2 U517 ( .A(n550), .B(n551), .Z(n549)); + OR2 U518 ( .A(n552), .B(n553), .Z(n551)); + AN2 U519 ( .A(n554), .B(n507), .Z(n553)); + AN2 U520 ( .A(n396), .B(pi0), .Z(n554)); + AN2 U521 ( .A(n555), .B(n556), .Z(n552)); + AN2 U522 ( .A(n557), .B(n415), .Z(n556)); + AN2 U523 ( .A(po3), .B(n558), .Z(n555)); + OR2 U524 ( .A(n559), .B(n560), .Z(n550)); + AN2 U525 ( .A(n561), .B(n429), .Z(n560)); + AN2 U526 ( .A(n417), .B(n562), .Z(n561)); + OR2 U527 ( .A(n563), .B(n564), .Z(n562)); + AN2 U528 ( .A(n558), .B(n428), .Z(n564)); + AN2 U529 ( .A(pi1), .B(n565), .Z(n563)); + AN2 U530 ( .A(pi3), .B(n566), .Z(n559)); + OR2 U531 ( .A(n567), .B(n414), .Z(n566)); + AN2 U532 ( .A(n568), .B(n569), .Z(n567)); + AN2 U533 ( .A(n565), .B(n428), .Z(n568)); + OR2 U534 ( .A(n570), .B(n571), .Z(n539)); + AN2 U535 ( .A(n572), .B(n429), .Z(n571)); + AN2 U536 ( .A(po3), .B(n573), .Z(n570)); + OR2 U537 ( .A(n574), .B(n575), .Z(n538)); + OR2 U538 ( .A(n445), .B(n576), .Z(n575)); + AN2 U539 ( .A(n577), .B(pi3), .Z(n576)); + AN2 U540 ( .A(n578), .B(pi1), .Z(n574)); + AN2 U541 ( .A(n507), .B(pi1), .Z(n533)); + OR2 U542 ( .A(n579), .B(n580), .Z(n531)); + OR2 U543 ( .A(n581), .B(n582), .Z(n580)); + AN2 U544 ( .A(n444), .B(po3), .Z(n582)); + AN2 U545 ( .A(pi1), .B(pi3), .Z(po3)); + AN2 U546 ( .A(n583), .B(n557), .Z(n581)); + AN2 U547 ( .A(n584), .B(n429), .Z(n583)); + OR2 U548 ( .A(n585), .B(n414), .Z(n584)); + AN2 U549 ( .A(n417), .B(n428), .Z(n585)); + AN2 U550 ( .A(n586), .B(pi7), .Z(n579)); + AN2 U551 ( .A(n587), .B(n588), .Z(n586)); + OR2 U552 ( .A(pi3), .B(n589), .Z(n588)); + AN2 U553 ( .A(pi1), .B(n523), .Z(n589)); + OR2 U554 ( .A(n429), .B(n590), .Z(n587)); + OR2 U555 ( .A(n417), .B(n591), .Z(n590)); + AN2 U556 ( .A(n592), .B(n428), .Z(n591)); + IV2 U557 ( .A(pi1), .Z(n428)); + IV2 U558 ( .A(pi3), .Z(n429)); + OR2 U559 ( .A(n593), .B(n594), .Z(po0)); + OR2 U560 ( .A(n595), .B(n596), .Z(n594)); + AN2 U561 ( .A(n597), .B(pi8), .Z(n596)); + AN2 U562 ( .A(n598), .B(n381), .Z(n597)); + AN2 U563 ( .A(pi0), .B(n385), .Z(n381)); + AN2 U564 ( .A(n507), .B(n487), .Z(n598)); + AN2 U565 ( .A(n528), .B(n426), .Z(n595)); + AN2 U566 ( .A(pi6), .B(n599), .Z(n528)); + IV2 U567 ( .A(n600), .Z(n599)); + OR2 U568 ( .A(n601), .B(n361), .Z(n600)); + AN2 U569 ( .A(n602), .B(n603), .Z(n601)); + AN2 U570 ( .A(n604), .B(n605), .Z(n603)); + OR2 U571 ( .A(pi7), .B(n606), .Z(n605)); + OR2 U572 ( .A(n607), .B(n387), .Z(n606)); + OR2 U573 ( .A(n378), .B(n487), .Z(n604)); + AN2 U574 ( .A(n608), .B(n609), .Z(n602)); + OR2 U575 ( .A(pi2), .B(n415), .Z(n608)); + OR2 U576 ( .A(n610), .B(n611), .Z(n593)); + AN2 U577 ( .A(pi9), .B(n612), .Z(n611)); + OR2 U578 ( .A(n613), .B(n614), .Z(n612)); + OR2 U579 ( .A(n433), .B(n615), .Z(n614)); + AN2 U580 ( .A(n527), .B(n426), .Z(n615)); + OR2 U581 ( .A(n616), .B(n617), .Z(n527)); + AN2 U582 ( .A(n618), .B(n361), .Z(n617)); + OR2 U583 ( .A(n619), .B(n620), .Z(n618)); + OR2 U584 ( .A(n621), .B(n622), .Z(n620)); + AN2 U585 ( .A(n592), .B(n362), .Z(n622)); + AN2 U586 ( .A(n385), .B(n623), .Z(n621)); + OR2 U587 ( .A(n624), .B(n625), .Z(n623)); + AN2 U588 ( .A(n626), .B(n415), .Z(n625)); + AN2 U589 ( .A(n507), .B(n523), .Z(n624)); + AN2 U590 ( .A(n473), .B(n557), .Z(n619)); + AN2 U591 ( .A(n523), .B(n387), .Z(n473)); + AN2 U592 ( .A(n569), .B(n387), .Z(n616)); + AN2 U593 ( .A(n578), .B(n627), .Z(n433)); + AN2 U594 ( .A(n378), .B(n475), .Z(n627)); + OR2 U595 ( .A(n628), .B(n629), .Z(n613)); + AN2 U596 ( .A(n526), .B(n426), .Z(n629)); + IV2 U597 ( .A(pi8), .Z(n426)); + AN2 U598 ( .A(n360), .B(n405), .Z(n526)); + AN2 U599 ( .A(pi8), .B(n630), .Z(n628)); + OR2 U600 ( .A(n631), .B(n439), .Z(n630)); + OR2 U601 ( .A(n632), .B(n633), .Z(n439)); + OR2 U602 ( .A(n634), .B(n635), .Z(n633)); + AN2 U603 ( .A(n636), .B(n378), .Z(n635)); + OR2 U604 ( .A(n637), .B(n360), .Z(n636)); + AN2 U605 ( .A(n387), .B(n475), .Z(n637)); + AN2 U606 ( .A(n638), .B(n475), .Z(n634)); + OR2 U607 ( .A(n639), .B(n640), .Z(n638)); + AN2 U608 ( .A(n558), .B(pi4), .Z(n639)); + OR2 U609 ( .A(n463), .B(n641), .Z(n632)); + AN2 U610 ( .A(n642), .B(n385), .Z(n641)); + AN2 U611 ( .A(n557), .B(n361), .Z(n642)); + AN2 U612 ( .A(n361), .B(n578), .Z(n463)); + AN2 U613 ( .A(n403), .B(n361), .Z(n631)); + IV2 U614 ( .A(n609), .Z(n403)); + OR2 U615 ( .A(n385), .B(n378), .Z(n609)); + AN2 U616 ( .A(n643), .B(n530), .Z(n610)); + OR2 U617 ( .A(n644), .B(n645), .Z(n643)); + OR2 U618 ( .A(n646), .B(n647), .Z(n645)); + OR2 U619 ( .A(n648), .B(n649), .Z(n647)); + AN2 U620 ( .A(n537), .B(n385), .Z(n649)); + IV2 U621 ( .A(n387), .Z(n385)); + OR2 U622 ( .A(n650), .B(n651), .Z(n537)); + AN2 U623 ( .A(n396), .B(pi6), .Z(n651)); + AN2 U624 ( .A(n400), .B(n475), .Z(n650)); + AN2 U625 ( .A(n652), .B(n387), .Z(n648)); + OR2 U626 ( .A(n653), .B(n654), .Z(n387)); + OR2 U627 ( .A(n655), .B(n656), .Z(n654)); + OR2 U628 ( .A(n657), .B(n658), .Z(n656)); + AN2 U629 ( .A(n360), .B(n573), .Z(n658)); + OR2 U630 ( .A(n659), .B(n660), .Z(n573)); + AN2 U631 ( .A(n405), .B(n578), .Z(n660)); + AN2 U632 ( .A(n396), .B(n661), .Z(n659)); + OR2 U633 ( .A(n405), .B(n557), .Z(n661)); + AN2 U634 ( .A(n475), .B(pi7), .Z(n405)); + IV2 U635 ( .A(n607), .Z(n396)); + OR2 U636 ( .A(pi5), .B(pi4), .Z(n607)); + AN2 U637 ( .A(n640), .B(n417), .Z(n657)); + AN2 U638 ( .A(n572), .B(n362), .Z(n655)); + OR2 U639 ( .A(n662), .B(n663), .Z(n572)); + OR2 U640 ( .A(n664), .B(n665), .Z(n663)); + AN2 U641 ( .A(n578), .B(n557), .Z(n665)); + AN2 U642 ( .A(n417), .B(n626), .Z(n664)); + AN2 U643 ( .A(n507), .B(n530), .Z(n662)); + OR2 U644 ( .A(n666), .B(n667), .Z(n653)); + OR2 U645 ( .A(n668), .B(n669), .Z(n667)); + AN2 U646 ( .A(n670), .B(n545), .Z(n669)); + OR2 U647 ( .A(n400), .B(n671), .Z(n545)); + AN2 U648 ( .A(pi9), .B(n414), .Z(n671)); + AN2 U649 ( .A(n378), .B(n414), .Z(n400)); + IV2 U650 ( .A(pi7), .Z(n378)); + OR2 U651 ( .A(pi0), .B(pi2), .Z(n670)); + AN2 U652 ( .A(n672), .B(n547), .Z(n668)); + AN2 U653 ( .A(n475), .B(n530), .Z(n547)); + IV2 U654 ( .A(pi9), .Z(n530)); + AN2 U655 ( .A(n361), .B(n415), .Z(n672)); + AN2 U656 ( .A(n558), .B(n569), .Z(n666)); + AN2 U657 ( .A(n557), .B(n417), .Z(n569)); + OR2 U658 ( .A(n673), .B(n674), .Z(n652)); + OR2 U659 ( .A(n445), .B(n675), .Z(n674)); + AN2 U660 ( .A(n577), .B(pi2), .Z(n675)); + AN2 U661 ( .A(n523), .B(n447), .Z(n445)); + OR2 U662 ( .A(n577), .B(n507), .Z(n447)); + AN2 U663 ( .A(n475), .B(n415), .Z(n577)); + AN2 U664 ( .A(n578), .B(pi0), .Z(n673)); + IV2 U665 ( .A(n487), .Z(n578)); + OR2 U666 ( .A(n415), .B(n523), .Z(n487)); + AN2 U667 ( .A(n507), .B(pi0), .Z(n646)); + AN2 U668 ( .A(pi6), .B(pi7), .Z(n507)); + OR2 U669 ( .A(n676), .B(n677), .Z(n644)); + OR2 U670 ( .A(n678), .B(n679), .Z(n677)); + AN2 U671 ( .A(n444), .B(n360), .Z(n679)); + IV2 U672 ( .A(n401), .Z(n360)); + OR2 U673 ( .A(n362), .B(n361), .Z(n401)); + AN2 U674 ( .A(pi6), .B(n417), .Z(n444)); + AN2 U675 ( .A(n680), .B(n557), .Z(n678)); + IV2 U676 ( .A(n626), .Z(n557)); + OR2 U677 ( .A(pi7), .B(n475), .Z(n626)); + AN2 U678 ( .A(n681), .B(n362), .Z(n680)); + OR2 U679 ( .A(n682), .B(n414), .Z(n681)); + AN2 U680 ( .A(n417), .B(n361), .Z(n682)); + IV2 U681 ( .A(pi0), .Z(n361)); + AN2 U682 ( .A(pi7), .B(n683), .Z(n676)); + OR2 U683 ( .A(n684), .B(n685), .Z(n683)); + OR2 U684 ( .A(n686), .B(n687), .Z(n685)); + AN2 U685 ( .A(n592), .B(n558), .Z(n687)); + IV2 U686 ( .A(n565), .Z(n558)); + OR2 U687 ( .A(pi0), .B(n362), .Z(n565)); + AN2 U688 ( .A(n475), .B(n414), .Z(n592)); + IV2 U689 ( .A(n515), .Z(n414)); + OR2 U690 ( .A(pi5), .B(n415), .Z(n515)); + IV2 U691 ( .A(pi6), .Z(n475)); + AN2 U692 ( .A(n640), .B(n523), .Z(n686)); + IV2 U693 ( .A(pi5), .Z(n523)); + AN2 U694 ( .A(n362), .B(pi0), .Z(n640)); + IV2 U695 ( .A(pi2), .Z(n362)); + AN2 U696 ( .A(n417), .B(pi2), .Z(n684)); + AN2 U697 ( .A(n415), .B(pi5), .Z(n417)); + IV2 U698 ( .A(pi4), .Z(n415)); + +endmodule + +module IV2(A, Z); + input A; + output Z; + + assign Z = ~A; +endmodule + +module AN2(A, B, Z); + input A, B; + output Z; + + assign Z = A & B; +endmodule + +module OR2(A, B, Z); + input A, B; + output Z; + + assign Z = A | B; +endmodule diff --git a/examples/smtbmc/glift/alu2.ys b/examples/smtbmc/glift/alu2.ys new file mode 100644 index 00000000000..b1671752e9b --- /dev/null +++ b/examples/smtbmc/glift/alu2.ys @@ -0,0 +1,41 @@ +read_verilog alu2.v +techmap +flatten +select alu2_lev2 +glift -create-instrumented-model +techmap +opt +rename alu2_lev2 uut +cd .. +delete [AIONX][NVXR]2 +read_verilog alu2.v +techmap +flatten +select alu2_lev2 +glift -create-precise-model +techmap +opt +rename alu2_lev2 spec +cd .. +delete [AIONX][NVXR]2 + +design -push-copy +miter -equiv spec uut miter +flatten +delete uut spec +techmap +opt +stat miter +qbfsat -O2 -write-solution alu2.soln -solver yices -timeout 3600 -nocleanup -assume-outputs -assume-negative-polarity miter +design -pop +stat + +copy uut solved +qbfsat -specialize-from-file alu2.soln solved +opt solved +miter -equiv spec solved satmiter +flatten +sat -prove trigger 0 satmiter +delete satmiter +stat +shell diff --git a/examples/smtbmc/glift/alu4.v b/examples/smtbmc/glift/alu4.v new file mode 100755 index 00000000000..e110612e53f --- /dev/null +++ b/examples/smtbmc/glift/alu4.v @@ -0,0 +1,802 @@ +module alu4_lev2(pi00, pi01, pi02, pi03, pi04, pi05, pi06, pi07, pi08, pi09, + pi10, pi11, pi12, pi13, po0, po1, po2, po3, po4, po5, po6, po7); + +input pi00, pi01, pi02, pi03, pi04, pi05, pi06, pi07, pi08, pi09, + pi10, pi11, pi12, pi13; + +output po0, po1, po2, po3, po4, po5, po6, po7; + +wire n705, n706, n707, n708, n709, n710, n711, n712, n713, n714, + n715, n716, n717, n718, n719, n720, n721, n722, n723, n724, + n725, n726, n727, n728, n729, n730, n731, n732, n733, n734, + n735, n736, n737, n738, n739, n740, n741, n742, n743, n744, + n745, n746, n747, n748, n749, n750, n751, n752, n753, n754, + n755, n756, n757, n758, n759, n760, n761, n762, n763, n764, + n765, n766, n767, n768, n769, n770, n771, n772, n773, n774, + n775, n776, n777, n778, n779, n780, n781, n782, n783, n784, + n785, n786, n787, n788, n789, n790, n791, n792, n793, n794, + n795, n796, n797, n798, n799, n800, n801, n802, n803, n804, + n805, n806, n807, n808, n809, n810, n811, n812, n813, n814, + n815, n816, n817, n818, n819, n820, n821, n822, n823, n824, + n825, n826, n827, n828, n829, n830, n831, n832, n833, n834, + n835, n836, n837, n838, n839, n840, n841, n842, n843, n844, + n845, n846, n847, n848, n849, n850, n851, n852, n853, n854, + n855, n856, n857, n858, n859, n860, n861, n862, n863, n864, + n865, n866, n867, n868, n869, n870, n871, n872, n873, n874, + n875, n876, n877, n878, n879, n880, n881, n882, n883, n884, + n885, n886, n887, n888, n889, n890, n891, n892, n893, n894, + n895, n896, n897, n898, n899, n900, n901, n902, n903, n904, + n905, n906, n907, n908, n909, n910, n911, n912, n913, n914, + n915, n916, n917, n918, n919, n920, n921, n922, n923, n924, + n925, n926, n927, n928, n929, n930, n931, n932, n933, n934, + n935, n936, n937, n938, n939, n940, n941, n942, n943, n944, + n945, n946, n947, n948, n949, n950, n951, n952, n953, n954, + n955, n956, n957, n958, n959, n960, n961, n962, n963, n964, + n965, n966, n967, n968, n969, n970, n971, n972, n973, n974, + n975, n976, n977, n978, n979, n980, n981, n982, n983, n984, + n985, n986, n987, n988, n989, n990, n991, n992, n993, n994, + n995, n996, n997, n998, n999, n1000, n1001, n1002, n1003, n1004, + n1005, n1006, n1007, n1008, n1009, n1010, n1011, n1012, n1013, n1014, + n1015, n1016, n1017, n1018, n1019, n1020, n1021, n1022, n1023, n1024, + n1025, n1026, n1027, n1028, n1029, n1030, n1031, n1032, n1033, n1034, + n1035, n1036, n1037, n1038, n1039, n1040, n1041, n1042, n1043, n1044, + n1045, n1046, n1047, n1048, n1049, n1050, n1051, n1052, n1053, n1054, + n1055, n1056, n1057, n1058, n1059, n1060, n1061, n1062, n1063, n1064, + n1065, n1066, n1067, n1068, n1069, n1070, n1071, n1072, n1073, n1074, + n1075, n1076, n1077, n1078, n1079, n1080, n1081, n1082, n1083, n1084, + n1085, n1086, n1087, n1088, n1089, n1090, n1091, n1092, n1093, n1094, + n1095, n1096, n1097, n1098, n1099, n1100, n1101, n1102, n1103, n1104, + n1105, n1106, n1107, n1108, n1109, n1110, n1111, n1112, n1113, n1114, + n1115, n1116, n1117, n1118, n1119, n1120, n1121, n1122, n1123, n1124, + n1125, n1126, n1127, n1128, n1129, n1130, n1131, n1132, n1133, n1134, + n1135, n1136, n1137, n1138, n1139, n1140, n1141, n1142, n1143, n1144, + n1145, n1146, n1147, n1148, n1149, n1150, n1151, n1152, n1153, n1154, + n1155, n1156, n1157, n1158, n1159, n1160, n1161, n1162, n1163, n1164, + n1165, n1166, n1167, n1168, n1169, n1170, n1171, n1172, n1173, n1174, + n1175, n1176, n1177, n1178, n1179, n1180, n1181, n1182, n1183, n1184, + n1185, n1186, n1187, n1188, n1189, n1190, n1191, n1192, n1193, n1194, + n1195, n1196, n1197, n1198, n1199, n1200, n1201, n1202, n1203, n1204, + n1205, n1206, n1207, n1208, n1209, n1210, n1211, n1212, n1213, n1214, + n1215, n1216, n1217, n1218, n1219, n1220, n1221, n1222, n1223, n1224, + n1225, n1226, n1227, n1228, n1229, n1230, n1231, n1232, n1233, n1234, + n1235, n1236, n1237, n1238, n1239, n1240, n1241, n1242, n1243, n1244, + n1245, n1246, n1247, n1248, n1249, n1250, n1251, n1252, n1253, n1254, + n1255, n1256, n1257, n1258, n1259, n1260, n1261, n1262, n1263, n1264, + n1265, n1266, n1267, n1268, n1269, n1270, n1271, n1272, n1273, n1274, + n1275, n1276, n1277, n1278, n1279, n1280, n1281, n1282, n1283, n1284, + n1285, n1286, n1287, n1288, n1289, n1290, n1291, n1292, n1293, n1294, + n1295, n1296, n1297, n1298, n1299, n1300, n1301, n1302, n1303, n1304, + n1305, n1306, n1307, n1308, n1309, n1310, n1311, n1312, n1313, n1314, + n1315, n1316, n1317, n1318, n1319, n1320, n1321, n1322, n1323, n1324, + n1325, n1326, n1327, n1328, n1329, n1330, n1331, n1332, n1333, n1334, + n1335, n1336, n1337, n1338, n1339, n1340, n1341, n1342, n1343, n1344, + n1345, n1346, n1347, n1348, n1349, n1350, n1351, n1352, n1353, n1354, + n1355, n1356, n1357, n1358, n1359, n1360, n1361, n1362, n1363, n1364, + n1365, n1366, n1367, n1368, n1369, n1370, n1371, n1372, n1373, n1374, + n1375, n1376, n1377, n1378, n1379, n1380, n1381, n1382, n1383, n1384, + n1385, n1386, n1387, n1388, n1389, n1390, n1391, n1392, n1393, n1394, + n1395, n1396; + + AN2 U712 ( .A(n705), .B(po4), .Z(po7)); + OR2 U713 ( .A(n706), .B(n707), .Z(n705)); + AN2 U714 ( .A(n708), .B(n709), .Z(n707)); + OR2 U715 ( .A(n710), .B(n711), .Z(n708)); + AN2 U716 ( .A(n712), .B(n713), .Z(n711)); + AN2 U717 ( .A(n714), .B(n715), .Z(n710)); + AN2 U718 ( .A(n716), .B(n717), .Z(n714)); + AN2 U719 ( .A(n718), .B(n719), .Z(n706)); + OR2 U720 ( .A(n720), .B(n712), .Z(n719)); + OR2 U721 ( .A(n721), .B(n722), .Z(n712)); + AN2 U722 ( .A(n723), .B(n724), .Z(n722)); + AN2 U723 ( .A(n725), .B(n726), .Z(n721)); + OR2 U724 ( .A(n724), .B(n727), .Z(n726)); + AN2 U725 ( .A(n727), .B(n723), .Z(n720)); + OR2 U726 ( .A(n728), .B(n729), .Z(po6)); + AN2 U727 ( .A(pi13), .B(n730), .Z(n729)); + OR2 U728 ( .A(n731), .B(n732), .Z(n730)); + OR2 U729 ( .A(n733), .B(n734), .Z(n732)); + OR2 U730 ( .A(n735), .B(n736), .Z(n734)); + AN2 U731 ( .A(n737), .B(n738), .Z(n736)); + OR2 U732 ( .A(n739), .B(n740), .Z(n737)); + OR2 U733 ( .A(n741), .B(n742), .Z(n740)); + AN2 U734 ( .A(n743), .B(n744), .Z(n742)); + OR2 U735 ( .A(n745), .B(n746), .Z(n743)); + AN2 U736 ( .A(pi03), .B(n747), .Z(n745)); + AN2 U737 ( .A(n748), .B(n749), .Z(n741)); + AN2 U738 ( .A(n750), .B(n751), .Z(n748)); + AN2 U739 ( .A(pi11), .B(n752), .Z(n735)); + OR2 U740 ( .A(n753), .B(n754), .Z(n752)); + OR2 U741 ( .A(n755), .B(n756), .Z(n754)); + AN2 U742 ( .A(n757), .B(n747), .Z(n756)); + OR2 U743 ( .A(n758), .B(n759), .Z(n757)); + IV2 U744 ( .A(n760), .Z(n755)); + AN2 U745 ( .A(n761), .B(n762), .Z(n753)); + OR2 U746 ( .A(n763), .B(po5), .Z(n762)); + AN2 U747 ( .A(n764), .B(n765), .Z(n763)); + AN2 U748 ( .A(n766), .B(n767), .Z(n733)); + OR2 U749 ( .A(n768), .B(n769), .Z(n767)); + AN2 U750 ( .A(n770), .B(n771), .Z(n768)); + IV2 U751 ( .A(n772), .Z(n766)); + OR2 U752 ( .A(n773), .B(n774), .Z(n731)); + AN2 U753 ( .A(n775), .B(n776), .Z(n774)); + AN2 U754 ( .A(n777), .B(n778), .Z(n775)); + AN2 U755 ( .A(n779), .B(n780), .Z(n773)); + AN2 U756 ( .A(n781), .B(n782), .Z(n779)); + AN2 U757 ( .A(n783), .B(n781), .Z(n728)); + AN2 U758 ( .A(n782), .B(n784), .Z(n783)); + OR2 U759 ( .A(n785), .B(n786), .Z(po3)); + OR2 U760 ( .A(n787), .B(n788), .Z(n786)); + OR2 U761 ( .A(n789), .B(n790), .Z(n788)); + OR2 U762 ( .A(n791), .B(n792), .Z(n790)); + AN2 U763 ( .A(n793), .B(n782), .Z(n792)); + AN2 U764 ( .A(n794), .B(n795), .Z(n791)); + OR2 U765 ( .A(n796), .B(n797), .Z(n789)); + IV2 U766 ( .A(n798), .Z(n797)); + OR2 U767 ( .A(n799), .B(n778), .Z(n798)); + AN2 U768 ( .A(n778), .B(n799), .Z(n796)); + OR2 U769 ( .A(n800), .B(n801), .Z(n799)); + IV2 U770 ( .A(n802), .Z(n778)); + OR2 U771 ( .A(n803), .B(n804), .Z(n802)); + AN2 U772 ( .A(n805), .B(n806), .Z(n803)); + AN2 U773 ( .A(n807), .B(n808), .Z(n806)); + AN2 U774 ( .A(n809), .B(n810), .Z(n808)); + OR2 U775 ( .A(pi11), .B(n811), .Z(n810)); + AN2 U776 ( .A(n812), .B(n813), .Z(n811)); + AN2 U777 ( .A(n814), .B(n815), .Z(n813)); + OR2 U778 ( .A(n782), .B(n816), .Z(n815)); + OR2 U779 ( .A(n817), .B(n818), .Z(n814)); + OR2 U780 ( .A(n819), .B(n820), .Z(n818)); + AN2 U781 ( .A(n750), .B(n821), .Z(n820)); + AN2 U782 ( .A(n749), .B(n747), .Z(n819)); + IV2 U783 ( .A(n821), .Z(n749)); + AN2 U784 ( .A(n822), .B(n823), .Z(n812)); + OR2 U785 ( .A(n824), .B(n825), .Z(n823)); + OR2 U786 ( .A(n826), .B(n827), .Z(n822)); + AN2 U787 ( .A(pi10), .B(n828), .Z(n826)); + OR2 U788 ( .A(n829), .B(n830), .Z(n828)); + OR2 U789 ( .A(n831), .B(n738), .Z(n809)); + AN2 U790 ( .A(n832), .B(n833), .Z(n831)); + AN2 U791 ( .A(n834), .B(n760), .Z(n833)); + OR2 U792 ( .A(n817), .B(n835), .Z(n760)); + OR2 U793 ( .A(pi03), .B(n836), .Z(n835)); + OR2 U794 ( .A(n817), .B(n837), .Z(n834)); + OR2 U795 ( .A(n715), .B(n827), .Z(n837)); + IV2 U796 ( .A(n836), .Z(n715)); + AN2 U797 ( .A(n838), .B(n839), .Z(n832)); + OR2 U798 ( .A(n765), .B(n840), .Z(n839)); + OR2 U799 ( .A(n841), .B(n825), .Z(n840)); + OR2 U800 ( .A(n816), .B(n842), .Z(n838)); + OR2 U801 ( .A(n843), .B(n844), .Z(n842)); + AN2 U802 ( .A(n845), .B(n846), .Z(n844)); + OR2 U803 ( .A(n847), .B(n848), .Z(n845)); + AN2 U804 ( .A(n758), .B(n747), .Z(n848)); + IV2 U805 ( .A(n849), .Z(n758)); + AN2 U806 ( .A(n750), .B(n849), .Z(n847)); + AN2 U807 ( .A(n849), .B(n759), .Z(n843)); + OR2 U808 ( .A(n850), .B(n851), .Z(n849)); + AN2 U809 ( .A(n852), .B(n853), .Z(n850)); + IV2 U810 ( .A(n854), .Z(n816)); + AN2 U811 ( .A(n855), .B(n856), .Z(n807)); + OR2 U812 ( .A(n857), .B(n858), .Z(n856)); + OR2 U813 ( .A(n859), .B(n860), .Z(n858)); + AN2 U814 ( .A(n861), .B(n739), .Z(n859)); + OR2 U815 ( .A(n862), .B(n863), .Z(n739)); + AN2 U816 ( .A(n759), .B(n795), .Z(n862)); + OR2 U817 ( .A(n846), .B(n864), .Z(n861)); + IV2 U818 ( .A(n865), .Z(n864)); + AN2 U819 ( .A(n795), .B(n863), .Z(n865)); + IV2 U820 ( .A(n759), .Z(n846)); + OR2 U821 ( .A(n866), .B(n867), .Z(n759)); + AN2 U822 ( .A(n868), .B(po5), .Z(n867)); + AN2 U823 ( .A(n750), .B(n869), .Z(n866)); + OR2 U824 ( .A(n825), .B(n870), .Z(n855)); + OR2 U825 ( .A(n871), .B(n872), .Z(n870)); + AN2 U826 ( .A(n764), .B(n873), .Z(n872)); + IV2 U827 ( .A(po5), .Z(n873)); + AN2 U828 ( .A(n841), .B(po4), .Z(n871)); + OR2 U829 ( .A(n874), .B(po5), .Z(po4)); + IV2 U830 ( .A(n764), .Z(n841)); + OR2 U831 ( .A(n875), .B(n724), .Z(n764)); + AN2 U832 ( .A(n876), .B(n877), .Z(n875)); + AN2 U833 ( .A(n878), .B(n879), .Z(n805)); + AN2 U834 ( .A(n880), .B(n881), .Z(n879)); + OR2 U835 ( .A(n782), .B(n882), .Z(n881)); + OR2 U836 ( .A(n781), .B(n883), .Z(n882)); + IV2 U837 ( .A(n884), .Z(n781)); + OR2 U838 ( .A(n795), .B(n885), .Z(n880)); + AN2 U839 ( .A(n886), .B(n887), .Z(n878)); + OR2 U840 ( .A(pi03), .B(n888), .Z(n887)); + AN2 U841 ( .A(n889), .B(n890), .Z(n888)); + IV2 U842 ( .A(n891), .Z(n890)); + AN2 U843 ( .A(n830), .B(n892), .Z(n891)); + OR2 U844 ( .A(n893), .B(n894), .Z(n830)); + IV2 U845 ( .A(n895), .Z(n894)); + OR2 U846 ( .A(n746), .B(n750), .Z(n895)); + AN2 U847 ( .A(n750), .B(n746), .Z(n893)); + OR2 U848 ( .A(n896), .B(n897), .Z(n746)); + AN2 U849 ( .A(pi02), .B(n898), .Z(n896)); + IV2 U850 ( .A(n747), .Z(n750)); + OR2 U851 ( .A(n899), .B(n900), .Z(n747)); + AN2 U852 ( .A(n901), .B(n771), .Z(n900)); + OR2 U853 ( .A(pi03), .B(n795), .Z(n771)); + AN2 U854 ( .A(n902), .B(n903), .Z(n899)); + OR2 U855 ( .A(n904), .B(n905), .Z(n903)); + OR2 U856 ( .A(n906), .B(n907), .Z(n905)); + AN2 U857 ( .A(n782), .B(n892), .Z(n907)); + AN2 U858 ( .A(n769), .B(n751), .Z(n906)); + AN2 U859 ( .A(po5), .B(n908), .Z(n904)); + OR2 U860 ( .A(n909), .B(n772), .Z(n889)); + AN2 U861 ( .A(n910), .B(n911), .Z(n909)); + OR2 U862 ( .A(n912), .B(n795), .Z(n911)); + OR2 U863 ( .A(n782), .B(n770), .Z(n910)); + OR2 U864 ( .A(n827), .B(n913), .Z(n886)); + OR2 U865 ( .A(n914), .B(n772), .Z(n913)); + OR2 U866 ( .A(n915), .B(n916), .Z(n914)); + AN2 U867 ( .A(n912), .B(n795), .Z(n916)); + IV2 U868 ( .A(n770), .Z(n912)); + AN2 U869 ( .A(n782), .B(n770), .Z(n915)); + OR2 U870 ( .A(n917), .B(n918), .Z(n770)); + AN2 U871 ( .A(n919), .B(n920), .Z(n917)); + IV2 U872 ( .A(n795), .Z(n782)); + OR2 U873 ( .A(n921), .B(n922), .Z(n787)); + AN2 U874 ( .A(n923), .B(n824), .Z(n922)); + AN2 U875 ( .A(n924), .B(n925), .Z(n923)); + OR2 U876 ( .A(n926), .B(n927), .Z(n925)); + AN2 U877 ( .A(pi11), .B(pi03), .Z(n926)); + AN2 U878 ( .A(pi07), .B(n928), .Z(n921)); + OR2 U879 ( .A(n929), .B(n930), .Z(n928)); + AN2 U880 ( .A(n931), .B(n804), .Z(n929)); + OR2 U881 ( .A(n932), .B(n933), .Z(n931)); + AN2 U882 ( .A(n854), .B(n795), .Z(n933)); + AN2 U883 ( .A(n934), .B(n827), .Z(n932)); + OR2 U884 ( .A(n935), .B(n936), .Z(n785)); + OR2 U885 ( .A(n937), .B(n938), .Z(n936)); + AN2 U886 ( .A(n939), .B(n940), .Z(n938)); + OR2 U887 ( .A(n941), .B(n942), .Z(n940)); + OR2 U888 ( .A(n769), .B(n943), .Z(n942)); + AN2 U889 ( .A(n874), .B(n944), .Z(n943)); + AN2 U890 ( .A(n795), .B(pi03), .Z(n769)); + OR2 U891 ( .A(n945), .B(n946), .Z(n795)); + OR2 U892 ( .A(n947), .B(n948), .Z(n946)); + AN2 U893 ( .A(n949), .B(n824), .Z(n948)); + AN2 U894 ( .A(n950), .B(n765), .Z(n947)); + IV2 U895 ( .A(n874), .Z(n765)); + OR2 U896 ( .A(n951), .B(n952), .Z(n945)); + OR2 U897 ( .A(n953), .B(n954), .Z(n952)); + AN2 U898 ( .A(n955), .B(n827), .Z(n954)); + OR2 U899 ( .A(n956), .B(n957), .Z(n955)); + AN2 U900 ( .A(n958), .B(n784), .Z(n956)); + AN2 U901 ( .A(pi07), .B(n959), .Z(n958)); + AN2 U902 ( .A(po5), .B(n960), .Z(n953)); + OR2 U903 ( .A(n961), .B(n962), .Z(n960)); + AN2 U904 ( .A(n892), .B(n963), .Z(n962)); + AN2 U905 ( .A(n964), .B(n965), .Z(n961)); + AN2 U906 ( .A(pi13), .B(n966), .Z(n951)); + OR2 U907 ( .A(n967), .B(n968), .Z(n966)); + OR2 U908 ( .A(n969), .B(n970), .Z(n968)); + AN2 U909 ( .A(n971), .B(pi02), .Z(n970)); + AN2 U910 ( .A(n972), .B(n973), .Z(n969)); + AN2 U911 ( .A(n974), .B(n975), .Z(n972)); + OR2 U912 ( .A(n874), .B(n959), .Z(n975)); + AN2 U913 ( .A(n827), .B(n824), .Z(n874)); + IV2 U914 ( .A(pi03), .Z(n827)); + OR2 U915 ( .A(n964), .B(n976), .Z(n974)); + AN2 U916 ( .A(pi03), .B(n824), .Z(n976)); + IV2 U917 ( .A(pi07), .Z(n824)); + IV2 U918 ( .A(n959), .Z(n964)); + OR2 U919 ( .A(n977), .B(n978), .Z(n959)); + AN2 U920 ( .A(n979), .B(pi02), .Z(n978)); + AN2 U921 ( .A(n980), .B(n717), .Z(n977)); + OR2 U922 ( .A(n979), .B(pi02), .Z(n980)); + AN2 U923 ( .A(n981), .B(po5), .Z(n967)); + AN2 U924 ( .A(po5), .B(n982), .Z(n941)); + AN2 U925 ( .A(pi03), .B(pi07), .Z(po5)); + AN2 U926 ( .A(n983), .B(pi03), .Z(n935)); + OR2 U927 ( .A(n984), .B(n985), .Z(po2)); + OR2 U928 ( .A(n986), .B(n987), .Z(n985)); + OR2 U929 ( .A(n988), .B(n989), .Z(n987)); + OR2 U930 ( .A(n990), .B(n991), .Z(n989)); + AN2 U931 ( .A(n793), .B(n992), .Z(n991)); + AN2 U932 ( .A(n794), .B(n993), .Z(n990)); + OR2 U933 ( .A(n994), .B(n995), .Z(n988)); + AN2 U934 ( .A(n777), .B(n801), .Z(n995)); + IV2 U935 ( .A(n800), .Z(n777)); + AN2 U936 ( .A(n776), .B(n800), .Z(n994)); + OR2 U937 ( .A(n996), .B(n804), .Z(n800)); + AN2 U938 ( .A(n997), .B(n998), .Z(n996)); + AN2 U939 ( .A(n999), .B(n1000), .Z(n998)); + AN2 U940 ( .A(n1001), .B(n1002), .Z(n1000)); + OR2 U941 ( .A(n1003), .B(n817), .Z(n1002)); + AN2 U942 ( .A(n1004), .B(n836), .Z(n1003)); + OR2 U943 ( .A(pi00), .B(n1005), .Z(n836)); + OR2 U944 ( .A(pi02), .B(pi01), .Z(n1005)); + AN2 U945 ( .A(n1006), .B(n1007), .Z(n1004)); + OR2 U946 ( .A(pi11), .B(n1008), .Z(n1007)); + AN2 U947 ( .A(n1009), .B(n821), .Z(n1008)); + OR2 U948 ( .A(n898), .B(n1010), .Z(n821)); + OR2 U949 ( .A(n1011), .B(n851), .Z(n1009)); + AN2 U950 ( .A(n1012), .B(n1013), .Z(n1011)); + OR2 U951 ( .A(n738), .B(n1014), .Z(n1006)); + OR2 U952 ( .A(n1015), .B(n1016), .Z(n1014)); + AN2 U953 ( .A(n713), .B(n1017), .Z(n1015)); + OR2 U954 ( .A(n1018), .B(n1019), .Z(n1001)); + OR2 U955 ( .A(n744), .B(n1020), .Z(n1019)); + OR2 U956 ( .A(n1021), .B(n1022), .Z(n1018)); + AN2 U957 ( .A(n717), .B(n1023), .Z(n1022)); + AN2 U958 ( .A(n863), .B(n1024), .Z(n1021)); + OR2 U959 ( .A(n1025), .B(n1026), .Z(n1024)); + OR2 U960 ( .A(n992), .B(n852), .Z(n1026)); + IV2 U961 ( .A(n1027), .Z(n1025)); + OR2 U962 ( .A(n1028), .B(n1027), .Z(n863)); + OR2 U963 ( .A(n1029), .B(n1030), .Z(n1027)); + AN2 U964 ( .A(n1031), .B(n1032), .Z(n1029)); + AN2 U965 ( .A(n1033), .B(n993), .Z(n1028)); + AN2 U966 ( .A(n885), .B(n1034), .Z(n999)); + OR2 U967 ( .A(n825), .B(n1035), .Z(n1034)); + OR2 U968 ( .A(n1036), .B(n1037), .Z(n1035)); + AN2 U969 ( .A(n1038), .B(n1039), .Z(n1037)); + IV2 U970 ( .A(n724), .Z(n1039)); + OR2 U971 ( .A(n877), .B(n738), .Z(n1038)); + IV2 U972 ( .A(n876), .Z(n1036)); + OR2 U973 ( .A(n1040), .B(n884), .Z(n885)); + OR2 U974 ( .A(n1041), .B(n1042), .Z(n884)); + OR2 U975 ( .A(n993), .B(n1032), .Z(n1042)); + AN2 U976 ( .A(n1043), .B(n1044), .Z(n997)); + AN2 U977 ( .A(n1045), .B(n1046), .Z(n1044)); + OR2 U978 ( .A(pi02), .B(n1047), .Z(n1046)); + AN2 U979 ( .A(n1048), .B(n1049), .Z(n1047)); + OR2 U980 ( .A(n876), .B(n1050), .Z(n1049)); + OR2 U981 ( .A(n717), .B(n825), .Z(n1050)); + AN2 U982 ( .A(n1051), .B(n1052), .Z(n1048)); + OR2 U983 ( .A(n1053), .B(n1054), .Z(n1052)); + OR2 U984 ( .A(n1055), .B(n772), .Z(n1051)); + AN2 U985 ( .A(n1056), .B(n1057), .Z(n1055)); + OR2 U986 ( .A(n1058), .B(n993), .Z(n1057)); + OR2 U987 ( .A(n992), .B(n919), .Z(n1056)); + OR2 U988 ( .A(n1059), .B(n1016), .Z(n1045)); + AN2 U989 ( .A(n1060), .B(n1061), .Z(n1059)); + AN2 U990 ( .A(n1062), .B(n1063), .Z(n1061)); + OR2 U991 ( .A(n876), .B(n1064), .Z(n1062)); + OR2 U992 ( .A(pi06), .B(n825), .Z(n1064)); + OR2 U993 ( .A(n1065), .B(n725), .Z(n876)); + AN2 U994 ( .A(n718), .B(n1066), .Z(n1065)); + AN2 U995 ( .A(n1067), .B(n1068), .Z(n1060)); + OR2 U996 ( .A(n772), .B(n1069), .Z(n1068)); + OR2 U997 ( .A(n1070), .B(n1071), .Z(n1069)); + AN2 U998 ( .A(n1058), .B(n993), .Z(n1071)); + IV2 U999 ( .A(n919), .Z(n1058)); + AN2 U1000 ( .A(n992), .B(n919), .Z(n1070)); + OR2 U1001 ( .A(n1072), .B(n1073), .Z(n919)); + AN2 U1002 ( .A(n1074), .B(n1075), .Z(n1072)); + OR2 U1003 ( .A(n1054), .B(n1076), .Z(n1067)); + IV2 U1004 ( .A(n1053), .Z(n1076)); + AN2 U1005 ( .A(n1077), .B(n1078), .Z(n1053)); + OR2 U1006 ( .A(n897), .B(n851), .Z(n1078)); + IV2 U1007 ( .A(n1079), .Z(n1077)); + AN2 U1008 ( .A(n851), .B(n897), .Z(n1079)); + OR2 U1009 ( .A(n1080), .B(n1081), .Z(n897)); + AN2 U1010 ( .A(pi01), .B(n1082), .Z(n1080)); + AN2 U1011 ( .A(n1083), .B(n1084), .Z(n1043)); + OR2 U1012 ( .A(pi10), .B(n1085), .Z(n1084)); + OR2 U1013 ( .A(n1086), .B(n1087), .Z(n1085)); + AN2 U1014 ( .A(n1088), .B(n1089), .Z(n1087)); + AN2 U1015 ( .A(n852), .B(n898), .Z(n1088)); + IV2 U1016 ( .A(n1033), .Z(n852)); + AN2 U1017 ( .A(n1090), .B(n853), .Z(n1086)); + IV2 U1018 ( .A(n1089), .Z(n853)); + AN2 U1019 ( .A(n1091), .B(n1082), .Z(n1089)); + OR2 U1020 ( .A(n1031), .B(n1092), .Z(n1091)); + OR2 U1021 ( .A(n851), .B(n1033), .Z(n1090)); + OR2 U1022 ( .A(n1093), .B(n1094), .Z(n1033)); + AN2 U1023 ( .A(n868), .B(n724), .Z(n1094)); + AN2 U1024 ( .A(n851), .B(n869), .Z(n1093)); + IV2 U1025 ( .A(n898), .Z(n851)); + OR2 U1026 ( .A(n1095), .B(n1096), .Z(n898)); + AN2 U1027 ( .A(n901), .B(n920), .Z(n1096)); + OR2 U1028 ( .A(pi02), .B(n993), .Z(n920)); + AN2 U1029 ( .A(n902), .B(n1097), .Z(n1095)); + OR2 U1030 ( .A(n1098), .B(n1099), .Z(n1097)); + OR2 U1031 ( .A(n1100), .B(n1101), .Z(n1099)); + AN2 U1032 ( .A(n992), .B(n892), .Z(n1101)); + AN2 U1033 ( .A(n918), .B(n751), .Z(n1100)); + AN2 U1034 ( .A(n908), .B(n724), .Z(n1098)); + OR2 U1035 ( .A(n1102), .B(n992), .Z(n1083)); + IV2 U1036 ( .A(n993), .Z(n992)); + AN2 U1037 ( .A(n1103), .B(n1104), .Z(n1102)); + OR2 U1038 ( .A(n883), .B(n1105), .Z(n1104)); + IV2 U1039 ( .A(n1106), .Z(n883)); + IV2 U1040 ( .A(n801), .Z(n776)); + OR2 U1041 ( .A(n1107), .B(n1108), .Z(n801)); + OR2 U1042 ( .A(pi12), .B(n1109), .Z(n1108)); + OR2 U1043 ( .A(n1110), .B(n1111), .Z(n986)); + AN2 U1044 ( .A(n1112), .B(n717), .Z(n1111)); + AN2 U1045 ( .A(n924), .B(n1113), .Z(n1112)); + OR2 U1046 ( .A(n1114), .B(n927), .Z(n1113)); + AN2 U1047 ( .A(pi11), .B(pi02), .Z(n1114)); + AN2 U1048 ( .A(pi06), .B(n1115), .Z(n1110)); + OR2 U1049 ( .A(n1116), .B(n930), .Z(n1115)); + AN2 U1050 ( .A(n1117), .B(n804), .Z(n1116)); + OR2 U1051 ( .A(n1118), .B(n1119), .Z(n1117)); + AN2 U1052 ( .A(n854), .B(n993), .Z(n1119)); + AN2 U1053 ( .A(n934), .B(n1016), .Z(n1118)); + OR2 U1054 ( .A(n1120), .B(n1121), .Z(n984)); + OR2 U1055 ( .A(n937), .B(n1122), .Z(n1121)); + AN2 U1056 ( .A(n939), .B(n1123), .Z(n1122)); + OR2 U1057 ( .A(n1124), .B(n1125), .Z(n1123)); + OR2 U1058 ( .A(n918), .B(n1126), .Z(n1125)); + AN2 U1059 ( .A(n724), .B(n982), .Z(n1126)); + AN2 U1060 ( .A(n993), .B(pi02), .Z(n918)); + OR2 U1061 ( .A(n1127), .B(n1128), .Z(n993)); + OR2 U1062 ( .A(n1129), .B(n1130), .Z(n1128)); + AN2 U1063 ( .A(n949), .B(n717), .Z(n1130)); + AN2 U1064 ( .A(n950), .B(n877), .Z(n1129)); + IV2 U1065 ( .A(n727), .Z(n877)); + OR2 U1066 ( .A(n1131), .B(n1132), .Z(n1127)); + OR2 U1067 ( .A(n1133), .B(n1134), .Z(n1132)); + AN2 U1068 ( .A(n1135), .B(n1016), .Z(n1134)); + OR2 U1069 ( .A(n1136), .B(n957), .Z(n1135)); + AN2 U1070 ( .A(n1137), .B(n979), .Z(n1136)); + AN2 U1071 ( .A(n784), .B(pi06), .Z(n1137)); + AN2 U1072 ( .A(n724), .B(n1138), .Z(n1133)); + OR2 U1073 ( .A(n1139), .B(n1140), .Z(n1138)); + AN2 U1074 ( .A(n965), .B(n1141), .Z(n1139)); + AN2 U1075 ( .A(pi02), .B(pi06), .Z(n724)); + AN2 U1076 ( .A(pi13), .B(n1142), .Z(n1131)); + OR2 U1077 ( .A(n1143), .B(n1144), .Z(n1142)); + AN2 U1078 ( .A(n971), .B(pi01), .Z(n1144)); + AN2 U1079 ( .A(n1145), .B(n973), .Z(n1143)); + AN2 U1080 ( .A(n1146), .B(n1147), .Z(n1145)); + OR2 U1081 ( .A(n727), .B(n979), .Z(n1147)); + IV2 U1082 ( .A(n1141), .Z(n979)); + OR2 U1083 ( .A(n1148), .B(n1141), .Z(n1146)); + OR2 U1084 ( .A(n1149), .B(n1150), .Z(n1141)); + AN2 U1085 ( .A(n1151), .B(n1017), .Z(n1150)); + AN2 U1086 ( .A(pi05), .B(n1152), .Z(n1149)); + OR2 U1087 ( .A(n1151), .B(n1017), .Z(n1152)); + AN2 U1088 ( .A(pi02), .B(n717), .Z(n1148)); + AN2 U1089 ( .A(n944), .B(n727), .Z(n1124)); + AN2 U1090 ( .A(n1016), .B(n717), .Z(n727)); + IV2 U1091 ( .A(pi06), .Z(n717)); + IV2 U1092 ( .A(pi02), .Z(n1016)); + AN2 U1093 ( .A(n983), .B(pi02), .Z(n1120)); + OR2 U1094 ( .A(n1153), .B(n1154), .Z(po1)); + OR2 U1095 ( .A(n1155), .B(n1156), .Z(n1154)); + OR2 U1096 ( .A(n1157), .B(n1158), .Z(n1156)); + OR2 U1097 ( .A(n1159), .B(n1160), .Z(n1158)); + AN2 U1098 ( .A(n793), .B(n1105), .Z(n1160)); + AN2 U1099 ( .A(n794), .B(n1032), .Z(n1159)); + OR2 U1100 ( .A(n1161), .B(n1162), .Z(n1157)); + AN2 U1101 ( .A(n1163), .B(n1107), .Z(n1162)); + IV2 U1102 ( .A(n1164), .Z(n1161)); + OR2 U1103 ( .A(n1107), .B(n1163), .Z(n1164)); + AN2 U1104 ( .A(n1165), .B(n1166), .Z(n1163)); + OR2 U1105 ( .A(n1167), .B(n804), .Z(n1107)); + AN2 U1106 ( .A(n1168), .B(n1169), .Z(n1167)); + AN2 U1107 ( .A(n1170), .B(n1171), .Z(n1169)); + OR2 U1108 ( .A(n817), .B(n1172), .Z(n1171)); + OR2 U1109 ( .A(pi11), .B(n1173), .Z(n1172)); + AN2 U1110 ( .A(n1010), .B(n1174), .Z(n1173)); + OR2 U1111 ( .A(n1012), .B(n1013), .Z(n1174)); + OR2 U1112 ( .A(n1175), .B(n1082), .Z(n1010)); + AN2 U1113 ( .A(n1176), .B(n1177), .Z(n1170)); + OR2 U1114 ( .A(pi10), .B(n1178), .Z(n1177)); + OR2 U1115 ( .A(n1179), .B(n1180), .Z(n1178)); + AN2 U1116 ( .A(n1181), .B(n1031), .Z(n1180)); + IV2 U1117 ( .A(n1182), .Z(n1179)); + OR2 U1118 ( .A(n1181), .B(n1031), .Z(n1182)); + OR2 U1119 ( .A(n1183), .B(n1184), .Z(n1181)); + AN2 U1120 ( .A(n1185), .B(n1082), .Z(n1184)); + AN2 U1121 ( .A(n1012), .B(n1092), .Z(n1183)); + OR2 U1122 ( .A(n825), .B(n1186), .Z(n1176)); + OR2 U1123 ( .A(n1187), .B(n1188), .Z(n1186)); + AN2 U1124 ( .A(n1189), .B(n1190), .Z(n1187)); + IV2 U1125 ( .A(n725), .Z(n1190)); + OR2 U1126 ( .A(n1066), .B(n738), .Z(n1189)); + AN2 U1127 ( .A(n1191), .B(n1192), .Z(n1168)); + AN2 U1128 ( .A(n1193), .B(n1194), .Z(n1192)); + OR2 U1129 ( .A(n1195), .B(n1017), .Z(n1194)); + AN2 U1130 ( .A(n1196), .B(n1197), .Z(n1195)); + AN2 U1131 ( .A(n1198), .B(n1199), .Z(n1197)); + OR2 U1132 ( .A(pi05), .B(n1200), .Z(n1199)); + AN2 U1133 ( .A(n1201), .B(n1063), .Z(n1198)); + OR2 U1134 ( .A(n1202), .B(n772), .Z(n1201)); + AN2 U1135 ( .A(n1203), .B(n1204), .Z(n1202)); + OR2 U1136 ( .A(n1074), .B(n1032), .Z(n1204)); + OR2 U1137 ( .A(n1105), .B(n1205), .Z(n1203)); + AN2 U1138 ( .A(n1206), .B(n1207), .Z(n1196)); + OR2 U1139 ( .A(n1208), .B(n1054), .Z(n1207)); + AN2 U1140 ( .A(n1209), .B(n1210), .Z(n1208)); + OR2 U1141 ( .A(n1081), .B(n1082), .Z(n1210)); + OR2 U1142 ( .A(n1012), .B(n1211), .Z(n1209)); + IV2 U1143 ( .A(n1081), .Z(n1211)); + OR2 U1144 ( .A(n817), .B(n1212), .Z(n1206)); + OR2 U1145 ( .A(n713), .B(n738), .Z(n1212)); + OR2 U1146 ( .A(pi01), .B(n1213), .Z(n1193)); + AN2 U1147 ( .A(n1214), .B(n1215), .Z(n1213)); + AN2 U1148 ( .A(n1216), .B(n1217), .Z(n1215)); + OR2 U1149 ( .A(n1054), .B(n1218), .Z(n1216)); + OR2 U1150 ( .A(n1012), .B(n1081), .Z(n1218)); + AN2 U1151 ( .A(pi00), .B(n1175), .Z(n1081)); + OR2 U1152 ( .A(pi08), .B(n1020), .Z(n1054)); + AN2 U1153 ( .A(n1219), .B(n1220), .Z(n1214)); + OR2 U1154 ( .A(n772), .B(n1221), .Z(n1220)); + OR2 U1155 ( .A(n1222), .B(n1223), .Z(n1221)); + AN2 U1156 ( .A(n1074), .B(n1032), .Z(n1223)); + AN2 U1157 ( .A(n1105), .B(n1205), .Z(n1222)); + OR2 U1158 ( .A(n1224), .B(n738), .Z(n772)); + AN2 U1159 ( .A(n1225), .B(n829), .Z(n1224)); + OR2 U1160 ( .A(n751), .B(n1023), .Z(n1225)); + OR2 U1161 ( .A(n716), .B(n1200), .Z(n1219)); + OR2 U1162 ( .A(n718), .B(n825), .Z(n1200)); + IV2 U1163 ( .A(n761), .Z(n825)); + AN2 U1164 ( .A(n1226), .B(n1227), .Z(n1191)); + OR2 U1165 ( .A(n1228), .B(n1229), .Z(n1227)); + OR2 U1166 ( .A(n1020), .B(n1230), .Z(n1229)); + OR2 U1167 ( .A(n1231), .B(n1232), .Z(n1230)); + AN2 U1168 ( .A(n1233), .B(n1234), .Z(n1232)); + AN2 U1169 ( .A(n1235), .B(n1031), .Z(n1233)); + OR2 U1170 ( .A(n1030), .B(n1032), .Z(n1235)); + AN2 U1171 ( .A(n1092), .B(n1041), .Z(n1030)); + IV2 U1172 ( .A(n1236), .Z(n1231)); + OR2 U1173 ( .A(n1234), .B(n1031), .Z(n1236)); + OR2 U1174 ( .A(n1237), .B(n1238), .Z(n1031)); + AN2 U1175 ( .A(n868), .B(n725), .Z(n1238)); + AN2 U1176 ( .A(n1012), .B(n869), .Z(n1237)); + IV2 U1177 ( .A(n1082), .Z(n1012)); + OR2 U1178 ( .A(n1239), .B(n1240), .Z(n1082)); + AN2 U1179 ( .A(n901), .B(n1075), .Z(n1240)); + OR2 U1180 ( .A(pi01), .B(n1032), .Z(n1075)); + AN2 U1181 ( .A(n902), .B(n1241), .Z(n1239)); + OR2 U1182 ( .A(n1242), .B(n1243), .Z(n1241)); + OR2 U1183 ( .A(n1244), .B(n1245), .Z(n1243)); + AN2 U1184 ( .A(n1105), .B(n892), .Z(n1245)); + AN2 U1185 ( .A(n1073), .B(n751), .Z(n1244)); + AN2 U1186 ( .A(n908), .B(n725), .Z(n1242)); + OR2 U1187 ( .A(n1185), .B(n1246), .Z(n1234)); + OR2 U1188 ( .A(n1247), .B(n1105), .Z(n1246)); + IV2 U1189 ( .A(n1248), .Z(n1020)); + OR2 U1190 ( .A(n1249), .B(n744), .Z(n1228)); + AN2 U1191 ( .A(n716), .B(n1023), .Z(n1249)); + AN2 U1192 ( .A(n1250), .B(n1251), .Z(n1226)); + OR2 U1193 ( .A(n1105), .B(n1103), .Z(n1251)); + IV2 U1194 ( .A(n1252), .Z(n1103)); + OR2 U1195 ( .A(n1253), .B(n1254), .Z(n1252)); + AN2 U1196 ( .A(n1106), .B(n1041), .Z(n1254)); + OR2 U1197 ( .A(n1255), .B(n780), .Z(n1106)); + AN2 U1198 ( .A(n973), .B(n738), .Z(n1255)); + AN2 U1199 ( .A(n854), .B(n738), .Z(n1253)); + IV2 U1200 ( .A(n1032), .Z(n1105)); + OR2 U1201 ( .A(n1032), .B(n1256), .Z(n1250)); + OR2 U1202 ( .A(n1040), .B(n1041), .Z(n1256)); + IV2 U1203 ( .A(n1257), .Z(n1040)); + OR2 U1204 ( .A(n1258), .B(n1259), .Z(n1155)); + AN2 U1205 ( .A(n1260), .B(n716), .Z(n1259)); + AN2 U1206 ( .A(n924), .B(n1261), .Z(n1260)); + OR2 U1207 ( .A(n1262), .B(n927), .Z(n1261)); + AN2 U1208 ( .A(pi11), .B(pi01), .Z(n1262)); + AN2 U1209 ( .A(pi05), .B(n1263), .Z(n1258)); + OR2 U1210 ( .A(n1264), .B(n930), .Z(n1263)); + AN2 U1211 ( .A(n1265), .B(n804), .Z(n1264)); + OR2 U1212 ( .A(n1266), .B(n1267), .Z(n1265)); + AN2 U1213 ( .A(n854), .B(n1032), .Z(n1267)); + AN2 U1214 ( .A(n934), .B(n1017), .Z(n1266)); + AN2 U1215 ( .A(pi11), .B(n761), .Z(n934)); + OR2 U1216 ( .A(n1268), .B(n1269), .Z(n1153)); + OR2 U1217 ( .A(n937), .B(n1270), .Z(n1269)); + AN2 U1218 ( .A(n939), .B(n1271), .Z(n1270)); + OR2 U1219 ( .A(n1272), .B(n1273), .Z(n1271)); + OR2 U1220 ( .A(n1073), .B(n1274), .Z(n1273)); + AN2 U1221 ( .A(n725), .B(n982), .Z(n1274)); + AN2 U1222 ( .A(n1032), .B(pi01), .Z(n1073)); + OR2 U1223 ( .A(n1275), .B(n1276), .Z(n1032)); + OR2 U1224 ( .A(n1277), .B(n1278), .Z(n1276)); + AN2 U1225 ( .A(n949), .B(n716), .Z(n1278)); + AN2 U1226 ( .A(n950), .B(n1066), .Z(n1277)); + IV2 U1227 ( .A(n723), .Z(n1066)); + OR2 U1228 ( .A(n1279), .B(n1280), .Z(n1275)); + OR2 U1229 ( .A(n1281), .B(n1282), .Z(n1280)); + AN2 U1230 ( .A(n1283), .B(n1017), .Z(n1282)); + OR2 U1231 ( .A(n1284), .B(n957), .Z(n1283)); + AN2 U1232 ( .A(n1285), .B(n784), .Z(n1284)); + AN2 U1233 ( .A(pi05), .B(n1286), .Z(n1285)); + AN2 U1234 ( .A(n725), .B(n1287), .Z(n1281)); + OR2 U1235 ( .A(n1288), .B(n1140), .Z(n1287)); + AN2 U1236 ( .A(n965), .B(n1151), .Z(n1288)); + AN2 U1237 ( .A(n744), .B(n902), .Z(n965)); + AN2 U1238 ( .A(pi01), .B(pi05), .Z(n725)); + AN2 U1239 ( .A(pi13), .B(n1289), .Z(n1279)); + OR2 U1240 ( .A(n1290), .B(n1291), .Z(n1289)); + AN2 U1241 ( .A(n971), .B(pi00), .Z(n1291)); + AN2 U1242 ( .A(n892), .B(n1292), .Z(n971)); + AN2 U1243 ( .A(pi10), .B(pi11), .Z(n1292)); + AN2 U1244 ( .A(n1293), .B(n973), .Z(n1290)); + AN2 U1245 ( .A(n1294), .B(n1295), .Z(n1293)); + OR2 U1246 ( .A(n723), .B(n1286), .Z(n1295)); + IV2 U1247 ( .A(n1151), .Z(n1286)); + OR2 U1248 ( .A(n1151), .B(n1296), .Z(n1294)); + AN2 U1249 ( .A(pi01), .B(n716), .Z(n1296)); + AN2 U1250 ( .A(n944), .B(n723), .Z(n1272)); + AN2 U1251 ( .A(n1017), .B(n716), .Z(n723)); + IV2 U1252 ( .A(pi05), .Z(n716)); + IV2 U1253 ( .A(pi01), .Z(n1017)); + AN2 U1254 ( .A(n983), .B(pi01), .Z(n1268)); + AN2 U1255 ( .A(pi11), .B(n1297), .Z(n983)); + OR2 U1256 ( .A(n1298), .B(n1299), .Z(po0)); + OR2 U1257 ( .A(n1300), .B(n1301), .Z(n1299)); + OR2 U1258 ( .A(n1302), .B(n1303), .Z(n1301)); + OR2 U1259 ( .A(n1304), .B(n1305), .Z(n1303)); + AN2 U1260 ( .A(n793), .B(n1247), .Z(n1305)); + AN2 U1261 ( .A(n924), .B(n1306), .Z(n793)); + IV2 U1262 ( .A(n1307), .Z(n1306)); + OR2 U1263 ( .A(n854), .B(n1308), .Z(n1307)); + AN2 U1264 ( .A(pi08), .B(n1063), .Z(n1308)); + IV2 U1265 ( .A(n1309), .Z(n1063)); + AN2 U1266 ( .A(n794), .B(n1041), .Z(n1304)); + AN2 U1267 ( .A(n1310), .B(n924), .Z(n794)); + OR2 U1268 ( .A(pi11), .B(n854), .Z(n1310)); + AN2 U1269 ( .A(pi11), .B(n1311), .Z(n1302)); + OR2 U1270 ( .A(n1312), .B(n1313), .Z(n1311)); + OR2 U1271 ( .A(n1314), .B(n1315), .Z(n1313)); + AN2 U1272 ( .A(n924), .B(n1316), .Z(n1315)); + AN2 U1273 ( .A(n1317), .B(n761), .Z(n1314)); + AN2 U1274 ( .A(n1023), .B(n908), .Z(n761)); + AN2 U1275 ( .A(n1151), .B(n804), .Z(n1317)); + AN2 U1276 ( .A(n1297), .B(pi00), .Z(n1312)); + AN2 U1277 ( .A(n804), .B(n1318), .Z(n1297)); + OR2 U1278 ( .A(pi10), .B(n892), .Z(n1318)); + OR2 U1279 ( .A(n1319), .B(n1320), .Z(n1300)); + AN2 U1280 ( .A(n1321), .B(n709), .Z(n1320)); + AN2 U1281 ( .A(n927), .B(n924), .Z(n1321)); + AN2 U1282 ( .A(pi04), .B(n1322), .Z(n1319)); + OR2 U1283 ( .A(n1323), .B(n930), .Z(n1322)); + AN2 U1284 ( .A(n939), .B(n1324), .Z(n930)); + AN2 U1285 ( .A(n744), .B(pi11), .Z(n1324)); + AN2 U1286 ( .A(n957), .B(n1041), .Z(n1323)); + OR2 U1287 ( .A(n1325), .B(n1326), .Z(n1298)); + OR2 U1288 ( .A(n937), .B(n1327), .Z(n1326)); + AN2 U1289 ( .A(pi13), .B(n1328), .Z(n1327)); + OR2 U1290 ( .A(n1329), .B(n1330), .Z(n1328)); + AN2 U1291 ( .A(n1109), .B(n1166), .Z(n1330)); + IV2 U1292 ( .A(pi12), .Z(n1166)); + IV2 U1293 ( .A(n1165), .Z(n1109)); + AN2 U1294 ( .A(pi12), .B(n1165), .Z(n1329)); + OR2 U1295 ( .A(n1331), .B(n1332), .Z(n1165)); + AN2 U1296 ( .A(pi13), .B(n1333), .Z(n1332)); + OR2 U1297 ( .A(n1334), .B(n1335), .Z(n1333)); + OR2 U1298 ( .A(n1336), .B(n1337), .Z(n1335)); + AN2 U1299 ( .A(n1247), .B(n1257), .Z(n1337)); + OR2 U1300 ( .A(n1338), .B(n780), .Z(n1257)); + AN2 U1301 ( .A(n1023), .B(n751), .Z(n780)); + AN2 U1302 ( .A(n944), .B(pi09), .Z(n1338)); + AN2 U1303 ( .A(pi11), .B(n1339), .Z(n1336)); + OR2 U1304 ( .A(n1340), .B(n1341), .Z(n1339)); + OR2 U1305 ( .A(n1342), .B(n1343), .Z(n1341)); + AN2 U1306 ( .A(n1344), .B(pi00), .Z(n1343)); + AN2 U1307 ( .A(n1345), .B(n1247), .Z(n1344)); + AN2 U1308 ( .A(pi10), .B(n1346), .Z(n1345)); + AN2 U1309 ( .A(n1041), .B(n713), .Z(n1342)); + IV2 U1310 ( .A(n1217), .Z(n1340)); + OR2 U1311 ( .A(pi00), .B(n817), .Z(n1217)); + OR2 U1312 ( .A(n1023), .B(n1346), .Z(n817)); + OR2 U1313 ( .A(n1347), .B(n1348), .Z(n1334)); + OR2 U1314 ( .A(n1349), .B(n1350), .Z(n1348)); + AN2 U1315 ( .A(n1316), .B(n1023), .Z(n1350)); + AN2 U1316 ( .A(n854), .B(n1351), .Z(n1349)); + OR2 U1317 ( .A(n1352), .B(n1353), .Z(n1351)); + AN2 U1318 ( .A(pi00), .B(n738), .Z(n1353)); + AN2 U1319 ( .A(pi09), .B(n1041), .Z(n1352)); + AN2 U1320 ( .A(n1248), .B(n1354), .Z(n1347)); + OR2 U1321 ( .A(n1355), .B(n1356), .Z(n1354)); + OR2 U1322 ( .A(n1357), .B(n1358), .Z(n1356)); + AN2 U1323 ( .A(n1185), .B(n1041), .Z(n1358)); + IV2 U1324 ( .A(n1092), .Z(n1185)); + AN2 U1325 ( .A(n1247), .B(n1092), .Z(n1357)); + OR2 U1326 ( .A(n1359), .B(n1360), .Z(n1092)); + AN2 U1327 ( .A(n868), .B(n718), .Z(n1360)); + AN2 U1328 ( .A(n1023), .B(n901), .Z(n868)); + AN2 U1329 ( .A(n973), .B(pi11), .Z(n901)); + AN2 U1330 ( .A(n869), .B(n1013), .Z(n1359)); + AN2 U1331 ( .A(n1361), .B(n927), .Z(n869)); + AN2 U1332 ( .A(n963), .B(pi08), .Z(n927)); + IV2 U1333 ( .A(n1041), .Z(n1247)); + AN2 U1334 ( .A(n1175), .B(n713), .Z(n1355)); + IV2 U1335 ( .A(n1013), .Z(n1175)); + AN2 U1336 ( .A(n1361), .B(n738), .Z(n1248)); + AN2 U1337 ( .A(n1362), .B(n751), .Z(n1331)); + AN2 U1338 ( .A(n902), .B(n1013), .Z(n1362)); + OR2 U1339 ( .A(n1363), .B(n1364), .Z(n1013)); + IV2 U1340 ( .A(n902), .Z(n1364)); + AN2 U1341 ( .A(n1365), .B(n1366), .Z(n1363)); + OR2 U1342 ( .A(n1188), .B(n857), .Z(n1366)); + IV2 U1343 ( .A(n908), .Z(n857)); + IV2 U1344 ( .A(n718), .Z(n1188)); + AN2 U1345 ( .A(n1367), .B(n1368), .Z(n1365)); + OR2 U1346 ( .A(n1346), .B(n1205), .Z(n1368)); + IV2 U1347 ( .A(n1074), .Z(n1205)); + IV2 U1348 ( .A(n751), .Z(n1346)); + OR2 U1349 ( .A(n829), .B(n1041), .Z(n1367)); + IV2 U1350 ( .A(n892), .Z(n829)); + AN2 U1351 ( .A(n1309), .B(n1369), .Z(n937)); + AN2 U1352 ( .A(pi13), .B(n751), .Z(n1369)); + AN2 U1353 ( .A(n939), .B(n1370), .Z(n1325)); + OR2 U1354 ( .A(n1371), .B(n1372), .Z(n1370)); + OR2 U1355 ( .A(n1074), .B(n1373), .Z(n1372)); + AN2 U1356 ( .A(n1374), .B(n944), .Z(n1373)); + AN2 U1357 ( .A(n713), .B(n709), .Z(n1374)); + AN2 U1358 ( .A(n1041), .B(pi00), .Z(n1074)); + OR2 U1359 ( .A(n1375), .B(n1376), .Z(n1041)); + OR2 U1360 ( .A(n1377), .B(n1378), .Z(n1376)); + OR2 U1361 ( .A(n1379), .B(n1380), .Z(n1378)); + AN2 U1362 ( .A(n949), .B(n709), .Z(n1380)); + OR2 U1363 ( .A(n1381), .B(n1382), .Z(n949)); + OR2 U1364 ( .A(n1383), .B(n1384), .Z(n1382)); + AN2 U1365 ( .A(n751), .B(n963), .Z(n1384)); + AN2 U1366 ( .A(n1385), .B(n860), .Z(n1383)); + IV2 U1367 ( .A(n963), .Z(n860)); + AN2 U1368 ( .A(n1386), .B(n924), .Z(n1381)); + AN2 U1369 ( .A(n804), .B(n1361), .Z(n924)); + AN2 U1370 ( .A(pi08), .B(pi10), .Z(n1386)); + AN2 U1371 ( .A(n718), .B(n1140), .Z(n1379)); + OR2 U1372 ( .A(n1387), .B(n981), .Z(n1140)); + AN2 U1373 ( .A(pi11), .B(n1388), .Z(n981)); + AN2 U1374 ( .A(n1023), .B(n1389), .Z(n1388)); + OR2 U1375 ( .A(n751), .B(n892), .Z(n1389)); + AN2 U1376 ( .A(n744), .B(n1361), .Z(n892)); + AN2 U1377 ( .A(pi09), .B(pi08), .Z(n751)); + AN2 U1378 ( .A(n944), .B(n1361), .Z(n1387)); + AN2 U1379 ( .A(n744), .B(n963), .Z(n944)); + AN2 U1380 ( .A(n784), .B(n1151), .Z(n1377)); + AN2 U1381 ( .A(n713), .B(pi04), .Z(n1151)); + AN2 U1382 ( .A(n973), .B(n902), .Z(n784)); + AN2 U1383 ( .A(n963), .B(pi13), .Z(n902)); + AN2 U1384 ( .A(n738), .B(pi10), .Z(n963)); + OR2 U1385 ( .A(n1390), .B(n1391), .Z(n1375)); + OR2 U1386 ( .A(n1392), .B(n1393), .Z(n1391)); + AN2 U1387 ( .A(n950), .B(n1394), .Z(n1393)); + OR2 U1388 ( .A(pi00), .B(pi04), .Z(n1394)); + AN2 U1389 ( .A(n1395), .B(n908), .Z(n950)); + AN2 U1390 ( .A(n1361), .B(pi08), .Z(n908)); + IV2 U1391 ( .A(pi09), .Z(n1361)); + OR2 U1392 ( .A(pi13), .B(n1309), .Z(n1395)); + AN2 U1393 ( .A(n1023), .B(n738), .Z(n1309)); + IV2 U1394 ( .A(pi11), .Z(n738)); + AN2 U1395 ( .A(n1385), .B(n1316), .Z(n1392)); + AN2 U1396 ( .A(n709), .B(pi00), .Z(n1316)); + IV2 U1397 ( .A(pi04), .Z(n709)); + AN2 U1398 ( .A(n973), .B(pi13), .Z(n1385)); + AN2 U1399 ( .A(n744), .B(pi09), .Z(n973)); + AN2 U1400 ( .A(n957), .B(n713), .Z(n1390)); + IV2 U1401 ( .A(pi00), .Z(n713)); + AN2 U1402 ( .A(n804), .B(n854), .Z(n957)); + AN2 U1403 ( .A(n744), .B(n1023), .Z(n854)); + IV2 U1404 ( .A(pi10), .Z(n1023)); + AN2 U1405 ( .A(n718), .B(n982), .Z(n1371)); + OR2 U1406 ( .A(n1396), .B(pi11), .Z(n982)); + AN2 U1407 ( .A(pi10), .B(n744), .Z(n1396)); + IV2 U1408 ( .A(pi08), .Z(n744)); + AN2 U1409 ( .A(pi00), .B(pi04), .Z(n718)); + AN2 U1410 ( .A(n804), .B(pi09), .Z(n939)); + IV2 U1411 ( .A(pi13), .Z(n804)); + +endmodule + +module IV2(A, Z); + input A; + output Z; + + assign Z = ~A; +endmodule + +module AN2(A, B, Z); + input A, B; + output Z; + + assign Z = A & B; +endmodule + +module OR2(A, B, Z); + input A, B; + output Z; + + assign Z = A | B; +endmodule diff --git a/examples/smtbmc/glift/alu4.ys b/examples/smtbmc/glift/alu4.ys new file mode 100644 index 00000000000..8e8d14225f3 --- /dev/null +++ b/examples/smtbmc/glift/alu4.ys @@ -0,0 +1,41 @@ +read_verilog alu4.v +techmap +flatten +select alu4_lev2 +glift -create-instrumented-model +techmap +opt +rename alu4_lev2 uut +cd .. +delete [AIONX][NVXR]2 +read_verilog alu4.v +techmap +flatten +select alu4_lev2 +glift -create-precise-model +techmap +opt +rename alu4_lev2 spec +cd .. +delete [AIONX][NVXR]2 + +design -push-copy +miter -equiv spec uut miter +flatten +delete uut spec +techmap +opt +stat miter +qbfsat -O2 -write-solution alu4.soln -solver yices -timeout 3600 -nocleanup -assume-outputs -assume-negative-polarity miter +design -pop +stat + +copy uut solved +qbfsat -specialize-from-file alu4.soln solved +opt solved +miter -equiv spec solved satmiter +flatten +sat -prove trigger 0 satmiter +delete satmiter +stat +shell diff --git a/examples/smtbmc/glift/mux2.ys b/examples/smtbmc/glift/mux2.ys new file mode 100644 index 00000000000..a8e99912b26 --- /dev/null +++ b/examples/smtbmc/glift/mux2.ys @@ -0,0 +1,40 @@ +logger -expect log "SAT proof finished - no model found: SUCCESS!" 1 +logger -expect log "Number of cells:.*[\t ]12" 1 +logger -expect log "Number of cells:.*[\t ]20" 1 +logger -expect log "Problem is satisfiable with \\gate.__glift_weight = 11." 1 +logger -expect log "Problem is NOT satisfiable with \\gate.__glift_weight <= 10." 1 +logger -expect log "Wire \\gate.__glift_weight is minimized at 11." 1 +logger -expect log "Specializing .* from file with .* = 1." 2 +logger -expect log "Specializing .* from file with .* = 0." 4 +read_verilog < + * Copyright (C) 2012 Claire Xenia Wolf * 2019 Eddie Hung * * Permission to use, copy, modify, and/or distribute this software for any @@ -55,6 +55,17 @@ inline int32_t from_big_endian(int32_t i32) { #define log_debug2(...) ; //#define log_debug2(...) log_debug(__VA_ARGS__) +static int decimal_digits(uint32_t n) { + static uint32_t digit_cutoff[9] = { + 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000, 1000000000 + }; + for (int i = 0; i < 9; ++i) { + if (n < digit_cutoff[i]) return i + 1; + } + return 10; +} + struct ConstEvalAig { RTLIL::Module *module; @@ -515,7 +526,7 @@ void AigerReader::parse_aiger_ascii() unsigned l1, l2, l3; // Parse inputs - int digits = ceil(log10(I)); + int digits = decimal_digits(I); for (unsigned i = 1; i <= I; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as an input!\n", line_count); @@ -537,7 +548,7 @@ void AigerReader::parse_aiger_ascii() clk_wire->port_input = true; clk_wire->port_output = false; } - digits = ceil(log10(L)); + digits = decimal_digits(L); for (unsigned i = 0; i < L; ++i, ++line_count) { if (!(f >> l1 >> l2)) log_error("Line %u cannot be interpreted as a latch!\n", line_count); @@ -575,7 +586,7 @@ void AigerReader::parse_aiger_ascii() } // Parse outputs - digits = ceil(log10(O)); + digits = decimal_digits(O); for (unsigned i = 0; i < O; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); @@ -643,7 +654,7 @@ void AigerReader::parse_aiger_binary() std::string line; // Parse inputs - int digits = ceil(log10(I)); + int digits = decimal_digits(I); for (unsigned i = 1; i <= I; ++i) { log_debug2("%d is an input\n", i); RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, i)); @@ -662,7 +673,7 @@ void AigerReader::parse_aiger_binary() clk_wire->port_input = true; clk_wire->port_output = false; } - digits = ceil(log10(L)); + digits = decimal_digits(L); l1 = (I+1) * 2; for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { if (!(f >> l2)) @@ -700,7 +711,7 @@ void AigerReader::parse_aiger_binary() } // Parse outputs - digits = ceil(log10(O)); + digits = decimal_digits(O); for (unsigned i = 0; i < O; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 251a24977e2..81b9559474f 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * 2019 Eddie Hung * * Permission to use, copy, modify, and/or distribute this software for any diff --git a/frontends/ast/Makefile.inc b/frontends/ast/Makefile.inc index 91d917c9169..9e6eee1e8fb 100644 --- a/frontends/ast/Makefile.inc +++ b/frontends/ast/Makefile.inc @@ -3,4 +3,5 @@ OBJS += frontends/ast/ast.o OBJS += frontends/ast/simplify.o OBJS += frontends/ast/genrtlil.o OBJS += frontends/ast/dpicall.o +OBJS += frontends/ast/ast_binding.o diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index c8183580bca..996f6715d05 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * Copyright (C) 2018 Ruben Undheim * * Permission to use, copy, modify, and/or distribute this software for any @@ -45,15 +45,17 @@ namespace AST { // instantiate global variables (private API) namespace AST_INTERNAL { - bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; + bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; bool flag_nomem2reg, flag_mem2reg, flag_noblackbox, flag_lib, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map current_scope; const dict *genRTLIL_subst_ptr = NULL; RTLIL::SigSpec ignoreThisSignalsInInitial; AstNode *current_always, *current_top_block, *current_block, *current_block_child; - AstModule *current_module; + Module *current_module; bool current_always_clocked; + dict current_memwr_count; + dict> current_memwr_visible; } // convert node types to string @@ -175,6 +177,7 @@ std::string AST::type2str(AstNodeType type) X(AST_STRUCT) X(AST_UNION) X(AST_STRUCT_ITEM) + X(AST_BIND) #undef X default: log_abort(); @@ -189,14 +192,14 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) AstNode *attr = attributes.at(id); if (attr->type != AST_CONSTANT) - log_file_error(attr->filename, attr->location.first_line, "Attribute `%s' with non-constant value!\n", id.c_str()); + attr->input_error("Attribute `%s' with non-constant value!\n", id.c_str()); return attr->integer != 0; } // create new node (AstNode constructor) // (the optional child arguments make it easier to create AST trees) -AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3) +AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *child3, AstNode *child4) { static unsigned int hashidx_count = 123456789; hashidx_count = mkhash_xorshift(hashidx_count); @@ -221,11 +224,16 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch port_id = 0; range_left = -1; range_right = 0; + unpacked_dimensions = 0; integer = 0; realvalue = 0; id2ast = NULL; basic_prep = false; lookahead = false; + in_lvalue_from_above = false; + in_param_from_above = false; + in_lvalue = false; + in_param = false; if (child1) children.push_back(child1); @@ -233,6 +241,10 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch children.push_back(child2); if (child3) children.push_back(child3); + if (child4) + children.push_back(child4); + + fixup_hierarchy_flags(); } // create a (deep recursive) copy of a node @@ -244,6 +256,10 @@ AstNode *AstNode::clone() const it = it->clone(); for (auto &it : that->attributes) it.second = it.second->clone(); + + that->set_in_lvalue_flag(false); + that->set_in_param_flag(false); + that->fixup_hierarchy_flags(); // fixup to set flags on cloned children return that; } @@ -251,10 +267,13 @@ AstNode *AstNode::clone() const void AstNode::cloneInto(AstNode *other) const { AstNode *tmp = clone(); + tmp->in_lvalue_from_above = other->in_lvalue_from_above; + tmp->in_param_from_above = other->in_param_from_above; other->delete_children(); *other = *tmp; tmp->children.clear(); tmp->attributes.clear(); + other->fixup_hierarchy_flags(); delete tmp; } @@ -287,8 +306,7 @@ void AstNode::dumpAst(FILE *f, std::string indent) const } std::string type_name = type2str(type); - fprintf(f, "%s%s <%s:%d.%d-%d.%d>", indent.c_str(), type_name.c_str(), filename.c_str(), location.first_line, - location.first_column, location.last_line, location.last_column); + fprintf(f, "%s%s <%s>", indent.c_str(), type_name.c_str(), loc_string().c_str()); if (!flag_no_dump_ptr) { if (id2ast) @@ -318,6 +336,8 @@ void AstNode::dumpAst(FILE *f, std::string indent) const fprintf(f, " reg"); if (is_signed) fprintf(f, " signed"); + if (is_unsized) + fprintf(f, " unsized"); if (basic_prep) fprintf(f, " basic_prep"); if (lookahead) @@ -330,21 +350,23 @@ void AstNode::dumpAst(FILE *f, std::string indent) const fprintf(f, " int=%u", (int)integer); if (realvalue != 0) fprintf(f, " real=%e", realvalue); - if (!multirange_dimensions.empty()) { - fprintf(f, " multirange=["); - for (int v : multirange_dimensions) - fprintf(f, " %d", v); - fprintf(f, " ]"); - } - if (!multirange_swapped.empty()) { - fprintf(f, " multirange_swapped=["); - for (auto v : multirange_swapped) - fprintf(f, " %d", v); - fprintf(f, " ]"); + if (!dimensions.empty()) { + fprintf(f, " dimensions="); + for (auto &dim : dimensions) { + int left = dim.range_right + dim.range_width - 1; + int right = dim.range_right; + if (dim.range_swapped) + std::swap(left, right); + fprintf(f, "[%d:%d]", left, right); + } } if (is_enum) { fprintf(f, " type=enum"); } + if (in_lvalue) + fprintf(f, " in_lvalue"); + if (in_param) + fprintf(f, " in_param"); fprintf(f, "\n"); for (auto &it : attributes) { @@ -466,6 +488,20 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const fprintf(f, ";\n"); break; + if (0) { case AST_MEMRD: txt = "@memrd@"; } + if (0) { case AST_MEMINIT: txt = "@meminit@"; } + if (0) { case AST_MEMWR: txt = "@memwr@"; } + fprintf(f, "%s%s", indent.c_str(), txt.c_str()); + for (auto child : children) { + fprintf(f, first ? "(" : ", "); + child->dumpVlog(f, ""); + first = false; + } + fprintf(f, ")"); + if (type != AST_MEMRD) + fprintf(f, ";\n"); + break; + case AST_RANGE: if (range_valid) { if (range_swapped) @@ -482,6 +518,11 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const } break; + case AST_MULTIRANGE: + for (auto child : children) + child->dumpVlog(f, ""); + break; + case AST_ALWAYS: fprintf(f, "%s" "always @", indent.c_str()); for (auto child : children) { @@ -518,11 +559,23 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const break; case AST_IDENTIFIER: - fprintf(f, "%s", id2vl(str).c_str()); + { + AstNode *member_node = get_struct_member(); + if (member_node) + fprintf(f, "%s[%d:%d]", id2vl(str).c_str(), member_node->range_left, member_node->range_right); + else + fprintf(f, "%s", id2vl(str).c_str()); + } for (auto child : children) child->dumpVlog(f, ""); break; + case AST_STRUCT: + case AST_UNION: + case AST_STRUCT_ITEM: + fprintf(f, "%s", id2vl(str).c_str()); + break; + case AST_CONSTANT: if (!str.empty()) fprintf(f, "\"%s\"", str.c_str()); @@ -548,9 +601,9 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const break; case AST_CASE: - if (!children.empty() && children[0]->type == AST_CONDX) + if (children.size() > 1 && children[1]->type == AST_CONDX) fprintf(f, "%s" "casex (", indent.c_str()); - else if (!children.empty() && children[0]->type == AST_CONDZ) + else if (children.size() > 1 && children[1]->type == AST_CONDZ) fprintf(f, "%s" "casez (", indent.c_str()); else fprintf(f, "%s" "case (", indent.c_str()); @@ -629,11 +682,20 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const if (0) { case AST_NEG: txt = "-"; } if (0) { case AST_LOGIC_NOT: txt = "!"; } if (0) { case AST_SELFSZ: txt = "@selfsz@"; } + if (0) { case AST_TO_SIGNED: txt = "signed'"; } + if (0) { case AST_TO_UNSIGNED: txt = "unsigned'"; } fprintf(f, "%s(", txt.c_str()); children[0]->dumpVlog(f, ""); fprintf(f, ")"); break; + case AST_CAST_SIZE: + children[0]->dumpVlog(f, ""); + fprintf(f, "'("); + children[1]->dumpVlog(f, ""); + fprintf(f, ")"); + break; + if (0) { case AST_BIT_AND: txt = "&"; } if (0) { case AST_BIT_OR: txt = "|"; } if (0) { case AST_BIT_XOR: txt = "^"; } @@ -812,6 +874,25 @@ AstNode *AstNode::mkconst_str(const std::string &str) return node; } +// create a temporary register +AstNode *AstNode::mktemp_logic(const std::string &name, AstNode *mod, bool nosync, int range_left, int range_right, bool is_signed) +{ + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(range_left, true), mkconst_int(range_right, true))); + wire->str = stringf("%s%s:%d$%d", name.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + if (nosync) + wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + wire->is_signed = is_signed; + wire->is_logic = true; + mod->children.push_back(wire); + while (wire->simplify(true, 1, -1, false)) { } + + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = wire->str; + ident->id2ast = wire; + + return ident; +} + bool AstNode::bits_only_01() const { for (auto bit : bits) @@ -835,7 +916,7 @@ RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed) bits.resize(width); if (width >= 0 && width > int(bits.size())) { RTLIL::State extbit = RTLIL::State::S0; - if (is_signed && !bits.empty()) + if ((is_signed || is_unsized) && !bits.empty()) extbit = bits.back(); while (width > int(bits.size())) bits.push_back(extbit); @@ -848,7 +929,7 @@ RTLIL::Const AstNode::bitsAsConst(int width) return bitsAsConst(width, is_signed); } -RTLIL::Const AstNode::asAttrConst() +RTLIL::Const AstNode::asAttrConst() const { log_assert(type == AST_CONSTANT); @@ -863,8 +944,17 @@ RTLIL::Const AstNode::asAttrConst() return val; } -RTLIL::Const AstNode::asParaConst() +RTLIL::Const AstNode::asParaConst() const { + if (type == AST_REALVALUE) + { + AstNode *strnode = AstNode::mkconst_str(stringf("%f", realvalue)); + RTLIL::Const val = strnode->asAttrConst(); + val.flags |= RTLIL::CONST_FLAG_REAL; + delete strnode; + return val; + } + RTLIL::Const val = asAttrConst(); if (is_signed) val.flags |= RTLIL::CONST_FLAG_SIGNED; @@ -959,8 +1049,25 @@ RTLIL::Const AstNode::realAsConst(int width) return result; } -// create a new AstModule from an AST_MODULE AST node -static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false) +std::string AstNode::loc_string() const +{ + return stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); +} + +void AST::set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast) +{ + obj->attributes[ID::src] = ast->loc_string(); +} + +static bool param_has_no_default(const AstNode *param) { + const auto &children = param->children; + log_assert(param->type == AST_PARAMETER); + log_assert(children.size() <= 2); + return children.empty() || + (children.size() == 1 && children[0]->type == AST_RANGE); +} + +static RTLIL::Module *process_module(RTLIL::Design *design, AstNode *ast, bool defer, AstNode *original_ast = NULL, bool quiet = false) { log_assert(current_scope.empty()); log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE); @@ -971,12 +1078,13 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); } - current_module = new AstModule; - current_module->ast = NULL; - current_module->name = ast->str; - current_module->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, - ast->location.first_column, ast->location.last_line, ast->location.last_column); - current_module->set_bool_attribute(ID::cells_not_processed); + AstModule *module = new AstModule; + current_module = module; + + module->ast = NULL; + module->name = ast->str; + set_src_attr(module, ast); + module->set_bool_attribute(ID::cells_not_processed); current_ast_mod = ast; AstNode *ast_before_simplify; @@ -998,6 +1106,10 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast if (!defer) { + for (const AstNode *node : ast->children) + if (node->type == AST_PARAMETER && param_has_no_default(node)) + node->input_error("Parameter `%s' has no default value and has not been overridden!\n", node->str.c_str()); + bool blackbox_module = flag_lib; if (!blackbox_module && !flag_noblackbox) { @@ -1015,7 +1127,11 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast } } - while (ast->simplify(!flag_noopt, false, false, 0, -1, false, false)) { } + // simplify this module or interface using the current design as context + // for lookup up ports and wires within cells + set_simplify_design_context(design); + while (ast->simplify(!flag_noopt, 0, -1, false)) { } + set_simplify_design_context(nullptr); if (flag_dump_ast2) { log("Dumping AST after simplification:\n"); @@ -1044,7 +1160,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast ast->attributes.erase(ID::whitebox); } AstNode *n = ast->attributes.at(ID::lib_whitebox); - ast->attributes[ID::whitebox] = n; + ast->set_attribute(ID::whitebox, n); ast->attributes.erase(ID::lib_whitebox); } } @@ -1052,14 +1168,14 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast if (!blackbox_module && ast->attributes.count(ID::blackbox)) { AstNode *n = ast->attributes.at(ID::blackbox); if (n->type != AST_CONSTANT) - log_file_error(ast->filename, ast->location.first_line, "Got blackbox attribute with non-constant value!\n"); + ast->input_error("Got blackbox attribute with non-constant value!\n"); blackbox_module = n->asBool(); } if (blackbox_module && ast->attributes.count(ID::whitebox)) { AstNode *n = ast->attributes.at(ID::whitebox); if (n->type != AST_CONSTANT) - log_file_error(ast->filename, ast->location.first_line, "Got whitebox attribute with non-constant value!\n"); + ast->input_error("Got whitebox attribute with non-constant value!\n"); blackbox_module = !n->asBool(); } @@ -1067,7 +1183,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast if (blackbox_module) { AstNode *n = ast->attributes.at(ID::noblackbox); if (n->type != AST_CONSTANT) - log_file_error(ast->filename, ast->location.first_line, "Got noblackbox attribute with non-constant value!\n"); + ast->input_error("Got noblackbox attribute with non-constant value!\n"); blackbox_module = !n->asBool(); } delete ast->attributes.at(ID::noblackbox); @@ -1103,7 +1219,7 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast ast->children.swap(new_children); if (ast->attributes.count(ID::blackbox) == 0) { - ast->attributes[ID::blackbox] = AstNode::mkconst_int(1, false); + ast->set_attribute(ID::blackbox, AstNode::mkconst_int(1, false)); } } @@ -1111,8 +1227,8 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); - current_module->attributes[attr.first] = attr.second->asAttrConst(); + ast->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); + module->attributes[attr.first] = attr.second->asAttrConst(); } for (size_t i = 0; i < ast->children.size(); i++) { AstNode *node = ast->children[i]; @@ -1140,41 +1256,100 @@ static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) continue; - current_module->attributes[attr.first] = attr.second->asAttrConst(); + module->attributes[attr.first] = attr.second->asAttrConst(); } + for (const AstNode *node : ast->children) + if (node->type == AST_PARAMETER) + current_module->avail_parameters(node->str); } if (ast->type == AST_INTERFACE) - current_module->set_bool_attribute(ID::is_interface); - current_module->ast = ast_before_simplify; - current_module->nolatches = flag_nolatches; - current_module->nomeminit = flag_nomeminit; - current_module->nomem2reg = flag_nomem2reg; - current_module->mem2reg = flag_mem2reg; - current_module->noblackbox = flag_noblackbox; - current_module->lib = flag_lib; - current_module->nowb = flag_nowb; - current_module->noopt = flag_noopt; - current_module->icells = flag_icells; - current_module->pwires = flag_pwires; - current_module->autowire = flag_autowire; - current_module->fixup_ports(); + module->set_bool_attribute(ID::is_interface); + module->ast = ast_before_simplify; + module->nolatches = flag_nolatches; + module->nomeminit = flag_nomeminit; + module->nomem2reg = flag_nomem2reg; + module->mem2reg = flag_mem2reg; + module->noblackbox = flag_noblackbox; + module->lib = flag_lib; + module->nowb = flag_nowb; + module->noopt = flag_noopt; + module->icells = flag_icells; + module->pwires = flag_pwires; + module->autowire = flag_autowire; + module->fixup_ports(); if (flag_dump_rtlil) { log("Dumping generated RTLIL:\n"); - log_module(current_module); + log_module(module); log("--- END OF RTLIL DUMP ---\n"); } + design->add(current_module); return current_module; } +RTLIL::Module * +AST_INTERNAL::process_and_replace_module(RTLIL::Design *design, + RTLIL::Module *old_module, + AstNode *new_ast, + AstNode *original_ast) +{ + // The old module will be deleted. Rename and mark for deletion, using + // a static counter to make sure we get a unique name. + static unsigned counter; + std::ostringstream new_name; + new_name << old_module->name.str() + << "_before_process_and_replace_module_" + << counter; + ++counter; + + design->rename(old_module, new_name.str()); + old_module->set_bool_attribute(ID::to_delete); + + // Check if the module was the top module. If it was, we need to remove + // the top attribute and put it on the new module. + bool is_top = false; + if (old_module->get_bool_attribute(ID::initial_top)) { + old_module->attributes.erase(ID::initial_top); + is_top = true; + } + + // Generate RTLIL from AST for the new module and add to the design: + RTLIL::Module* new_module = process_module(design, new_ast, false, original_ast); + + if (is_top) + new_module->set_bool_attribute(ID::top); + + return new_module; +} + +// renames identifiers in tasks and functions within a package +static void rename_in_package_stmts(AstNode *pkg) +{ + std::unordered_set idents; + for (AstNode *item : pkg->children) + idents.insert(item->str); + std::function rename = + [&rename, &idents, pkg](AstNode *node) { + for (AstNode *child : node->children) { + if (idents.count(child->str)) + child->str = pkg->str + "::" + child->str.substr(1); + rename(child); + } + }; + for (AstNode *item : pkg->children) + if (item->type == AST_FUNCTION || item->type == AST_TASK) + rename(item); +} + // create AstModule instances for all modules in the AST tree and add them to 'design' -void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, +void AST::process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; current_ast_mod = nullptr; + flag_nodisplay = nodisplay; flag_dump_ast1 = dump_ast1; flag_dump_ast2 = dump_ast2; flag_no_dump_ptr = no_dump_ptr; @@ -1193,13 +1368,15 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump flag_pwires = pwires; flag_autowire = autowire; + ast->fixup_hierarchy_flags(true); + log_assert(current_ast->type == AST_DESIGN); - for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) + for (AstNode *child : current_ast->children) { - if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE) + if (child->type == AST_MODULE || child->type == AST_INTERFACE) { for (auto n : design->verilog_globals) - (*it)->children.push_back(n->clone()); + child->children.push_back(n->clone()); // append nodes from previous packages using package-qualified names for (auto &n : design->verilog_packages) { @@ -1214,45 +1391,63 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump } else { cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1); } - (*it)->children.push_back(cloned_node); + child->children.push_back(cloned_node); } } - if (flag_icells && (*it)->str.compare(0, 2, "\\$") == 0) - (*it)->str = (*it)->str.substr(1); + if (flag_icells && child->str.compare(0, 2, "\\$") == 0) + child->str = child->str.substr(1); - if (defer) - (*it)->str = "$abstract" + (*it)->str; + bool defer_local = defer; + if (!defer_local) + for (const AstNode *node : child->children) + if (node->type == AST_PARAMETER && param_has_no_default(node)) + { + log("Deferring `%s' because it contains parameter(s) without defaults.\n", child->str.c_str()); + defer_local = true; + break; + } + + + if (defer_local) + child->str = "$abstract" + child->str; - if (design->has((*it)->str)) { - RTLIL::Module *existing_mod = design->module((*it)->str); + if (design->has(child->str)) { + RTLIL::Module *existing_mod = design->module(child->str); if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) { - log_file_error((*it)->filename, (*it)->location.first_line, "Re-definition of module `%s'!\n", (*it)->str.c_str()); + log_file_error(child->filename, child->location.first_line, "Re-definition of module `%s'!\n", child->str.c_str()); } else if (nooverwrite) { - log("Ignoring re-definition of module `%s' at %s:%d.%d-%d.%d.\n", - (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->location.first_line, (*it)->location.first_column, (*it)->location.last_line, (*it)->location.last_column); + log("Ignoring re-definition of module `%s' at %s.\n", + child->str.c_str(), child->loc_string().c_str()); continue; } else { - log("Replacing existing%s module `%s' at %s:%d.%d-%d.%d.\n", + log("Replacing existing%s module `%s' at %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", - (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->location.first_line, (*it)->location.first_column, (*it)->location.last_line, (*it)->location.last_column); + child->str.c_str(), child->loc_string().c_str()); design->remove(existing_mod); } } - design->add(process_module(*it, defer)); + process_module(design, child, defer_local); current_ast_mod = nullptr; } - else if ((*it)->type == AST_PACKAGE) { + else if (child->type == AST_PACKAGE) { // process enum/other declarations - (*it)->simplify(true, false, false, 1, -1, false, false); - design->verilog_packages.push_back((*it)->clone()); + child->simplify(true, 1, -1, false); + rename_in_package_stmts(child); + design->verilog_packages.push_back(child->clone()); current_scope.clear(); } + else if (child->type == AST_BIND) { + // top-level bind construct + for (RTLIL::Binding *binding : child->genBindings()) + design->add(binding); + } else { // must be global definition - (*it)->simplify(false, false, false, 1, -1, false, false); //process enum/other declarations - design->verilog_globals.push_back((*it)->clone()); + if (child->type == AST_PARAMETER) + child->type = AST_LOCALPARAM; // cannot be overridden + design->verilog_globals.push_back(child->clone()); current_scope.clear(); } } @@ -1343,13 +1538,32 @@ void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule } } +// AstModules may contain cells marked with ID::reprocess_after, which indicates +// that it should be reprocessed once the specified module has been elaborated. +bool AstModule::reprocess_if_necessary(RTLIL::Design *design) +{ + for (const RTLIL::Cell *cell : cells()) + { + std::string modname = cell->get_string_attribute(ID::reprocess_after); + if (modname.empty()) + continue; + if (design->module(modname) || design->module("$abstract" + modname)) { + log("Reprocessing module %s because instantiated module %s has become available.\n", + log_id(name), log_id(modname)); + loadconfig(); + process_and_replace_module(design, this, ast, NULL); + return true; + } + } + return false; +} + // When an interface instance is found in a module, the whole RTLIL for the module will be rederived again // from AST. The interface members are copied into the AST module with the prefix of the interface. -void AstModule::reprocess_module(RTLIL::Design *design, const dict &local_interfaces) +void AstModule::expand_interfaces(RTLIL::Design *design, const dict &local_interfaces) { loadconfig(); - bool is_top = false; AstNode *new_ast = ast->clone(); for (auto &intf : local_interfaces) { std::string intfname = intf.first.str(); @@ -1406,29 +1620,15 @@ void AstModule::reprocess_module(RTLIL::Design *design, const dictname.str(); - std::string changed_name = original_name + "_before_replacing_local_interfaces"; - design->rename(this, changed_name); - this->set_bool_attribute(ID::to_delete); - - // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the - // new module. - if (this->get_bool_attribute(ID::initial_top)) { - this->attributes.erase(ID::initial_top); - is_top = true; - } + // Generate RTLIL from AST for the new module and add to the design, + // renaming this module to move it out of the way. + RTLIL::Module* new_module = + process_and_replace_module(design, this, new_ast, ast_before_replacing_interface_ports); - // Generate RTLIL from AST for the new module and add to the design: - AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports); - delete(new_ast); - design->add(newmod); - RTLIL::Module* mod = design->module(original_name); - if (is_top) - mod->set_bool_attribute(ID::top); + delete new_ast; // Set the attribute "interfaces_replaced_in_module" so that it does not happen again. - mod->set_bool_attribute(ID::interfaces_replaced_in_module); + new_module->set_bool_attribute(ID::interfaces_replaced_in_module); } // create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces @@ -1478,7 +1678,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dictadd(process_module(new_ast, false)); + process_module(design, new_ast, false); design->module(modname)->check(); RTLIL::Module* mod = design->module(modname); @@ -1511,6 +1711,7 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dicthas(modname)) { + if (!design->has(modname) && new_ast) { new_ast->str = modname; - design->add(process_module(new_ast, false, NULL, quiet)); + process_module(design, new_ast, false, NULL, quiet); design->module(modname)->check(); } else if (!quiet) { log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); @@ -1538,17 +1739,51 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict= 0; i--) { + switch (val.bits[i]) { + case RTLIL::State::S0: res.push_back('0'); break; + case RTLIL::State::S1: res.push_back('1'); break; + case RTLIL::State::Sx: res.push_back('x'); break; + case RTLIL::State::Sz: res.push_back('z'); break; + case RTLIL::State::Sa: res.push_back('?'); break; + case RTLIL::State::Sm: res.push_back('m'); break; + } + } + return res; +} + +std::string AST::derived_module_name(std::string stripped_name, const std::vector> ¶meters) { + std::string para_info; + for (const auto &elem : parameters) + para_info += stringf("%s=%s", elem.first.c_str(), serialize_param_value(elem.second).c_str()); + + if (para_info.size() > 60) + return "$paramod$" + sha1(para_info) + stripped_name; + else + return "$paramod" + stripped_name + para_info; +} + // create a new parametric module (when needed) and return the name of the generated module std::string AstModule::derive_common(RTLIL::Design *design, const dict ¶meters, AstNode **new_ast_out, bool quiet) { std::string stripped_name = name.str(); + (*new_ast_out) = nullptr; if (stripped_name.compare(0, 9, "$abstract") == 0) stripped_name = stripped_name.substr(9); - std::string para_info; - int para_counter = 0; + std::vector> named_parameters; for (const auto child : ast->children) { if (child->type != AST_PARAMETER) continue; @@ -1557,25 +1792,21 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dictstr.c_str(), log_signal(it->second)); - para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second)); + named_parameters.emplace_back(child->str, it->second); continue; } it = parameters.find(stringf("$%d", para_counter)); if (it != parameters.end()) { if (!quiet) log("Parameter %d (%s) = %s\n", para_counter, child->str.c_str(), log_signal(it->second)); - para_info += stringf("%s=%s", child->str.c_str(), log_signal(it->second)); + named_parameters.emplace_back(child->str, it->second); continue; } } - std::string modname; - if (parameters.size() == 0) - modname = stripped_name; - else if (para_info.size() > 60) - modname = "$paramod$" + sha1(para_info) + stripped_name; - else - modname = "$paramod" + stripped_name + para_info; + std::string modname = stripped_name; + if (parameters.size()) // not named_parameters to cover hierarchical defparams + modname = derived_module_name(stripped_name, named_parameters); if (design->has(modname)) return modname; @@ -1589,7 +1820,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dictclone(); if (!new_ast->attributes.count(ID::hdlname)) - new_ast->attributes[ID::hdlname] = AstNode::mkconst_str(stripped_name); + new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name.substr(1))); para_counter = 0; for (auto child : new_ast->children) { @@ -1610,6 +1841,8 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dictchildren.insert(child->children.begin(), nullptr); delete child->children.at(0); if ((it->second.flags & RTLIL::CONST_FLAG_REAL) != 0) { child->children[0] = new AstNode(AST_REALVALUE); @@ -1634,6 +1867,7 @@ std::string AstModule::derive_common(RTLIL::Design *design, const dictchildren.push_back(defparam); } + new_ast->fixup_hierarchy_flags(true); (*new_ast_out) = new_ast; return modname; } @@ -1680,4 +1914,11 @@ void AstModule::loadconfig() const flag_autowire = autowire; } +void AstNode::input_error(const char *format, ...) const +{ + va_list ap; + va_start(ap, format); + logv_file_error(filename, location.first_line, format, ap); +} + YOSYS_NAMESPACE_END diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 1b8ed22ca6e..f05b568be22 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -30,6 +30,7 @@ #define AST_H #include "kernel/rtlil.h" +#include "kernel/fmt.h" #include #include @@ -160,7 +161,8 @@ namespace AST AST_TYPEDEF, AST_STRUCT, AST_UNION, - AST_STRUCT_ITEM + AST_STRUCT_ITEM, + AST_BIND }; struct AstSrcLocType { @@ -200,9 +202,17 @@ namespace AST // set for IDs typed to an enumeration, not used bool is_enum; - // if this is a multirange memory then this vector contains offset and length of each dimension - std::vector multirange_dimensions; - std::vector multirange_swapped; // true if range is swapped, not used for structs + // Declared range for array dimension. + struct dimension_t { + int range_right; // lsb in [msb:lsb] + int range_width; // msb - lsb + 1 + bool range_swapped; // if the declared msb < lsb, msb and lsb above are swapped + }; + // Packed and unpacked dimensions for arrays. + // Unpacked dimensions go first, to follow the order of indexing. + std::vector dimensions; + // Number of unpacked dimensions. + int unpacked_dimensions; // this is set by simplify and used during RTLIL generation AstNode *id2ast; @@ -219,8 +229,15 @@ namespace AST std::string filename; AstSrcLocType location; + // are we embedded in an lvalue, param? + // (see fixup_hierarchy_flags) + bool in_lvalue; + bool in_param; + bool in_lvalue_from_above; + bool in_param_from_above; + // creating and deleting nodes - AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL); + AstNode(AstNodeType type = AST_NONE, AstNode *child1 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr); AstNode *clone() const; void cloneInto(AstNode *other) const; void delete_children(); @@ -249,10 +266,11 @@ namespace AST // simplify() creates a simpler AST by unrolling for-loops, expanding generate blocks, etc. // it also sets the id2ast pointers so that identifier lookups are fast in genRTLIL() - bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param); + bool simplify(bool const_fold, int stage, int width_hint, bool sign_hint); + void replace_result_wire_name_in_function(const std::string &from, const std::string &to); AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init); - void expand_genblock(std::string index_var, std::string prefix, std::map &name_map, bool original_scope = true); - void replace_ids(const std::string &prefix, const std::map &rules); + void expand_genblock(const std::string &prefix); + void label_genblks(std::set& existing, int &counter); void mem2reg_as_needed_pass1(dict> &mem2reg_places, dict &mem2reg_flags, dict &proc_flags, uint32_t &status_flags); bool mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block); @@ -260,19 +278,35 @@ namespace AST void mem2reg_remove(pool &mem2reg_set, vector &delnodes); void meminfo(int &mem_width, int &mem_size, int &addr_bits); bool detect_latch(const std::string &var); + const RTLIL::Module* lookup_cell_module(); // additional functionality for evaluating constant functions - struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; }; - bool has_const_only_constructs(bool &recommend_const_eval); - void replace_variables(std::map &variables, AstNode *fcall); - AstNode *eval_const_function(AstNode *fcall); + struct varinfo_t { + RTLIL::Const val; + int offset; + bool range_swapped; + bool is_signed; + AstNode *arg = nullptr; + bool explicitly_sized; + }; + bool has_const_only_constructs(); + bool replace_variables(std::map &variables, AstNode *fcall, bool must_succeed); + AstNode *eval_const_function(AstNode *fcall, bool must_succeed); bool is_simple_const_expr(); - std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); + + // helper for parsing format strings + Fmt processFormat(int stage, bool sformat_like, int default_base = 10, size_t first_arg_at = 0, bool may_fail = false); + + bool is_recursive_function() const; + std::pair get_tern_choice(); // create a human-readable text representation of the AST (for debugging) void dumpAst(FILE *f, std::string indent) const; void dumpVlog(FILE *f, std::string indent) const; + // Generate RTLIL for a bind construct + std::vector genBindings() const; + // used by genRTLIL() for detecting expression width and sign void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL); void detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real = NULL); @@ -281,7 +315,7 @@ namespace AST // for expressions the resulting signal vector is returned // all generated cell instances, etc. are written to the RTLIL::Module pointed to by AST_INTERNAL::current_module RTLIL::SigSpec genRTLIL(int width_hint = -1, bool sign_hint = false); - RTLIL::SigSpec genWidthRTLIL(int width, const dict *new_subst_ptr = NULL); + RTLIL::SigSpec genWidthRTLIL(int width, bool sgn, const dict *new_subst_ptr = NULL); // compare AST nodes bool operator==(const AstNode &other) const; @@ -295,12 +329,15 @@ namespace AST static AstNode *mkconst_str(const std::vector &v); static AstNode *mkconst_str(const std::string &str); + // helper function to create an AST node for a temporary register + AstNode *mktemp_logic(const std::string &name, AstNode *mod, bool nosync, int range_left, int range_right, bool is_signed); + // helper function for creating sign-extended const objects RTLIL::Const bitsAsConst(int width, bool is_signed); RTLIL::Const bitsAsConst(int width = -1); RTLIL::Const bitsAsUnsizedConst(int width); - RTLIL::Const asAttrConst(); - RTLIL::Const asParaConst(); + RTLIL::Const asAttrConst() const; + RTLIL::Const asParaConst() const; uint64_t asInt(bool is_signed); bool bits_only_01() const; bool asBool() const; @@ -313,10 +350,45 @@ namespace AST // helpers for enum void allocateDefaultEnumValues(); void annotateTypedEnums(AstNode *template_node); + + // helpers for locations + std::string loc_string() const; + + // Helper for looking up identifiers which are prefixed with the current module name + std::string try_pop_module_prefix() const; + + // helper to clone the node with some of its subexpressions replaced with zero (this is used + // to evaluate widths of dynamic ranges) + AstNode *clone_at_zero(); + + void set_attribute(RTLIL::IdString key, AstNode *node) + { + attributes[key] = node; + node->set_in_param_flag(true); + } + + // helper to set in_lvalue/in_param flags from the hierarchy context (the actual flag + // can be overridden based on the intrinsic properties of this node, i.e. based on its type) + void set_in_lvalue_flag(bool flag, bool no_descend = false); + void set_in_param_flag(bool flag, bool no_descend = false); + + // fix up the hierarchy flags (in_lvalue/in_param) of this node and its children + // + // to keep the flags in sync, fixup_hierarchy_flags(true) needs to be called once after + // parsing the AST to walk the full tree, then plain fixup_hierarchy_flags() performs + // localized fixups after modifying children/attributes of a particular node + void fixup_hierarchy_flags(bool force_descend = false); + + // helpers for indexing + AstNode *make_index_range(AstNode *node, bool unpacked_range = false); + AstNode *get_struct_member() const; + + // helper to print errors from simplify/genrtlil code + [[noreturn]] void input_error(const char *format, ...) const YS_ATTRIBUTE(format(printf, 2, 3)); }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code - void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, + void process(RTLIL::Design *design, AstNode *ast, bool nodisplay, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog1, bool dump_vlog2, bool dump_rtlil, bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool noblackbox, bool lib, bool nowb, bool noopt, bool icells, bool pwires, bool nooverwrite, bool overwrite, bool defer, bool autowire); // parametric modules are supported directly by the AST library @@ -328,7 +400,8 @@ namespace AST RTLIL::IdString derive(RTLIL::Design *design, const dict ¶meters, bool mayfail) override; RTLIL::IdString derive(RTLIL::Design *design, const dict ¶meters, const dict &interfaces, const dict &modports, bool mayfail) override; std::string derive_common(RTLIL::Design *design, const dict ¶meters, AstNode **new_ast_out, bool quiet = false); - void reprocess_module(RTLIL::Design *design, const dict &local_interfaces) override; + void expand_interfaces(RTLIL::Design *design, const dict &local_interfaces) override; + bool reprocess_if_necessary(RTLIL::Design *design) override; RTLIL::Module *clone() const override; void loadconfig() const; }; @@ -351,22 +424,47 @@ namespace AST std::pair split_modport_from_type(std::string name_type); AstNode * find_modport(AstNode *intf, std::string name); void explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport); + + // Helper for setting the src attribute. + void set_src_attr(RTLIL::AttrObject *obj, const AstNode *ast); + + // generate standard $paramod... derived module name; parameters should be + // in the order they are declared in the instantiated module + std::string derived_module_name(std::string stripped_name, const std::vector> ¶meters); + + // used to provide simplify() access to the current design for looking up + // modules, ports, wires, etc. + void set_simplify_design_context(const RTLIL::Design *design); } namespace AST_INTERNAL { // internal state variables - extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit; + extern bool flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit; extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_pwires, flag_autowire; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map current_scope; extern const dict *genRTLIL_subst_ptr; extern RTLIL::SigSpec ignoreThisSignalsInInitial; extern AST::AstNode *current_always, *current_top_block, *current_block, *current_block_child; - extern AST::AstModule *current_module; + extern RTLIL::Module *current_module; extern bool current_always_clocked; + extern dict current_memwr_count; + extern dict> current_memwr_visible; struct LookaheadRewriter; struct ProcessGenerator; + + // Create and add a new AstModule from new_ast, then use it to replace + // old_module in design, renaming old_module to move it out of the way. + // Return the new module. + // + // If original_ast is not null, it will be used as the AST node for the + // new module. Otherwise, new_ast will be used. + RTLIL::Module * + process_and_replace_module(RTLIL::Design *design, + RTLIL::Module *old_module, + AST::AstNode *new_ast, + AST::AstNode *original_ast = nullptr); } YOSYS_NAMESPACE_END diff --git a/frontends/ast/ast_binding.cc b/frontends/ast/ast_binding.cc new file mode 100644 index 00000000000..c20d1df4d6e --- /dev/null +++ b/frontends/ast/ast_binding.cc @@ -0,0 +1,49 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "ast_binding.h" +#include "ast.h" + +YOSYS_NAMESPACE_BEGIN + +using namespace AST_INTERNAL; + +AST::Binding::Binding(RTLIL::IdString target_type, + RTLIL::IdString target_name, + const AstNode &cell) + : RTLIL::Binding(target_type, target_name), + ast_node(cell.clone()) +{ + log_assert(cell.type == AST_CELL); +} + +std::string +AST::Binding::describe() const +{ + std::ostringstream oss; + oss << "directive to bind " << ast_node->str + << " to " << target_name.str(); + if (!target_type.empty()) + oss << " (target type: " + << target_type.str() + << ")"; + return oss.str(); +} + +PRIVATE_NAMESPACE_END diff --git a/frontends/ast/ast_binding.h b/frontends/ast/ast_binding.h new file mode 100644 index 00000000000..641497d52d4 --- /dev/null +++ b/frontends/ast/ast_binding.h @@ -0,0 +1,58 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * --- + * + * This header declares the AST::Binding class + * + * This is used to support the bind directive and is to RTLIL::Binding as + * AST::AstModule is to RTLIL::Module, holding a syntax-level representation of + * cells until we get to a stage where they make sense. In the case of a bind + * directive, this is when we elaborate the design in the hierarchy pass. + * + */ + +#ifndef AST_BINDING_H +#define AST_BINDING_H + +#include "kernel/rtlil.h" +#include "kernel/binding.h" + +#include + +YOSYS_NAMESPACE_BEGIN + +namespace AST +{ + class Binding : public RTLIL::Binding + { + public: + Binding(RTLIL::IdString target_type, + RTLIL::IdString target_name, + const AstNode &cell); + + std::string describe() const override; + + private: + // The syntax-level representation of the cell to be bound. + std::unique_ptr ast_node; + }; +} + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/ast/dpicall.cc b/frontends/ast/dpicall.cc index e241142d352..12a7e1183f7 100644 --- a/frontends/ast/dpicall.cc +++ b/frontends/ast/dpicall.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -67,7 +67,7 @@ static ffi_fptr resolve_fn (std::string symbol_name) AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector &args) { AST::AstNode *newNode = nullptr; - union { double f64; float f32; int32_t i32; } value_store [args.size() + 1]; + union { double f64; float f32; int32_t i32; void *ptr; } value_store [args.size() + 1]; ffi_type *types [args.size() + 1]; void *values [args.size() + 1]; ffi_cif cif; @@ -92,6 +92,11 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, value_store[i].i32 = args[i]->asInt(args[i]->is_signed); values[i] = &value_store[i].i32; types[i] = &ffi_type_sint32; + } else if (argtypes[i] == "chandle") { + log(" arg %d (%s): %llx\n", i, argtypes[i].c_str(), (unsigned long long)args[i]->asInt(false)); + value_store[i].ptr = (void *)args[i]->asInt(args[i]->is_signed); + values[i] = &value_store[i].ptr; + types[i] = &ffi_type_pointer; } else { log_error("invalid argtype '%s' for argument %d.\n", argtypes[i].c_str(), i); } @@ -106,6 +111,9 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, } else if (rtype == "real") { types[args.size()] = &ffi_type_double; values[args.size()] = &value_store[args.size()].f64; + } else if (rtype == "chandle") { + types[args.size()] = &ffi_type_pointer; + values[args.size()] = &value_store[args.size()].ptr; } else { log_error("invalid rtype '%s'.\n", rtype.c_str()); } @@ -123,6 +131,13 @@ AST::AstNode *AST::dpi_call(const std::string &rtype, const std::string &fname, newNode = new AstNode(AST_REALVALUE); newNode->realvalue = value_store[args.size()].f32; log(" return realvalue: %g\n", newNode->asReal(true)); + } else if (rtype == "chandle") { + uint64_t rawval = (uint64_t)value_store[args.size()].ptr; + std::vector bits(64); + for (int i = 0; i < 64; i++) + bits.at(i) = (rawval & (1ULL << i)) ? RTLIL::State::S1 : RTLIL::State::S0; + newNode = AstNode::mkconst_bits(bits, false); + log(" return chandle: %llx\n", (unsigned long long)newNode->asInt(false)); } else { newNode = AstNode::mkconst_int(value_store[args.size()].i32, false); log(" return integer: %lld\n", (long long)newNode->asInt(true)); diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index e878d0dd2ba..fe67f00c692 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,8 +28,10 @@ #include "kernel/log.h" #include "kernel/utils.h" +#include "kernel/binding.h" #include "libs/sha1/sha1.h" #include "ast.h" +#include "ast_binding.h" #include #include @@ -43,17 +45,18 @@ using namespace AST_INTERNAL; // helper function for creating RTLIL code for unary operations static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); + wire->is_signed = that->is_signed; if (gen_attributes) for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(that->filename, that->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -74,17 +77,18 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s return; } - IdString name = stringf("$extend$%s:%d$%d", that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("$extend$%s:%d$%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, ID($pos)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); + wire->is_signed = that->is_signed; if (that != NULL) for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(that->filename, that->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -100,16 +104,17 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s // helper function for creating RTLIL code for binary operations static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type.c_str(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); + wire->is_signed = that->is_signed; for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(that->filename, that->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -133,17 +138,18 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const log_assert(cond.size() == 1); std::stringstream sstr; - sstr << "$ternary$" << that->filename << ":" << that->location.first_line << "$" << (autoidx++); + sstr << "$ternary$" << RTLIL::encode_filename(that->filename) << ":" << that->location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size()); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); + set_src_attr(wire, that); + wire->is_signed = that->is_signed; for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(that->filename, that->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -157,6 +163,28 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const return wire; } +static void check_unique_id(RTLIL::Module *module, RTLIL::IdString id, + const AstNode *node, const char *to_add_kind) +{ + auto already_exists = [&](const RTLIL::AttrObject *existing, const char *existing_kind) { + std::string src = existing->get_string_attribute(ID::src); + std::string location_str = "earlier"; + if (!src.empty()) + location_str = "at " + src; + node->input_error("Cannot add %s `%s' because a %s with the same name was already created %s!\n", + to_add_kind, id.c_str(), existing_kind, location_str.c_str()); + }; + + if (const RTLIL::Wire *wire = module->wire(id)) + already_exists(wire, "signal"); + if (const RTLIL::Cell *cell = module->cell(id)) + already_exists(cell, "cell"); + if (module->processes.count(id)) + already_exists(module->processes.at(id), "process"); + if (module->memories.count(id)) + already_exists(module->memories.at(id), "memory"); +} + // helper class for rewriting simple lookahead references in AST always blocks struct AST_INTERNAL::LookaheadRewriter { @@ -170,10 +198,11 @@ struct AST_INTERNAL::LookaheadRewriter AstNode *wire = new AstNode(AST_WIRE); for (auto c : node->id2ast->children) wire->children.push_back(c->clone()); + wire->fixup_hierarchy_flags(); wire->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++); - wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); wire->is_logic = true; - while (wire->simplify(true, false, false, 1, -1, false, false)) { } + while (wire->simplify(true, 1, -1, false)) { } current_ast_mod->children.push_back(wire); lookaheadids[node->str] = make_pair(node->id2ast, wire); wire->genRTLIL(); @@ -309,22 +338,22 @@ struct AST_INTERNAL::ProcessGenerator // Buffer for generating the init action RTLIL::SigSpec init_lvalue, init_rvalue; - ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg) + // The most recently assigned $print or $check cell \PRIORITY. + int last_effect_priority; + + ProcessGenerator(AstNode *always, RTLIL::SigSpec initSyncSignalsArg = RTLIL::SigSpec()) : always(always), initSyncSignals(initSyncSignalsArg), last_effect_priority(0) { // rewrite lookahead references LookaheadRewriter la_rewriter(always); // generate process and simple root case - proc = new RTLIL::Process; - proc->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); - proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->location.first_line, autoidx++); + proc = current_module->addProcess(stringf("$proc$%s:%d$%d", RTLIL::encode_filename(always->filename).c_str(), always->location.first_line, autoidx++)); + set_src_attr(proc, always); for (auto &attr : always->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(always->filename, always->location.first_line, "Attribute `%s' with non-constant value!\n", - attr.first.c_str()); + always->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); proc->attributes[attr.first] = attr.second->asAttrConst(); } - current_module->processes[proc->name] = proc; current_case = &proc->root_case; // create initial temporary signal for all output registers @@ -351,8 +380,8 @@ struct AST_INTERNAL::ProcessGenerator if (found_anyedge_syncs) { if (found_global_syncs) - log_file_error(always->filename, always->location.first_line, "Found non-synthesizable event list!\n"); - log("Note: Assuming pure combinatorial block at %s:%d.%d-%d.%d in\n", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); + always->input_error("Found non-synthesizable event list!\n"); + log("Note: Assuming pure combinatorial block at %s in\n", always->loc_string().c_str()); log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n"); log("use of @* instead of @(...) for better match of synthesis and simulation.\n"); } @@ -366,12 +395,12 @@ struct AST_INTERNAL::ProcessGenerator continue; found_clocked_sync = true; if (found_global_syncs || found_anyedge_syncs) - log_file_error(always->filename, always->location.first_line, "Found non-synthesizable event list!\n"); + always->input_error("Found non-synthesizable event list!\n"); RTLIL::SyncRule *syncrule = new RTLIL::SyncRule; syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn; syncrule->signal = child->children[0]->genRTLIL(); if (GetSize(syncrule->signal) != 1) - log_file_error(always->filename, always->location.first_line, "Found posedge/negedge event on a signal that is not 1 bit wide!\n"); + always->input_error("Found posedge/negedge event on a signal that is not 1 bit wide!\n"); addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true); proc->syncs.push_back(syncrule); } @@ -395,6 +424,9 @@ struct AST_INTERNAL::ProcessGenerator if (child->type == AST_BLOCK) processAst(child); + for (auto sync: proc->syncs) + processMemWrites(sync); + if (initSyncSignals.size() > 0) { RTLIL::SyncRule *sync = new RTLIL::SyncRule; @@ -452,7 +484,7 @@ struct AST_INTERNAL::ProcessGenerator } while (current_module->wires_.count(wire_name) > 0); RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", always->filename.c_str(), always->location.first_line, always->location.first_column, always->location.last_line, always->location.last_column); + set_src_attr(wire, always); chunk.wire = wire; chunk.offset = 0; @@ -559,7 +591,7 @@ struct AST_INTERNAL::ProcessGenerator case AST_ASSIGN_LE: { RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue; - RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), &subst_rvalue_map.stdmap()); + RTLIL::SigSpec rvalue = ast->children[1]->genWidthRTLIL(lvalue.size(), true, &subst_rvalue_map.stdmap()); pool lvalue_sigbits; for (int i = 0; i < GetSize(lvalue); i++) { @@ -586,14 +618,18 @@ struct AST_INTERNAL::ProcessGenerator case AST_CASE: { + int width_hint; + bool sign_hint; + ast->detectSignWidth(width_hint, sign_hint); + RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; - sw->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", ast->filename.c_str(), ast->location.first_line, ast->location.first_column, ast->location.last_line, ast->location.last_column); - sw->signal = ast->children[0]->genWidthRTLIL(-1, &subst_rvalue_map.stdmap()); + set_src_attr(sw, ast); + sw->signal = ast->children[0]->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap()); current_case->switches.push_back(sw); for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + ast->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); sw->attributes[attr.first] = attr.second->asAttrConst(); } @@ -621,7 +657,7 @@ struct AST_INTERNAL::ProcessGenerator RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; - current_case->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", child->filename.c_str(), child->location.first_line, child->location.first_column, child->location.last_line, child->location.last_column); + set_src_attr(current_case, child); last_generated_case = current_case; addChunkActions(current_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); for (auto node : child->children) { @@ -630,7 +666,7 @@ struct AST_INTERNAL::ProcessGenerator else if (node->type == AST_BLOCK) processAst(node); else - current_case->compare.push_back(node->genWidthRTLIL(sw->signal.size(), &subst_rvalue_map.stdmap())); + current_case->compare.push_back(node->genWidthRTLIL(width_hint, sign_hint, &subst_rvalue_map.stdmap())); } if (default_case != current_case) sw->cases.push_back(current_case); @@ -671,20 +707,161 @@ struct AST_INTERNAL::ProcessGenerator break; case AST_WIRE: - log_file_error(ast->filename, ast->location.first_line, "Found reg declaration in block without label!\n"); + ast->input_error("Found reg declaration in block without label!\n"); break; case AST_ASSIGN: - log_file_error(ast->filename, ast->location.first_line, "Found continous assignment in always/initial block!\n"); + ast->input_error("Found continous assignment in always/initial block!\n"); break; case AST_PARAMETER: case AST_LOCALPARAM: - log_file_error(ast->filename, ast->location.first_line, "Found parameter declaration in block without label!\n"); + ast->input_error("Found parameter declaration in block without label!\n"); break; - case AST_NONE: case AST_TCALL: + if (ast->str == "$display" || ast->str == "$displayb" || ast->str == "$displayh" || ast->str == "$displayo" || + ast->str == "$write" || ast->str == "$writeb" || ast->str == "$writeh" || ast->str == "$writeo") { + std::stringstream sstr; + sstr << ast->str << "$" << ast->filename << ":" << ast->location.first_line << "$" << (autoidx++); + + Wire *en = current_module->addWire(sstr.str() + "_EN", 1); + set_src_attr(en, ast); + proc->root_case.actions.push_back(SigSig(en, false)); + current_case->actions.push_back(SigSig(en, true)); + + RTLIL::SigSpec triggers; + RTLIL::Const polarity; + for (auto sync : proc->syncs) { + if (sync->type == RTLIL::STp) { + triggers.append(sync->signal); + polarity.bits.push_back(RTLIL::S1); + } else if (sync->type == RTLIL::STn) { + triggers.append(sync->signal); + polarity.bits.push_back(RTLIL::S0); + } + } + + RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($print)); + set_src_attr(cell, ast); + cell->setParam(ID::TRG_WIDTH, triggers.size()); + cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty()); + cell->setParam(ID::TRG_POLARITY, polarity); + cell->setParam(ID::PRIORITY, --last_effect_priority); + cell->setPort(ID::TRG, triggers); + cell->setPort(ID::EN, en); + + int default_base = 10; + if (ast->str.back() == 'b') + default_base = 2; + else if (ast->str.back() == 'o') + default_base = 8; + else if (ast->str.back() == 'h') + default_base = 16; + + std::vector args; + for (auto node : ast->children) { + int width; + bool is_signed; + node->detectSignWidth(width, is_signed, nullptr); + + VerilogFmtArg arg = {}; + arg.filename = node->filename; + arg.first_line = node->location.first_line; + if (node->type == AST_CONSTANT && node->is_string) { + arg.type = VerilogFmtArg::STRING; + arg.str = node->bitsAsConst().decode_string(); + // and in case this will be used as an argument... + arg.sig = node->bitsAsConst(); + arg.signed_ = false; + } else if (node->type == AST_IDENTIFIER && node->str == "$time") { + arg.type = VerilogFmtArg::TIME; + } else if (node->type == AST_IDENTIFIER && node->str == "$realtime") { + arg.type = VerilogFmtArg::TIME; + arg.realtime = true; + } else { + arg.type = VerilogFmtArg::INTEGER; + arg.sig = node->genWidthRTLIL(-1, false, &subst_rvalue_map.stdmap()); + arg.signed_ = is_signed; + } + args.push_back(arg); + } + + Fmt fmt; + fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name); + if (ast->str.substr(0, 8) == "$display") + fmt.append_string("\n"); + fmt.emit_rtlil(cell); + } else if (!ast->str.empty()) { + log_file_error(ast->filename, ast->location.first_line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str()); + } + break; + + // generate $check cells + case AST_ASSERT: + case AST_ASSUME: + case AST_LIVE: + case AST_FAIR: + case AST_COVER: + { + std::string flavor, desc; + if (ast->type == AST_ASSERT) { flavor = "assert"; desc = "assert ()"; } + if (ast->type == AST_ASSUME) { flavor = "assume"; desc = "assume ()"; } + if (ast->type == AST_LIVE) { flavor = "live"; desc = "assert (eventually)"; } + if (ast->type == AST_FAIR) { flavor = "fair"; desc = "assume (eventually)"; } + if (ast->type == AST_COVER) { flavor = "cover"; desc = "cover ()"; } + + IdString cellname; + if (ast->str.empty()) + cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(ast->filename).c_str(), ast->location.first_line, autoidx++); + else + cellname = ast->str; + check_unique_id(current_module, cellname, ast, "procedural assertion"); + + RTLIL::SigSpec check = ast->children[0]->genWidthRTLIL(-1, false, &subst_rvalue_map.stdmap()); + if (GetSize(check) != 1) + check = current_module->ReduceBool(NEW_ID, check); + + Wire *en = current_module->addWire(cellname.str() + "_EN", 1); + set_src_attr(en, ast); + proc->root_case.actions.push_back(SigSig(en, false)); + current_case->actions.push_back(SigSig(en, true)); + + RTLIL::SigSpec triggers; + RTLIL::Const polarity; + for (auto sync : proc->syncs) { + if (sync->type == RTLIL::STp) { + triggers.append(sync->signal); + polarity.bits.push_back(RTLIL::S1); + } else if (sync->type == RTLIL::STn) { + triggers.append(sync->signal); + polarity.bits.push_back(RTLIL::S0); + } + } + + RTLIL::Cell *cell = current_module->addCell(cellname, ID($check)); + set_src_attr(cell, ast); + for (auto &attr : ast->attributes) { + if (attr.second->type != AST_CONSTANT) + log_file_error(ast->filename, ast->location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + cell->attributes[attr.first] = attr.second->asAttrConst(); + } + cell->setParam(ID::FLAVOR, flavor); + cell->setParam(ID::TRG_WIDTH, triggers.size()); + cell->setParam(ID::TRG_ENABLE, (always->type == AST_INITIAL) || !triggers.empty()); + cell->setParam(ID::TRG_POLARITY, polarity); + cell->setParam(ID::PRIORITY, --last_effect_priority); + cell->setPort(ID::TRG, triggers); + cell->setPort(ID::EN, en); + cell->setPort(ID::A, check); + + // No message is emitted to ensure Verilog code roundtrips correctly. + Fmt fmt; + fmt.emit_rtlil(cell); + break; + } + + case AST_NONE: case AST_FOR: break; @@ -694,8 +871,99 @@ struct AST_INTERNAL::ProcessGenerator log_abort(); } } + + void processMemWrites(RTLIL::SyncRule *sync) + { + // Maps per-memid AST_MEMWR IDs to indices in the mem_write_actions array. + dict, int> port_map; + for (auto child : always->children) + if (child->type == AST_MEMWR) + { + std::string memid = child->str; + int portid = child->children[3]->asInt(false); + int cur_idx = GetSize(sync->mem_write_actions); + RTLIL::MemWriteAction action; + set_src_attr(&action, child); + action.memid = memid; + action.address = child->children[0]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap()); + action.data = child->children[1]->genWidthRTLIL(current_module->memories[memid]->width, true, &subst_rvalue_map.stdmap()); + action.enable = child->children[2]->genWidthRTLIL(-1, true, &subst_rvalue_map.stdmap()); + RTLIL::Const orig_priority_mask = child->children[4]->bitsAsConst(); + RTLIL::Const priority_mask = RTLIL::Const(0, cur_idx); + for (int i = 0; i < portid; i++) { + int new_bit = port_map[std::make_pair(memid, i)]; + priority_mask.bits[new_bit] = orig_priority_mask.bits[i]; + } + action.priority_mask = priority_mask; + sync->mem_write_actions.push_back(action); + port_map[std::make_pair(memid, portid)] = cur_idx; + } + } }; +// Generate RTLIL for a bind construct +// +// The AST node will have one or more AST_IDENTIFIER children, which were added +// by bind_target_instance in the parser. After these, it will have one or more +// cells, as parsed by single_cell. These have type AST_CELL. +// +// If there is more than one AST_IDENTIFIER, the first one should be considered +// a module identifier. If there is only one AST_IDENTIFIER, we can't tell at +// this point whether it's a module/interface name or the name of an instance +// because the correct interpretation depends on what's visible at elaboration +// time. For now, we just treat it as a target instance with unknown type, and +// we'll deal with the corner case in the hierarchy pass. +// +// To simplify downstream code, RTLIL::Binding only has a single target and +// single bound instance. If we see the syntax that allows more than one of +// either, we split it into multiple Binding objects. +std::vector AstNode::genBindings() const +{ + // Partition children into identifiers and cells + int num_ids = 0; + for (int i = 0; i < GetSize(children); ++i) { + if (children[i]->type != AST_IDENTIFIER) { + log_assert(i > 0); + num_ids = i; + break; + } + } + + // We should have found at least one child that's not an identifier + log_assert(num_ids > 0); + + // Make sense of the identifiers, extracting a possible type name and a + // list of hierarchical IDs. We represent an unknown type with an empty + // string. + RTLIL::IdString tgt_type; + int first_tgt_inst = 0; + if (num_ids > 1) { + tgt_type = children[0]->str; + first_tgt_inst = 1; + } + + std::vector ret; + + // At this point, we know that children with index >= first_tgt_inst and + // index < num_ids are (hierarchical?) names of target instances. Make a + // binding object for each of them, and fill in the generated instance + // cells each time. + for (int i = first_tgt_inst; i < num_ids; ++i) { + const AstNode &tgt_child = *children[i]; + + for (int j = num_ids; j < GetSize(children); ++j) { + const AstNode &cell_child = *children[j]; + + log_assert(cell_child.type == AST_CELL); + + ret.push_back(new AST::Binding(tgt_type, tgt_child.str, + cell_child)); + } + } + + return ret; +} + // detect sign and width of an expression void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real) { @@ -728,20 +996,28 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_IDENTIFIER: id_ast = id2ast; - if (id_ast == NULL && current_scope.count(str)) - id_ast = current_scope.at(str); + if (!id_ast) { + if (current_scope.count(str)) + id_ast = current_scope[str]; + else { + std::string alt = try_pop_module_prefix(); + if (current_scope.count(alt)) + id_ast = current_scope[alt]; + } + } if (!id_ast) - log_file_error(filename, location.first_line, "Failed to resolve identifier %s for width detection!\n", str.c_str()); + input_error("Failed to resolve identifier %s for width detection!\n", str.c_str()); if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) { if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) { this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1; - } else - if (id_ast->children[0]->type != AST_CONSTANT) - while (id_ast->simplify(true, false, false, 1, -1, false, true)) { } - if (id_ast->children[0]->type == AST_CONSTANT) - this_width = id_ast->children[0]->bits.size(); - else - log_file_error(filename, location.first_line, "Failed to detect width for parameter %s!\n", str.c_str()); + } else { + if (id_ast->children[0]->type != AST_CONSTANT) + while (id_ast->simplify(true, 1, -1, false)) { } + if (id_ast->children[0]->type == AST_CONSTANT) + this_width = id_ast->children[0]->bits.size(); + else + input_error("Failed to detect width for parameter %s!\n", str.c_str()); + } if (children.size() != 0) range = children[0]; } else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) { @@ -753,7 +1029,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun // log("---\n"); // id_ast->dumpAst(NULL, "decl> "); // dumpAst(NULL, "ref> "); - log_file_error(filename, location.first_line, "Failed to detect width of signal access `%s'!\n", str.c_str()); + input_error("Failed to detect width of signal access `%s'!\n", str.c_str()); } } else { this_width = id_ast->range_left - id_ast->range_right + 1; @@ -764,22 +1040,26 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun this_width = 32; } else if (id_ast->type == AST_MEMORY) { if (!id_ast->children[0]->range_valid) - log_file_error(filename, location.first_line, "Failed to detect width of memory access `%s'!\n", str.c_str()); + input_error("Failed to detect width of memory access `%s'!\n", str.c_str()); this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; if (children.size() > 1) range = children[1]; + } else if (id_ast->type == AST_STRUCT_ITEM || id_ast->type == AST_STRUCT || id_ast->type == AST_UNION) { + AstNode *tmp_range = make_index_range(id_ast); + this_width = tmp_range->range_left - tmp_range->range_right + 1; + delete tmp_range; } else - log_file_error(filename, location.first_line, "Failed to detect width for identifier %s!\n", str.c_str()); + input_error("Failed to detect width for identifier %s!\n", str.c_str()); if (range) { if (range->children.size() == 1) this_width = 1; else if (!range->range_valid) { - AstNode *left_at_zero_ast = children[0]->children[0]->clone(); - AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); - while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } - while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } + AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero(); + AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); + while (left_at_zero_ast->simplify(true, 1, -1, false)) { } + while (right_at_zero_ast->simplify(true, 1, -1, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); this_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; delete left_at_zero_ast; delete right_at_zero_ast; @@ -793,9 +1073,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun break; case AST_TO_BITS: - while (children[0]->simplify(true, false, false, 1, -1, false, false) == true) { } + while (children[0]->simplify(true, 1, -1, false) == true) { } if (children[0]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Left operand of tobits expression is not constant!\n"); + input_error("Left operand of tobits expression is not constant!\n"); children[1]->detectSignWidthWorker(sub_width_hint, sign_hint); width_hint = max(width_hint, children[0]->bitsAsConst().as_int()); break; @@ -815,13 +1095,14 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun break; case AST_CAST_SIZE: - while (children.at(0)->simplify(true, false, false, 1, -1, false, false)) { } + while (children.at(0)->simplify(true, 1, -1, false)) { } if (children.at(0)->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Static cast with non constant expression!\n"); + input_error("Static cast with non constant expression!\n"); children.at(1)->detectSignWidthWorker(width_hint, sign_hint); - width_hint = children.at(0)->bitsAsConst().as_int(); + this_width = children.at(0)->bitsAsConst().as_int(); + width_hint = max(width_hint, this_width); if (width_hint <= 0) - log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n"); + input_error("Static cast with zero or negative size!\n"); break; case AST_CONCAT: @@ -836,9 +1117,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun break; case AST_REPLICATE: - while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { } + while (children[0]->simplify(true, 1, -1, false) == true) { } if (children[0]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Left operand of replicate expression is not constant!\n"); + input_error("Left operand of replicate expression is not constant!\n"); children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); sign_hint = false; @@ -914,17 +1195,51 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!id2ast->is_signed) sign_hint = false; if (!id2ast->children[0]->range_valid) - log_file_error(filename, location.first_line, "Failed to detect width of memory access `%s'!\n", str.c_str()); + input_error("Failed to detect width of memory access `%s'!\n", str.c_str()); this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1; width_hint = max(width_hint, this_width); break; + case AST_CASE: + { + // This detects the _overall_ sign and width to be used for comparing + // the case expression with the case item expressions. The case + // expression and case item expressions are extended to the maximum + // width among them, and are only interpreted as signed if all of them + // are signed. + width_hint = -1; + sign_hint = true; + auto visit_case_expr = [&width_hint, &sign_hint] (AstNode *node) { + int sub_width_hint = -1; + bool sub_sign_hint = true; + node->detectSignWidth(sub_width_hint, sub_sign_hint); + width_hint = max(width_hint, sub_width_hint); + sign_hint &= sub_sign_hint; + }; + visit_case_expr(children[0]); + for (size_t i = 1; i < children.size(); i++) { + AstNode *child = children[i]; + for (AstNode *v : child->children) + if (v->type != AST_DEFAULT && v->type != AST_BLOCK) + visit_case_expr(v); + } + break; + } + + case AST_PREFIX: + // Prefix nodes always resolve to identifiers in generate loops, so we + // can simply perform the resolution to determine the sign and width. + simplify(true, 1, -1, false); + log_assert(type == AST_IDENTIFIER); + detectSignWidthWorker(width_hint, sign_hint, found_real); + break; + case AST_FCALL: if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { if (GetSize(children) == 1) { - while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { } + while (children[0]->simplify(true, 1, -1, false) == true) { } if (children[0]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "System function %s called with non-const argument!\n", + input_error("System function %s called with non-const argument!\n", RTLIL::unescape_id(str).c_str()); width_hint = max(width_hint, int(children[0]->asInt(true))); } @@ -936,17 +1251,61 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun sub_sign_hint = true; children.at(0)->detectSignWidthWorker(sub_width_hint, sub_sign_hint); width_hint = max(width_hint, sub_width_hint); - sign_hint = false; + sign_hint &= sub_sign_hint; } break; } + if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") { + width_hint = max(width_hint, 32); + break; + } + if (current_scope.count(str)) + { + // This width detection is needed for function calls which are + // unelaborated, which currently applies to calls to functions + // reached via unevaluated ternary branches or used in case or case + // item expressions. + const AstNode *func = current_scope.at(str); + if (func->type != AST_FUNCTION) + input_error("Function call to %s resolved to something that isn't a function!\n", RTLIL::unescape_id(str).c_str()); + const AstNode *wire = nullptr; + for (const AstNode *child : func->children) + if (child->str == func->str) { + wire = child; + break; + } + log_assert(wire && wire->type == AST_WIRE); + sign_hint &= wire->is_signed; + int result_width = 1; + if (!wire->children.empty()) + { + log_assert(wire->children.size() == 1); + const AstNode *range = wire->children.at(0); + log_assert(range->type == AST_RANGE && range->children.size() == 2); + AstNode *left = range->children.at(0)->clone(); + AstNode *right = range->children.at(1)->clone(); + left->set_in_param_flag(true); + right->set_in_param_flag(true); + while (left->simplify(true, 1, -1, false)) { } + while (right->simplify(true, 1, -1, false)) { } + if (left->type != AST_CONSTANT || right->type != AST_CONSTANT) + input_error("Function %s has non-constant width!", + RTLIL::unescape_id(str).c_str()); + result_width = abs(int(left->asInt(true) - right->asInt(true))); + delete left; + delete right; + } + width_hint = max(width_hint, result_width); + break; + } YS_FALLTHROUGH // everything should have been handled above -> print error if not. default: + AstNode *current_scope_ast = current_ast_mod == nullptr ? current_ast : current_ast_mod; for (auto f : log_files) - current_ast_mod->dumpAst(f, "verilog-ast> "); - log_file_error(filename, location.first_line, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str()); + current_scope_ast->dumpAst(f, "verilog-ast> "); + input_error("Don't know how to detect sign and width for %s node!\n", type2str(type).c_str()); } if (*found_real) @@ -961,6 +1320,11 @@ void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real if (found_real) *found_real = false; detectSignWidthWorker(width_hint, sign_hint, found_real); + + constexpr int kWidthLimit = 1 << 24; + if (width_hint >= kWidthLimit) + input_error("Expression width %d exceeds implementation limit of %d!\n", + width_hint, kWidthLimit); } // create RTLIL from an AST node @@ -1008,8 +1372,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // If a port in a module with unknown type is found, mark it with the attribute 'is_interface' // This is used by the hierarchy pass to know when it can replace interface connection with the individual // signals. - RTLIL::Wire *wire = current_module->addWire(str, 1); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "interface port"); + RTLIL::Wire *wire = current_module->addWire(id, 1); + set_src_attr(wire, this); wire->start_offset = 0; wire->port_id = port_id; wire->port_input = true; @@ -1043,18 +1409,21 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (flag_pwires) { if (GetSize(children) < 1 || children[0]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Parameter `%s' with non-constant value!\n", str.c_str()); + input_error("Parameter `%s' with non-constant value!\n", str.c_str()); RTLIL::Const val = children[0]->bitsAsConst(); - RTLIL::Wire *wire = current_module->addWire(str, GetSize(val)); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "pwire"); + RTLIL::Wire *wire = current_module->addWire(id, GetSize(val)); current_module->connect(wire, val); + wire->is_signed = children[0]->is_signed; - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); wire->attributes[type == AST_PARAMETER ? ID::parameter : ID::localparam] = 1; for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); wire->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -1062,16 +1431,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // create an RTLIL::Wire for an AST_WIRE node case AST_WIRE: { - if (current_module->wires_.count(str) != 0) - log_file_error(filename, location.first_line, "Re-definition of signal `%s'!\n", str.c_str()); if (!range_valid) - log_file_error(filename, location.first_line, "Signal `%s' with non-constant width!\n", str.c_str()); + input_error("Signal `%s' with non-constant width!\n", str.c_str()); if (!(range_left + 1 >= range_right)) - log_file_error(filename, location.first_line, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1); + input_error("Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1); - RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "signal"); + RTLIL::Wire *wire = current_module->addWire(id, range_left - range_right + 1); + set_src_attr(wire, this); wire->start_offset = range_right; wire->port_id = port_id; wire->port_input = is_input; @@ -1081,7 +1450,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); wire->attributes[attr.first] = attr.second->asAttrConst(); } @@ -1092,18 +1461,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // create an RTLIL::Memory for an AST_MEMORY node case AST_MEMORY: { - if (current_module->memories.count(str) != 0) - log_file_error(filename, location.first_line, "Re-definition of memory `%s'!\n", str.c_str()); - log_assert(children.size() >= 2); log_assert(children[0]->type == AST_RANGE); log_assert(children[1]->type == AST_RANGE); if (!children[0]->range_valid || !children[1]->range_valid) - log_file_error(filename, location.first_line, "Memory `%s' with non-constant width or size!\n", str.c_str()); + input_error("Memory `%s' with non-constant width or size!\n", str.c_str()); RTLIL::Memory *memory = new RTLIL::Memory; - memory->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(memory, this); memory->name = str; memory->width = children[0]->range_left - children[0]->range_right + 1; if (children[1]->range_right < children[1]->range_left) { @@ -1113,11 +1479,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) memory->start_offset = children[1]->range_left; memory->size = children[1]->range_right - children[1]->range_left + 1; } + check_unique_id(current_module, memory->name, this, "memory"); current_module->memories[memory->name] = memory; for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); memory->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -1153,6 +1520,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigChunk chunk; bool is_interface = false; + AST::AstNode *member_node = NULL; int add_undef_bits_msb = 0; int add_undef_bits_lsb = 0; @@ -1160,16 +1528,24 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) { RTLIL::Wire *wire = current_module->addWire(str); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); wire->name = str; - if (flag_autowire) + + // If we are currently processing a bind directive which wires up + // signals or parameters explicitly, rather than with .*, then + // current_module will start out empty and we don't want to warn the + // user about it: we'll spot broken wiring later, when we run the + // hierarchy pass. + if (dynamic_cast(current_module)) { + /* nothing to do here */ + } else if (flag_autowire) log_file_warning(filename, location.first_line, "Identifier `%s' is implicitly declared.\n", str.c_str()); else - log_file_error(filename, location.first_line, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); + input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); } else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM || id2ast->type == AST_ENUM_ITEM) { if (id2ast->children[0]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Parameter %s does not evaluate to constant value!\n", str.c_str()); + input_error("Parameter %s does not evaluate to constant value!\n", str.c_str()); chunk = RTLIL::Const(id2ast->children[0]->bits); goto use_const_chunk; } @@ -1184,11 +1560,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) is_interface = true; } else { - log_file_error(filename, location.first_line, "Identifier `%s' doesn't map to any signal!\n", str.c_str()); + input_error("Identifier `%s' doesn't map to any signal!\n", str.c_str()); } if (id2ast->type == AST_MEMORY) - log_file_error(filename, location.first_line, "Identifier `%s' does map to an unexpanded memory!\n", str.c_str()); + input_error("Identifier `%s' does map to an unexpanded memory!\n", str.c_str()); // If identifier is an interface, create a RTLIL::SigSpec with a dummy wire with a attribute called 'is_interface' // This makes it possible for the hierarchy pass to see what are interface connections and then replace them @@ -1208,31 +1584,49 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.width = wire->width; chunk.offset = 0; + if ((member_node = get_struct_member())) { + // Clamp wire chunk to range of member within struct/union. + chunk.width = member_node->range_left - member_node->range_right + 1; + chunk.offset = member_node->range_right; + } + use_const_chunk: if (children.size() != 0) { if (children[0]->type != AST_RANGE) - log_file_error(filename, location.first_line, "Single range expected.\n"); + input_error("Single range expected.\n"); int source_width = id2ast->range_left - id2ast->range_right + 1; int source_offset = id2ast->range_right; + int chunk_left = source_width - 1; + int chunk_right = 0; + + if (member_node) { + // Clamp wire chunk to range of member within struct/union. + log_assert(!source_offset && !id2ast->range_swapped); + chunk_left = chunk.offset + chunk.width - 1; + chunk_right = chunk.offset; + } + if (!children[0]->range_valid) { - AstNode *left_at_zero_ast = children[0]->children[0]->clone(); - AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : left_at_zero_ast->clone(); - while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } - while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } + AstNode *left_at_zero_ast = children[0]->children[0]->clone_at_zero(); + AstNode *right_at_zero_ast = children[0]->children.size() >= 2 ? children[0]->children[1]->clone_at_zero() : left_at_zero_ast->clone(); + while (left_at_zero_ast->simplify(true, 1, -1, false)) { } + while (right_at_zero_ast->simplify(true, 1, -1, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : children[0]->children[0]->clone()); fake_ast->children[0]->delete_children(); + if (member_node) + fake_ast->children[0]->set_attribute(ID::wiretype, member_node->clone()); int fake_ast_width = 0; bool fake_ast_sign = true; fake_ast->children[1]->detectSignWidth(fake_ast_width, fake_ast_sign); RTLIL::SigSpec shift_val = fake_ast->children[1]->genRTLIL(fake_ast_width, fake_ast_sign); - if (id2ast->range_right != 0) { - shift_val = current_module->Sub(NEW_ID, shift_val, id2ast->range_right, fake_ast_sign); + if (source_offset != 0) { + shift_val = current_module->Sub(NEW_ID, shift_val, source_offset, fake_ast_sign); fake_ast->children[1]->is_signed = true; } if (id2ast->range_swapped) { @@ -1248,10 +1642,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) return sig; } else { chunk.width = children[0]->range_left - children[0]->range_right + 1; - chunk.offset = children[0]->range_right - source_offset; + chunk.offset += children[0]->range_right - source_offset; if (id2ast->range_swapped) - chunk.offset = (id2ast->range_left - id2ast->range_right + 1) - (chunk.offset + chunk.width); - if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) { + chunk.offset = source_width - (chunk.offset + chunk.width); + if (chunk.offset > chunk_left || chunk.offset + chunk.width < chunk_right) { if (chunk.width == 1) log_file_warning(filename, location.first_line, "Range select out of bounds on signal `%s': Setting result bit to undef.\n", str.c_str()); @@ -1260,12 +1654,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width); chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); } else { - if (chunk.width + chunk.offset > source_width) { - add_undef_bits_msb = (chunk.width + chunk.offset) - source_width; + if (chunk.offset + chunk.width - 1 > chunk_left) { + add_undef_bits_msb = (chunk.offset + chunk.width - 1) - chunk_left; chunk.width -= add_undef_bits_msb; } - if (chunk.offset < 0) { - add_undef_bits_lsb = -chunk.offset; + if (chunk.offset < chunk_right) { + add_undef_bits_lsb = chunk_right - chunk.offset; chunk.width -= add_undef_bits_lsb; chunk.offset += add_undef_bits_lsb; } @@ -1302,13 +1696,20 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // changing the size of signal can be done directly using RTLIL::SigSpec case AST_CAST_SIZE: { RTLIL::SigSpec size = children[0]->genRTLIL(); - RTLIL::SigSpec sig = children[1]->genRTLIL(); if (!size.is_fully_const()) - log_file_error(filename, location.first_line, "Static cast with non constant expression!\n"); + input_error("Static cast with non constant expression!\n"); int width = size.as_int(); if (width <= 0) - log_file_error(filename, location.first_line, "Static cast with zero or negative size!\n"); - sig.extend_u0(width, sign_hint); + input_error("Static cast with zero or negative size!\n"); + // determine the *signedness* of the expression + int sub_width_hint = -1; + bool sub_sign_hint = true; + children[1]->detectSignWidth(sub_width_hint, sub_sign_hint); + // generate the signal given the *cast's* size and the + // *expression's* signedness + RTLIL::SigSpec sig = children[1]->genWidthRTLIL(width, sub_sign_hint); + // context may effect this node's signedness, but not that of the + // casted expression is_signed = sign_hint; return sig; } @@ -1328,7 +1729,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec left = children[0]->genRTLIL(); RTLIL::SigSpec right = children[1]->genRTLIL(); if (!left.is_fully_const()) - log_file_error(filename, location.first_line, "Left operand of replicate expression is not constant!\n"); + input_error("Left operand of replicate expression is not constant!\n"); int count = left.as_int(); RTLIL::SigSpec sig; for (int i = 0; i < count; i++) @@ -1402,7 +1803,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (width_hint < 0) detectSignWidth(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); - RTLIL::SigSpec right = children[1]->genRTLIL(); + // for $shift and $shiftx, the second operand can be negative + RTLIL::SigSpec right = children[1]->genRTLIL(-1, type == AST_SHIFT || type == AST_SHIFTX); int width = width_hint > 0 ? width_hint : left.size(); is_signed = children[0]->is_signed; return binop2rtlil(this, type_name, width, left, right); @@ -1539,16 +1941,17 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MEMRD: { std::stringstream sstr; - sstr << "$memrd$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); int mem_width, mem_size, addr_bits; is_signed = id2ast->is_signed; + wire->is_signed = is_signed; id2ast->meminfo(mem_width, mem_size, addr_bits); RTLIL::SigSpec addr_sig = children[0]->genRTLIL(); @@ -1572,88 +1975,83 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) return RTLIL::SigSpec(wire); } - // generate $memwr cells for memory write ports - case AST_MEMWR: + // generate $meminit cells case AST_MEMINIT: { std::stringstream sstr; - sstr << (type == AST_MEMWR ? "$memwr$" : "$meminit$") << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$meminit$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + + SigSpec en_sig = children[2]->genRTLIL(); - RTLIL::Cell *cell = current_module->addCell(sstr.str(), type == AST_MEMWR ? ID($memwr) : ID($meminit)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($meminit_v2)); + set_src_attr(cell, this); int mem_width, mem_size, addr_bits; id2ast->meminfo(mem_width, mem_size, addr_bits); - int num_words = 1; - if (type == AST_MEMINIT) { - if (children[2]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Memory init with non-constant word count!\n"); - num_words = int(children[2]->asInt(false)); - cell->parameters[ID::WORDS] = RTLIL::Const(num_words); - } + if (children[3]->type != AST_CONSTANT) + input_error("Memory init with non-constant word count!\n"); + int num_words = int(children[3]->asInt(false)); + cell->parameters[ID::WORDS] = RTLIL::Const(num_words); SigSpec addr_sig = children[0]->genRTLIL(); cell->setPort(ID::ADDR, addr_sig); - cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words)); + cell->setPort(ID::DATA, children[1]->genWidthRTLIL(current_module->memories[str]->width * num_words, true)); + cell->setPort(ID::EN, en_sig); cell->parameters[ID::MEMID] = RTLIL::Const(str); cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig)); cell->parameters[ID::WIDTH] = RTLIL::Const(current_module->memories[str]->width); - if (type == AST_MEMWR) { - cell->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::Sx, 1)); - cell->setPort(ID::EN, children[2]->genRTLIL()); - cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(0); - cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(0); - } - cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1); } break; - // generate $assert cells + // generate $check cells case AST_ASSERT: case AST_ASSUME: case AST_LIVE: case AST_FAIR: case AST_COVER: { - IdString celltype; - if (type == AST_ASSERT) celltype = ID($assert); - if (type == AST_ASSUME) celltype = ID($assume); - if (type == AST_LIVE) celltype = ID($live); - if (type == AST_FAIR) celltype = ID($fair); - if (type == AST_COVER) celltype = ID($cover); - - log_assert(children.size() == 2); - - RTLIL::SigSpec check = children[0]->genRTLIL(); - if (GetSize(check) != 1) - check = current_module->ReduceBool(NEW_ID, check); - - RTLIL::SigSpec en = children[1]->genRTLIL(); - if (GetSize(en) != 1) - en = current_module->ReduceBool(NEW_ID, en); + std::string flavor, desc; + if (type == AST_ASSERT) { flavor = "assert"; desc = "assert property ()"; } + if (type == AST_ASSUME) { flavor = "assume"; desc = "assume property ()"; } + if (type == AST_LIVE) { flavor = "live"; desc = "assert property (eventually)"; } + if (type == AST_FAIR) { flavor = "fair"; desc = "assume property (eventually)"; } + if (type == AST_COVER) { flavor = "cover"; desc = "cover property ()"; } IdString cellname; if (str.empty()) - cellname = stringf("%s$%s:%d$%d", celltype.c_str(), filename.c_str(), location.first_line, autoidx++); + cellname = stringf("$%s$%s:%d$%d", flavor.c_str(), RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); else cellname = str; + check_unique_id(current_module, cellname, this, "procedural assertion"); - RTLIL::Cell *cell = current_module->addCell(cellname, celltype); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + RTLIL::SigSpec check = children[0]->genRTLIL(); + if (GetSize(check) != 1) + check = current_module->ReduceBool(NEW_ID, check); + RTLIL::Cell *cell = current_module->addCell(cellname, ID($check)); + set_src_attr(cell, this); for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); + input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } - + cell->setParam(ID(FLAVOR), flavor); + cell->parameters[ID::TRG_WIDTH] = 0; + cell->parameters[ID::TRG_ENABLE] = 0; + cell->parameters[ID::TRG_POLARITY] = 0; + cell->parameters[ID::PRIORITY] = 0; + cell->setPort(ID::TRG, RTLIL::SigSpec()); + cell->setPort(ID::EN, RTLIL::S1); cell->setPort(ID::A, check); - cell->setPort(ID::EN, en); + + // No message is emitted to ensure Verilog code roundtrips correctly. + Fmt fmt; + fmt.emit_rtlil(cell); } break; @@ -1661,7 +2059,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_ASSIGN: { RTLIL::SigSpec left = children[0]->genRTLIL(); - RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size()); + RTLIL::SigSpec right = children[1]->genWidthRTLIL(left.size(), true); if (left.has_const()) { RTLIL::SigSpec new_left, new_right; for (int i = 0; i < GetSize(left); i++) @@ -1685,11 +2083,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { int port_counter = 0, para_counter = 0; - if (current_module->count_id(str) != 0) - log_file_error(filename, location.first_line, "Re-definition of cell `%s'!\n", str.c_str()); - - RTLIL::Cell *cell = current_module->addCell(str, ""); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + RTLIL::IdString id = str; + check_unique_id(current_module, id, this, "cell"); + RTLIL::Cell *cell = current_module->addCell(id, ""); + set_src_attr(cell, this); // Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass cell->set_bool_attribute(ID::module_not_derived); @@ -1702,27 +2099,47 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) continue; } if (child->type == AST_PARASET) { - int extra_const_flags = 0; IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; - if (child->children[0]->type == AST_REALVALUE) { + const AstNode *value = child->children[0]; + if (value->type == AST_REALVALUE) log_file_warning(filename, location.first_line, "Replacing floating point parameter %s.%s = %f with string.\n", - log_id(cell), log_id(paraname), child->children[0]->realvalue); - extra_const_flags = RTLIL::CONST_FLAG_REAL; - auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue)); - strnode->cloneInto(child->children[0]); - delete strnode; - } - if (child->children[0]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Parameter %s.%s with non-constant value!\n", + log_id(cell), log_id(paraname), value->realvalue); + else if (value->type != AST_CONSTANT) + input_error("Parameter %s.%s with non-constant value!\n", log_id(cell), log_id(paraname)); - cell->parameters[paraname] = child->children[0]->asParaConst(); - cell->parameters[paraname].flags |= extra_const_flags; + cell->parameters[paraname] = value->asParaConst(); continue; } if (child->type == AST_ARGUMENT) { RTLIL::SigSpec sig; - if (child->children.size() > 0) - sig = child->children[0]->genRTLIL(); + if (child->children.size() > 0) { + AstNode *arg = child->children[0]; + int local_width_hint = -1; + bool local_sign_hint = false; + // don't inadvertently attempt to detect the width of interfaces + if (arg->type != AST_IDENTIFIER || !arg->id2ast || arg->id2ast->type != AST_CELL) + arg->detectSignWidth(local_width_hint, local_sign_hint); + sig = arg->genRTLIL(local_width_hint, local_sign_hint); + log_assert(local_sign_hint == arg->is_signed); + if (sig.is_wire()) { + // if the resulting SigSpec is a wire, its + // signedness should match that of the AstNode + if (arg->type == AST_IDENTIFIER && arg->id2ast && arg->id2ast->is_signed && !arg->is_signed) + // fully-sliced signed wire will be resolved + // once the module becomes available + log_assert(attributes.count(ID::reprocess_after)); + else + log_assert(arg->is_signed == sig.as_wire()->is_signed); + } else if (arg->is_signed) { + // non-trivial signed nodes are indirected through + // signed wires to enable sign extension + RTLIL::IdString wire_name = NEW_ID; + RTLIL::Wire *wire = current_module->addWire(wire_name, GetSize(sig)); + wire->is_signed = true; + current_module->connect(wire, sig); + sig = wire; + } + } if (child->str.size() == 0) { char buf[100]; snprintf(buf, 100, "$%d", ++port_counter); @@ -1736,7 +2153,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Attribute `%s' with non-constant value.\n", attr.first.c_str()); + input_error("Attribute `%s' with non-constant value.\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } if (cell->type == ID($specify2)) { @@ -1744,7 +2161,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) int dst_width = GetSize(cell->getPort(ID::DST)); bool full = cell->getParam(ID::FULL).as_bool(); if (!full && src_width != dst_width) - log_file_error(filename, location.first_line, "Parallel specify SRC width does not match DST width.\n"); + input_error("Parallel specify SRC width does not match DST width.\n"); cell->setParam(ID::SRC_WIDTH, Const(src_width)); cell->setParam(ID::DST_WIDTH, Const(dst_width)); } @@ -1752,7 +2169,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) int dat_width = GetSize(cell->getPort(ID::DAT)); int dst_width = GetSize(cell->getPort(ID::DST)); if (dat_width != dst_width) - log_file_error(filename, location.first_line, "Specify DAT width does not match DST width.\n"); + input_error("Specify DAT width does not match DST width.\n"); int src_width = GetSize(cell->getPort(ID::SRC)); cell->setParam(ID::SRC_WIDTH, Const(src_width)); cell->setParam(ID::DST_WIDTH, Const(dst_width)); @@ -1794,23 +2211,30 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) log_file_warning(filename, location.first_line, "\n"); } else if (str == "$error") { if (sz > 0) - log_file_error(filename, location.first_line, "%s.\n", children[0]->str.c_str()); + input_error("%s.\n", children[0]->str.c_str()); else - log_file_error(filename, location.first_line, "\n"); + input_error("\n"); } else if (str == "$fatal") { // TODO: 1st parameter, if exists, is 0,1 or 2, and passed to $finish() // if no parameter is given, default value is 1 // dollar_finish(sz ? children[0] : 1); // perhaps create & use log_file_fatal() if (sz > 0) - log_file_error(filename, location.first_line, "FATAL: %s.\n", children[0]->str.c_str()); + input_error("FATAL: %s.\n", children[0]->str.c_str()); else - log_file_error(filename, location.first_line, "FATAL.\n"); + input_error("FATAL.\n"); } else { - log_file_error(filename, location.first_line, "Unknown elabortoon system task '%s'.\n", str.c_str()); + input_error("Unknown elabortoon system task '%s'.\n", str.c_str()); } } break; + case AST_BIND: { + // Read a bind construct. This should have one or more cells as children. + for (RTLIL::Binding *binding : genBindings()) + current_module->add(binding); + break; + } + case AST_FCALL: { if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { @@ -1818,32 +2242,32 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) int width = width_hint; if (GetSize(children) > 1) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1 or 0.\n", + input_error("System function %s got %d arguments, expected 1 or 0.\n", RTLIL::unescape_id(str).c_str(), GetSize(children)); if (GetSize(children) == 1) { if (children[0]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "System function %s called with non-const argument!\n", + input_error("System function %s called with non-const argument!\n", RTLIL::unescape_id(str).c_str()); width = children[0]->asInt(true); } if (width <= 0) - log_file_error(filename, location.first_line, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str()); + input_error("Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str()); Cell *cell = current_module->addCell(myid, str.substr(1)); - cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(cell, this); cell->parameters[ID::WIDTH] = width; if (attributes.count(ID::reg)) { auto &attr = attributes.at(ID::reg); if (attr->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Attribute `reg' with non-constant value!\n"); + input_error("Attribute `reg' with non-constant value!\n"); cell->attributes[ID::reg] = attr->asAttrConst(); } Wire *wire = current_module->addWire(myid + "_wire", width); - wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + set_src_attr(wire, this); cell->setPort(ID::Y, wire); is_signed = sign_hint; @@ -1856,8 +2280,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) default: for (auto f : log_files) current_ast_mod->dumpAst(f, "verilog-ast> "); - type_name = type2str(type); - log_file_error(filename, location.first_line, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str()); + input_error("Don't know how to generate RTLIL code for %s node!\n", type2str(type).c_str()); } return RTLIL::SigSpec(); @@ -1866,14 +2289,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // this is a wrapper for AstNode::genRTLIL() when a specific signal width is requested and/or // signals must be substituted before being used as input values (used by ProcessGenerator) // note that this is using some global variables to communicate this special settings to AstNode::genRTLIL(). -RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict *new_subst_ptr) +RTLIL::SigSpec AstNode::genWidthRTLIL(int width, bool sgn, const dict *new_subst_ptr) { const dict *backup_subst_ptr = genRTLIL_subst_ptr; if (new_subst_ptr) genRTLIL_subst_ptr = new_subst_ptr; - bool sign_hint = true; + bool sign_hint = sgn; int width_hint = width; detectSignWidthWorker(width_hint, sign_hint); RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index fb6623f0238..43a4e03a29d 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -35,149 +35,153 @@ #include #include #include +// For std::gcd in C++17 +// #include YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; -// Process a format string and arguments for $display, $write, $sprintf, etc - -std::string AstNode::process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint) { - // Other arguments are placeholders. Process the string as we go through it - std::string sout; - for (size_t i = 0; i < sformat.length(); i++) - { - // format specifier - if (sformat[i] == '%') - { - // If there's no next character, that's a problem - if (i+1 >= sformat.length()) - log_file_error(filename, location.first_line, "System task `%s' called with `%%' at end of string.\n", str.c_str()); - - char cformat = sformat[++i]; - - // %% is special, does not need a matching argument - if (cformat == '%') - { - sout += '%'; - continue; - } - - bool got_len = false; - bool got_zlen = false; - int len_value = 0; - - while ('0' <= cformat && cformat <= '9') - { - if (!got_len && cformat == '0') - got_zlen = true; - - got_len = true; - len_value = 10*len_value + (cformat - '0'); +// gcd computed by Euclidian division. +// To be replaced by C++17 std::gcd +template I gcd(I a, I b) { + while (b != 0) { + I tmp = b; + b = a%b; + a = tmp; + } + return std::abs(a); +} - cformat = sformat[++i]; - } +void AstNode::set_in_lvalue_flag(bool flag, bool no_descend) +{ + if (flag != in_lvalue_from_above) { + in_lvalue_from_above = flag; + if (!no_descend) + fixup_hierarchy_flags(); + } +} - // Simplify the argument - AstNode *node_arg = nullptr; +void AstNode::set_in_param_flag(bool flag, bool no_descend) +{ + if (flag != in_param_from_above) { + in_param_from_above = flag; + if (!no_descend) + fixup_hierarchy_flags(); + } +} - // Everything from here on depends on the format specifier - switch (cformat) - { - case 's': - case 'S': - case 'd': - case 'D': - if (got_len && len_value != 0) - goto unsupported_format; - YS_FALLTHROUGH - case 'x': - case 'X': - if (next_arg >= GetSize(children)) - log_file_error(filename, location.first_line, "Missing argument for %%%c format specifier in system task `%s'.\n", - cformat, str.c_str()); - - node_arg = children[next_arg++]; - while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_arg->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); - break; +void AstNode::fixup_hierarchy_flags(bool force_descend) +{ + // With forced descend, we disable the implicit + // descend from within the set_* functions, instead + // we do an explicit descend at the end of this function - case 'm': - case 'M': - if (got_len) - goto unsupported_format; - break; + in_param = in_param_from_above; - case 'l': - case 'L': - if (got_len) - goto unsupported_format; - break; + switch (type) { + case AST_PARAMETER: + case AST_LOCALPARAM: + case AST_DEFPARAM: + case AST_PARASET: + case AST_PREFIX: + in_param = true; + for (auto child : children) + child->set_in_param_flag(true, force_descend); + break; - default: - unsupported_format: - log_file_error(filename, location.first_line, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); - break; - } + case AST_REPLICATE: + case AST_WIRE: + case AST_GENIF: + case AST_GENCASE: + for (auto child : children) + child->set_in_param_flag(in_param, force_descend); + if (children.size() >= 1) + children[0]->set_in_param_flag(true, force_descend); + break; - switch (cformat) - { - case 's': - case 'S': - sout += node_arg->bitsAsConst().decode_string(); - break; + case AST_GENFOR: + case AST_FOR: + for (auto child : children) + child->set_in_param_flag(in_param, force_descend); + if (children.size() >= 2) + children[1]->set_in_param_flag(true, force_descend); + break; - case 'd': - case 'D': - sout += stringf("%d", node_arg->bitsAsConst().as_int()); - break; + default: + in_param = in_param_from_above; + for (auto child : children) + child->set_in_param_flag(in_param, force_descend); + } - case 'x': - case 'X': - { - Const val = node_arg->bitsAsConst(); + for (auto attr : attributes) + attr.second->set_in_param_flag(true, force_descend); - while (GetSize(val) % 4 != 0) - val.bits.push_back(State::S0); + in_lvalue = in_lvalue_from_above; - int len = GetSize(val) / 4; - for (int i = len; i < len_value; i++) - sout += got_zlen ? '0' : ' '; + switch (type) { + case AST_ASSIGN: + case AST_ASSIGN_EQ: + case AST_ASSIGN_LE: + if (children.size() >= 1) + children[0]->set_in_lvalue_flag(true, force_descend); + if (children.size() >= 2) + children[1]->set_in_lvalue_flag(in_lvalue, force_descend); + break; - for (int i = len-1; i >= 0; i--) { - Const digit = val.extract(4*i, 4); - if (digit.is_fully_def()) - sout += stringf(cformat == 'x' ? "%x" : "%X", digit.as_int()); - else - sout += cformat == 'x' ? "x" : "X"; - } - } - break; + default: + for (auto child : children) + child->set_in_lvalue_flag(in_lvalue, force_descend); + } - case 'm': - case 'M': - sout += log_id(current_module->name); - break; + if (force_descend) { + for (auto child : children) + child->fixup_hierarchy_flags(true); + for (auto attr : attributes) + attr.second->fixup_hierarchy_flags(true); + } +} - case 'l': - case 'L': - sout += log_id(current_module->name); - break; +// Process a format string and arguments for $display, $write, $sprintf, etc - default: - log_abort(); - } +Fmt AstNode::processFormat(int stage, bool sformat_like, int default_base, size_t first_arg_at, bool may_fail) { + std::vector args; + for (size_t index = first_arg_at; index < children.size(); index++) { + AstNode *node_arg = children[index]; + while (node_arg->simplify(true, stage, -1, false)) { } + + VerilogFmtArg arg = {}; + arg.filename = filename; + arg.first_line = location.first_line; + if (node_arg->type == AST_CONSTANT && node_arg->is_string) { + arg.type = VerilogFmtArg::STRING; + arg.str = node_arg->bitsAsConst().decode_string(); + // and in case this will be used as an argument... + arg.sig = node_arg->bitsAsConst(); + arg.signed_ = false; + } else if (node_arg->type == AST_IDENTIFIER && node_arg->str == "$time") { + arg.type = VerilogFmtArg::TIME; + } else if (node_arg->type == AST_IDENTIFIER && node_arg->str == "$realtime") { + arg.type = VerilogFmtArg::TIME; + arg.realtime = true; + } else if (node_arg->type == AST_CONSTANT) { + arg.type = VerilogFmtArg::INTEGER; + arg.sig = node_arg->bitsAsConst(); + arg.signed_ = node_arg->is_signed; + } else if (may_fail) { + log_file_info(filename, location.first_line, "Skipping system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); + return Fmt(); + } else { + log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument at position %zu.\n", str.c_str(), index + 1); } - - // not a format specifier - else - sout += sformat[i]; + args.push_back(arg); } - return sout; -} + Fmt fmt; + fmt.parse_verilog(args, sformat_like, default_base, /*task_name=*/str, current_module->name); + return fmt; +} void AstNode::annotateTypedEnums(AstNode *template_node) { @@ -192,6 +196,7 @@ void AstNode::annotateTypedEnums(AstNode *template_node) log_assert(current_scope.count(enum_type) == 1); AstNode *enum_node = current_scope.at(enum_type); log_assert(enum_node->type == AST_ENUM); + while (enum_node->simplify(true, 1, -1, false)) { } //get width from 1st enum item: log_assert(enum_node->children.size() >= 1); AstNode *enum_item0 = enum_node->children[0]; @@ -215,8 +220,8 @@ void AstNode::annotateTypedEnums(AstNode *template_node) log_assert(enum_item->children[1]->type == AST_RANGE); is_signed = enum_item->children[1]->is_signed; } else { - log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n", - enum_item->children.size(), + log_error("enum_item children size==%zu, expected 1 or 2 for %s (%s)\n", + (size_t) enum_item->children.size(), enum_item->str.c_str(), enum_node->str.c_str() ); } @@ -232,22 +237,11 @@ void AstNode::annotateTypedEnums(AstNode *template_node) RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed); enum_item_str.append(val.as_string()); //set attribute for available val to enum item name mappings - attributes[enum_item_str.c_str()] = mkconst_str(enum_item->str); + set_attribute(enum_item_str.c_str(), mkconst_str(enum_item->str)); } } } -static bool name_has_dot(const std::string &name, std::string &struct_name) -{ - // check if plausible struct member name \sss.mmm - std::string::size_type pos; - if (name.substr(0, 1) == "\\" && (pos = name.find('.', 0)) != std::string::npos) { - struct_name = name.substr(0, pos); - return true; - } - return false; -} - static AstNode *make_range(int left, int right, bool is_signed = false) { // generate a pre-validated range node for a fixed signal range. @@ -265,30 +259,22 @@ static int range_width(AstNode *node, AstNode *rnode) { log_assert(rnode->type==AST_RANGE); if (!rnode->range_valid) { - log_file_error(node->filename, node->location.first_line, "Size must be constant in packed struct/union member %s\n", node->str.c_str()); - + node->input_error("Non-constant range in declaration of %s\n", node->str.c_str()); } // note: range swapping has already been checked for return rnode->range_left - rnode->range_right + 1; } -[[noreturn]] static void struct_array_packing_error(AstNode *node) +static int add_dimension(AstNode *node, AstNode *rnode) { - log_file_error(node->filename, node->location.first_line, "Unpacked array in packed struct/union member %s\n", node->str.c_str()); + int width = range_width(node, rnode); + node->dimensions.push_back({ rnode->range_right, width, rnode->range_swapped }); + return width; } -static void save_struct_array_width(AstNode *node, int width) -{ - // stash the stride for the array - node->multirange_dimensions.push_back(width); - -} - -static int get_struct_array_width(AstNode *node) +[[noreturn]] static void struct_array_packing_error(AstNode *node) { - // the stride for the array, 1 if not an array - return (node->multirange_dimensions.empty() ? 1 : node->multirange_dimensions.back()); - + node->input_error("Unpacked array in packed struct/union member %s\n", node->str.c_str()); } static int size_packed_struct(AstNode *snode, int base_offset) @@ -313,42 +299,55 @@ static int size_packed_struct(AstNode *snode, int base_offset) // member width e.g. bit [7:0] a width = range_width(node, node->children[0]); if (node->children.size() == 2) { + // Unpacked array. Note that this is a Yosys extension; only packed data types + // and integer data types are allowed in packed structs / unions in SystemVerilog. if (node->children[1]->type == AST_RANGE) { - // unpacked array e.g. bit [63:0] a [0:3] + // Unpacked array, e.g. bit [63:0] a [0:3] + // Pretend it's declared as a packed array, e.g. bit [0:3][63:0] a auto rnode = node->children[1]; - int array_count = range_width(node, rnode); - if (array_count == 1) { - // C-type array size e.g. bit [63:0] a [4] - array_count = rnode->range_left; + if (rnode->children.size() == 1) { + // C-style array size, e.g. bit [63:0] a [4] + node->dimensions.push_back({ 0, rnode->range_left, true }); + width *= rnode->range_left; + } else { + width *= add_dimension(node, rnode); } - save_struct_array_width(node, width); - width *= array_count; + add_dimension(node, node->children[0]); } else { - // array element must be single bit for a packed array + // The Yosys extension for unpacked arrays in packed structs / unions + // only supports memories, i.e. e.g. logic [7:0] a [256] - see above. struct_array_packing_error(node); } + } else { + // Vector + add_dimension(node, node->children[0]); } // range nodes are now redundant + for (AstNode *child : node->children) + delete child; node->children.clear(); } - else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) { - // packed 2D array, e.g. bit [3:0][63:0] a - auto rnode = node->children[0]; - if (rnode->children.size() != 2) { - // packed arrays can only be 2D + else if (node->children.size() > 0 && node->children[0]->type == AST_MULTIRANGE) { + // Packed array, e.g. bit [3:0][63:0] a + if (node->children.size() != 1) { + // The Yosys extension for unpacked arrays in packed structs / unions + // only supports memories, i.e. e.g. logic [7:0] a [256] - see above. struct_array_packing_error(node); } - int array_count = range_width(node, rnode->children[0]); - width = range_width(node, rnode->children[1]); - save_struct_array_width(node, width); - width *= array_count; + width = 1; + for (auto rnode : node->children[0]->children) { + width *= add_dimension(node, rnode); + } // range nodes are now redundant + for (AstNode *child : node->children) + delete child; node->children.clear(); } else if (node->range_left < 0) { // 1 bit signal: bit, logic or reg width = 1; + node->dimensions.push_back({ 0, width, false }); } else { // already resolved and compacted @@ -371,22 +370,23 @@ static int size_packed_struct(AstNode *snode, int base_offset) packed_width = width; } else { - if (packed_width != width) { - - log_file_error(node->filename, node->location.first_line, "member %s of a packed union has %d bits, expecting %d\n", node->str.c_str(), width, packed_width); - } + if (packed_width != width) + node->input_error("member %s of a packed union has %d bits, expecting %d\n", node->str.c_str(), width, packed_width); } } else { offset += width; } } - return (is_union ? packed_width : offset); -} -[[noreturn]] static void struct_op_error(AstNode *node) -{ - log_file_error(node->filename, node->location.first_line, "Unsupported operation for struct/union member %s\n", node->str.c_str()+1); + int width = is_union ? packed_width : offset; + + snode->range_right = base_offset; + snode->range_left = base_offset + width - 1; + snode->range_valid = true; + snode->dimensions.push_back({ 0, width, false }); + + return width; } static AstNode *node_int(int ival) @@ -399,87 +399,123 @@ static AstNode *multiply_by_const(AstNode *expr_node, int stride) return new AstNode(AST_MUL, expr_node, node_int(stride)); } -static AstNode *offset_indexed_range(int offset, int stride, AstNode *left_expr, AstNode *right_expr) +static AstNode *normalize_index(AstNode *expr, AstNode *decl_node, int dimension) { - // adjust the range expressions to add an offset into the struct - // and maybe index using an array stride - auto left = left_expr->clone(); - auto right = right_expr->clone(); - if (stride > 1) { - // newleft = (left + 1) * stride - 1 - left = new AstNode(AST_SUB, multiply_by_const(new AstNode(AST_ADD, left, node_int(1)), stride), node_int(1)); - // newright = right * stride - right = multiply_by_const(right, stride); - } - // add the offset + expr = expr->clone(); + + int offset = decl_node->dimensions[dimension].range_right; if (offset) { - left = new AstNode(AST_ADD, node_int(offset), left); - right = new AstNode(AST_ADD, node_int(offset), right); + expr = new AstNode(AST_SUB, expr, node_int(offset)); } - return new AstNode(AST_RANGE, left, right); + + // Packed dimensions are normally indexed by lsb, while unpacked dimensions are normally indexed by msb. + if ((dimension < decl_node->unpacked_dimensions) ^ decl_node->dimensions[dimension].range_swapped) { + // Swap the index if the dimension is declared the "wrong" way. + int left = decl_node->dimensions[dimension].range_width - 1; + expr = new AstNode(AST_SUB, node_int(left), expr); + } + + return expr; } -static AstNode *make_struct_index_range(AstNode *node, AstNode *rnode, int stride, int offset) +static AstNode *index_offset(AstNode *offset, AstNode *rnode, AstNode *decl_node, int dimension, int &stride) { - // generate a range node to perform either bit or array indexing + stride /= decl_node->dimensions[dimension].range_width; + auto right = normalize_index(rnode->children.back(), decl_node, dimension); + auto add_offset = stride > 1 ? multiply_by_const(right, stride) : right; + return offset ? new AstNode(AST_ADD, offset, add_offset) : add_offset; +} + +static AstNode *index_msb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *decl_node, int dimension, int stride) +{ + log_assert(rnode->children.size() <= 2); + + // Offset to add to LSB + AstNode *add_offset; if (rnode->children.size() == 1) { - // index e.g. s.a[i] - return offset_indexed_range(offset, stride, rnode->children[0], rnode->children[0]); - } - else if (rnode->children.size() == 2) { - // slice e.g. s.a[i:j] - return offset_indexed_range(offset, stride, rnode->children[0], rnode->children[1]); + // Index, e.g. s.a[i] + add_offset = node_int(stride - 1); } else { - struct_op_error(node); + // rnode->children.size() == 2 + // Slice, e.g. s.a[i:j] + auto left = normalize_index(rnode->children[0], decl_node, dimension); + auto right = normalize_index(rnode->children[1], decl_node, dimension); + add_offset = new AstNode(AST_SUB, left, right); + if (stride > 1) { + // offset = (msb - lsb + 1)*stride - 1 + auto slice_width = new AstNode(AST_ADD, add_offset, node_int(1)); + add_offset = new AstNode(AST_SUB, multiply_by_const(slice_width, stride), node_int(1)); + } } -} -static AstNode *slice_range(AstNode *rnode, AstNode *snode) -{ - // apply the bit slice indicated by snode to the range rnode - log_assert(rnode->type==AST_RANGE); - auto left = rnode->children[0]; - auto right = rnode->children[1]; - log_assert(snode->type==AST_RANGE); - auto slice_left = snode->children[0]; - auto slice_right = snode->children[1]; - auto width = new AstNode(AST_SUB, slice_left->clone(), slice_right->clone()); - right = new AstNode(AST_ADD, right->clone(), slice_right->clone()); - left = new AstNode(AST_ADD, right->clone(), width); - return new AstNode(AST_RANGE, left, right); + return new AstNode(AST_ADD, lsb_offset, add_offset); } -static AstNode *make_struct_member_range(AstNode *node, AstNode *member_node) +AstNode *AstNode::make_index_range(AstNode *decl_node, bool unpacked_range) { // Work out the range in the packed array that corresponds to a struct member // taking into account any range operations applicable to the current node // such as array indexing or slicing - int range_left = member_node->range_left; - int range_right = member_node->range_right; - if (node->children.empty()) { + if (children.empty()) { // no range operations apply, return the whole width - return make_range(range_left, range_right); + return make_range(decl_node->range_left - decl_node->range_right, 0); } - int stride = get_struct_array_width(member_node); - if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { - // bit or array indexing e.g. s.a[2] or s.a[1:0] - return make_struct_index_range(node, node->children[0], stride, range_right); + + log_assert(children.size() == 1); + + // Range operations + AstNode *rnode = children[0]; + AstNode *offset = NULL; + int dim = unpacked_range ? 0 : decl_node->unpacked_dimensions; + int max_dim = unpacked_range ? decl_node->unpacked_dimensions : GetSize(decl_node->dimensions); + + int stride = 1; + for (int i = dim; i < max_dim; i++) { + stride *= decl_node->dimensions[i].range_width; + } + + // Calculate LSB offset for the final index / slice + if (rnode->type == AST_RANGE) { + offset = index_offset(offset, rnode, decl_node, dim, stride); } - else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) { - // multirange, i.e. bit slice after array index, e.g. s.a[i][p:q] - log_assert(stride > 1); - auto mrnode = node->children[0]; - auto element_range = make_struct_index_range(node, mrnode->children[0], stride, range_right); - // then apply bit slice range - auto range = slice_range(element_range, mrnode->children[1]); - delete element_range; - return range; + else if (rnode->type == AST_MULTIRANGE) { + // Add offset for each dimension + AstNode *mrnode = rnode; + int stop_dim = std::min(GetSize(mrnode->children), max_dim); + for (; dim < stop_dim; dim++) { + rnode = mrnode->children[dim]; + offset = index_offset(offset, rnode, decl_node, dim, stride); + } + dim--; // Step back to the final index / slice } else { - struct_op_error(node); + input_error("Unsupported range operation for %s\n", str.c_str()); + } + + AstNode *index_range = new AstNode(AST_RANGE); + + if (!unpacked_range && (stride > 1 || GetSize(rnode->children) == 2)) { + // Calculate MSB offset for the final index / slice of packed dimensions. + AstNode *msb_offset = index_msb_offset(offset->clone(), rnode, decl_node, dim, stride); + index_range->children.push_back(msb_offset); + } + + index_range->children.push_back(offset); + + return index_range; +} + +AstNode *AstNode::get_struct_member() const +{ + AstNode *member_node; + if (attributes.count(ID::wiretype) && (member_node = attributes.at(ID::wiretype)) && + (member_node->type == AST_STRUCT_ITEM || member_node->type == AST_STRUCT || member_node->type == AST_UNION)) + { + return member_node; } + return nullptr; } static void add_members_to_scope(AstNode *snode, std::string name) @@ -488,39 +524,28 @@ static void add_members_to_scope(AstNode *snode, std::string name) // in case later referenced in assignments log_assert(snode->type==AST_STRUCT || snode->type==AST_UNION); for (auto *node : snode->children) { + auto member_name = name + "." + node->str; + current_scope[member_name] = node; if (node->type != AST_STRUCT_ITEM) { // embedded struct or union add_members_to_scope(node, name + "." + node->str); } - else { - auto member_name = name + "." + node->str; - current_scope[member_name] = node; - } } } -static int get_max_offset(AstNode *node) -{ - // get the width from the MS member in the struct - // as members are laid out from left to right in the packed wire - log_assert(node->type==AST_STRUCT || node->type==AST_UNION); - while (node->type != AST_STRUCT_ITEM) { - node = node->children[0]; - } - return node->range_left; -} - -static AstNode *make_packed_struct(AstNode *template_node, std::string &name) +static AstNode *make_packed_struct(AstNode *template_node, std::string &name, decltype(AstNode::attributes) &attributes) { // create a wire for the packed struct - auto wnode = new AstNode(AST_WIRE); + auto wnode = new AstNode(AST_WIRE, make_range(template_node->range_left, 0)); wnode->str = name; wnode->is_logic = true; wnode->range_valid = true; wnode->is_signed = template_node->is_signed; - int offset = get_max_offset(template_node); - auto range = make_range(offset, 0); - wnode->children.push_back(range); + for (auto &pair : attributes) { + wnode->set_attribute(pair.first, pair.second->clone()); + } + // resolve packed dimension + while (wnode->simplify(true, 1, -1, false)) {} // make sure this node is the one in scope for this name current_scope[name] = wnode; // add all the struct members to scope under the wire's name @@ -528,6 +553,22 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name) return wnode; } +static void prepend_ranges(AstNode *&range, AstNode *range_add) +{ + // Convert range to multirange. + if (range->type == AST_RANGE) + range = new AstNode(AST_MULTIRANGE, range); + + // Add range or ranges. + if (range_add->type == AST_RANGE) + range->children.insert(range->children.begin(), range_add->clone()); + else { + int i = 0; + for (auto child : range_add->children) + range->children.insert(range->children.begin() + i++, child->clone()); + } +} + // check if a node or its children contains an assignment to the given variable static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) { @@ -549,30 +590,342 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) return true; } +static std::string prefix_id(const std::string &prefix, const std::string &str) +{ + log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\')); + log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\')); + log_assert(prefix.back() == '.'); + if (str.front() == '\\') + return prefix + str.substr(1); + return prefix + str; +} + +// direct access to this global should be limited to the following two functions +static const RTLIL::Design *simplify_design_context = nullptr; + +void AST::set_simplify_design_context(const RTLIL::Design *design) +{ + log_assert(!simplify_design_context || !design); + simplify_design_context = design; +} + +// lookup the module with the given name in the current design context +static const RTLIL::Module* lookup_module(const std::string &name) +{ + return simplify_design_context->module(name); +} + +const RTLIL::Module* AstNode::lookup_cell_module() +{ + log_assert(type == AST_CELL); + + auto reprocess_after = [this] (const std::string &modname) { + if (!attributes.count(ID::reprocess_after)) + set_attribute(ID::reprocess_after, AstNode::mkconst_str(modname)); + }; + + const AstNode *celltype = nullptr; + for (const AstNode *child : children) + if (child->type == AST_CELLTYPE) { + celltype = child; + break; + } + log_assert(celltype != nullptr); + + const RTLIL::Module *module = lookup_module(celltype->str); + if (!module) + module = lookup_module("$abstract" + celltype->str); + if (!module) { + if (celltype->str.at(0) != '$') + reprocess_after(celltype->str); + return nullptr; + } + + // build a mapping from true param name to param value + size_t para_counter = 0; + dict cell_params_map; + for (AstNode *child : children) { + if (child->type != AST_PARASET) + continue; + + if (child->str.empty() && para_counter >= module->avail_parameters.size()) + return nullptr; // let hierarchy handle this error + IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str; + + const AstNode *value = child->children[0]; + if (value->type != AST_REALVALUE && value->type != AST_CONSTANT) + return nullptr; // let genrtlil handle this error + cell_params_map[paraname] = value->asParaConst(); + } + + // put the parameters in order and generate the derived module name + std::vector> named_parameters; + for (RTLIL::IdString param : module->avail_parameters) { + auto it = cell_params_map.find(param); + if (it != cell_params_map.end()) + named_parameters.emplace_back(it->first, it->second); + } + std::string modname = celltype->str; + if (cell_params_map.size()) // not named_parameters to cover hierarchical defparams + modname = derived_module_name(celltype->str, named_parameters); + + // try to find the resolved module + module = lookup_module(modname); + if (!module) { + reprocess_after(modname); + return nullptr; + } + return module; +} + +// returns whether an expression contains an unbased unsized literal; does not +// check the literal exists in a self-determined context +static bool contains_unbased_unsized(const AstNode *node) +{ + if (node->type == AST_CONSTANT) + return node->is_unsized; + for (const AstNode *child : node->children) + if (contains_unbased_unsized(child)) + return true; + return false; +} + +// adds a wire to the current module with the given name that matches the +// dimensions of the given wire reference +void add_wire_for_ref(const RTLIL::Wire *ref, const std::string &str) +{ + AstNode *left = AstNode::mkconst_int(ref->width - 1 + ref->start_offset, true); + AstNode *right = AstNode::mkconst_int(ref->start_offset, true); + if (ref->upto) + std::swap(left, right); + AstNode *range = new AstNode(AST_RANGE, left, right); + + AstNode *wire = new AstNode(AST_WIRE, range); + wire->is_signed = ref->is_signed; + wire->is_logic = true; + wire->str = str; + + current_ast_mod->children.push_back(wire); + current_scope[str] = wire; +} + +enum class IdentUsage { + NotReferenced, // target variable is neither read or written in the block + Assigned, // target variable is always assigned before use + SyncRequired, // target variable may be used before it has been assigned +}; + +// determines whether a local variable a block is always assigned before it is +// used, meaning the nosync attribute can automatically be added to that +// variable +static IdentUsage always_asgn_before_use(const AstNode *node, const std::string &target) +{ + // This variable has been referenced before it has necessarily been assigned + // a value in this procedure. + if (node->type == AST_IDENTIFIER && node->str == target) + return IdentUsage::SyncRequired; + + // For case statements (which are also used for if/else), we check each + // possible branch. If the variable is assigned in all branches, then it is + // assigned, and a sync isn't required. If it used before assignment in any + // branch, then a sync is required. + if (node->type == AST_CASE) { + bool all_defined = true; + bool any_used = false; + bool has_default = false; + for (const AstNode *child : node->children) { + if (child->type == AST_COND && child->children.at(0)->type == AST_DEFAULT) + has_default = true; + IdentUsage nested = always_asgn_before_use(child, target); + if (nested != IdentUsage::Assigned && child->type == AST_COND) + all_defined = false; + if (nested == IdentUsage::SyncRequired) + any_used = true; + } + if (any_used) + return IdentUsage::SyncRequired; + else if (all_defined && has_default) + return IdentUsage::Assigned; + else + return IdentUsage::NotReferenced; + } + + // Check if this is an assignment to the target variable. For simplicity, we + // don't analyze sub-ranges of the variable. + if (node->type == AST_ASSIGN_EQ) { + const AstNode *ident = node->children.at(0); + if (ident->type == AST_IDENTIFIER && ident->str == target) + return IdentUsage::Assigned; + } + + for (const AstNode *child : node->children) { + IdentUsage nested = always_asgn_before_use(child, target); + if (nested != IdentUsage::NotReferenced) + return nested; + } + return IdentUsage::NotReferenced; +} + +AstNode *AstNode::clone_at_zero() +{ + int width_hint; + bool sign_hint; + AstNode *pointee; + + switch (type) { + case AST_IDENTIFIER: + if (id2ast) + pointee = id2ast; + else if (current_scope.count(str)) + pointee = current_scope[str]; + else + break; + + if (pointee->type != AST_WIRE && + pointee->type != AST_AUTOWIRE && + pointee->type != AST_MEMORY) + break; + + YS_FALLTHROUGH + case AST_MEMRD: + detectSignWidth(width_hint, sign_hint); + return mkconst_int(0, sign_hint, width_hint); + + default: + break; + } + + AstNode *that = new AstNode; + *that = *this; + for (auto &it : that->children) + it = it->clone_at_zero(); + for (auto &it : that->attributes) + it.second = it.second->clone(); + + that->set_in_lvalue_flag(false); + that->set_in_param_flag(false); + that->fixup_hierarchy_flags(); + + return that; +} + +static bool try_determine_range_width(AstNode *range, int &result_width) +{ + log_assert(range->type == AST_RANGE); + + if (range->children.size() == 1) { + result_width = 1; + return true; + } + + AstNode *left_at_zero_ast = range->children[0]->clone_at_zero(); + AstNode *right_at_zero_ast = range->children[1]->clone_at_zero(); + + while (left_at_zero_ast->simplify(true, 1, -1, false)) {} + while (right_at_zero_ast->simplify(true, 1, -1, false)) {} + + bool ok = false; + if (left_at_zero_ast->type == AST_CONSTANT + && right_at_zero_ast->type == AST_CONSTANT) { + ok = true; + result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; + } + + delete left_at_zero_ast; + delete right_at_zero_ast; + return ok; +} + +static const std::string auto_nosync_prefix = "\\AutoNosync"; + +// mark a local variable in an always_comb block for automatic nosync +// consideration +static void mark_auto_nosync(AstNode *block, const AstNode *wire) +{ + log_assert(block->type == AST_BLOCK); + log_assert(wire->type == AST_WIRE); + block->set_attribute(auto_nosync_prefix + wire->str, AstNode::mkconst_int(1, false)); +} + +// block names can be prefixed with an explicit scope during elaboration +static bool is_autonamed_block(const std::string &str) { + size_t last_dot = str.rfind('.'); + // unprefixed names: autonamed if the first char is a dollar sign + if (last_dot == std::string::npos) + return str.at(0) == '$'; // e.g., `$fordecl_block$1` + // prefixed names: autonamed if the final chunk begins with a dollar sign + return str.rfind(".$") == last_dot; // e.g., `\foo.bar.$fordecl_block$1` +} + +// check a procedural block for auto-nosync markings, remove them, and add +// nosync to local variables as necessary +static void check_auto_nosync(AstNode *node) +{ + std::vector attrs_to_drop; + for (const auto& elem : node->attributes) { + // skip attributes that don't begin with the prefix + if (elem.first.compare(0, auto_nosync_prefix.size(), + auto_nosync_prefix.c_str())) + continue; + + // delete and remove the attribute once we're done iterating + attrs_to_drop.push_back(elem.first); + + // find the wire based on the attribute + std::string wire_name = elem.first.substr(auto_nosync_prefix.size()); + auto it = current_scope.find(wire_name); + if (it == current_scope.end()) + continue; + + // analyze the usage of the local variable in this block + IdentUsage ident_usage = always_asgn_before_use(node, wire_name); + if (ident_usage != IdentUsage::Assigned) + continue; + + // mark the wire with `nosync` + AstNode *wire = it->second; + log_assert(wire->type == AST_WIRE); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + } + + // remove the attributes we've "consumed" + for (const RTLIL::IdString &str : attrs_to_drop) { + auto it = node->attributes.find(str); + delete it->second; + node->attributes.erase(it); + } + + // check local variables in any nested blocks + for (AstNode *child : node->children) + check_auto_nosync(child); +} + // convert the AST into a simpler AST that has all parameters substituted by their // values, unrolled for-loops, expanded generate blocks, etc. when this function // is done with an AST it can be converted into RTLIL using genRTLIL(). // // this function also does all name resolving and sets the id2ast member of all // nodes that link to a different node using names and lexical scoping. -bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param) +bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hint) { static int recursion_counter = 0; static bool deep_recursion_warning = false; if (recursion_counter++ == 1000 && deep_recursion_warning) { - log_warning("Deep recursion in AST simplifier.\nDoes this design contain insanely long expressions?\n"); + log_warning("Deep recursion in AST simplifier.\nDoes this design contain overly long or deeply nested expressions, or excessive recursion?\n"); deep_recursion_warning = false; } + static bool unevaluated_tern_branch = false; + AstNode *newNode = NULL; bool did_something = false; #if 0 log("-------------\n"); log("AST simplify[%d] depth %d at %s:%d on %s %p:\n", stage, recursion_counter, filename.c_str(), location.first_line, type2str(type).c_str(), this); - log("const_fold=%d, at_zero=%d, in_lvalue=%d, stage=%d, width_hint=%d, sign_hint=%d, in_param=%d\n", - int(const_fold), int(at_zero), int(in_lvalue), int(stage), int(width_hint), int(sign_hint), int(in_param)); + log("const_fold=%d, stage=%d, width_hint=%d, sign_hint=%d\n", + int(const_fold), int(stage), int(width_hint), int(sign_hint)); // dumpAst(NULL, "> "); #endif @@ -581,7 +934,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_assert(type == AST_MODULE || type == AST_INTERFACE); deep_recursion_warning = true; - while (simplify(const_fold, at_zero, in_lvalue, 1, width_hint, sign_hint, in_param)) { } + while (simplify(const_fold, 1, width_hint, sign_hint)) { } if (!flag_nomem2reg && !get_bool_attribute(ID::nomem2reg)) { @@ -660,11 +1013,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, reg->is_signed = node->is_signed; for (auto &it : node->attributes) if (it.first != ID::mem2reg) - reg->attributes.emplace(it.first, it.second->clone()); + reg->set_attribute(it.first, it.second->clone()); reg->filename = node->filename; reg->location = node->location; children.push_back(reg); - while (reg->simplify(true, false, false, 1, -1, false, false)) { } + while (reg->simplify(true, 1, -1, false)) { } } } @@ -678,7 +1031,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, delete node; } - while (simplify(const_fold, at_zero, in_lvalue, 2, width_hint, sign_hint, in_param)) { } + while (simplify(const_fold, 2, width_hint, sign_hint)) { } recursion_counter--; return false; } @@ -701,35 +1054,37 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, str = std::string(); } - if ((type == AST_TCALL) && (str == "$display" || str == "$write") && (!current_always || current_always->type != AST_INITIAL)) { - log_file_warning(filename, location.first_line, "System task `%s' outside initial block is unsupported.\n", str.c_str()); - delete_children(); - str = std::string(); - } - - // print messages if this a call to $display() or $write() - // This code implements only a small subset of Verilog-2005 $display() format specifiers, - // but should be good enough for most uses - if ((type == AST_TCALL) && ((str == "$display") || (str == "$write"))) + if ((type == AST_TCALL) && + (str == "$display" || str == "$displayb" || str == "$displayh" || str == "$displayo" || + str == "$write" || str == "$writeb" || str == "$writeh" || str == "$writeo")) { - int nargs = GetSize(children); - if (nargs < 1) - log_file_error(filename, location.first_line, "System task `%s' got %d arguments, expected >= 1.\n", - str.c_str(), int(children.size())); - - // First argument is the format string - AstNode *node_string = children[0]; - while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_string->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str()); - std::string sformat = node_string->bitsAsConst().decode_string(); - std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); - // Finally, print the message (only include a \n for $display, not for $write) - log("%s", sout.c_str()); - if (str == "$display") - log("\n"); - delete_children(); - str = std::string(); + if (!current_always) { + log_file_warning(filename, location.first_line, "System task `%s' outside initial or always block is unsupported.\n", str.c_str()); + delete_children(); + str = std::string(); + } else { + // simplify the expressions and convert them to a special cell later in genrtlil + for (auto node : children) + while (node->simplify(true, stage, -1, false)) {} + + if (current_always->type == AST_INITIAL && !flag_nodisplay && stage == 2) { + int default_base = 10; + if (str.back() == 'b') + default_base = 2; + else if (str.back() == 'o') + default_base = 8; + else if (str.back() == 'h') + default_base = 16; + + // when $display()/$write() functions are used in an initial block, print them during synthesis + Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true); + if (str.substr(0, 8) == "$display") + fmt.append_string("\n"); + log("%s", fmt.render().c_str()); + } + + return false; + } } // activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.) @@ -738,16 +1093,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM)) const_fold = true; - // in certain cases a function must be evaluated constant. this is what in_param controls. - if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) - in_param = true; - std::map backup_scope; // create name resolution entries for all objects with names // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;") - if (type == AST_MODULE) { + if (type == AST_MODULE || type == AST_INTERFACE) { current_scope.clear(); + std::set existing; + int counter = 0; + label_genblks(existing, counter); std::map this_wire_scope; for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; @@ -758,7 +1112,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (!c->is_simple_const_expr()) { if (attributes.count(ID::dynports)) delete attributes.at(ID::dynports); - attributes[ID::dynports] = AstNode::mkconst_int(1, true); + set_attribute(ID::dynports, AstNode::mkconst_int(1, true)); } } } @@ -807,7 +1161,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (auto &it : node->attributes) { if (first_node->attributes.count(it.first) > 0) delete first_node->attributes[it.first]; - first_node->attributes[it.first] = it.second->clone(); + first_node->set_attribute(it.first, it.second->clone()); } children.erase(children.begin()+(i--)); did_something = true; @@ -815,7 +1169,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, continue; wires_are_incompatible: if (stage > 1) - log_file_error(filename, location.first_line, "Incompatible re-declaration of wire %s.\n", node->str.c_str()); + input_error("Incompatible re-declaration of wire %s.\n", node->str.c_str()); continue; } this_wire_scope[node->str] = node; @@ -834,23 +1188,28 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (current_scope.count(enode->str) == 0) current_scope[enode->str] = enode; else - log_file_error(filename, location.first_line, "enum item %s already exists\n", enode->str.c_str()); + input_error("enum item %s already exists\n", enode->str.c_str()); } } } for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY || node->type == AST_TYPEDEF) - while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) + while (node->simplify(true, 1, -1, false)) did_something = true; if (node->type == AST_ENUM) { for (auto enode : node->children){ log_assert(enode->type==AST_ENUM_ITEM); - while (node->simplify(true, false, false, 1, -1, false, in_param)) + while (node->simplify(true, 1, -1, false)) did_something = true; } } } + + for (AstNode *child : children) + if (child->type == AST_ALWAYS && + child->attributes.count(ID::always_comb)) + check_auto_nosync(child); } // create name resolution entries for all objects with names @@ -859,7 +1218,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; // these nodes appear at the top level in a package and can define names - if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF) { + if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_TYPEDEF || node->type == AST_FUNCTION || node->type == AST_TASK) { current_scope[node->str] = node; } if (node->type == AST_ENUM) { @@ -869,7 +1228,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (current_scope.count(enode->str) == 0) current_scope[enode->str] = enode; else - log_file_error(filename, location.first_line, "enum item %s already exists in package\n", enode->str.c_str()); + input_error("enum item %s already exists in package\n", enode->str.c_str()); } } } @@ -885,7 +1244,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_ALWAYS || type == AST_INITIAL) { if (current_always != nullptr) - log_file_error(filename, location.first_line, "Invalid nesting of always blocks and/or initializations.\n"); + input_error("Invalid nesting of always blocks and/or initializations.\n"); current_always = this; current_always_clocked = false; @@ -900,6 +1259,112 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + if (type == AST_CELL) { + bool lookup_suggested = false; + + for (AstNode *child : children) { + // simplify any parameters to constants + if (child->type == AST_PARASET) + while (child->simplify(true, 1, -1, false)) { } + + // look for patterns which _may_ indicate ambiguity requiring + // resolution of the underlying module + if (child->type == AST_ARGUMENT) { + if (child->children.size() != 1) + continue; + const AstNode *value = child->children[0]; + if (value->type == AST_IDENTIFIER) { + const AstNode *elem = value->id2ast; + if (elem == nullptr) { + if (current_scope.count(value->str)) + elem = current_scope.at(value->str); + else + continue; + } + if (elem->type == AST_MEMORY) + // need to determine is the is a read or wire + lookup_suggested = true; + else if (elem->type == AST_WIRE && elem->is_signed && !value->children.empty()) + // this may be a fully sliced signed wire which needs + // to be indirected to produce an unsigned connection + lookup_suggested = true; + } + else if (contains_unbased_unsized(value)) + // unbased unsized literals extend to width of the context + lookup_suggested = true; + } + } + + const RTLIL::Module *module = nullptr; + if (lookup_suggested) + module = lookup_cell_module(); + if (module) { + size_t port_counter = 0; + for (AstNode *child : children) { + if (child->type != AST_ARGUMENT) + continue; + + // determine the full name of port this argument is connected to + RTLIL::IdString port_name; + if (child->str.size()) + port_name = child->str; + else { + if (port_counter >= module->ports.size()) + input_error("Cell instance has more ports than the module!\n"); + port_name = module->ports[port_counter++]; + } + + // find the port's wire in the underlying module + const RTLIL::Wire *ref = module->wire(port_name); + if (ref == nullptr) + input_error("Cell instance refers to port %s which does not exist in module %s!.\n", + log_id(port_name), log_id(module->name)); + + // select the argument, if present + log_assert(child->children.size() <= 1); + if (child->children.empty()) + continue; + AstNode *arg = child->children[0]; + + // plain identifiers never need indirection; this also prevents + // adding infinite levels of indirection + if (arg->type == AST_IDENTIFIER && arg->children.empty()) + continue; + + // only add indirection for standard inputs or outputs + if (ref->port_input == ref->port_output) + continue; + + did_something = true; + + // create the indirection wire + std::stringstream sstr; + sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + std::string tmp_str = sstr.str(); + add_wire_for_ref(ref, tmp_str); + + AstNode *asgn = new AstNode(AST_ASSIGN); + current_ast_mod->children.push_back(asgn); + + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = tmp_str; + child->children[0] = ident->clone(); + + if (ref->port_input && !ref->port_output) { + asgn->children.push_back(ident); + asgn->children.push_back(arg); + } else { + log_assert(!ref->port_input && ref->port_output); + asgn->children.push_back(arg); + asgn->children.push_back(ident); + } + asgn->fixup_hierarchy_flags(); + } + + + } + } + int backup_width_hint = width_hint; bool backup_sign_hint = sign_hint; @@ -915,9 +1380,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, case AST_ASSIGN_EQ: case AST_ASSIGN_LE: case AST_ASSIGN: - while (!children[0]->basic_prep && children[0]->simplify(false, false, true, stage, -1, false, in_param) == true) + while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false) == true) did_something = true; - while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, in_param) == true) + while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false) == true) did_something = true; children[0]->detectSignWidth(backup_width_hint, backup_sign_hint); children[1]->detectSignWidth(width_hint, sign_hint); @@ -928,7 +1393,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->id2ast->is_logic) children[0]->id2ast->is_reg = true; // if logic type is used in a block asignment if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && !children[0]->id2ast->is_reg) - log_warning("wire '%s' is assigned in a block at %s:%d.%d-%d.%d.\n", children[0]->str.c_str(), filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + log_warning("wire '%s' is assigned in a block at %s.\n", children[0]->str.c_str(), loc_string().c_str()); if (type == AST_ASSIGN && children[0]->id2ast->is_reg) { bool is_rand_reg = false; if (children[1]->type == AST_FCALL) { @@ -942,7 +1407,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, is_rand_reg = true; } if (!is_rand_reg) - log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.%d-%d.%d.\n", children[0]->str.c_str(), filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); + log_warning("reg '%s' is assigned in a continuous assignment at %s.\n", children[0]->str.c_str(), loc_string().c_str()); } children[0]->was_checked = true; } @@ -953,7 +1418,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (!basic_prep) { for (auto *node : children) { // resolve any ranges - while (!node->basic_prep && node->simplify(true, false, false, stage, -1, false, false)) { + while (!node->basic_prep && node->simplify(true, stage, -1, false)) { did_something = true; } } @@ -963,7 +1428,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // instance rather than just a type in a typedef or outer struct? if (!str.empty() && str[0] == '\\') { // instance so add a wire for the packed structure - auto wnode = make_packed_struct(this, str); + auto wnode = make_packed_struct(this, str, attributes); log_assert(current_ast_mod); current_ast_mod->children.push_back(wnode); } @@ -972,13 +1437,26 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; case AST_STRUCT_ITEM: + if (is_custom_type) { + log_assert(children.size() >= 1); + log_assert(children[0]->type == AST_WIRETYPE); + + // Pretend it's just a wire in order to resolve the type. + type = AST_WIRE; + while (is_custom_type && simplify(const_fold, stage, width_hint, sign_hint)) {}; + if (type == AST_WIRE) + type = AST_STRUCT_ITEM; + + did_something = true; + } + log_assert(!is_custom_type); break; case AST_ENUM: //log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep); if (!basic_prep) { for (auto item_node : children) { - while (!item_node->basic_prep && item_node->simplify(false, false, false, stage, -1, false, in_param)) + while (!item_node->basic_prep && item_node->simplify(false, stage, -1, false)) did_something = true; } // allocate values (called more than once) @@ -988,26 +1466,36 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, case AST_PARAMETER: case AST_LOCALPARAM: - while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true) + // if parameter is implicit type which is the typename of a struct or union, + // save information about struct in wiretype attribute + if (children[0]->type == AST_IDENTIFIER && current_scope.count(children[0]->str) > 0) { + auto item_node = current_scope[children[0]->str]; + if (item_node->type == AST_STRUCT || item_node->type == AST_UNION) { + set_attribute(ID::wiretype, item_node->clone()); + size_packed_struct(attributes[ID::wiretype], 0); + add_members_to_scope(attributes[ID::wiretype], str); + } + } + while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false) == true) did_something = true; children[0]->detectSignWidth(width_hint, sign_hint); if (children.size() > 1 && children[1]->type == AST_RANGE) { - while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, true) == true) + while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false) == true) did_something = true; if (!children[1]->range_valid) - log_file_error(filename, location.first_line, "Non-constant width range on parameter decl.\n"); + input_error("Non-constant width range on parameter decl.\n"); width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1); } break; case AST_ENUM_ITEM: - while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, in_param)) + while (!children[0]->basic_prep && children[0]->simplify(false, stage, -1, false)) did_something = true; children[0]->detectSignWidth(width_hint, sign_hint); if (children.size() > 1 && children[1]->type == AST_RANGE) { - while (!children[1]->basic_prep && children[1]->simplify(false, false, false, stage, -1, false, in_param)) + while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false)) did_something = true; if (!children[1]->range_valid) - log_file_error(filename, location.first_line, "Non-constant width range on enum item decl.\n"); + input_error("Non-constant width range on enum item decl.\n"); width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1); } break; @@ -1063,7 +1551,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, width_hint = -1; sign_hint = true; for (auto child : children) { - while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true) + while (!child->basic_prep && child->simplify(false, stage, -1, false) == true) did_something = true; child->detectSignWidthWorker(width_hint, sign_hint); } @@ -1078,7 +1566,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; case AST_TERNARY: - detect_width_simple = true; child_0_is_self_determined = true; break; @@ -1099,10 +1586,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (detect_width_simple && width_hint < 0) { if (type == AST_REPLICATE) - while (children[0]->simplify(true, false, in_lvalue, stage, -1, false, true) == true) + while (children[0]->simplify(true, stage, -1, false) == true) did_something = true; for (auto child : children) - while (!child->basic_prep && child->simplify(false, false, in_lvalue, stage, -1, false, in_param) == true) + while (!child->basic_prep && child->simplify(false, stage, -1, false) == true) did_something = true; detectSignWidth(width_hint, sign_hint); } @@ -1111,6 +1598,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, detectSignWidth(width_hint, sign_hint); if (type == AST_TERNARY) { + if (width_hint < 0) { + while (!children[0]->basic_prep && children[0]->simplify(true, stage, -1, false)) + did_something = true; + + bool backup_unevaluated_tern_branch = unevaluated_tern_branch; + AstNode *chosen = get_tern_choice().first; + + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[2]; + while (!children[1]->basic_prep && children[1]->simplify(false, stage, -1, false)) + did_something = true; + + unevaluated_tern_branch = backup_unevaluated_tern_branch || chosen == children[1]; + while (!children[2]->basic_prep && children[2]->simplify(false, stage, -1, false)) + did_something = true; + + unevaluated_tern_branch = backup_unevaluated_tern_branch; + detectSignWidth(width_hint, sign_hint); + } int width_hint_left, width_hint_right; bool sign_hint_left, sign_hint_right; bool found_real_left, found_real_right; @@ -1136,8 +1641,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (const_fold && type == AST_CASE) { - while (children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } + detectSignWidth(width_hint, sign_hint); + while (children[0]->simplify(const_fold, stage, width_hint, sign_hint)) { } if (children[0]->type == AST_CONSTANT && children[0]->bits_only_01()) { + children[0]->is_signed = sign_hint; + RTLIL::Const case_expr = children[0]->bitsAsConst(width_hint, sign_hint); std::vector new_children; new_children.push_back(children[0]); for (int i = 1; i < GetSize(children); i++) { @@ -1148,9 +1656,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, goto keep_const_cond; if (v->type == AST_BLOCK) continue; - while (v->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { } + while (v->simplify(const_fold, stage, width_hint, sign_hint)) { } if (v->type == AST_CONSTANT && v->bits_only_01()) { - if (v->bits == children[0]->bits) { + RTLIL::Const case_item_expr = v->bitsAsConst(width_hint, sign_hint); + RTLIL::Const match = const_eq(case_expr, case_item_expr, sign_hint, sign_hint, 1); + log_assert(match.bits.size() == 1); + if (match.bits.front() == RTLIL::State::S1) { while (i+1 < GetSize(children)) delete children[++i]; goto keep_const_cond; @@ -1169,38 +1680,46 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } + dict> backup_memwr_visible; + dict> final_memwr_visible; + + if (type == AST_CASE && stage == 2) { + backup_memwr_visible = current_memwr_visible; + final_memwr_visible = current_memwr_visible; + } + // simplify all children first // (iterate by index as e.g. auto wires can add new children in the process) for (size_t i = 0; i < children.size(); i++) { bool did_something_here = true; bool backup_flag_autowire = flag_autowire; + bool backup_unevaluated_tern_branch = unevaluated_tern_branch; if ((type == AST_GENFOR || type == AST_FOR) && i >= 3) break; if ((type == AST_GENIF || type == AST_GENCASE) && i >= 1) break; if (type == AST_GENBLOCK) break; + if (type == AST_CELLARRAY && children[i]->type == AST_CELL) + continue; if (type == AST_BLOCK && !str.empty()) break; if (type == AST_PREFIX && i >= 1) break; if (type == AST_DEFPARAM && i == 0) flag_autowire = true; + if (type == AST_TERNARY && i > 0 && !unevaluated_tern_branch) { + AstNode *chosen = get_tern_choice().first; + unevaluated_tern_branch = chosen && chosen != children[i]; + } while (did_something_here && i < children.size()) { - bool const_fold_here = const_fold, in_lvalue_here = in_lvalue; + bool const_fold_here = const_fold; int width_hint_here = width_hint; bool sign_hint_here = sign_hint; - bool in_param_here = in_param; if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE)) - const_fold_here = true, in_param_here = true; - if (i == 0 && (type == AST_GENIF || type == AST_GENCASE)) - in_param_here = true; - if (i == 1 && (type == AST_FOR || type == AST_GENFOR)) - in_param_here = true; + const_fold_here = true; if (type == AST_PARAMETER || type == AST_LOCALPARAM) const_fold_here = true; - if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)) - in_lvalue_here = true; if (type == AST_BLOCK) { current_block = this; current_block_child = children[i]; @@ -1215,7 +1734,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, width_hint_here = -1, sign_hint_here = false; if (children_are_self_determined) width_hint_here = -1, sign_hint_here = false; - did_something_here = children[i]->simplify(const_fold_here, at_zero, in_lvalue_here, stage, width_hint_here, sign_hint_here, in_param_here); + did_something_here = children[i]->simplify(const_fold_here, stage, width_hint_here, sign_hint_here); if (did_something_here) did_something = true; } @@ -1225,11 +1744,26 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; } flag_autowire = backup_flag_autowire; + unevaluated_tern_branch = backup_unevaluated_tern_branch; + if (stage == 2 && type == AST_CASE) { + for (auto &x : current_memwr_visible) { + for (int y : x.second) + final_memwr_visible[x.first].insert(y); + } + current_memwr_visible = backup_memwr_visible; + } } for (auto &attr : attributes) { - while (attr.second->simplify(true, false, false, stage, -1, false, true)) + while (attr.second->simplify(true, stage, -1, false)) did_something = true; } + if (type == AST_CASE && stage == 2) { + current_memwr_visible = final_memwr_visible; + } + if (type == AST_ALWAYS && stage == 2) { + current_memwr_visible.clear(); + current_memwr_count.clear(); + } if (reset_width_after_children) { width_hint = backup_width_hint; @@ -1253,14 +1787,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_filename = filename; - if (type == AST_MODULE) + if (type == AST_MODULE || type == AST_INTERFACE) current_scope.clear(); // convert defparam nodes to cell parameters if (type == AST_DEFPARAM && !children.empty()) { if (children[0]->type != AST_IDENTIFIER) - log_file_error(filename, location.first_line, "Module name in defparam contains non-constant expressions!\n"); + input_error("Module name in defparam contains non-constant expressions!\n"); string modname, paramname = children[0]->str; @@ -1277,12 +1811,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (pos == std::string::npos) - log_file_error(filename, location.first_line, "Can't find object for defparam `%s`!\n", RTLIL::unescape_id(paramname).c_str()); + input_error("Can't find object for defparam `%s`!\n", RTLIL::unescape_id(paramname).c_str()); paramname = "\\" + paramname.substr(pos+1); if (current_scope.at(modname)->type != AST_CELL) - log_file_error(filename, location.first_line, "Defparam argument `%s . %s` does not match a cell!\n", + input_error("Defparam argument `%s . %s` does not match a cell!\n", RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str()); AstNode *paraset = new AstNode(AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : NULL); @@ -1298,7 +1832,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_assert(children.size() == 1); auto type_node = children[0]; log_assert(type_node->type == AST_WIRE || type_node->type == AST_MEMORY || type_node->type == AST_STRUCT || type_node->type == AST_UNION); - while (type_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) { + while (type_node->simplify(const_fold, stage, width_hint, sign_hint)) { did_something = true; } log_assert(!type_node->is_custom_type); @@ -1311,91 +1845,95 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, log_assert(children[0]->type == AST_WIRETYPE); auto type_name = children[0]->str; if (!current_scope.count(type_name)) { - log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str()); + input_error("Unknown identifier `%s' used as type name\n", type_name.c_str()); } AstNode *resolved_type_node = current_scope.at(type_name); if (resolved_type_node->type != AST_TYPEDEF) - log_file_error(filename, location.first_line, "`%s' does not name a type\n", type_name.c_str()); + input_error("`%s' does not name a type\n", type_name.c_str()); log_assert(resolved_type_node->children.size() == 1); AstNode *template_node = resolved_type_node->children[0]; - // Ensure typedef itself is fully simplified - while (template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; - - if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) { - // replace with wire representing the packed structure - newNode = make_packed_struct(template_node, str); + // Resolve the typedef from the bottom up, recursing within the current + // block of code. Defer further simplification until the complete type is + // resolved. + while (template_node->is_custom_type && template_node->simplify(const_fold, stage, width_hint, sign_hint)) {}; + + if (!str.empty() && str[0] == '\\' && (template_node->type == AST_STRUCT || template_node->type == AST_UNION)) { + // replace instance with wire representing the packed structure + newNode = make_packed_struct(template_node, str, attributes); + newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str)); + // add original input/output attribute to resolved wire + newNode->is_input = this->is_input; + newNode->is_output = this->is_output; current_scope[str] = this; goto apply_newNode; } - // Remove type reference - delete children[0]; - children.erase(children.begin()); - - if (type == AST_WIRE) - type = template_node->type; - is_reg = template_node->is_reg; - is_logic = template_node->is_logic; - is_signed = template_node->is_signed; - is_string = template_node->is_string; - is_custom_type = template_node->is_custom_type; + // Prepare replacement node. + newNode = template_node->clone(); + newNode->str = str; + newNode->set_attribute(ID::wiretype, mkconst_str(resolved_type_node->str)); + newNode->is_input = is_input; + newNode->is_output = is_output; + newNode->is_wand = is_wand; + newNode->is_wor = is_wor; + for (auto &pair : attributes) + newNode->set_attribute(pair.first, pair.second->clone()); - range_valid = template_node->range_valid; - range_swapped = template_node->range_swapped; - range_left = template_node->range_left; - range_right = template_node->range_right; + // if an enum then add attributes to support simulator tracing + newNode->annotateTypedEnums(template_node); - attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); + bool add_packed_dimensions = (type == AST_WIRE && GetSize(children) > 1) || (type == AST_MEMORY && GetSize(children) > 2); - // if an enum then add attributes to support simulator tracing - annotateTypedEnums(template_node); + // Cannot add packed dimensions if unpacked dimensions are already specified. + if (add_packed_dimensions && newNode->type == AST_MEMORY) + input_error("Cannot extend unpacked type `%s' with packed dimensions\n", type_name.c_str()); - // Insert clones children from template at beginning - for (int i = 0; i < GetSize(template_node->children); i++) - children.insert(children.begin() + i, template_node->children[i]->clone()); + // Add packed dimensions. + if (add_packed_dimensions) { + AstNode *packed = children[1]; + if (newNode->children.empty()) + newNode->children.insert(newNode->children.begin(), packed->clone()); + else + prepend_ranges(newNode->children[0], packed); + } - if (type == AST_MEMORY && GetSize(children) == 1) { - // Single-bit memories must have [0:0] range - AstNode *rng = make_range(0, 0); - children.insert(children.begin(), rng); + // Add unpacked dimensions. + if (type == AST_MEMORY) { + AstNode *unpacked = children.back(); + if (GetSize(newNode->children) < 2) + newNode->children.push_back(unpacked->clone()); + else + prepend_ranges(newNode->children[1], unpacked); + newNode->type = type; } - did_something = true; + + // Prepare to generate dimensions metadata for the resolved type. + newNode->dimensions.clear(); + newNode->unpacked_dimensions = 0; + + goto apply_newNode; } - log_assert(!is_custom_type); } // resolve types of parameters if (type == AST_LOCALPARAM || type == AST_PARAMETER) { if (is_custom_type) { - log_assert(children.size() == 2); + log_assert(children.size() >= 2); log_assert(children[1]->type == AST_WIRETYPE); - if (!current_scope.count(children[1]->str)) - log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str()); - AstNode *resolved_type_node = current_scope.at(children[1]->str); - if (resolved_type_node->type != AST_TYPEDEF) - log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str()); - log_assert(resolved_type_node->children.size() == 1); - AstNode *template_node = resolved_type_node->children[0]; - delete children[1]; - children.pop_back(); - - // Ensure typedef itself is fully simplified - while(template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {}; - - if (template_node->type == AST_MEMORY) - log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str()); - is_signed = template_node->is_signed; - is_string = template_node->is_string; - is_custom_type = template_node->is_custom_type; - - range_valid = template_node->range_valid; - range_swapped = template_node->range_swapped; - range_left = template_node->range_left; - range_right = template_node->range_right; - attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); - for (auto template_child : template_node->children) - children.push_back(template_child->clone()); + + // Pretend it's just a wire in order to resolve the type in the code block above. + AstNodeType param_type = type; + type = AST_WIRE; + AstNode *expr = children[0]; + children.erase(children.begin()); + while (is_custom_type && simplify(const_fold, stage, width_hint, sign_hint)) {}; + type = param_type; + children.insert(children.begin(), expr); + + if (children[1]->type == AST_MEMORY) + input_error("unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str()); + fixup_hierarchy_flags(); did_something = true; } log_assert(!is_custom_type); @@ -1405,10 +1943,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_PREFIX) { if (children[0]->type != AST_CONSTANT) { // dumpAst(NULL, "> "); - log_file_error(filename, location.first_line, "Index in generate block prefix syntax is not constant!\n"); + input_error("Index in generate block prefix syntax is not constant!\n"); } if (children[1]->type == AST_PREFIX) - children[1]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param); + children[1]->simplify(const_fold, stage, width_hint, sign_hint); log_assert(children[1]->type == AST_IDENTIFIER); newNode = children[1]->clone(); const char *second_part = children[1]->str.c_str(); @@ -1421,9 +1959,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // evaluate TO_BITS nodes if (type == AST_TO_BITS) { if (children[0]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Left operand of to_bits expression is not constant!\n"); + input_error("Left operand of to_bits expression is not constant!\n"); if (children[1]->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Right operand of to_bits expression is not constant!\n"); + input_error("Right operand of to_bits expression is not constant!\n"); RTLIL::Const new_value = children[1]->bitsAsConst(children[0]->bitsAsConst().as_int(), children[1]->is_signed); newNode = mkconst_bits(new_value.bits, children[1]->is_signed); goto apply_newNode; @@ -1452,9 +1990,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (old_range_valid != range_valid) did_something = true; if (range_valid && range_right > range_left) { - int tmp = range_right; - range_right = range_left; - range_left = tmp; + std::swap(range_left, range_right); range_swapped = true; } } @@ -1473,17 +2009,17 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (attributes.count(ID::force_upto)) { AstNode *val = attributes[ID::force_upto]; if (val->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Attribute `force_upto' with non-constant value!\n"); + input_error("Attribute `force_upto' with non-constant value!\n"); force_upto = val->asAttrConst().as_bool(); } if (attributes.count(ID::force_downto)) { AstNode *val = attributes[ID::force_downto]; if (val->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Attribute `force_downto' with non-constant value!\n"); + input_error("Attribute `force_downto' with non-constant value!\n"); force_downto = val->asAttrConst().as_bool(); } if (force_upto && force_downto) - log_file_error(filename, location.first_line, "Attributes `force_downto' and `force_upto' cannot be both set!\n"); + input_error("Attributes `force_downto' and `force_upto' cannot be both set!\n"); if ((force_upto && !range_swapped) || (force_downto && range_swapped)) { std::swap(range_left, range_right); range_swapped = force_upto; @@ -1499,56 +2035,83 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } } - // resolve multiranges on memory decl - if (type == AST_MEMORY && children.size() > 1 && children[1]->type == AST_MULTIRANGE) - { - int total_size = 1; - multirange_dimensions.clear(); - multirange_swapped.clear(); - for (auto range : children[1]->children) { - if (!range->range_valid) - log_file_error(filename, location.first_line, "Non-constant range on memory decl.\n"); - multirange_dimensions.push_back(min(range->range_left, range->range_right)); - multirange_dimensions.push_back(max(range->range_left, range->range_right) - min(range->range_left, range->range_right) + 1); - multirange_swapped.push_back(range->range_swapped); - total_size *= multirange_dimensions.back(); + // Resolve packed and unpacked ranges in declarations. + if ((type == AST_WIRE || type == AST_MEMORY) && dimensions.empty()) { + if (!children.empty()) { + // Unpacked ranges first, then packed ranges. + for (int i = std::min(GetSize(children), 2) - 1; i >= 0; i--) { + if (children[i]->type == AST_MULTIRANGE) { + int width = 1; + for (auto range : children[i]->children) { + width *= add_dimension(this, range); + if (i) unpacked_dimensions++; + } + delete children[i]; + int left = width - 1, right = 0; + if (i) + std::swap(left, right); + children[i] = new AstNode(AST_RANGE, mkconst_int(left, true), mkconst_int(right, true)); + fixup_hierarchy_flags(); + did_something = true; + } else if (children[i]->type == AST_RANGE) { + add_dimension(this, children[i]); + if (i) unpacked_dimensions++; + } + } + } else { + // 1 bit signal: bit, logic or reg + dimensions.push_back({ 0, 1, false }); } - delete children[1]; - children[1] = new AstNode(AST_RANGE, AstNode::mkconst_int(0, true), AstNode::mkconst_int(total_size-1, true)); - did_something = true; } - // resolve multiranges on memory access - if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY && children.size() > 0 && children[0]->type == AST_MULTIRANGE) + // Resolve multidimensional array access. + if (type == AST_IDENTIFIER && !basic_prep && id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_MEMORY) && + children.size() > 0 && (children[0]->type == AST_RANGE || children[0]->type == AST_MULTIRANGE)) { - AstNode *index_expr = nullptr; - - integer = children[0]->children.size(); // save original number of dimensions for $size() etc. - for (int i = 0; 2*i < GetSize(id2ast->multirange_dimensions); i++) - { - if (GetSize(children[0]->children) <= i) - log_file_error(filename, location.first_line, "Insufficient number of array indices for %s.\n", log_id(str)); - - AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone(); - - if (id2ast->multirange_dimensions[2*i]) - new_index_expr = new AstNode(AST_SUB, new_index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i], true)); + int dims_sel = children[0]->type == AST_MULTIRANGE ? children[0]->children.size() : 1; + // Save original number of dimensions for $size() etc. + integer = dims_sel; + + // Split access into unpacked and packed parts. + AstNode *unpacked_range = nullptr; + AstNode *packed_range = nullptr; + + if (id2ast->unpacked_dimensions) { + if (id2ast->unpacked_dimensions > 1) { + // Flattened range for access to unpacked dimensions. + unpacked_range = make_index_range(id2ast, true); + } else { + // Index into one-dimensional unpacked part; unlink simple range node. + AstNode *&range = children[0]->type == AST_MULTIRANGE ? children[0]->children[0] : children[0]; + unpacked_range = range; + range = nullptr; + } + } - if (i == 0) - index_expr = new_index_expr; - else - index_expr = new AstNode(AST_ADD, new AstNode(AST_MUL, index_expr, AstNode::mkconst_int(id2ast->multirange_dimensions[2*i+1], true)), new_index_expr); + if (dims_sel > id2ast->unpacked_dimensions) { + if (GetSize(id2ast->dimensions) - id2ast->unpacked_dimensions > 1) { + // Flattened range for access to packed dimensions. + packed_range = make_index_range(id2ast, false); + } else { + // Index into one-dimensional packed part; unlink simple range node. + AstNode *&range = children[0]->type == AST_MULTIRANGE ? children[0]->children[dims_sel - 1] : children[0]; + packed_range = range; + range = nullptr; + } } - for (int i = GetSize(id2ast->multirange_dimensions)/2; i < GetSize(children[0]->children); i++) - children.push_back(children[0]->children[i]->clone()); + for (auto &it : children) + delete it; + children.clear(); - delete children[0]; - if (index_expr == nullptr) - children.erase(children.begin()); - else - children[0] = new AstNode(AST_RANGE, index_expr); + if (unpacked_range) + children.push_back(unpacked_range); + + if (packed_range) + children.push_back(packed_range); + fixup_hierarchy_flags(); + basic_prep = true; did_something = true; } @@ -1556,7 +2119,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM) { if (children.size() > 1 && children[1]->type == AST_RANGE) { if (!children[1]->range_valid) - log_file_error(filename, location.first_line, "Non-constant width range on parameter decl.\n"); + input_error("Non-constant width range on parameter decl.\n"); int width = std::abs(children[1]->range_left - children[1]->range_right) + 1; if (children[0]->type == AST_REALVALUE) { RTLIL::Const constvalue = children[0]->realAsConst(width); @@ -1564,6 +2127,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, children[0]->realvalue, log_signal(constvalue)); delete children[0]; children[0] = mkconst_bits(constvalue.bits, sign_hint); + fixup_hierarchy_flags(); did_something = true; } if (children[0]->type == AST_CONSTANT) { @@ -1573,6 +2137,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *old_child_0 = children[0]; children[0] = mkconst_bits(sig.as_const().bits, is_signed); delete old_child_0; + fixup_hierarchy_flags(); } children[0]->is_signed = is_signed; } @@ -1586,22 +2151,46 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, delete children[0]; children[0] = new AstNode(AST_REALVALUE); children[0]->realvalue = as_realvalue; + fixup_hierarchy_flags(); did_something = true; } } if (type == AST_IDENTIFIER && !basic_prep) { // check if a plausible struct member sss.mmmm - std::string sname; - if (name_has_dot(str, sname)) { - if (current_scope.count(str) > 0) { - auto item_node = current_scope[str]; - if (item_node->type == AST_STRUCT_ITEM) { + if (!str.empty() && str[0] == '\\' && current_scope.count(str)) { + auto item_node = current_scope[str]; + if (item_node->type == AST_STRUCT_ITEM || item_node->type == AST_STRUCT || item_node->type == AST_UNION) { + // Traverse any hierarchical path until the full name for the referenced struct/union is found. + std::string sname; + bool found_sname = false; + for (std::string::size_type pos = 0; (pos = str.find('.', pos)) != std::string::npos; pos++) { + sname = str.substr(0, pos); + if (current_scope.count(sname)) { + auto stype = current_scope[sname]->type; + if (stype == AST_WIRE || stype == AST_PARAMETER || stype == AST_LOCALPARAM) { + found_sname = true; + break; + } + } + } + + if (found_sname) { // structure member, rewrite this node to reference the packed struct wire - auto range = make_struct_member_range(this, item_node); + auto range = make_index_range(item_node); newNode = new AstNode(AST_IDENTIFIER, range); newNode->str = sname; + // save type and original number of dimensions for $size() etc. + newNode->set_attribute(ID::wiretype, item_node->clone()); + if (!item_node->dimensions.empty() && children.size() > 0) { + if (children[0]->type == AST_RANGE) + newNode->integer = 1; + else if (children[0]->type == AST_MULTIRANGE) + newNode->integer = children[0]->children.size(); + } newNode->basic_prep = true; + if (item_node->is_signed) + newNode = new AstNode(AST_TO_SIGNED, newNode); goto apply_newNode; } } @@ -1611,13 +2200,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_IDENTIFIER) { if (current_scope.count(str) == 0) { AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; - const std::string& mod_scope = current_scope_ast->str; - if (str[0] == '\\' && str.substr(0, mod_scope.size()) == mod_scope) { - std::string new_str = "\\" + str.substr(mod_scope.size() + 1); - if (current_scope.count(new_str)) { - str = new_str; - } - } + str = try_pop_module_prefix(); for (auto node : current_scope_ast->children) { //log("looking at mod scope child %s\n", type2str(node->type).c_str()); switch (node->type) { @@ -1653,7 +2236,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (current_scope.count(str) == 0) { if (current_ast_mod == nullptr) { - log_file_error(filename, location.first_line, "Identifier `%s' is implicitly declared outside of a module.\n", str.c_str()); + input_error("Identifier `%s' is implicitly declared outside of a module.\n", str.c_str()); } else if (flag_autowire || str == "\\$global_clock") { AstNode *auto_wire = new AstNode(AST_AUTOWIRE); auto_wire->str = str; @@ -1661,7 +2244,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_scope[str] = auto_wire; did_something = true; } else { - log_file_error(filename, location.first_line, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); + input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); } } if (id2ast != current_scope[str]) { @@ -1671,10 +2254,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } // split memory access with bit select to individual statements - if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue) + if (type == AST_IDENTIFIER && children.size() == 2 && children[0]->type == AST_RANGE && children[1]->type == AST_RANGE && !in_lvalue && stage == 2) { if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1) - log_file_error(filename, location.first_line, "Invalid bit-select on memory access!\n"); + input_error("Invalid bit-select on memory access!\n"); int mem_width, mem_size, addr_bits; id2ast->meminfo(mem_width, mem_size, addr_bits); @@ -1686,15 +2269,15 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, std::swap(data_range_left, data_range_right); std::stringstream sstr; - sstr << "$mem2bits$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string wire_id = sstr.str(); AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true))); wire->str = wire_id; if (current_block) - wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false, false)) { } + while (wire->simplify(true, 1, -1, false)) { } AstNode *data = clone(); delete data->children[1]; @@ -1728,7 +2311,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (type == AST_WHILE) - log_file_error(filename, location.first_line, "While loops are only allowed in constant functions!\n"); + input_error("While loops are only allowed in constant functions!\n"); if (type == AST_REPEAT) { @@ -1736,10 +2319,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *body = children[1]; // eval count expression - while (count->simplify(true, false, false, stage, 32, true, false)) { } + while (count->simplify(true, stage, 32, true)) { } if (count->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Repeat loops outside must have constant repeat counts!\n"); + input_error("Repeat loops outside must have constant repeat counts!\n"); // convert to a block with the body repeated n times type = AST_BLOCK; @@ -1774,17 +2357,17 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (init_ast->type != AST_ASSIGN_EQ) - log_file_error(filename, location.first_line, "Unsupported 1st expression of %s for-loop!\n", loop_type_str); + input_error("Unsupported 1st expression of %s for-loop!\n", loop_type_str); if (next_ast->type != AST_ASSIGN_EQ) - log_file_error(filename, location.first_line, "Unsupported 3rd expression of %s for-loop!\n", loop_type_str); + input_error("Unsupported 3rd expression of %s for-loop!\n", loop_type_str); if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != var_type) - log_file_error(filename, location.first_line, "Left hand side of 1st expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); + input_error("Left hand side of 1st expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != var_type) - log_file_error(filename, location.first_line, "Left hand side of 3rd expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); + input_error("Left hand side of 3rd expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast) - log_file_error(filename, location.first_line, "Incompatible left-hand sides in 1st and 3rd expression of %s for-loop!\n", loop_type_str); + input_error("Incompatible left-hand sides in 1st and 3rd expression of %s for-loop!\n", loop_type_str); // eval 1st expression AstNode *varbuf = init_ast->children[1]->clone(); @@ -1792,11 +2375,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int expr_width_hint = -1; bool expr_sign_hint = true; varbuf->detectSignWidth(expr_width_hint, expr_sign_hint); - while (varbuf->simplify(true, false, false, stage, 32, true, false)) { } + while (varbuf->simplify(true, stage, 32, true)) { } } if (varbuf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Right hand side of 1st expression of %s for-loop is not constant!\n", loop_type_str); + input_error("Right hand side of 1st expression of %s for-loop is not constant!\n", loop_type_str); auto resolved = current_scope.at(init_ast->children[0]->str); if (resolved->range_valid) { @@ -1833,11 +2416,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int expr_width_hint = -1; bool expr_sign_hint = true; buf->detectSignWidth(expr_width_hint, expr_sign_hint); - while (buf->simplify(true, false, false, stage, expr_width_hint, expr_sign_hint, false)) { } + while (buf->simplify(true, stage, expr_width_hint, expr_sign_hint)) { } } if (buf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "2nd expression of %s for-loop is not constant!\n", loop_type_str); + input_error("2nd expression of %s for-loop is not constant!\n", loop_type_str); if (buf->integer == 0) { delete buf; @@ -1847,23 +2430,28 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // expand body int index = varbuf->children[0]->integer; - if (body_ast->type == AST_GENBLOCK) - buf = body_ast->clone(); - else - buf = new AstNode(AST_GENBLOCK, body_ast->clone()); - if (buf->str.empty()) { - std::stringstream sstr; - sstr << "$genblock$" << filename << ":" << location.first_line << "$" << (autoidx++); - buf->str = sstr.str(); - } - std::map name_map; + log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK); + log_assert(!body_ast->str.empty()); + buf = body_ast->clone(); + std::stringstream sstr; sstr << buf->str << "[" << index << "]."; - buf->expand_genblock(varbuf->str, sstr.str(), name_map); + std::string prefix = sstr.str(); + + // create a scoped localparam for the current value of the loop variable + AstNode *local_index = varbuf->clone(); + size_t pos = local_index->str.rfind('.'); + if (pos != std::string::npos) // remove outer prefix + local_index->str = "\\" + local_index->str.substr(pos + 1); + local_index->str = prefix_id(prefix, local_index->str); + current_scope[local_index->str] = local_index; + current_ast_mod->children.push_back(local_index); + + buf->expand_genblock(prefix); if (type == AST_GENFOR) { for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, stage, -1, false); current_ast_mod->children.push_back(buf->children[i]); } } else { @@ -1875,15 +2463,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // eval 3rd expression buf = next_ast->children[1]->clone(); + buf->set_in_param_flag(true); { int expr_width_hint = -1; bool expr_sign_hint = true; buf->detectSignWidth(expr_width_hint, expr_sign_hint); - while (buf->simplify(true, false, false, stage, expr_width_hint, expr_sign_hint, true)) { } + while (buf->simplify(true, stage, expr_width_hint, expr_sign_hint)) { } } if (buf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type).c_str()); + input_error("Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type).c_str()); delete varbuf->children[0]; varbuf->children[0] = buf; @@ -1907,19 +2496,31 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { for (size_t i = 0; i < children.size(); i++) if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) - log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); + { + log_assert(!VERILOG_FRONTEND::sv_mode); + children[i]->input_error("Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); + } } // transform block with name if (type == AST_BLOCK && !str.empty()) { - std::map name_map; - expand_genblock(std::string(), str + ".", name_map); + expand_genblock(str + "."); + + // if this is an autonamed block is in an always_comb + if (current_always && current_always->attributes.count(ID::always_comb) + && is_autonamed_block(str)) + // track local variables in this block so we can consider adding + // nosync once the block has been fully elaborated + for (AstNode *child : children) + if (child->type == AST_WIRE && + !child->attributes.count(ID::nosync)) + mark_auto_nosync(this, child); std::vector new_children; for (size_t i = 0; i < children.size(); i++) if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) { - children[i]->simplify(false, false, false, stage, -1, false, false); + children[i]->simplify(false, stage, -1, false); current_ast_mod->children.push_back(children[i]); current_scope[children[i]->str] = children[i]; } else @@ -1934,12 +2535,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENBLOCK && children.size() != 0) { if (!str.empty()) { - std::map name_map; - expand_genblock(std::string(), str + ".", name_map); + expand_genblock(str + "."); } for (size_t i = 0; i < children.size(); i++) { - children[i]->simplify(const_fold, false, false, stage, -1, false, false); + children[i]->simplify(const_fold, stage, -1, false); current_ast_mod->children.push_back(children[i]); } @@ -1951,11 +2551,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENIF && children.size() != 0) { AstNode *buf = children[0]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); - log_file_error(filename, location.first_line, "Condition for generate if is not constant!\n"); + input_error("Condition for generate if is not constant!\n"); } if (buf->asBool() != 0) { delete buf; @@ -1971,12 +2571,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = new AstNode(AST_GENBLOCK, buf); if (!buf->str.empty()) { - std::map name_map; - buf->expand_genblock(std::string(), buf->str + ".", name_map); + buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, stage, -1, false); current_ast_mod->children.push_back(buf->children[i]); } @@ -1992,11 +2591,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENCASE && children.size() != 0) { AstNode *buf = children[0]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); - log_file_error(filename, location.first_line, "Condition for generate case is not constant!\n"); + input_error("Condition for generate case is not constant!\n"); } bool ref_signed = buf->is_signed; @@ -2026,11 +2625,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, continue; buf = child->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, true)) { } + buf->set_in_param_flag(true); + while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); - log_file_error(filename, location.first_line, "Expression in generate case is not constant!\n"); + input_error("Expression in generate case is not constant!\n"); } bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool(); @@ -2050,12 +2650,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = selected_case->clone(); if (!buf->str.empty()) { - std::map name_map; - buf->expand_genblock(std::string(), buf->str + ".", name_map); + buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, stage, -1, false); current_ast_mod->children.push_back(buf->children[i]); } @@ -2071,7 +2670,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_CELLARRAY) { if (!children.at(0)->range_valid) - log_file_error(filename, location.first_line, "Non-constant array range on cell array.\n"); + input_error("Non-constant array range on cell array.\n"); newNode = new AstNode(AST_GENBLOCK); int num = max(children.at(0)->range_left, children.at(0)->range_right) - min(children.at(0)->range_left, children.at(0)->range_right) + 1; @@ -2082,7 +2681,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, newNode->children.push_back(new_cell); new_cell->str += stringf("[%d]", idx); if (new_cell->type == AST_PRIMITIVE) { - log_file_error(filename, location.first_line, "Cell arrays of primitives are currently not supported.\n"); + input_error("Cell arrays of primitives are currently not supported.\n"); } else { log_assert(new_cell->children.at(0)->type == AST_CELLTYPE); new_cell->children.at(0)->str = stringf("$array:%d:%d:%s", i, num, new_cell->children.at(0)->str.c_str()); @@ -2096,7 +2695,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_PRIMITIVE) { if (children.size() < 2) - log_file_error(filename, location.first_line, "Insufficient number of arguments for primitive `%s'!\n", str.c_str()); + input_error("Insufficient number of arguments for primitive `%s'!\n", str.c_str()); std::vector children_list; for (auto child : children) { @@ -2111,7 +2710,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1") { if (children_list.size() != 3) - log_file_error(filename, location.first_line, "Invalid number of arguments for primitive `%s'!\n", str.c_str()); + input_error("Invalid number of arguments for primitive `%s'!\n", str.c_str()); std::vector z_const(1, RTLIL::State::Sz); @@ -2133,6 +2732,22 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, children.push_back(children_list.at(0)); children.back()->was_checked = true; children.push_back(node); + fixup_hierarchy_flags(); + did_something = true; + } + else if (str == "buf" || str == "not") + { + AstNode *input = children_list.back(); + if (str == "not") + input = new AstNode(AST_BIT_NOT, input); + + newNode = new AstNode(AST_GENBLOCK); + for (auto it = children_list.begin(); it != std::prev(children_list.end()); it++) { + newNode->children.push_back(new AstNode(AST_ASSIGN, *it, input->clone())); + newNode->children.back()->was_checked = true; + } + delete input; + did_something = true; } else @@ -2152,10 +2767,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, op_type = AST_BIT_XOR; if (str == "xnor") op_type = AST_BIT_XOR, invert_results = true; - if (str == "buf") - op_type = AST_POS; - if (str == "not") - op_type = AST_POS, invert_results = true; log_assert(op_type != AST_NONE); AstNode *node = children_list[1]; @@ -2172,6 +2783,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, children.push_back(children_list[0]); children.back()->was_checked = true; children.push_back(node); + fixup_hierarchy_flags(); did_something = true; } } @@ -2190,228 +2802,215 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (!children[0]->id2ast->range_valid) goto skip_dynamic_range_lvalue_expansion; - int source_width = children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1; + AST::AstNode *member_node = children[0]->get_struct_member(); + int wire_width = member_node ? + member_node->range_left - member_node->range_right + 1 : + children[0]->id2ast->range_left - children[0]->id2ast->range_right + 1; + int wire_offset = children[0]->id2ast->range_right; int result_width = 1; AstNode *shift_expr = NULL; AstNode *range = children[0]->children[0]; - if (range->children.size() == 1) { - shift_expr = range->children[0]->clone(); - } else { - shift_expr = range->children[1]->clone(); - AstNode *left_at_zero_ast = range->children[0]->clone(); - AstNode *right_at_zero_ast = range->children[1]->clone(); - while (left_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { } - while (right_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { } - if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); - result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; - } + if (!try_determine_range_width(range, result_width)) + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); - bool use_case_method = false; + if (range->children.size() >= 2) + shift_expr = range->children[1]->clone(); + else + shift_expr = range->children[0]->clone(); - if (children[0]->id2ast->attributes.count(ID::nowrshmsk)) { - AstNode *node = children[0]->id2ast->attributes.at(ID::nowrshmsk); - while (node->simplify(true, false, false, stage, -1, false, false)) { } - if (node->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Non-constant value for `nowrshmsk' attribute on `%s'!\n", children[0]->id2ast->str.c_str()); - if (node->asAttrConst().as_bool()) - use_case_method = true; - } + bool use_case_method = children[0]->id2ast->get_bool_attribute(ID::nowrshmsk); if (!use_case_method && current_always->detect_latch(children[0]->str)) use_case_method = true; - if (use_case_method) - { - // big case block + if (use_case_method) { + // big case block + + int stride = 1; + long long bitno_div = stride; + + int case_width_hint; + bool case_sign_hint; + shift_expr->detectSignWidth(case_width_hint, case_sign_hint); + int max_width = case_width_hint; + + if (member_node) { // Member in packed struct/union + // Clamp chunk to range of member within struct/union. + log_assert(!wire_offset && !children[0]->id2ast->range_swapped); + + // When the (* nowrshmsk *) attribute is set, a CASE block is generated below + // to select the indexed bit slice. When a multirange array is indexed, the + // start of each possible slice is separated by the bit stride of the last + // index dimension, and we can optimize the CASE block accordingly. + // The dimension of the original array expression is saved in the 'integer' field. + int dims = children[0]->integer; + stride = wire_width; + for (int dim = 0; dim < dims; dim++) { + stride /= member_node->dimensions[dim].range_width; + } + bitno_div = stride; + } else { + // Extract (index)*(width) from non_opt_range pattern ((@selfsz@((index)*(width)))+(0)). + AstNode *lsb_expr = + shift_expr->type == AST_ADD && shift_expr->children[0]->type == AST_SELFSZ && + shift_expr->children[1]->type == AST_CONSTANT && shift_expr->children[1]->integer == 0 ? + shift_expr->children[0]->children[0] : + shift_expr; + + // Extract stride from indexing of two-dimensional packed arrays and + // variable slices on the form dst[i*stride +: width] = src. + if (lsb_expr->type == AST_MUL && + (lsb_expr->children[0]->type == AST_CONSTANT || + lsb_expr->children[1]->type == AST_CONSTANT)) + { + int stride_ix = lsb_expr->children[1]->type == AST_CONSTANT; + stride = (int)lsb_expr->children[stride_ix]->integer; + bitno_div = stride != 0 ? stride : 1; + + // Check whether i*stride can overflow. + int i_width; + bool i_sign; + lsb_expr->children[1 - stride_ix]->detectSignWidth(i_width, i_sign); + int stride_width; + bool stride_sign; + lsb_expr->children[stride_ix]->detectSignWidth(stride_width, stride_sign); + max_width = std::max(i_width, stride_width); + // Stride width calculated from actual stride value. + stride_width = std::ceil(std::log2(std::abs(stride))); + + if (i_width + stride_width > max_width) { + // For (truncated) i*stride to be within the range of dst, the following must hold: + // i*stride ≡ bitno (mod shift_mod), i.e. + // i*stride = k*shift_mod + bitno + // + // The Diophantine equation on the form ax + by = c: + // stride*i - shift_mod*k = bitno + // has solutions iff c is a multiple of d = gcd(a, b), i.e. + // bitno mod gcd(stride, shift_mod) = 0 + // + // long long is at least 64 bits in C++11 + long long shift_mod = 1ll << (max_width - case_sign_hint); + // std::gcd requires C++17 + // bitno_div = std::gcd(stride, shift_mod); + bitno_div = gcd((long long)stride, shift_mod); + } + } + } + + // long long is at least 64 bits in C++11 + long long max_offset = (1ll << (max_width - case_sign_hint)) - 1; + long long min_offset = case_sign_hint ? -(1ll << (max_width - 1)) : 0; + + // A temporary register holds the result of the (possibly complex) rvalue expression, + // avoiding repetition in each AST_COND below. + int rvalue_width; + bool rvalue_sign; + children[1]->detectSignWidth(rvalue_width, rvalue_sign); + AstNode *rvalue = mktemp_logic("$bitselwrite$rvalue$", current_ast_mod, true, rvalue_width - 1, 0, rvalue_sign); + AstNode *caseNode = new AstNode(AST_CASE, shift_expr); + newNode = new AstNode(AST_BLOCK, + new AstNode(AST_ASSIGN_EQ, rvalue, children[1]->clone()), + caseNode); did_something = true; - newNode = new AstNode(AST_CASE, shift_expr); - for (int i = 0; i < source_width; i++) { - int start_bit = children[0]->id2ast->range_right + i; - int end_bit = std::min(start_bit+result_width,source_width) - 1; - AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true)); + for (int i = 1 - result_width; i < wire_width; i++) { + // Out of range indexes are handled in genrtlil.cc + int start_bit = wire_offset + i; + int end_bit = start_bit + result_width - 1; + // Check whether the current index can be generated by shift_expr. + if (start_bit < min_offset || start_bit > max_offset) + continue; + if (start_bit%bitno_div != 0 || (stride == 0 && start_bit != 0)) + continue; + + AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, case_sign_hint, max_width)); AstNode *lvalue = children[0]->clone(); lvalue->delete_children(); + if (member_node) + lvalue->set_attribute(ID::wiretype, member_node->clone()); lvalue->children.push_back(new AstNode(AST_RANGE, mkconst_int(end_bit, true), mkconst_int(start_bit, true))); - cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone()))); - newNode->children.push_back(cond); + cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, rvalue->clone()))); + caseNode->children.push_back(cond); } - } - else - { - // mask and shift operations, disabled for now - - AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); - wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); - wire_mask->attributes[ID::nosync] = AstNode::mkconst_int(1, false); - wire_mask->is_logic = true; - while (wire_mask->simplify(true, false, false, 1, -1, false, false)) { } - current_ast_mod->children.push_back(wire_mask); - - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); - wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); - wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false); - wire_data->is_logic = true; - while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - current_ast_mod->children.push_back(wire_data); - - did_something = true; - newNode = new AstNode(AST_BLOCK); + } else { + // mask and shift operations + // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) AstNode *lvalue = children[0]->clone(); lvalue->delete_children(); - - AstNode *ref_mask = new AstNode(AST_IDENTIFIER); - ref_mask->str = wire_mask->str; - ref_mask->id2ast = wire_mask; - ref_mask->was_checked = true; - - AstNode *ref_data = new AstNode(AST_IDENTIFIER); - ref_data->str = wire_data->str; - ref_data->id2ast = wire_data; - ref_data->was_checked = true; + if (member_node) + lvalue->set_attribute(ID::wiretype, member_node->clone()); AstNode *old_data = lvalue->clone(); if (type == AST_ASSIGN_LE) old_data->lookahead = true; - AstNode *shamt = shift_expr; - - int shamt_width_hint = 0; - bool shamt_sign_hint = true; - shamt->detectSignWidth(shamt_width_hint, shamt_sign_hint); - - int start_bit = children[0]->id2ast->range_right; - bool use_shift = shamt_sign_hint; - - if (start_bit != 0) { - shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true)); - use_shift = true; - } - - AstNode *t; - - t = mkconst_bits(std::vector(result_width, State::S1), false); - if (use_shift) - t = new AstNode(AST_SHIFT, t, new AstNode(AST_NEG, shamt->clone())); - else - t = new AstNode(AST_SHIFT_LEFT, t, shamt->clone()); - t = new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), t); - newNode->children.push_back(t); - - t = new AstNode(AST_BIT_AND, mkconst_bits(std::vector(result_width, State::S1), false), children[1]->clone()); - if (use_shift) - t = new AstNode(AST_SHIFT, t, new AstNode(AST_NEG, shamt)); - else - t = new AstNode(AST_SHIFT_LEFT, t, shamt); - t = new AstNode(AST_ASSIGN_EQ, ref_data->clone(), t); - newNode->children.push_back(t); - - t = new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask)); - t = new AstNode(AST_BIT_OR, t, ref_data); - t = new AstNode(type, lvalue, t); - newNode->children.push_back(t); - } - - goto apply_newNode; - } -skip_dynamic_range_lvalue_expansion:; - - if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && current_block != NULL) - { - std::stringstream sstr; - sstr << "$formal$" << filename << ":" << location.first_line << "$" << (autoidx++); - std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN"; + int shift_width_hint; + bool shift_sign_hint; + shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint); - AstNode *wire_check = new AstNode(AST_WIRE); - wire_check->str = id_check; - wire_check->was_checked = true; - current_ast_mod->children.push_back(wire_check); - current_scope[wire_check->str] = wire_check; - while (wire_check->simplify(true, false, false, 1, -1, false, false)) { } + // All operations are carried out in a new block. + newNode = new AstNode(AST_BLOCK); - AstNode *wire_en = new AstNode(AST_WIRE); - wire_en->str = id_en; - wire_en->was_checked = true; - current_ast_mod->children.push_back(wire_en); - if (current_always_clocked) { - current_ast_mod->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1))))); - current_ast_mod->children.back()->children[0]->children[0]->children[0]->str = id_en; - current_ast_mod->children.back()->children[0]->children[0]->children[0]->was_checked = true; - } - current_scope[wire_en->str] = wire_en; - while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } + // Temporary register holding the result of the bit- or part-select position expression. + AstNode *pos = mktemp_logic("$bitselwrite$pos$", current_ast_mod, true, shift_width_hint - 1, 0, shift_sign_hint); + newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, pos, shift_expr)); - AstNode *check_defval; - if (type == AST_LIVE || type == AST_FAIR) { - check_defval = new AstNode(AST_REDUCE_BOOL, children[0]->clone()); - } else { - std::vector x_bit; - x_bit.push_back(RTLIL::State::Sx); - check_defval = mkconst_bits(x_bit, false); - } + // Calculate lsb from position. + AstNode *shift_val = pos->clone(); - AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), check_defval); - assign_check->children[0]->str = id_check; - assign_check->children[0]->was_checked = true; + // If the expression is signed, we must add an extra bit for possible negation of the most negative number. + // If the expression is unsigned, we must add an extra bit for sign. + shift_val = new AstNode(AST_CAST_SIZE, mkconst_int(shift_width_hint + 1, true), shift_val); + if (!shift_sign_hint) + shift_val = new AstNode(AST_TO_SIGNED, shift_val); - AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1)); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; + // offset the shift amount by the lower bound of the dimension + if (wire_offset != 0) + shift_val = new AstNode(AST_SUB, shift_val, mkconst_int(wire_offset, true)); - AstNode *default_signals = new AstNode(AST_BLOCK); - default_signals->children.push_back(assign_check); - default_signals->children.push_back(assign_en); - current_top_block->children.insert(current_top_block->children.begin(), default_signals); + // reflect the shift amount if the dimension is swapped + if (children[0]->id2ast->range_swapped) + shift_val = new AstNode(AST_SUB, mkconst_int(wire_width - result_width, true), shift_val); - if (type == AST_LIVE || type == AST_FAIR) { - assign_check = nullptr; - } else { - assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_REDUCE_BOOL, children[0]->clone())); - assign_check->children[0]->str = id_check; - assign_check->children[0]->was_checked = true; - } + // AST_SHIFT uses negative amounts for shifting left + shift_val = new AstNode(AST_NEG, shift_val); - if (current_always == nullptr || current_always->type != AST_INITIAL) { - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(1, false, 1)); - } else { - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), new AstNode(AST_FCALL)); - assign_en->children[1]->str = "\\$initstate"; + // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) + did_something = true; + AstNode *bitmask = mkconst_bits(std::vector(result_width, State::S1), false); + newNode->children.push_back( + new AstNode(type, + lvalue, + new AstNode(AST_BIT_OR, + new AstNode(AST_BIT_AND, + old_data, + new AstNode(AST_BIT_NOT, + new AstNode(AST_SHIFT, + bitmask, + shift_val->clone()))), + new AstNode(AST_SHIFT, + new AstNode(AST_TO_UNSIGNED, + new AstNode(AST_CAST_SIZE, + mkconst_int(result_width, true), + children[1]->clone())), + shift_val)))); + + newNode->fixup_hierarchy_flags(true); } - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - - newNode = new AstNode(AST_BLOCK); - if (assign_check != nullptr) - newNode->children.push_back(assign_check); - newNode->children.push_back(assign_en); - - AstNode *assertnode = new AstNode(type); - assertnode->location = location; - assertnode->str = str; - assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); - assertnode->children.push_back(new AstNode(AST_IDENTIFIER)); - assertnode->children[0]->str = id_check; - assertnode->children[1]->str = id_en; - assertnode->attributes.swap(attributes); - current_ast_mod->children.push_back(assertnode); goto apply_newNode; } - - if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && children.size() == 1) - { - children.push_back(mkconst_int(1, false, 1)); - did_something = true; - } +skip_dynamic_range_lvalue_expansion:; // found right-hand side identifier for memory -> replace with memory read port if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue && children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) { + if (integer < (unsigned)id2ast->unpacked_dimensions) + input_error("Insufficient number of array indices for %s.\n", log_id(str)); newNode = new AstNode(AST_MEMRD, children[0]->children[0]->clone()); newNode->str = str; newNode->id2ast = id2ast; @@ -2433,11 +3032,11 @@ skip_dynamic_range_lvalue_expansion:; newNode = new AstNode(AST_BLOCK); AstNode *wire_tmp = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true))); - wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); + wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); current_ast_mod->children.push_back(wire_tmp); current_scope[wire_tmp->str] = wire_tmp; - wire_tmp->attributes[ID::nosync] = AstNode::mkconst_int(1, false); - while (wire_tmp->simplify(true, false, false, 1, -1, false, false)) { } + wire_tmp->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); + while (wire_tmp->simplify(true, 1, -1, false)) { } wire_tmp->is_logic = true; AstNode *wire_tmp_id = new AstNode(AST_IDENTIFIER); @@ -2470,8 +3069,11 @@ skip_dynamic_range_lvalue_expansion:; children[0]->id2ast->children[0]->range_valid && children[0]->id2ast->children[1]->range_valid && (children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE) { + if (children[0]->integer < (unsigned)children[0]->id2ast->unpacked_dimensions) + input_error("Insufficient number of array indices for %s.\n", log_id(str)); + std::stringstream sstr; - sstr << "$memwr$" << children[0]->str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; int mem_width, mem_size, addr_bits; @@ -2507,14 +3109,14 @@ skip_dynamic_range_lvalue_expansion:; wire_addr->was_checked = true; current_ast_mod->children.push_back(wire_addr); current_scope[wire_addr->str] = wire_addr; - while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { } + while (wire_addr->simplify(true, 1, -1, false)) { } - AstNode *assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false)); + AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_addr, false)); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; defNode->children.push_back(assign_addr); - assign_addr = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); + assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; assign_addr->children[0]->was_checked = true; newNode->children.push_back(assign_addr); @@ -2533,9 +3135,9 @@ skip_dynamic_range_lvalue_expansion:; wire_data->is_signed = mem_signed; current_ast_mod->children.push_back(wire_data); current_scope[wire_data->str] = wire_data; - while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } + while (wire_data->simplify(true, 1, -1, false)) { } - AstNode *assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false)); + AstNode *assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bits_data, false)); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; defNode->children.push_back(assign_data); @@ -2544,25 +3146,20 @@ skip_dynamic_range_lvalue_expansion:; node_data->str = id_data; } - AstNode *node_en = nullptr; - if (current_always->type == AST_INITIAL) { - node_en = AstNode::mkconst_int(1, false); - } else { - AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); - wire_en->str = id_en; - wire_en->was_checked = true; - current_ast_mod->children.push_back(wire_en); - current_scope[wire_en->str] = wire_en; - while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } - - AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - defNode->children.push_back(assign_en); + AstNode *wire_en = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); + wire_en->str = id_en; + wire_en->was_checked = true; + current_ast_mod->children.push_back(wire_en); + current_scope[wire_en->str] = wire_en; + while (wire_en->simplify(true, 1, -1, false)) { } - node_en = new AstNode(AST_IDENTIFIER); - node_en->str = id_en; - } + AstNode *assign_en_first = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, mem_width)); + assign_en_first->children[0]->str = id_en; + assign_en_first->children[0]->was_checked = true; + defNode->children.push_back(assign_en_first); + + AstNode *node_en = new AstNode(AST_IDENTIFIER); + node_en->str = id_en; if (!defNode->children.empty()) current_top_block->children.insert(current_top_block->children.begin(), defNode); @@ -2581,77 +3178,87 @@ skip_dynamic_range_lvalue_expansion:; std::vector padding_x(offset, RTLIL::State::Sx); - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), + assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_CONCAT, mkconst_bits(padding_x, false), children[1]->clone())); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; - if (current_always->type != AST_INITIAL) { - for (int i = 0; i < mem_width; i++) - set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - } + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = offset <= i && i < offset+width ? RTLIL::State::S1 : RTLIL::State::S0; + assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + assign_en->children[0]->was_checked = true; } else { AstNode *the_range = children[0]->children[1]; - AstNode *left_at_zero_ast = the_range->children[0]->clone(); - AstNode *right_at_zero_ast = the_range->children.size() >= 2 ? the_range->children[1]->clone() : left_at_zero_ast->clone(); - AstNode *offset_ast = right_at_zero_ast->clone(); + AstNode *offset_ast; + int width; + + if (!try_determine_range_width(the_range, width)) + input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); + + if (the_range->children.size() >= 2) + offset_ast = the_range->children[1]->clone(); + else + offset_ast = the_range->children[0]->clone(); if (mem_data_range_offset) offset_ast = new AstNode(AST_SUB, offset_ast, mkconst_int(mem_data_range_offset, true)); - while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } - while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } - if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); - int width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; - - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), + assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_SHIFT_LEFT, children[1]->clone(), offset_ast->clone())); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; - if (current_always->type != AST_INITIAL) { - for (int i = 0; i < mem_width; i++) - set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), - new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone())); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - } - - delete left_at_zero_ast; - delete right_at_zero_ast; + for (int i = 0; i < mem_width; i++) + set_bits_en[i] = i < width ? RTLIL::State::S1 : RTLIL::State::S0; + assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), + new AstNode(AST_SHIFT_LEFT, mkconst_bits(set_bits_en, false), offset_ast->clone())); + assign_en->children[0]->str = id_en; + assign_en->children[0]->was_checked = true; delete offset_ast; } } else { if (!(children[0]->children.size() == 1 && children[1]->isConst())) { - assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); + assign_data = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[1]->clone()); assign_data->children[0]->str = id_data; assign_data->children[0]->was_checked = true; } - if (current_always->type != AST_INITIAL) { - assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); - assign_en->children[0]->str = id_en; - assign_en->children[0]->was_checked = true; - } + assign_en = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), mkconst_bits(set_bits_en, false)); + assign_en->children[0]->str = id_en; + assign_en->children[0]->was_checked = true; } if (assign_data) newNode->children.push_back(assign_data); if (assign_en) newNode->children.push_back(assign_en); - AstNode *wrnode = new AstNode(current_always->type == AST_INITIAL ? AST_MEMINIT : AST_MEMWR, node_addr, node_data, node_en); + AstNode *wrnode; + if (current_always->type == AST_INITIAL) + wrnode = new AstNode(AST_MEMINIT, node_addr, node_data, node_en, mkconst_int(1, false)); + else + wrnode = new AstNode(AST_MEMWR, node_addr, node_data, node_en); wrnode->str = children[0]->str; wrnode->id2ast = children[0]->id2ast; - current_ast_mod->children.push_back(wrnode); + wrnode->location = location; + if (wrnode->type == AST_MEMWR) { + int portid = current_memwr_count[wrnode->str]++; + wrnode->children.push_back(mkconst_int(portid, false)); + std::vector priority_mask; + for (int i = 0; i < portid; i++) { + bool has_prio = current_memwr_visible[wrnode->str].count(i); + priority_mask.push_back(State(has_prio)); + } + wrnode->children.push_back(mkconst_bits(priority_mask, false)); + current_memwr_visible[wrnode->str].insert(portid); + current_always->children.push_back(wrnode); + } else { + current_ast_mod->children.push_back(wrnode); + } if (newNode->children.empty()) { delete newNode; @@ -2672,7 +3279,7 @@ skip_dynamic_range_lvalue_expansion:; AstNode *wire = new AstNode(AST_WIRE); wire->str = stringf("$initstate$%d_wire", myidx); current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false, false)) { } + while (wire->simplify(true, 1, -1, false)) { } AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE), new AstNode(AST_ARGUMENT, new AstNode(AST_IDENTIFIER))); cell->str = stringf("$initstate$%d", myidx); @@ -2681,7 +3288,7 @@ skip_dynamic_range_lvalue_expansion:; cell->children[1]->children[0]->str = wire->str; cell->children[1]->children[0]->id2ast = wire; current_ast_mod->children.push_back(cell); - while (cell->simplify(true, false, false, 1, -1, false, false)) { } + while (cell->simplify(true, 1, -1, false)) { } newNode = new AstNode(AST_IDENTIFIER); newNode->str = wire->str; @@ -2697,19 +3304,19 @@ skip_dynamic_range_lvalue_expansion:; int num_steps = 1; if (GetSize(children) != 1 && GetSize(children) != 2) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1 or 2.\n", + input_error("System function %s got %d arguments, expected 1 or 2.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); if (!current_always_clocked) - log_file_error(filename, location.first_line, "System function %s is only allowed in clocked blocks.\n", + input_error("System function %s is only allowed in clocked blocks.\n", RTLIL::unescape_id(str).c_str()); if (GetSize(children) == 2) { AstNode *buf = children[1]->clone(); - while (buf->simplify(true, false, false, stage, -1, false, false)) { } + while (buf->simplify(true, stage, -1, false)) { } if (buf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); + input_error("Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); num_steps = buf->asInt(true); delete buf; @@ -2736,12 +3343,13 @@ skip_dynamic_range_lvalue_expansion:; AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true))); - reg->str = stringf("$past$%s:%d$%d$%d", filename.c_str(), location.first_line, myidx, i); + reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, myidx, i); reg->is_reg = true; + reg->is_signed = sign_hint; current_ast_mod->children.push_back(reg); - while (reg->simplify(true, false, false, 1, -1, false, false)) { } + while (reg->simplify(true, 1, -1, false)) { } AstNode *regid = new AstNode(AST_IDENTIFIER); regid->str = reg->str; @@ -2771,11 +3379,11 @@ skip_dynamic_range_lvalue_expansion:; if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell" || str == "\\$changed") { if (GetSize(children) != 1) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1.\n", + input_error("System function %s got %d arguments, expected 1.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); if (!current_always_clocked) - log_file_error(filename, location.first_line, "System function %s is only allowed in clocked blocks.\n", + input_error("System function %s is only allowed in clocked blocks.\n", RTLIL::unescape_id(str).c_str()); AstNode *present = children.at(0)->clone(); @@ -2813,13 +3421,13 @@ skip_dynamic_range_lvalue_expansion:; if (str == "\\$clog2") { if (children.size() != 1) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1.\n", + input_error("System function %s got %d arguments, expected 1.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); AstNode *buf = children[0]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); + input_error("Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); RTLIL::Const arg_value = buf->bitsAsConst(); if (arg_value.as_bool()) @@ -2835,21 +3443,22 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") + if (str == "\\$dimensions" || str == "\\$unpacked_dimensions" || + str == "\\$increment" || str == "\\$size" || str == "\\$bits" || str == "\\$high" || str == "\\$low" || str == "\\$left" || str == "\\$right") { int dim = 1; - if (str == "\\$bits") { + if (str == "\\$dimensions" || str == "\\$unpacked_dimensions" || str == "\\$bits") { if (children.size() != 1) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1.\n", + input_error("System function %s got %d arguments, expected 1.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); } else { if (children.size() != 1 && children.size() != 2) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1 or 2.\n", + input_error("System function %s got %d arguments, expected 1 or 2.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); if (children.size() == 2) { AstNode *buf = children[1]->clone(); // Evaluate constant expression - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, stage, width_hint, sign_hint)) { } dim = buf->asInt(false); delete buf; } @@ -2857,10 +3466,9 @@ skip_dynamic_range_lvalue_expansion:; AstNode *buf = children[0]->clone(); int mem_depth = 1; int result, high = 0, low = 0, left = 0, right = 0, width = 1; // defaults for a simple wire + int expr_dimensions = 0, expr_unpacked_dimensions = 0; AstNode *id_ast = NULL; - // Is this needed? - //while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } buf->detectSignWidth(width_hint, sign_hint); if (buf->type == AST_IDENTIFIER) { @@ -2868,83 +3476,48 @@ skip_dynamic_range_lvalue_expansion:; if (id_ast == NULL && current_scope.count(buf->str)) id_ast = current_scope.at(buf->str); if (!id_ast) - log_file_error(filename, location.first_line, "Failed to resolve identifier %s for width detection!\n", buf->str.c_str()); - // a slice of our identifier means we advance to the next dimension, e.g. $size(a[3]) - if (buf->children.size() > 0) { - // something is hanging below this identifier - if (buf->children[0]->type == AST_RANGE && buf->integer == 0) - // if integer == 0, this node was originally created as AST_RANGE so it's dimension is 1 - dim++; - // more than one range, e.g. $size(a[3][2]) - else // created an AST_MULTIRANGE, converted to AST_RANGE, but original dimension saved in 'integer' field - dim += buf->integer; // increment by multirange size - } - // We have 4 cases: - // wire x; ==> AST_WIRE, no AST_RANGE children - // wire [1:0]x; ==> AST_WIRE, AST_RANGE children - // wire [1:0]x[1:0]; ==> AST_MEMORY, two AST_RANGE children (1st for packed, 2nd for unpacked) - // wire [1:0]x[1:0][1:0]; ==> AST_MEMORY, one AST_RANGE child (0) for packed, then AST_MULTIRANGE child (1) for unpacked - // (updated: actually by the time we are here, AST_MULTIRANGE is converted into one big AST_RANGE) - // case 0 handled by default - if ((id_ast->type == AST_WIRE || id_ast->type == AST_MEMORY) && id_ast->children.size() > 0) { - // handle packed array left/right for case 1, and cases 2/3 when requesting the last dimension (packed side) - AstNode *wire_range = id_ast->children[0]; - left = wire_range->children[0]->integer; - right = wire_range->children[1]->integer; - high = max(left, right); - low = min(left, right); - } - if (id_ast->type == AST_MEMORY) { - // We got here only if the argument is a memory - // Otherwise $size() and $bits() return the expression width - AstNode *mem_range = id_ast->children[1]; - if (str == "\\$bits") { - if (mem_range->type == AST_RANGE) { - if (!mem_range->range_valid) - log_file_error(filename, location.first_line, "Failed to detect width of memory access `%s'!\n", buf->str.c_str()); - mem_depth = mem_range->range_left - mem_range->range_right + 1; - } else - log_file_error(filename, location.first_line, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str()); - } else { - // $size(), $left(), $right(), $high(), $low() - int dims = 1; - if (mem_range->type == AST_RANGE) { - if (id_ast->multirange_dimensions.empty()) { - if (!mem_range->range_valid) - log_file_error(filename, location.first_line, "Failed to detect width of memory access `%s'!\n", buf->str.c_str()); - if (dim == 1) { - left = mem_range->range_right; - right = mem_range->range_left; - high = max(left, right); - low = min(left, right); - } - } else { - dims = GetSize(id_ast->multirange_dimensions)/2; - if (dim <= dims) { - width_hint = id_ast->multirange_dimensions[2*dim-1]; - high = id_ast->multirange_dimensions[2*dim-2] + id_ast->multirange_dimensions[2*dim-1] - 1; - low = id_ast->multirange_dimensions[2*dim-2]; - if (id_ast->multirange_swapped[dim-1]) { - left = low; - right = high; - } else { - right = low; - left = high; - } - } else if ((dim > dims+1) || (dim < 0)) - log_file_error(filename, location.first_line, "Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, buf->str.c_str(), dims+1); - } - } else { - log_file_error(filename, location.first_line, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str()); - } + input_error("Failed to resolve identifier %s for width detection!\n", buf->str.c_str()); + + if (id_ast->type == AST_WIRE || id_ast->type == AST_MEMORY) { + // Check for item in packed struct / union + AstNode *item_node = buf->get_struct_member(); + if (item_node) + id_ast = item_node; + + // The dimension of the original array expression is saved in the 'integer' field + dim += buf->integer; + + int dims = GetSize(id_ast->dimensions); + // TODO: IEEE Std 1800-2017 20.7: "If the first argument to an array query function would cause $dimensions to return 0 + // or if the second argument is out of range, then 'x shall be returned." + if (dim < 1 || dim > dims) + input_error("Dimension %d out of range in `%s', as it only has %d dimensions!\n", dim, id_ast->str.c_str(), dims); + + expr_dimensions = dims - dim + 1; + expr_unpacked_dimensions = std::max(id_ast->unpacked_dimensions - dim + 1, 0); + + right = low = id_ast->dimensions[dim - 1].range_right; + left = high = low + id_ast->dimensions[dim - 1].range_width - 1; + if (id_ast->dimensions[dim - 1].range_swapped) { + std::swap(left, right); + } + for (int i = dim; i < dims; i++) { + mem_depth *= id_ast->dimensions[i].range_width; } } width = high - low + 1; } else { width = width_hint; + right = low = 0; + left = high = width - 1; + expr_dimensions = 1; } delete buf; - if (str == "\\$high") + if (str == "\\$dimensions") + result = expr_dimensions; + else if (str == "\\$unpacked_dimensions") + result = expr_unpacked_dimensions; + else if (str == "\\$high") result = high; else if (str == "\\$low") result = low; @@ -2952,12 +3525,14 @@ skip_dynamic_range_lvalue_expansion:; result = left; else if (str == "\\$right") result = right; + else if (str == "\\$increment") + result = left >= right ? 1 : -1; else if (str == "\\$size") result = width; - else { + else { // str == "\\$bits" result = width * mem_depth; } - newNode = mkconst_int(result, false); + newNode = mkconst_int(result, true); goto apply_newNode; } @@ -2972,18 +3547,18 @@ skip_dynamic_range_lvalue_expansion:; if (func_with_two_arguments) { if (children.size() != 2) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 2.\n", + input_error("System function %s got %d arguments, expected 2.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); } else { if (children.size() != 1) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 1.\n", + input_error("System function %s got %d arguments, expected 1.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); } if (children.size() >= 1) { - while (children[0]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (children[0]->simplify(true, stage, width_hint, sign_hint)) { } if (!children[0]->isConst()) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant argument.\n", + input_error("Failed to evaluate system function `%s' with non-constant argument.\n", RTLIL::unescape_id(str).c_str()); int child_width_hint = width_hint; bool child_sign_hint = sign_hint; @@ -2992,9 +3567,9 @@ skip_dynamic_range_lvalue_expansion:; } if (children.size() >= 2) { - while (children[1]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (children[1]->simplify(true, stage, width_hint, sign_hint)) { } if (!children[1]->isConst()) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant argument.\n", + input_error("Failed to evaluate system function `%s' with non-constant argument.\n", RTLIL::unescape_id(str).c_str()); int child_width_hint = width_hint; bool child_sign_hint = sign_hint; @@ -3034,13 +3609,95 @@ skip_dynamic_range_lvalue_expansion:; } if (str == "\\$sformatf") { - AstNode *node_string = children[0]; - while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_string->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str()); - std::string sformat = node_string->bitsAsConst().decode_string(); - std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); - newNode = AstNode::mkconst_str(sout); + Fmt fmt = processFormat(stage, /*sformat_like=*/true); + newNode = AstNode::mkconst_str(fmt.render()); + goto apply_newNode; + } + + if (str == "\\$countbits") { + if (children.size() < 2) + input_error("System function %s got %d arguments, expected at least 2.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + + std::vector control_bits; + + // Determine which bits to count + for (size_t i = 1; i < children.size(); i++) { + AstNode *node = children[i]; + while (node->simplify(true, stage, -1, false)) { } + if (node->type != AST_CONSTANT) + input_error("Failed to evaluate system function `%s' with non-constant control bit argument.\n", str.c_str()); + if (node->bits.size() != 1) + input_error("Failed to evaluate system function `%s' with control bit width != 1.\n", str.c_str()); + control_bits.push_back(node->bits[0]); + } + + // Detect width of exp (first argument of $countbits) + int exp_width = -1; + bool exp_sign = false; + AstNode *exp = children[0]; + exp->detectSignWidth(exp_width, exp_sign, NULL); + + newNode = mkconst_int(0, false); + + for (int i = 0; i < exp_width; i++) { + // Generate nodes for: exp << i >> ($size(exp) - 1) + // ^^ ^^ + AstNode *lsh_node = new AstNode(AST_SHIFT_LEFT, exp->clone(), mkconst_int(i, false)); + AstNode *rsh_node = new AstNode(AST_SHIFT_RIGHT, lsh_node, mkconst_int(exp_width - 1, false)); + + AstNode *or_node = nullptr; + + for (RTLIL::State control_bit : control_bits) { + // Generate node for: (exp << i >> ($size(exp) - 1)) === control_bit + // ^^^ + AstNode *eq_node = new AstNode(AST_EQX, rsh_node->clone(), mkconst_bits({control_bit}, false)); + + // Or the result for each checked bit value + if (or_node) + or_node = new AstNode(AST_LOGIC_OR, or_node, eq_node); + else + or_node = eq_node; + } + + // We should have at least one element in control_bits, + // because we checked for the number of arguments above + log_assert(or_node != nullptr); + + delete rsh_node; + + // Generate node for adding with result of previous bit + newNode = new AstNode(AST_ADD, newNode, or_node); + } + + goto apply_newNode; + } + + if (str == "\\$countones" || str == "\\$isunknown" || str == "\\$onehot" || str == "\\$onehot0") { + if (children.size() != 1) + input_error("System function %s got %d arguments, expected 1.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + + AstNode *countbits = clone(); + countbits->str = "\\$countbits"; + + if (str == "\\$countones") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = countbits; + } else if (str == "\\$isunknown") { + countbits->children.push_back(mkconst_bits({RTLIL::Sx}, false)); + countbits->children.push_back(mkconst_bits({RTLIL::Sz}, false)); + newNode = new AstNode(AST_GT, countbits, mkconst_int(0, false)); + } else if (str == "\\$onehot") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = new AstNode(AST_EQ, countbits, mkconst_int(1, false)); + } else if (str == "\\$onehot0") { + countbits->children.push_back(mkconst_bits({RTLIL::State::S1}, false)); + newNode = new AstNode(AST_LE, countbits, mkconst_int(1, false)); + } else { + log_abort(); + } + goto apply_newNode; } @@ -3058,14 +3715,14 @@ skip_dynamic_range_lvalue_expansion:; for (int i = 2; i < GetSize(dpi_decl->children); i++) { if (i-2 >= GetSize(children)) - log_file_error(filename, location.first_line, "Insufficient number of arguments in DPI function call.\n"); + input_error("Insufficient number of arguments in DPI function call.\n"); argtypes.push_back(RTLIL::unescape_id(dpi_decl->children.at(i)->str)); args.push_back(children.at(i-2)->clone()); - while (args.back()->simplify(true, false, false, stage, -1, false, true)) { } + while (args.back()->simplify(true, stage, -1, false)) { } if (args.back()->type != AST_CONSTANT && args.back()->type != AST_REALVALUE) - log_file_error(filename, location.first_line, "Failed to evaluate DPI function with non-constant argument.\n"); + input_error("Failed to evaluate DPI function with non-constant argument.\n"); } newNode = dpi_call(rtype, fname, argtypes, args); @@ -3076,8 +3733,10 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } + if (current_scope.count(str) == 0) + str = try_pop_module_prefix(); if (current_scope.count(str) == 0 || current_scope[str]->type != AST_FUNCTION) - log_file_error(filename, location.first_line, "Can't resolve function name `%s'.\n", str.c_str()); + input_error("Can't resolve function name `%s'.\n", str.c_str()); } if (type == AST_TCALL) @@ -3085,42 +3744,42 @@ skip_dynamic_range_lvalue_expansion:; if (str == "$finish" || str == "$stop") { if (!current_always || current_always->type != AST_INITIAL) - log_file_error(filename, location.first_line, "System task `%s' outside initial block is unsupported.\n", str.c_str()); + input_error("System task `%s' outside initial block is unsupported.\n", str.c_str()); - log_file_error(filename, location.first_line, "System task `%s' executed.\n", str.c_str()); + input_error("System task `%s' executed.\n", str.c_str()); } if (str == "\\$readmemh" || str == "\\$readmemb") { if (GetSize(children) < 2 || GetSize(children) > 4) - log_file_error(filename, location.first_line, "System function %s got %d arguments, expected 2-4.\n", + input_error("System function %s got %d arguments, expected 2-4.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); AstNode *node_filename = children[0]->clone(); - while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (node_filename->simplify(true, stage, width_hint, sign_hint)) { } if (node_filename->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str()); + input_error("Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str()); AstNode *node_memory = children[1]->clone(); - while (node_memory->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (node_memory->simplify(true, stage, width_hint, sign_hint)) { } if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-memory 2nd argument.\n", str.c_str()); + input_error("Failed to evaluate system function `%s' with non-memory 2nd argument.\n", str.c_str()); int start_addr = -1, finish_addr = -1; if (GetSize(children) > 2) { AstNode *node_addr = children[2]->clone(); - while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (node_addr->simplify(true, stage, width_hint, sign_hint)) { } if (node_addr->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant 3rd argument.\n", str.c_str()); + input_error("Failed to evaluate system function `%s' with non-constant 3rd argument.\n", str.c_str()); start_addr = int(node_addr->asInt(false)); } if (GetSize(children) > 3) { AstNode *node_addr = children[3]->clone(); - while (node_addr->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (node_addr->simplify(true, stage, width_hint, sign_hint)) { } if (node_addr->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system function `%s' with non-constant 4th argument.\n", str.c_str()); + input_error("Failed to evaluate system function `%s' with non-constant 4th argument.\n", str.c_str()); finish_addr = int(node_addr->asInt(false)); } @@ -3147,43 +3806,53 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } + if (current_scope.count(str) == 0) + str = try_pop_module_prefix(); if (current_scope.count(str) == 0 || current_scope[str]->type != AST_TASK) - log_file_error(filename, location.first_line, "Can't resolve task name `%s'.\n", str.c_str()); + input_error("Can't resolve task name `%s'.\n", str.c_str()); } - AstNode *decl = current_scope[str]; std::stringstream sstr; - sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$"; + sstr << str << "$func$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); - bool recommend_const_eval = false; - bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); - if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) + AstNode *decl = current_scope[str]; + if (unevaluated_tern_branch && decl->is_recursive_function()) + goto replace_fcall_later; + decl = decl->clone(); + decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion + decl->expand_genblock(prefix); + + if (decl->type == AST_FUNCTION && !decl->attributes.count(ID::via_celltype)) { + bool require_const_eval = decl->has_const_only_constructs(); bool all_args_const = true; for (auto child : children) { - while (child->simplify(true, false, false, 1, -1, false, true)) { } + while (child->simplify(true, 1, -1, false)) { } if (child->type != AST_CONSTANT && child->type != AST_REALVALUE) all_args_const = false; } if (all_args_const) { - AstNode *func_workspace = current_scope[str]->clone(); - newNode = func_workspace->eval_const_function(this); + AstNode *func_workspace = decl->clone(); + func_workspace->set_in_param_flag(true); + func_workspace->str = prefix_id(prefix, "$result"); + newNode = func_workspace->eval_const_function(this, in_param || require_const_eval); delete func_workspace; - goto apply_newNode; + if (newNode) { + delete decl; + goto apply_newNode; + } } if (in_param) - log_file_error(filename, location.first_line, "Non-constant function call in constant expression.\n"); + input_error("Non-constant function call in constant expression.\n"); if (require_const_eval) - log_file_error(filename, location.first_line, "Function %s can only be called with constant arguments.\n", str.c_str()); + input_error("Function %s can only be called with constant arguments.\n", str.c_str()); } size_t arg_count = 0; - std::map replace_rules; - vector added_mod_children; dict wire_cache; vector new_stmts; vector output_assignments; @@ -3193,18 +3862,19 @@ skip_dynamic_range_lvalue_expansion:; log_assert(type == AST_FCALL); AstNode *wire = NULL; + std::string res_name = prefix_id(prefix, "$result"); for (auto child : decl->children) - if (child->type == AST_WIRE && child->str == str) + if (child->type == AST_WIRE && child->str == res_name) wire = child->clone(); log_assert(wire != NULL); - wire->str = prefix + str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; + current_scope[wire->str] = wire; current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false, false)) { } + while (wire->simplify(true, 1, -1, false)) { } AstNode *lvalue = new AstNode(AST_IDENTIFIER); lvalue->str = wire->str; @@ -3246,12 +3916,11 @@ skip_dynamic_range_lvalue_expansion:; if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) { AstNode *wire = child->clone(); - wire->str = prefix + wire->str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; current_ast_mod->children.push_back(wire); - while (wire->simplify(true, false, false, 1, -1, false, false)) { } + while (wire->simplify(true, 1, -1, false)) { } AstNode *wire_id = new AstNode(AST_IDENTIFIER); wire_id->str = wire->str; @@ -3294,40 +3963,35 @@ skip_dynamic_range_lvalue_expansion:; for (auto c : child->children) wire->children.push_back(c->clone()); } else if (!child->children.empty()) { - while (child->simplify(true, false, false, stage, -1, false, false)) { } + while (child->simplify(true, stage, -1, false)) { } if (GetSize(child->children) == GetSize(wire->children) - contains_value) { for (int i = 0; i < GetSize(child->children); i++) if (*child->children.at(i) != *wire->children.at(i + contains_value)) goto tcall_incompatible_wires; } else { tcall_incompatible_wires: - log_file_error(filename, location.first_line, "Incompatible re-declaration of wire %s.\n", child->str.c_str()); + input_error("Incompatible re-declaration of wire %s.\n", child->str.c_str()); } } } else { wire = child->clone(); - wire->str = prefix + wire->str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; wire->is_reg = true; - wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); if (child->type == AST_ENUM_ITEM) - wire->attributes[ID::enum_base_type] = child->attributes[ID::enum_base_type]; + wire->set_attribute(ID::enum_base_type, child->attributes[ID::enum_base_type]); wire_cache[child->str] = wire; + current_scope[wire->str] = wire; current_ast_mod->children.push_back(wire); - added_mod_children.push_back(wire); } - if (child->type == AST_WIRE) - while (wire->simplify(true, false, false, 1, -1, false, false)) { } - - replace_rules[child->str] = wire->str; - current_scope[wire->str] = wire; + while (wire->simplify(true, 1, -1, false)) { } if ((child->is_input || child->is_output) && arg_count < children.size()) { @@ -3335,8 +3999,33 @@ skip_dynamic_range_lvalue_expansion:; // convert purely constant arguments into localparams if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child)) { wire->type = AST_LOCALPARAM; + if (wire->attributes.count(ID::nosync)) + delete wire->attributes.at(ID::nosync); wire->attributes.erase(ID::nosync); wire->children.insert(wire->children.begin(), arg->clone()); + // args without a range implicitly have width 1 + if (wire->children.back()->type != AST_RANGE) { + // check if this wire is redeclared with an explicit size + bool uses_explicit_size = false; + for (const AstNode *other_child : decl->children) + if (other_child->type == AST_WIRE && child->str == other_child->str + && !other_child->children.empty() + && other_child->children.back()->type == AST_RANGE) { + uses_explicit_size = true; + break; + } + if (!uses_explicit_size) { + AstNode* range = new AstNode(); + range->type = AST_RANGE; + wire->children.push_back(range); + range->children.push_back(mkconst_int(0, true)); + range->children.push_back(mkconst_int(0, true)); + } + } + wire->fixup_hierarchy_flags(); + // updates the sizing + while (wire->simplify(true, 1, -1, false)) { } + delete arg; continue; } AstNode *wire_id = new AstNode(AST_IDENTIFIER); @@ -3352,18 +4041,9 @@ skip_dynamic_range_lvalue_expansion:; } } - for (auto child : added_mod_children) { - child->replace_ids(prefix, replace_rules); - while (child->simplify(true, false, false, 1, -1, false, false)) { } - } - for (auto child : decl->children) if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) - { - AstNode *stmt = child->clone(); - stmt->replace_ids(prefix, replace_rules); - new_stmts.push_back(stmt); - } + new_stmts.push_back(child->clone()); new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); @@ -3376,10 +4056,11 @@ skip_dynamic_range_lvalue_expansion:; } replace_fcall_with_id: + delete decl; if (type == AST_FCALL) { delete_children(); type = AST_IDENTIFIER; - str = prefix + str; + str = prefix_id(prefix, "$result"); } if (type == AST_TCALL) str = ""; @@ -3412,10 +4093,13 @@ replace_fcall_later:; tmp_range_left = (param_width + 2*param_offset) - children[0]->range_right - 1; tmp_range_right = (param_width + 2*param_offset) - children[0]->range_left - 1; } + AstNode *member_node = get_struct_member(); + int chunk_offset = member_node ? member_node->range_right : 0; + log_assert(!(chunk_offset && param_upto)); for (int i = tmp_range_right; i <= tmp_range_left; i++) { int index = i - param_offset; if (0 <= index && index < param_width) - data.push_back(current_scope[str]->children[0]->bits[index]); + data.push_back(current_scope[str]->children[0]->bits[chunk_offset + index]); else data.push_back(RTLIL::State::Sx); } @@ -3427,9 +4111,6 @@ replace_fcall_later:; if (current_scope[str]->children[0]->isConst()) newNode = current_scope[str]->children[0]->clone(); } - else if (at_zero && current_scope.count(str) > 0 && (current_scope[str]->type == AST_WIRE || current_scope[str]->type == AST_AUTOWIRE)) { - newNode = mkconst_int(0, sign_hint, width_hint); - } break; case AST_BIT_NOT: if (children[0]->type == AST_CONSTANT) { @@ -3572,24 +4253,9 @@ replace_fcall_later:; case AST_TERNARY: if (children[0]->isConst()) { - bool found_sure_true = false; - bool found_maybe_true = false; - - if (children[0]->type == AST_CONSTANT) - for (auto &bit : children[0]->bits) { - if (bit == RTLIL::State::S1) - found_sure_true = true; - if (bit > RTLIL::State::S1) - found_maybe_true = true; - } - else - found_sure_true = children[0]->asReal(sign_hint) != 0; - - AstNode *choice = NULL, *not_choice = NULL; - if (found_sure_true) - choice = children[1], not_choice = children[2]; - else if (!found_maybe_true) - choice = children[2], not_choice = children[1]; + auto pair = get_tern_choice(); + AstNode *choice = pair.first; + AstNode *not_choice = pair.second; if (choice != NULL) { if (choice->type == AST_CONSTANT) { @@ -3634,7 +4300,11 @@ replace_fcall_later:; case AST_CAST_SIZE: if (children.at(0)->type == AST_CONSTANT && children.at(1)->type == AST_CONSTANT) { int width = children[0]->bitsAsConst().as_int(); - RTLIL::Const val = children[1]->bitsAsConst(width); + RTLIL::Const val; + if (children[1]->is_unsized) + val = children[1]->bitsAsUnsizedConst(width); + else + val = children[1]->bitsAsConst(width); newNode = mkconst_bits(val.bits, children[1]->is_signed); } break; @@ -3672,6 +4342,7 @@ replace_fcall_later:; newNode->filename = filename; newNode->location = location; newNode->cloneInto(this); + fixup_hierarchy_flags(); delete newNode; did_something = true; } @@ -3683,12 +4354,12 @@ replace_fcall_later:; return did_something; } -static void replace_result_wire_name_in_function(AstNode *node, std::string &from, std::string &to) +void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to) { - for (auto &it : node->children) - replace_result_wire_name_in_function(it, from, to); - if (node->str == from) - node->str = to; + for (AstNode *child : children) + child->replace_result_wire_name_in_function(from, to); + if (str == from && type != AST_FCALL && type != AST_TCALL) + str = to; } // replace a readmem[bh] TCALL ast node with a block of memory assignments @@ -3702,8 +4373,12 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m AstNode *meminit = nullptr; int next_meminit_cursor=0; vector meminit_bits; + vector en_bits; int meminit_size=0; + for (int i = 0; i < mem_width; i++) + en_bits.push_back(State::S1); + std::ifstream f; f.open(mem_filename.c_str()); if (f.fail()) { @@ -3719,7 +4394,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m yosys_input_files.insert(mem_filename); } if (f.fail() || GetSize(mem_filename) == 0) - log_file_error(filename, location.first_line, "Can not open file `%s` for %s.\n", mem_filename.c_str(), str.c_str()); + input_error("Can not open file `%s` for %s.\n", mem_filename.c_str(), str.c_str()); log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid); int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right; @@ -3765,7 +4440,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m char *endptr; cursor = strtol(nptr, &endptr, 16); if (!*nptr || *endptr) - log_file_error(filename, location.first_line, "Can not parse address `%s` for %s.\n", nptr, str.c_str()); + input_error("Can not parse address `%s` for %s.\n", nptr, str.c_str()); continue; } @@ -3777,12 +4452,13 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m { if (meminit != nullptr) { meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[2] = AstNode::mkconst_int(meminit_size, false); + meminit->children[3] = AstNode::mkconst_int(meminit_size, false); } meminit = new AstNode(AST_MEMINIT); meminit->children.push_back(AstNode::mkconst_int(cursor, false)); meminit->children.push_back(nullptr); + meminit->children.push_back(AstNode::mkconst_bits(en_bits, false)); meminit->children.push_back(nullptr); meminit->str = memory->str; meminit->id2ast = memory; @@ -3817,71 +4493,60 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m if (meminit != nullptr) { meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[2] = AstNode::mkconst_int(meminit_size, false); + meminit->children[3] = AstNode::mkconst_int(meminit_size, false); } return block; } -// annotate the names of all wires and other named objects in a generate block -void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map &name_map, bool original_scope) +// annotate the names of all wires and other named objects in a named generate +// or procedural block; nested blocks are themselves annotated such that the +// prefix is carried forward, but resolution of their children is deferred +void AstNode::expand_genblock(const std::string &prefix) { - // `original_scope` defaults to false, and is used to prevent the premature - // prefixing of items in named sub-blocks - - if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { - if (children.empty()) { - current_scope[index_var]->children[0]->cloneInto(this); - } else { - AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone()); - p->str = stringf("$genval$%d", autoidx++); - current_ast_mod->children.push_back(p); - str = p->str; - id2ast = p; - } - } + if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE || type == AST_PREFIX) { + log_assert(!str.empty()); + + // search starting in the innermost scope and then stepping outward + for (size_t ppos = prefix.size() - 1; ppos; --ppos) { + if (prefix.at(ppos) != '.') continue; + + std::string new_prefix = prefix.substr(0, ppos + 1); + auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string { + std::string new_name = prefix_id(new_prefix, ident); + if (current_scope.count(new_name)) + return new_name; + return {}; + }; + + // attempt to resolve the full identifier + std::string resolved = attempt_resolve(str); + if (!resolved.empty()) { + str = resolved; + break; + } - if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) { - if (name_map.count(str) > 0) { - str = name_map[str]; - } else { - // remap the prefix of this ident if it is a local generate scope - size_t pos = str.rfind('.'); - if (pos != std::string::npos) { - std::string existing_prefix = str.substr(0, pos); - if (name_map.count(existing_prefix) > 0) { - str = name_map[existing_prefix] + str.substr(pos); + // attempt to resolve hierarchical prefixes within the identifier, + // as the prefix could refer to a local scope which exists but + // hasn't yet been elaborated + for (size_t spos = str.size() - 1; spos; --spos) { + if (str.at(spos) != '.') continue; + resolved = attempt_resolve(str.substr(0, spos)); + if (!resolved.empty()) { + str = resolved + str.substr(spos); + ppos = 1; // break outer loop + break; } } - } - } - - std::map backup_name_map; - auto prefix_node = [&](AstNode* child) { - if (backup_name_map.size() == 0) - backup_name_map = name_map; - - // if within a nested scope - if (!original_scope) { - // this declaration shadows anything in the parent scope(s) - name_map[child->str] = child->str; - return; } + } - std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; - size_t pos = child->str.rfind('.'); - if (pos == std::string::npos) - pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; - else - pos = pos + 1; - new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); - if (new_name[0] != '$' && new_name[0] != '\\') - new_name = prefix[0] + new_name; - - name_map[child->str] = new_name; + auto prefix_node = [&prefix](AstNode* child) { + if (child->str.empty()) return; + std::string new_name = prefix_id(prefix, child->str); if (child->type == AST_FUNCTION) - replace_result_wire_name_in_function(child, child->str, new_name); + child->replace_result_wire_name_in_function(child->str, new_name); else child->str = new_name; current_scope[new_name] = child; @@ -3893,6 +4558,8 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma switch (child->type) { case AST_WIRE: case AST_MEMORY: + case AST_STRUCT: + case AST_UNION: case AST_PARAMETER: case AST_LOCALPARAM: case AST_FUNCTION: @@ -3925,49 +4592,66 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma for (size_t i = 0; i < children.size(); i++) { AstNode *child = children[i]; - // AST_PREFIX member names should not be prefixed; a nested AST_PREFIX - // still needs to recursed-into - if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER) + // AST_PREFIX member names should not be prefixed; we recurse into them + // as normal to ensure indices and ranges are properly resolved, and + // then restore the previous string + if (type == AST_PREFIX && i == 1) { + std::string backup_scope_name = child->str; + child->expand_genblock(prefix); + child->str = backup_scope_name; continue; + } // functions/tasks may reference wires, constants, etc. in this scope if (child->type == AST_FUNCTION || child->type == AST_TASK) - child->expand_genblock(index_var, prefix, name_map, false); - // continue prefixing if this child block is anonymous - else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK) - child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty()); - else - child->expand_genblock(index_var, prefix, name_map, original_scope); - } - + continue; + // named blocks pick up the current prefix and will expanded later + if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty()) + continue; - if (backup_name_map.size() > 0) - name_map.swap(backup_name_map); + child->expand_genblock(prefix); + } } -// rename stuff (used when tasks of functions are instantiated) -void AstNode::replace_ids(const std::string &prefix, const std::map &rules) +// add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or +// IEEE 1800-2017 Section 27.6 +void AstNode::label_genblks(std::set& existing, int &counter) { - if (type == AST_BLOCK) - { - std::map new_rules = rules; - std::string new_prefix = prefix + str; - - for (auto child : children) - if (child->type == AST_WIRE) { - new_rules[child->str] = new_prefix + child->str; - child->str = new_prefix + child->str; - } + switch (type) { + case AST_GENIF: + case AST_GENFOR: + case AST_GENCASE: + // seeing a proper generate control flow construct increments the + // counter once + ++counter; + for (AstNode *child : children) + child->label_genblks(existing, counter); + break; - for (auto child : children) - if (child->type != AST_WIRE) - child->replace_ids(new_prefix, new_rules); + case AST_GENBLOCK: { + // if this block is unlabeled, generate its corresponding unique name + for (int padding = 0; str.empty(); ++padding) { + std::string candidate = "\\genblk"; + for (int i = 0; i < padding; ++i) + candidate += '0'; + candidate += std::to_string(counter); + if (!existing.count(candidate)) + str = candidate; + } + // within a genblk, the counter starts fresh + std::set existing_local = existing; + int counter_local = 0; + for (AstNode *child : children) + child->label_genblks(existing_local, counter_local); + break; } - else - { - if (type == AST_IDENTIFIER && rules.count(str) > 0) - str = rules.at(str); - for (auto child : children) - child->replace_ids(prefix, rules); + + default: + // track names which could conflict with implicit genblk names + if (str.rfind("\\genblk", 0) == 0) + existing.insert(str); + for (AstNode *child : children) + child->label_genblks(existing, counter); + break; } } @@ -3981,7 +4665,7 @@ static void mark_memories_assign_lhs_complex(dict> & if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) { AstNode *mem = that->id2ast; if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS)) - mem2reg_places[mem].insert(stringf("%s:%d", that->filename.c_str(), that->location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS; } } @@ -4009,14 +4693,14 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg // activate mem2reg if this is assigned in an async proc if (flags & AstNode::MEM2REG_FL_ASYNC) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC)) - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ASYNC; } // remember if this is assigned blocking (=) if (type == AST_ASSIGN_EQ) { if (!(proc_flags[mem] & AstNode::MEM2REG_FL_EQ1)) - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1; } @@ -4033,11 +4717,11 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg // remember where this is if (flags & MEM2REG_FL_INIT) { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT)) - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT; } else { if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE)) - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE; } } @@ -4049,9 +4733,12 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg { AstNode *mem = id2ast; + if (integer < (unsigned)mem->unpacked_dimensions) + input_error("Insufficient number of array indices for %s.\n", log_id(str)); + // flag if used after blocking assignment (in same proc) if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) { - mem2reg_places[mem].insert(stringf("%s:%d", filename.c_str(), location.first_line)); + mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2; } } @@ -4060,7 +4747,7 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg if (type == AST_MEMORY && (get_bool_attribute(ID::mem2reg) || (flags & AstNode::MEM2REG_FL_ALL) || !(is_reg || is_logic))) mem2reg_candidates[this] |= AstNode::MEM2REG_FL_FORCED; - if (type == AST_MODULE && get_bool_attribute(ID::mem2reg)) + if ((type == AST_MODULE || type == AST_INTERFACE) && get_bool_attribute(ID::mem2reg)) children_flags |= AstNode::MEM2REG_FL_ALL; dict *proc_flags_p = NULL; @@ -4074,8 +4761,7 @@ void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg children_flags |= AstNode::MEM2REG_FL_ASYNC; proc_flags_p = new dict; } - - if (type == AST_INITIAL) { + else if (type == AST_INITIAL) { children_flags |= AstNode::MEM2REG_FL_INIT; proc_flags_p = new dict; } @@ -4120,7 +4806,7 @@ bool AstNode::mem2reg_check(pool &mem2reg_set) return false; if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1) - log_file_error(filename, location.first_line, "Invalid array access.\n"); + input_error("Invalid array access.\n"); return true; } @@ -4161,10 +4847,12 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, log_assert(children[0]->type == AST_CONSTANT); log_assert(children[1]->type == AST_CONSTANT); log_assert(children[2]->type == AST_CONSTANT); + log_assert(children[3]->type == AST_CONSTANT); int cursor = children[0]->asInt(false); Const data = children[1]->bitsAsConst(); - int length = children[2]->asInt(false); + Const en = children[2]->bitsAsConst(); + int length = children[3]->asInt(false); if (length != 0) { @@ -4175,10 +4863,37 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, int wordsz = GetSize(data) / length; for (int i = 0; i < length; i++) { - block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false))), mkconst_bits(data.extract(i*wordsz, wordsz).bits, false))); - block->children.back()->children[0]->str = str; - block->children.back()->children[0]->id2ast = id2ast; - block->children.back()->children[0]->was_checked = true; + int pos = 0; + while (pos < wordsz) { + if (en[pos] != State::S1) { + pos++; + } else { + int epos = pos + 1; + while (epos < wordsz && en[epos] == State::S1) + epos++; + int clen = epos - pos; + AstNode *range = new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false)); + if (pos != 0 || epos != wordsz) { + int left; + int right; + AstNode *mrange = id2ast->children[0]; + if (mrange->range_left < mrange->range_right) { + right = mrange->range_right - pos; + left = mrange->range_right - epos + 1; + } else { + right = mrange->range_right + pos; + left = mrange->range_right + epos - 1; + } + range = new AstNode(AST_MULTIRANGE, range, new AstNode(AST_RANGE, AstNode::mkconst_int(left, true), AstNode::mkconst_int(right, true))); + } + AstNode *target = new AstNode(AST_IDENTIFIER, range); + target->str = str; + target->id2ast = id2ast; + target->was_checked = true; + block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).bits, false))); + pos = epos; + } + } } } @@ -4212,7 +4927,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; - sstr << "$mem2reg_wr$" << children[0]->str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; int mem_width, mem_size, addr_bits; @@ -4223,18 +4938,18 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, wire_addr->str = id_addr; wire_addr->is_reg = true; wire_addr->was_checked = true; - wire_addr->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); mod->children.push_back(wire_addr); - while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { } + while (wire_addr->simplify(true, 1, -1, false)) { } AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); wire_data->str = id_data; wire_data->is_reg = true; wire_data->was_checked = true; wire_data->is_signed = mem_signed; - wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); mod->children.push_back(wire_data); - while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } + while (wire_data->simplify(true, 1, -1, false)) { } log_assert(block != NULL); size_t assign_idx = 0; @@ -4261,6 +4976,10 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, cond_node->children[1]->children.push_back(assign_reg); case_node->children.push_back(cond_node); } + + // fixup on the full hierarchy below case_node + case_node->fixup_hierarchy_flags(true); + block->children.insert(block->children.begin()+assign_idx+2, case_node); children[0]->delete_children(); @@ -4270,6 +4989,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, type = AST_ASSIGN_EQ; children[0]->was_checked = true; + fixup_hierarchy_flags(); did_something = true; } @@ -4282,16 +5002,53 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, if (children[0]->children[0]->type == AST_CONSTANT) { int id = children[0]->children[0]->integer; - str = stringf("%s[%d]", str.c_str(), id); + int left = id2ast->children[1]->children[0]->integer; + int right = id2ast->children[1]->children[1]->integer; + bool valid_const_access = + (left <= id && id <= right) || + (right <= id && id <= left); + if (valid_const_access) + { + str = stringf("%s[%d]", str.c_str(), id); + delete_children(); + range_valid = false; + id2ast = NULL; + } + else + { + int width; + if (bit_part_sel) + { + // bit_part_sel->dumpAst(nullptr, "? "); + if (bit_part_sel->children.size() == 1) + width = 0; + else + width = bit_part_sel->children[0]->integer - + bit_part_sel->children[1]->integer; + delete bit_part_sel; + bit_part_sel = nullptr; + } + else + { + width = id2ast->children[0]->children[0]->integer - + id2ast->children[0]->children[1]->integer; + } + width = abs(width) + 1; - delete_children(); - range_valid = false; - id2ast = NULL; + delete_children(); + + std::vector x_bits; + for (int i = 0; i < width; i++) + x_bits.push_back(RTLIL::State::Sx); + AstNode *constant = AstNode::mkconst_bits(x_bits, false); + constant->cloneInto(this); + delete constant; + } } else { std::stringstream sstr; - sstr << "$mem2reg_rd$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; int mem_width, mem_size, addr_bits; @@ -4303,9 +5060,9 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, wire_addr->is_reg = true; wire_addr->was_checked = true; if (block) - wire_addr->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); mod->children.push_back(wire_addr); - while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { } + while (wire_addr->simplify(true, 1, -1, false)) { } AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); wire_data->str = id_data; @@ -4313,9 +5070,9 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, wire_data->was_checked = true; wire_data->is_signed = mem_signed; if (block) - wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false); + wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); mod->children.push_back(wire_data); - while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } + while (wire_data->simplify(true, 1, -1, false)) { } AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; @@ -4347,6 +5104,9 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, cond_node->children[1]->children.push_back(assign_reg); case_node->children.push_back(cond_node); + // fixup on the full hierarchy below case_node + case_node->fixup_hierarchy_flags(true); + if (block) { size_t assign_idx = 0; @@ -4358,10 +5118,10 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, } else { - AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); - proc->children[0]->children.push_back(case_node); + AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, case_node)); mod->children.push_back(proc); mod->children.push_back(assign_addr); + mod->fixup_hierarchy_flags(); } delete_children(); @@ -4370,8 +5130,10 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, str = id_data; } - if (bit_part_sel) + if (bit_part_sel) { children.push_back(bit_part_sel); + fixup_hierarchy_flags(); + } did_something = true; } @@ -4459,17 +5221,12 @@ bool AstNode::detect_latch(const std::string &var) } } -bool AstNode::has_const_only_constructs(bool &recommend_const_eval) +bool AstNode::has_const_only_constructs() { - if (type == AST_FOR) - recommend_const_eval = true; if (type == AST_WHILE || type == AST_REPEAT) return true; - if (type == AST_FCALL && current_scope.count(str)) - if (current_scope[str]->has_const_only_constructs(recommend_const_eval)) - return true; for (auto child : children) - if (child->AstNode::has_const_only_constructs(recommend_const_eval)) + if (child->has_const_only_constructs()) return true; return false; } @@ -4485,47 +5242,61 @@ bool AstNode::is_simple_const_expr() } // helper function for AstNode::eval_const_function() -void AstNode::replace_variables(std::map &variables, AstNode *fcall) +bool AstNode::replace_variables(std::map &variables, AstNode *fcall, bool must_succeed) { if (type == AST_IDENTIFIER && variables.count(str)) { int offset = variables.at(str).offset, width = variables.at(str).val.bits.size(); if (!children.empty()) { - if (children.size() != 1 || children.at(0)->type != AST_RANGE) - log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s:%d.%d-%d.%d: ...called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); - children.at(0)->replace_variables(variables, fcall); - while (simplify(true, false, false, 1, -1, false, true)) { } - if (!children.at(0)->range_valid) - log_file_error(filename, location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (children.size() != 1 || children.at(0)->type != AST_RANGE) { + if (!must_succeed) + return false; + input_error("Memory access in constant function is not supported\n%s: ...called from here.\n", + fcall->loc_string().c_str()); + } + if (!children.at(0)->replace_variables(variables, fcall, must_succeed)) + return false; + while (simplify(true, 1, -1, false)) { } + if (!children.at(0)->range_valid) { + if (!must_succeed) + return false; + input_error("Non-constant range\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } offset = min(children.at(0)->range_left, children.at(0)->range_right); width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); } offset -= variables.at(str).offset; + if (variables.at(str).range_swapped) + offset = -offset; std::vector &var_bits = variables.at(str).val.bits; std::vector new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width); AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); newNode->cloneInto(this); delete newNode; - return; + return true; } for (auto &child : children) - child->replace_variables(variables, fcall); + if (!child->replace_variables(variables, fcall, must_succeed)) + return false; + return true; } -// evaluate functions with all-const arguments -AstNode *AstNode::eval_const_function(AstNode *fcall) +// attempt to statically evaluate a functions with all-const arguments +AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { - std::map backup_scope; + std::map backup_scope = current_scope; std::map variables; + std::vector to_delete; AstNode *block = new AstNode(AST_BLOCK); + AstNode *result = nullptr; size_t argidx = 0; for (auto child : children) { block->children.push_back(child->clone()); } + block->set_in_param_flag(true); while (!block->children.empty()) { @@ -4540,28 +5311,43 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_WIRE) { - while (stmt->simplify(true, false, false, 1, -1, false, true)) { } - if (!stmt->range_valid) - log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", - stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); - variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1); - variables[stmt->str].offset = min(stmt->range_left, stmt->range_right); - variables[stmt->str].is_signed = stmt->is_signed; + while (stmt->simplify(true, 1, -1, false)) { } + if (!stmt->range_valid) { + if (!must_succeed) + goto finished; + stmt->input_error("Can't determine size of variable %s\n%s: ... called from here.\n", + stmt->str.c_str(), fcall->loc_string().c_str()); + } + AstNode::varinfo_t &variable = variables[stmt->str]; + int width = abs(stmt->range_left - stmt->range_right) + 1; + // if this variable has already been declared as an input, check the + // sizes match if it already had an explicit size + if (variable.arg && variable.explicitly_sized && variable.val.size() != width) { + input_error("Incompatible re-declaration of constant function wire %s.\n", stmt->str.c_str()); + } + variable.val = RTLIL::Const(RTLIL::State::Sx, width); + variable.offset = stmt->range_swapped ? stmt->range_left : stmt->range_right; + variable.range_swapped = stmt->range_swapped; + variable.is_signed = stmt->is_signed; + variable.explicitly_sized = stmt->children.size() && + stmt->children.back()->type == AST_RANGE; + // identify the argument corresponding to this wire, if applicable if (stmt->is_input && argidx < fcall->children.size()) { - int width = variables[stmt->str].val.bits.size(); - auto* arg_node = fcall->children.at(argidx++); - if (arg_node->type == AST_CONSTANT) { - variables[stmt->str].val = arg_node->bitsAsConst(width); + variable.arg = fcall->children.at(argidx++); + } + // load the constant arg's value into this variable + if (variable.arg) { + if (variable.arg->type == AST_CONSTANT) { + variable.val = variable.arg->bitsAsConst(width); } else { - log_assert(arg_node->type == AST_REALVALUE); - variables[stmt->str].val = arg_node->realAsConst(width); + log_assert(variable.arg->type == AST_REALVALUE); + variable.val = variable.arg->realAsConst(width); } } - if (!backup_scope.count(stmt->str)) - backup_scope[stmt->str] = current_scope[stmt->str]; current_scope[stmt->str] = stmt; block->children.erase(block->children.begin()); + to_delete.push_back(stmt); continue; } @@ -4569,13 +5355,12 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_LOCALPARAM) { - while (stmt->simplify(true, false, false, 1, -1, false, true)) { } + while (stmt->simplify(true, 1, -1, false)) { } - if (!backup_scope.count(stmt->str)) - backup_scope[stmt->str] = current_scope[stmt->str]; current_scope[stmt->str] = stmt; block->children.erase(block->children.begin()); + to_delete.push_back(stmt); continue; } @@ -4583,38 +5368,55 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) { if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && stmt->children.at(0)->children.at(0)->type == AST_RANGE) - stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall); - stmt->children.at(1)->replace_variables(variables, fcall); - while (stmt->simplify(true, false, false, 1, -1, false, true)) { } + if (!stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall, must_succeed)) + goto finished; + if (!stmt->children.at(1)->replace_variables(variables, fcall, must_succeed)) + goto finished; + while (stmt->simplify(true, 1, -1, false)) { } if (stmt->type != AST_ASSIGN_EQ) continue; - if (stmt->children.at(1)->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here. X\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (stmt->children.at(1)->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + stmt->input_error("Non-constant expression in constant function\n%s: ... called from here. X\n", + fcall->loc_string().c_str()); + } - if (stmt->children.at(0)->type != AST_IDENTIFIER) - log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (stmt->children.at(0)->type != AST_IDENTIFIER) { + if (!must_succeed) + goto finished; + stmt->input_error("Unsupported composite left hand side in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } - if (!variables.count(stmt->children.at(0)->str)) - log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!variables.count(stmt->children.at(0)->str)) { + if (!must_succeed) + goto finished; + stmt->input_error("Assignment to non-local variable in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } if (stmt->children.at(0)->children.empty()) { variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size()); } else { AstNode *range = stmt->children.at(0)->children.at(0); - if (!range->range_valid) - log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!range->range_valid) { + if (!must_succeed) + goto finished; + range->input_error("Non-constant range\n%s: ... called from here.\n", fcall->loc_string().c_str()); + } int offset = min(range->range_left, range->range_right); int width = std::abs(range->range_left - range->range_right) + 1; varinfo_t &v = variables[stmt->children.at(0)->str]; RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size()); - for (int i = 0; i < width; i++) - v.val.bits.at(i+offset-v.offset) = r.bits.at(i); + for (int i = 0; i < width; i++) { + int index = i + offset - v.offset; + if (v.range_swapped) + index = -index; + v.val.bits.at(index) = r.bits.at(i); + } } delete block->children.front(); @@ -4635,12 +5437,17 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_WHILE) { AstNode *cond = stmt->children.at(0)->clone(); - cond->replace_variables(variables, fcall); - while (cond->simplify(true, false, false, 1, -1, false, true)) { } - - if (cond->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!cond->replace_variables(variables, fcall, must_succeed)) + goto finished; + cond->set_in_param_flag(true); + while (cond->simplify(true, 1, -1, false)) { } + + if (cond->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + stmt->input_error("Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } if (cond->asBool()) { block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); @@ -4656,12 +5463,17 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_REPEAT) { AstNode *num = stmt->children.at(0)->clone(); - num->replace_variables(variables, fcall); - while (num->simplify(true, false, false, 1, -1, false, true)) { } - - if (num->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!num->replace_variables(variables, fcall, must_succeed)) + goto finished; + num->set_in_param_flag(true); + while (num->simplify(true, 1, -1, false)) { } + + if (num->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + stmt->input_error("Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } block->children.erase(block->children.begin()); for (int i = 0; i < num->bitsAsConst().as_int(); i++) @@ -4675,8 +5487,10 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_CASE) { AstNode *expr = stmt->children.at(0)->clone(); - expr->replace_variables(variables, fcall); - while (expr->simplify(true, false, false, 1, -1, false, true)) { } + if (!expr->replace_variables(variables, fcall, must_succeed)) + goto finished; + expr->set_in_param_flag(true); + while (expr->simplify(true, 1, -1, false)) { } AstNode *sel_case = NULL; for (size_t i = 1; i < stmt->children.size(); i++) @@ -4692,14 +5506,19 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) { AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); - cond->replace_variables(variables, fcall); + if (!cond->replace_variables(variables, fcall, must_succeed)) + goto finished; cond = new AstNode(AST_EQ, expr->clone(), cond); - while (cond->simplify(true, false, false, 1, -1, false, true)) { } - - if (cond->type != AST_CONSTANT) - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + cond->set_in_param_flag(true); + while (cond->simplify(true, 1, -1, false)) { } + + if (cond->type != AST_CONSTANT) { + if (!must_succeed) + goto finished; + stmt->input_error("Non-constant expression in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); + } found_match = cond->asBool(); delete cond; @@ -4721,36 +5540,48 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_BLOCK) { + if (!stmt->str.empty()) + stmt->expand_genblock(stmt->str + "."); + block->children.erase(block->children.begin()); block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); stmt->children.clear(); + block->fixup_hierarchy_flags(); delete stmt; continue; } - log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", - fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + if (!must_succeed) + goto finished; + stmt->input_error("Unsupported language construct in constant function\n%s: ... called from here.\n", + fcall->loc_string().c_str()); log_abort(); } + result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); + +finished: delete block; + current_scope = backup_scope; - for (auto &it : backup_scope) - if (it.second == NULL) - current_scope.erase(it.first); - else - current_scope[it.first] = it.second; + for (auto it : to_delete) { + delete it; + } + to_delete.clear(); - return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); + return result; } void AstNode::allocateDefaultEnumValues() { log_assert(type==AST_ENUM); + log_assert(children.size() > 0); + if (children.front()->attributes.count(ID::enum_base_type)) + return; // already elaborated int last_enum_int = -1; for (auto node : children) { log_assert(node->type==AST_ENUM_ITEM); - node->attributes[ID::enum_base_type] = mkconst_str(str); + node->set_attribute(ID::enum_base_type, mkconst_str(str)); for (size_t i = 0; i < node->children.size(); i++) { switch (node->children[i]->type) { case AST_NONE: @@ -4772,4 +5603,71 @@ void AstNode::allocateDefaultEnumValues() } } +bool AstNode::is_recursive_function() const +{ + std::set visited; + std::function visit = [&](const AstNode *node) { + if (visited.count(node)) + return node == this; + visited.insert(node); + if (node->type == AST_FCALL) { + auto it = current_scope.find(node->str); + if (it != current_scope.end() && visit(it->second)) + return true; + } + for (const AstNode *child : node->children) { + if (visit(child)) + return true; + } + return false; + }; + + log_assert(type == AST_FUNCTION); + return visit(this); +} + +std::pair AstNode::get_tern_choice() +{ + if (!children[0]->isConst()) + return {}; + + bool found_sure_true = false; + bool found_maybe_true = false; + + if (children[0]->type == AST_CONSTANT) + for (auto &bit : children[0]->bits) { + if (bit == RTLIL::State::S1) + found_sure_true = true; + if (bit > RTLIL::State::S1) + found_maybe_true = true; + } + else + found_sure_true = children[0]->asReal(true) != 0; + + AstNode *choice = nullptr, *not_choice = nullptr; + if (found_sure_true) + choice = children[1], not_choice = children[2]; + else if (!found_maybe_true) + choice = children[2], not_choice = children[1]; + + return {choice, not_choice}; +} + +std::string AstNode::try_pop_module_prefix() const +{ + AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; + size_t pos = str.find('.', 1); + if (str[0] == '\\' && pos != std::string::npos) { + std::string new_str = "\\" + str.substr(pos + 1); + if (current_scope.count(new_str)) { + std::string prefix = str.substr(0, pos); + auto it = current_scope_ast->attributes.find(ID::hdlname); + if ((it != current_scope_ast->attributes.end() && it->second->str == prefix.substr(1)) + || prefix == current_scope_ast->str) + return new_str; + } + } + return str; +} + YOSYS_NAMESPACE_END diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 9ae3fac2c0e..731656866ea 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +21,8 @@ YOSYS_NAMESPACE_BEGIN +const int lut_input_plane_limit = 12; + static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, std::istream &f) { string strbuf; @@ -65,17 +67,21 @@ static std::pair wideports_split(std::string name) for (int i = 0; i+1 < GetSize(name); i++) { if (name[i] == '[') pos = i; - else if (name[i] < '0' || name[i] > '9') + else if (name[i] != '-' && (name[i] < '0' || name[i] > '9')) + pos = -1; + else if (name[i] == '-' && ((i != pos+1) || name[i+1] == ']')) + pos = -1; + else if (i == pos+2 && name[i] == '0' && name[i-1] == '-') pos = -1; else if (i == pos+1 && name[i] == '0' && name[i+1] != ']') pos = -1; } if (pos >= 0) - return std::pair("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)+1); + return std::pair("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)); failed: - return std::pair("\\" + name, 0); + return std::pair(RTLIL::IdString(), 0); } void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool run_clean, bool sop_mode, bool wideports) @@ -162,7 +168,10 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool goto error; module = new RTLIL::Module; lastcell = nullptr; - module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n")); + char *name = strtok(NULL, " \t\r\n"); + if (name == nullptr) + goto error; + module->name = RTLIL::escape_id(name); obj_attributes = &module->attributes; obj_parameters = nullptr; if (design->module(module->name)) @@ -247,6 +256,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool continue; } + if (!strcmp(cmd, ".area") || !strcmp(cmd, ".delay") || !strcmp(cmd, ".wire_load_slope") || !strcmp(cmd, ".wire") || + !strcmp(cmd, ".input_arrival") || !strcmp(cmd, ".default_input_arrival") || !strcmp(cmd, ".output_required") || + !strcmp(cmd, ".default_output_required") || !strcmp(cmd, ".input_drive") || !strcmp(cmd, ".default_input_drive") || + !strcmp(cmd, ".max_input_load") || !strcmp(cmd, ".default_max_input_load") || !strcmp(cmd, ".output_load") || + !strcmp(cmd, ".default_output_load")) + { + log_warning("Blif delay constraints (%s) are not supported.", cmd); + continue; + } + if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) { char *p; @@ -263,8 +282,8 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool if (wideports) { std::pair wp = wideports_split(p); - if (wp.second > 0) { - wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second); + if (!wp.first.empty() && wp.second >= 0) { + wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second + 1); wideports_cache[wp.first].second = !strcmp(cmd, ".inputs"); } } @@ -375,6 +394,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool IdString celltype = RTLIL::escape_id(p); RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); + RTLIL::Module *cell_mod = design->module(celltype); dict> cell_wideports_cache; @@ -387,10 +407,10 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool if (wideports) { std::pair wp = wideports_split(p); - if (wp.second > 0) - cell_wideports_cache[wp.first][wp.second-1] = blif_wire(q); - else + if (wp.first.empty()) cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec()); + else + cell_wideports_cache[wp.first][wp.second] = blif_wire(q); } else { cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec()); } @@ -399,14 +419,26 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool for (auto &it : cell_wideports_cache) { int width = 0; + int offset = 0; + bool upto = false; for (auto &b : it.second) width = std::max(width, b.first + 1); + if (cell_mod) { + Wire *cell_port = cell_mod->wire(it.first); + if (cell_port && (cell_port->port_input || cell_port->port_output)) { + offset = cell_port->start_offset; + upto = cell_port->upto; + width = cell_port->width; + } + } + SigSpec sig; for (int i = 0; i < width; i++) { - if (it.second.count(i)) - sig.append(it.second.at(i)); + int idx = offset + (upto ? width - 1 - i: i); + if (it.second.count(idx)) + sig.append(it.second.at(idx)); else sig.append(module->addWire(NEW_ID)); } @@ -493,6 +525,11 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool sopmode = -1; lastcell = sopcell; } + else if (input_sig.size() > lut_input_plane_limit) + { + err_reason = stringf("names' input plane must have fewer than %d signals.", lut_input_plane_limit + 1); + goto error_with_reason; + } else { RTLIL::Cell *cell = module->addCell(NEW_ID, ID($lut)); @@ -556,7 +593,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool if (lutptr) { - if (input_len > 12) + if (input_len > lut_input_plane_limit) goto error; for (int i = 0; i < (1 << input_len); i++) { diff --git a/frontends/blif/blifparse.h b/frontends/blif/blifparse.h index 2b84cb795f0..d7a3c96b1fc 100644 --- a/frontends/blif/blifparse.h +++ b/frontends/blif/blifparse.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 1b34aaf3af7..1aab810153d 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -60,10 +60,38 @@ struct JsonNode break; if (ch == '\\') { - int ch = f.get(); + ch = f.get(); - if (ch == EOF) - log_error("Unexpected EOF in JSON string.\n"); + switch (ch) { + case EOF: log_error("Unexpected EOF in JSON string.\n"); break; + case '"': + case '/': + case '\\': break; + case 'b': ch = '\b'; break; + case 'f': ch = '\f'; break; + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'u': + int val = 0; + for (int i = 0; i < 4; i++) { + ch = f.get(); + val <<= 4; + if (ch >= '0' && '9' >= ch) { + val += ch - '0'; + } else if (ch >= 'A' && 'F' >= ch) { + val += 10 + ch - 'A'; + } else if (ch >= 'a' && 'f' >= ch) { + val += 10 + ch - 'a'; + } else + log_error("Unexpected non-digit character in \\uXXXX sequence: %c.\n", ch); + } + if (val < 128) + ch = val; + else + log_error("Unsupported \\uXXXX sequence in JSON string: %04X.\n", val); + break; + } } data_string += ch; @@ -72,10 +100,17 @@ struct JsonNode break; } - if ('0' <= ch && ch <= '9') + if (('0' <= ch && ch <= '9') || ch == '-') { + bool negative = false; type = 'N'; - data_number = ch - '0'; + if (ch == '-') { + data_number = 0; + negative = true; + } else { + data_number = ch - '0'; + } + data_string += ch; while (1) @@ -97,6 +132,7 @@ struct JsonNode data_string += ch; } + data_number = negative ? -data_number : data_number; data_string = ""; break; @@ -531,6 +567,56 @@ void json_import(Design *design, string &modname, JsonNode *node) json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters")); } } + + if (node->data_dict.count("memories")) + { + JsonNode *memories_node = node->data_dict.at("memories"); + + if (memories_node->type != 'D') + log_error("JSON memories node is not a dictionary.\n"); + + for (auto &memory_node_it : memories_node->data_dict) + { + IdString memory_name = RTLIL::escape_id(memory_node_it.first.c_str()); + JsonNode *memory_node = memory_node_it.second; + + RTLIL::Memory *mem = new RTLIL::Memory; + mem->name = memory_name; + + if (memory_node->type != 'D') + log_error("JSON memory node '%s' is not a dictionary.\n", log_id(memory_name)); + + if (memory_node->data_dict.count("width") == 0) + log_error("JSON memory node '%s' has no width attribute.\n", log_id(memory_name)); + JsonNode *width_node = memory_node->data_dict.at("width"); + if (width_node->type != 'N') + log_error("JSON memory node '%s' has a non-number width.\n", log_id(memory_name)); + mem->width = width_node->data_number; + + if (memory_node->data_dict.count("size") == 0) + log_error("JSON memory node '%s' has no size attribute.\n", log_id(memory_name)); + JsonNode *size_node = memory_node->data_dict.at("size"); + if (size_node->type != 'N') + log_error("JSON memory node '%s' has a non-number size.\n", log_id(memory_name)); + mem->size = size_node->data_number; + + mem->start_offset = 0; + if (memory_node->data_dict.count("start_offset") != 0) { + JsonNode *val = memory_node->data_dict.at("start_offset"); + if (val->type == 'N') + mem->start_offset = val->data_number; + } + + if (memory_node->data_dict.count("attributes")) + json_parse_attr_param(mem->attributes, memory_node->data_dict.at("attributes")); + + module->memories[mem->name] = mem; + } + } + + // remove duplicates from connections array + pool unique_connections(module->connections_.begin(), module->connections_.end()); + module->connections_ = std::vector(unique_connections.begin(), unique_connections.end()); } struct JsonFrontend : public Frontend { diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index f77d7da5601..cadbcaee69f 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -464,6 +464,9 @@ struct LibertyFrontend : public Frontend { log(" -lib\n"); log(" only create empty blackbox modules\n"); log("\n"); + log(" -wb\n"); + log(" mark imported cells as whiteboxes\n"); + log("\n"); log(" -nooverwrite\n"); log(" ignore re-definitions of modules. (the default behavior is to\n"); log(" create an error message if the existing module is not a blackbox\n"); @@ -489,6 +492,7 @@ struct LibertyFrontend : public Frontend { void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool flag_lib = false; + bool flag_wb = false; bool flag_nooverwrite = false; bool flag_overwrite = false; bool flag_ignore_miss_func = false; @@ -496,8 +500,6 @@ struct LibertyFrontend : public Frontend { bool flag_ignore_miss_data_latch = false; std::vector attributes; - log_header(design, "Executing Liberty frontend.\n"); - size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; @@ -505,6 +507,10 @@ struct LibertyFrontend : public Frontend { flag_lib = true; continue; } + if (arg == "-wb") { + flag_wb = true; + continue; + } if (arg == "-ignore_redef" || arg == "-nooverwrite") { flag_nooverwrite = true; flag_overwrite = false; @@ -535,6 +541,11 @@ struct LibertyFrontend : public Frontend { } extra_args(f, filename, args, argidx); + if (flag_wb && flag_lib) + log_error("-wb and -lib cannot be specified together!\n"); + + log_header(design, "Executing Liberty frontend: %s\n", filename.c_str()); + LibertyParser parser(*f); int cell_count = 0; @@ -572,6 +583,9 @@ struct LibertyFrontend : public Frontend { if (flag_lib) module->set_bool_attribute(ID::blackbox); + if (flag_wb) + module->set_bool_attribute(ID::whitebox); + for (auto &attr : attributes) module->attributes[attr] = 1; diff --git a/frontends/rpc/rpc_frontend.cc b/frontends/rpc/rpc_frontend.cc index c12640ef055..ec3952661ef 100644 --- a/frontends/rpc/rpc_frontend.cc +++ b/frontends/rpc/rpc_frontend.cc @@ -383,12 +383,12 @@ struct RpcFrontend : public Pass { log(" request for the module to be derived for a specific set of\n"); log(" parameters. starts with \\ for named parameters, and with $\n"); log(" for unnamed parameters, which are numbered starting at 1.\n"); - log(" for integer parameters is always specified as a binary string of unlimited\n"); - log(" precision. the returned by the frontend is hygienically parsed\n"); - log(" by a built-in Yosys , allowing the RPC frontend to return any\n"); - log(" convenient representation of the module. the derived module is cached,\n"); - log(" so the response should be the same whenever the same set of parameters\n"); - log(" is provided.\n"); + log(" for integer parameters is always specified as a binary string of\n"); + log(" unlimited precision. the returned by the frontend is\n"); + log(" hygienically parsedby a built-in Yosys , allowing the RPC\n"); + log(" frontend to return anyconvenient representation of the module. the\n"); + log(" derived module is cached,so the response should be the same whenever the\n"); + log(" same set of parameters is provided.\n"); } void execute(std::vector args, RTLIL::Design *design) override { diff --git a/frontends/rtlil/rtlil_frontend.cc b/frontends/rtlil/rtlil_frontend.cc index 00c34175e12..5f85ca2b88b 100644 --- a/frontends/rtlil/rtlil_frontend.cc +++ b/frontends/rtlil/rtlil_frontend.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/frontends/rtlil/rtlil_frontend.h b/frontends/rtlil/rtlil_frontend.h index a420778b042..18926060545 100644 --- a/frontends/rtlil/rtlil_frontend.h +++ b/frontends/rtlil/rtlil_frontend.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/frontends/rtlil/rtlil_lexer.l b/frontends/rtlil/rtlil_lexer.l index 295455f53e6..e164132167a 100644 --- a/frontends/rtlil/rtlil_lexer.l +++ b/frontends/rtlil/rtlil_lexer.l @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -79,6 +79,7 @@ USING_YOSYS_NAMESPACE "global" { return TOK_GLOBAL; } "init" { return TOK_INIT; } "update" { return TOK_UPDATE; } +"memwr" { return TOK_MEMWR; } "process" { return TOK_PROCESS; } "end" { return TOK_END; } @@ -86,7 +87,6 @@ USING_YOSYS_NAMESPACE "\\"[^ \t\r\n]+ { rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; } "$"[^ \t\r\n]+ { rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; } -"."[0-9]+ { rtlil_frontend_yylval.string = strdup(yytext); return TOK_ID; } [0-9]+'[01xzm-]* { rtlil_frontend_yylval.string = strdup(yytext); return TOK_VALUE; } -?[0-9]+ { diff --git a/frontends/rtlil/rtlil_parser.y b/frontends/rtlil/rtlil_parser.y index 64648919623..7d99b2c4220 100644 --- a/frontends/rtlil/rtlil_parser.y +++ b/frontends/rtlil/rtlil_parser.y @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,6 +22,8 @@ * */ +%require "3.0" + %{ #include #include "frontends/rtlil/rtlil_frontend.h" @@ -69,7 +71,7 @@ USING_YOSYS_NAMESPACE %token TOK_AUTOIDX TOK_MODULE TOK_WIRE TOK_WIDTH TOK_INPUT TOK_OUTPUT TOK_INOUT %token TOK_CELL TOK_CONNECT TOK_SWITCH TOK_CASE TOK_ASSIGN TOK_SYNC %token TOK_LOW TOK_HIGH TOK_POSEDGE TOK_NEGEDGE TOK_EDGE TOK_ALWAYS TOK_GLOBAL TOK_INIT -%token TOK_UPDATE TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET +%token TOK_UPDATE TOK_MEMWR TOK_PROCESS TOK_END TOK_INVALID TOK_EOL TOK_OFFSET %token TOK_PARAMETER TOK_ATTRIBUTE TOK_MEMORY TOK_SIZE TOK_SIGNED TOK_REAL TOK_UPTO %type sigspec_list_reversed @@ -155,6 +157,7 @@ param_defval_stmt: TOK_PARAMETER TOK_ID constant EOL { current_module->avail_parameters($2); current_module->parameter_default_values[$2] = *$3; + delete $3; free($2); }; @@ -282,10 +285,8 @@ proc_stmt: TOK_PROCESS TOK_ID EOL { if (current_module->processes.count($2) != 0) rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of process %s.", $2).c_str()); - current_process = new RTLIL::Process; - current_process->name = $2; + current_process = current_module->addProcess($2); current_process->attributes = attrbuf; - current_module->processes[$2] = current_process; switch_stack.clear(); switch_stack.push_back(¤t_process->root_case.switches); case_stack.clear(); @@ -389,6 +390,22 @@ update_list: delete $3; delete $4; } | + update_list attr_list TOK_MEMWR TOK_ID sigspec sigspec sigspec constant EOL { + RTLIL::MemWriteAction act; + act.attributes = attrbuf; + act.memid = $4; + act.address = *$5; + act.data = *$6; + act.enable = *$7; + act.priority_mask = *$8; + current_process->syncs.back()->mem_write_actions.push_back(std::move(act)); + attrbuf.clear(); + free($4); + delete $5; + delete $6; + delete $7; + delete $8; + } | /* empty */; constant: diff --git a/frontends/verific/Makefile.inc b/frontends/verific/Makefile.inc index 972f4f9f12a..c82428613af 100644 --- a/frontends/verific/Makefile.inc +++ b/frontends/verific/Makefile.inc @@ -10,9 +10,11 @@ EXTRA_TARGETS += share/verific share/verific: $(P) rm -rf share/verific.new $(Q) mkdir -p share/verific.new +ifneq ($(DISABLE_VERIFIC_VHDL),1) $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1987/. share/verific.new/vhdl_vdbs_1987 $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_1993/. share/verific.new/vhdl_vdbs_1993 $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2008/. share/verific.new/vhdl_vdbs_2008 +endif $(Q) chmod -R a+rX share/verific.new $(Q) mv share/verific.new share/verific diff --git a/frontends/verific/README b/frontends/verific/README index c37d7634346..921873af315 100644 --- a/frontends/verific/README +++ b/frontends/verific/README @@ -1,11 +1,11 @@ This directory contains Verific bindings for Yosys. -Use Symbiotic EDA Suite if you need Yosys+Verifc. -https://www.symbioticeda.com/seda-suite +Use Tabby CAD Suite from YosysHQ if you need Yosys+Verific. +https://www.yosyshq.com/ -Contact office@symbioticeda.com for free evaluation -binaries of Symbiotic EDA Suite. +Contact YosysHQ at contact@yosyshq.com for free evaluation +binaries of Tabby CAD Suite. Verific Features that should be enabled in your Verific library diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 31c77d39c4d..faa0e1bcdd3 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -43,20 +43,40 @@ USING_YOSYS_NAMESPACE #endif #include "veri_file.h" -#include "vhdl_file.h" #include "hier_tree.h" #include "VeriModule.h" #include "VeriWrite.h" -#include "VhdlUnits.h" #include "VeriLibrary.h" -#include "VeriExtensions.h" +#include "VeriExpression.h" + +#ifdef VERIFIC_VHDL_SUPPORT +#include "vhdl_file.h" +#include "VhdlUnits.h" +#include "NameSpace.h" +#endif + +#ifdef VERIFIC_EDIF_SUPPORT +#include "edif_file.h" +#endif -#ifndef SYMBIOTIC_VERIFIC_API_VERSION -# error "Only Symbiotic EDA flavored Verific is supported. Please contact office@symbioticeda.com for commercial support for Yosys+Verific." +#ifdef VERIFIC_LIBERTY_SUPPORT +#include "synlib_file.h" +#include "SynlibGroup.h" #endif -#if SYMBIOTIC_VERIFIC_API_VERSION < 20201001 -# error "Please update your version of Symbiotic EDA flavored Verific." +#include "VerificStream.h" +#include "FileSystem.h" + +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS +#include "VerificExtensions.h" +#endif + +#ifndef YOSYSHQ_VERIFIC_API_VERSION +# error "Only YosysHQ flavored Verific is supported. Please contact office@yosyshq.com for commercial support for Yosys+Verific." +#endif + +#if YOSYSHQ_VERIFIC_API_VERSION < 20230901 +# error "Please update your version of YosysHQ flavored Verific." #endif #ifdef __clang__ @@ -77,7 +97,7 @@ bool verific_import_pending; string verific_error_msg; int verific_sva_fsm_limit; -vector verific_incdirs, verific_libdirs; +vector verific_incdirs, verific_libdirs, verific_libexts; void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefile, const char *msg, va_list args) { @@ -93,15 +113,28 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : ""; message += vstringf(msg, args); - if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR) - log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str()); - else - log("%s%s\n", message_prefix.c_str(), message.c_str()); - + if (log_verific_callback) { + string full_message = stringf("%s%s\n", message_prefix.c_str(), message.c_str()); + log_verific_callback(int(msg_type), message_id, LineFile::GetFileName(linefile), + linefile ? linefile->GetLeftLine() : 0, linefile ? linefile->GetLeftCol() : 0, + linefile ? linefile->GetRightLine() : 0, linefile ? linefile->GetRightCol() : 0, full_message.c_str()); + } else { + if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR) + log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str()); + else + log("%s%s\n", message_prefix.c_str(), message.c_str()); + } if (verific_error_msg.empty() && (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_PROGRAM_ERROR)) verific_error_msg = message; } +void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int left_line, unsigned int left_col, unsigned int right_line, unsigned int right_col, const char *msg)) +{ + Message::SetConsoleOutput(0); + Message::RegisterCallBackMsg(msg_func); + log_verific_callback = cb; +} + string get_full_netlist_name(Netlist *nl) { if (nl->NumOfRefs() == 1) { @@ -112,6 +145,36 @@ string get_full_netlist_name(Netlist *nl) return nl->CellBaseName(); } +class YosysStreamCallBackHandler : public VerificStreamCallBackHandler +{ +public: + YosysStreamCallBackHandler() : VerificStreamCallBackHandler() { } + virtual ~YosysStreamCallBackHandler() { } + + virtual verific_stream *GetSysCallStream(const char *file_path) + { + if (!file_path) return nullptr; + + linefile_type src_loc = GetFromLocation(); + + char *this_file_name = nullptr; + if (src_loc && !FileSystem::IsAbsolutePath(file_path)) { + const char *src_file_name = LineFile::GetFileName(src_loc); + char *dir_name = FileSystem::DirectoryPath(src_file_name); + if (dir_name) { + this_file_name = Strings::save(dir_name, "/", file_path); + Strings::free(dir_name); + file_path = this_file_name; + } + } + verific_stream *strm = new verific_ifstream(file_path); + Strings::free(this_file_name); + return strm; + } +}; + +YosysStreamCallBackHandler verific_read_cb; + // ================================================================== VerificImporter::VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover, bool mode_fullinit) : @@ -146,24 +209,74 @@ RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj) { std::string s = stringf("$verific$%s", obj->Name()); if (obj->Linefile()) - s += stringf("$%s:%d", Verific::LineFile::GetFileName(obj->Linefile()), Verific::LineFile::GetLineNo(obj->Linefile())); + s += stringf("$%s:%d", RTLIL::encode_filename(Verific::LineFile::GetFileName(obj->Linefile())).c_str(), Verific::LineFile::GetLineNo(obj->Linefile())); s += stringf("$%d", autoidx++); return s; } +// When used as attributes or parameter values Verific constants come already processed. +// - Real string values are already under quotes +// - Numeric values with specified width are always converted to binary +// - Rest of user defined values are handled as 32bit integers +// - There could be some internal values that are strings without quotes +// so we check if value is all digits or not +// +// Note: For signed values, verific uses 'sb and decimal values can +// also be negative. +static const RTLIL::Const verific_const(const char *value, bool allow_string = true, bool output_signed = false) +{ + size_t found; + char *end; + int decimal; + bool is_signed = false; + RTLIL::Const c; + std::string val = std::string(value); + if (allow_string && val.size()>1 && val[0]=='\"' && val.back()=='\"') { + c = RTLIL::Const(val.substr(1,val.size()-2)); + } else if ((found = val.find("'sb")) != std::string::npos) { + is_signed = output_signed; + c = RTLIL::Const::from_string(val.substr(found + 3)); + } else if ((found = val.find("'b")) != std::string::npos) { + c = RTLIL::Const::from_string(val.substr(found + 2)); + } else if ((value[0] == '-' || (value[0] >= '0' && value[0] <= '9')) && + ((decimal = std::strtol(value, &end, 10)), !end[0])) { + is_signed = output_signed; + c = RTLIL::Const((int)decimal, 32); + } else if (allow_string) { + c = RTLIL::Const(val); + } else { + log_error("expected numeric constant but found '%s'", value); + } + + if (is_signed) + c.flags |= RTLIL::CONST_FLAG_SIGNED; + + return c; +} + +static const std::string verific_unescape(const char *value) +{ + std::string val = std::string(value); + if (val.size()>1 && val[0]=='\"' && val.back()=='\"') + return val.substr(1,val.size()-2); + return value; +} + void VerificImporter::import_attributes(dict &attributes, DesignObj *obj, Netlist *nl) { + if (!obj) + return; + MapIter mi; Att *attr; if (obj->Linefile()) - attributes[ID::src] = stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile())); + attributes[ID::src] = stringf("%s:%d.%d-%d.%d", LineFile::GetFileName(obj->Linefile()), obj->Linefile()->GetLeftLine(), obj->Linefile()->GetLeftCol(), obj->Linefile()->GetRightLine(), obj->Linefile()->GetRightCol()); - // FIXME: Parse numeric attributes FOREACH_ATTRIBUTE(obj, mi, attr) { if (attr->Key()[0] == ' ' || attr->Value() == nullptr) continue; - attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(std::string(attr->Value())); + attributes[RTLIL::escape_id(attr->Key())] = verific_const(attr->Value()); } if (nl) { @@ -172,8 +285,10 @@ void VerificImporter::import_attributes(dict &att return; if (!type_range->IsTypeEnum()) return; +#ifdef VERIFIC_VHDL_SUPPORT if (nl->IsFromVhdl() && strcmp(type_range->GetTypeName(), "STD_LOGIC") == 0) return; +#endif auto type_name = type_range->GetTypeName(); if (!type_name) return; @@ -183,22 +298,11 @@ void VerificImporter::import_attributes(dict &att const char *k, *v; FOREACH_MAP_ITEM(type_range->GetEnumIdMap(), mi, &k, &v) { if (nl->IsFromVerilog()) { - // Expect 'b - auto p = strchr(v, '\''); - if (p) { - if (*(p+1) != 'b') - p = nullptr; - else - for (auto q = p+2; *q != '\0'; q++) - if (*q != '0' && *q != '1') { - p = nullptr; - break; - } - } - if (p == nullptr) - log_error("Expected TypeRange value '%s' to be of form 'b.\n", v); - attributes.emplace(stringf("\\enum_value_%s", p+2), RTLIL::escape_id(k)); + auto const value = verific_const(v, false); + + attributes.emplace(stringf("\\enum_value_%s", value.as_string().c_str()), RTLIL::escape_id(k)); } +#ifdef VERIFIC_VHDL_SUPPORT else if (nl->IsFromVhdl()) { // Expect "" or plain auto p = v; @@ -234,40 +338,51 @@ void VerificImporter::import_attributes(dict &att if (p == nullptr) log_error("Expected TypeRange value '%s' to be of form \"\" or .\n", v); } +#endif } } } +RTLIL::SigBit VerificImporter::netToSigBit(Verific::Net *net) { + if (net && net->IsGnd()) + return RTLIL::State::S0; + else if (net && net->IsPwr()) + return RTLIL::State::S1; + else if (net && net->IsX()) + return RTLIL::State::Sx; + else if (net) + return net_map_at(net); + else + return RTLIL::State::Sz; +} + RTLIL::SigSpec VerificImporter::operatorInput(Instance *inst) { RTLIL::SigSpec sig; - for (int i = int(inst->InputSize())-1; i >= 0; i--) - if (inst->GetInputBit(i)) - sig.append(net_map_at(inst->GetInputBit(i))); - else - sig.append(RTLIL::State::Sz); + for (int i = int(inst->InputSize())-1; i >= 0; i--) { + Net *net = inst->GetInputBit(i); + sig.append(netToSigBit(net)); + } return sig; } RTLIL::SigSpec VerificImporter::operatorInput1(Instance *inst) { RTLIL::SigSpec sig; - for (int i = int(inst->Input1Size())-1; i >= 0; i--) - if (inst->GetInput1Bit(i)) - sig.append(net_map_at(inst->GetInput1Bit(i))); - else - sig.append(RTLIL::State::Sz); + for (int i = int(inst->Input1Size())-1; i >= 0; i--) { + Net *net = inst->GetInput1Bit(i); + sig.append(netToSigBit(net)); + } return sig; } RTLIL::SigSpec VerificImporter::operatorInput2(Instance *inst) { RTLIL::SigSpec sig; - for (int i = int(inst->Input2Size())-1; i >= 0; i--) - if (inst->GetInput2Bit(i)) - sig.append(net_map_at(inst->GetInput2Bit(i))); - else - sig.append(RTLIL::State::Sz); + for (int i = int(inst->Input2Size())-1; i >= 0; i--) { + Net *net = inst->GetInput2Bit(i); + sig.append(netToSigBit(net)); + } return sig; } @@ -279,10 +394,16 @@ RTLIL::SigSpec VerificImporter::operatorInport(Instance *inst, const char *portn for (unsigned i = 0; i < portbus->Size(); i++) { Net *net = inst->GetNet(portbus->ElementAtIndex(i)); if (net) { - if (net->IsGnd()) - sig.append(RTLIL::State::S0); - else if (net->IsPwr()) - sig.append(RTLIL::State::S1); + if (net->IsConstant()) { + if (net->IsGnd()) + sig.append(RTLIL::State::S0); + else if (net->IsPwr()) + sig.append(RTLIL::State::S1); + else if (net->IsX()) + sig.append(RTLIL::State::Sx); + else + sig.append(RTLIL::State::Sz); + } else sig.append(net_map_at(net)); } else @@ -297,6 +418,36 @@ RTLIL::SigSpec VerificImporter::operatorInport(Instance *inst, const char *portn } } +RTLIL::SigSpec VerificImporter::operatorInportCase(Instance *inst, const char *portname) +{ + PortBus *portbus = inst->View()->GetPortBus(portname); + if (portbus) { + RTLIL::SigSpec sig; + for (unsigned i = 0; i < portbus->Size(); i++) { + Net *net = inst->GetNet(portbus->ElementAtIndex(i)); + if (net) { + if (net->IsConstant()) { + if (net->IsGnd()) + sig.append(RTLIL::State::S0); + else if (net->IsPwr()) + sig.append(RTLIL::State::S1); + else + sig.append(RTLIL::State::Sa); + } + else + sig.append(net_map_at(net)); + } else + sig.append(RTLIL::State::Sa); + } + return sig; + } else { + Port *port = inst->View()->GetPort(portname); + log_assert(port != NULL); + Net *net = inst->GetNet(port); + return net_map_at(net); + } +} + RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool *any_all_nets) { RTLIL::SigSpec sig; @@ -368,7 +519,7 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr return true; } - if (inst->Type() == PRIM_TRI) { + if ((inst->Type() == PRIM_TRI) || (inst->Type() == PRIM_BUFIF1)) { module->addMuxGate(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); return true; } @@ -407,6 +558,42 @@ bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdStr return true; } + if (inst->Type() == PRIM_DLATCHRS) + { + if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) + module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + else + module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), + net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + return true; + } + + if (inst->Type() == PRIM_DFF) + { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + if (inst->GetAsyncCond()->IsGnd()) + clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + else + clocking.addAldff(inst_name, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()), + net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + return true; + } + + if (inst->Type() == PRIM_DLATCH) + { + if (inst->GetAsyncCond()->IsGnd()) { + module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + } else { + RTLIL::SigSpec sig_set = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal())); + RTLIL::SigSpec sig_clr = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), module->Not(NEW_ID, net_map_at(inst->GetAsyncVal()))); + module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_clr, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + } + return true; + } + return false; } @@ -468,7 +655,7 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr return true; } - if (inst->Type() == PRIM_TRI) { + if ((inst->Type() == PRIM_TRI) || (inst->Type() == PRIM_BUFIF1)) { cell = module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; @@ -517,6 +704,34 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr return true; } + if (inst->Type() == PRIM_DFF) + { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + if (inst->GetAsyncCond()->IsGnd()) + cell = clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + else + cell = clocking.addAldff(inst_name, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal()), + net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); + return true; + } + + if (inst->Type() == PRIM_DLATCH) + { + if (inst->GetAsyncCond()->IsGnd()) { + cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + } else { + RTLIL::SigSpec sig_set = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), net_map_at(inst->GetAsyncVal())); + RTLIL::SigSpec sig_clr = module->And(NEW_ID, net_map_at(inst->GetAsyncCond()), module->Not(NEW_ID, net_map_at(inst->GetAsyncVal()))); + cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_clr, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + } + import_attributes(cell->attributes, inst); + return true; + } + #define IN operatorInput(inst) #define IN1 operatorInput1(inst) #define IN2 operatorInput2(inst) @@ -724,28 +939,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr } if (inst->Type() == OPER_NTO1MUX) { - cell = module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput())); + cell = module->addBmux(inst_name, IN2, IN1, net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_NTO1MUX) { - SigSpec data = IN2, out = OUT; - - int wordsize_bits = ceil_log2(GetSize(out)); - int wordsize = 1 << wordsize_bits; - - SigSpec sel = {IN1, SigSpec(State::S0, wordsize_bits)}; - - SigSpec padded_data; - for (int i = 0; i < GetSize(data); i += GetSize(out)) { - SigSpec d = data.extract(i, GetSize(out)); - d.extend_u0(wordsize); - padded_data.append(d); - } - - cell = module->addShr(inst_name, padded_data, sel, out); + cell = module->addBmux(inst_name, IN2, IN1, OUT); import_attributes(cell->attributes, inst); return true; } @@ -789,6 +990,180 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr return true; } + if (inst->Type() == OPER_WIDE_DLATCHRS) + { + RTLIL::SigSpec sig_set = operatorInport(inst, "set"); + RTLIL::SigSpec sig_reset = operatorInport(inst, "reset"); + + if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool()) + cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), IN, OUT); + else + cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), sig_set, sig_reset, IN, OUT); + import_attributes(cell->attributes, inst); + + return true; + } + + if (inst->Type() == OPER_WIDE_DFF) + { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + RTLIL::SigSpec sig_d = IN; + RTLIL::SigSpec sig_q = OUT; + RTLIL::SigSpec sig_adata = IN1; + RTLIL::SigSpec sig_acond = IN2; + + if (sig_acond.is_fully_const() && !sig_acond.as_bool()) { + cell = clocking.addDff(inst_name, sig_d, sig_q); + import_attributes(cell->attributes, inst); + } else { + int offset = 0, width = 0; + for (offset = 0; offset < GetSize(sig_acond); offset += width) { + for (width = 1; offset+width < GetSize(sig_acond); width++) + if (sig_acond[offset] != sig_acond[offset+width]) break; + cell = clocking.addAldff(module->uniquify(inst_name), sig_acond[offset], sig_adata.extract(offset, width), + sig_d.extract(offset, width), sig_q.extract(offset, width)); + import_attributes(cell->attributes, inst); + } + } + + return true; + } + + if (inst->Type() == OPER_WIDE_DLATCH) + { + RTLIL::SigSpec sig_d = IN; + RTLIL::SigSpec sig_q = OUT; + RTLIL::SigSpec sig_adata = IN1; + RTLIL::SigSpec sig_acond = IN2; + + if (sig_acond.is_fully_const() && !sig_acond.as_bool()) { + cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), sig_d, sig_q); + import_attributes(cell->attributes, inst); + } else { + int offset = 0, width = 0; + for (offset = 0; offset < GetSize(sig_acond); offset += width) { + for (width = 1; offset+width < GetSize(sig_acond); width++) + if (sig_acond[offset] != sig_acond[offset+width]) break; + RTLIL::SigSpec sig_set = module->Mux(NEW_ID, RTLIL::SigSpec(0, width), sig_adata.extract(offset, width), sig_acond[offset]); + RTLIL::SigSpec sig_clr = module->Mux(NEW_ID, RTLIL::SigSpec(0, width), module->Not(NEW_ID, sig_adata.extract(offset, width)), sig_acond[offset]); + cell = module->addDlatchsr(module->uniquify(inst_name), net_map_at(inst->GetControl()), sig_set, sig_clr, + sig_d.extract(offset, width), sig_q.extract(offset, width)); + import_attributes(cell->attributes, inst); + } + } + + return true; + } + + if (inst->Type() == OPER_WIDE_CASE_SELECT_BOX) + { + RTLIL::SigSpec sig_out_val = operatorInport(inst, "out_value"); + RTLIL::SigSpec sig_select = operatorInport(inst, "select"); + RTLIL::SigSpec sig_select_values = operatorInportCase(inst, "select_values"); + RTLIL::SigSpec sig_data_values = operatorInport(inst, "data_values"); + RTLIL::SigSpec sig_data_default = operatorInport(inst, "default_value"); + + RTLIL::Process *proc = module->addProcess(new_verific_id(inst)); + import_attributes(proc->attributes, inst); + + RTLIL::CaseRule *current_case = &proc->root_case; + current_case = &proc->root_case; + + RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; + sw->signal = sig_select; + current_case->switches.push_back(sw); + + unsigned select_width = inst->InputSize(); + unsigned data_width = inst->OutputSize(); + unsigned offset_data = 0; + unsigned offset_select = 0; + + OperWideCaseSelector* selector = (OperWideCaseSelector*) inst->View(); + + for (unsigned i = 0 ; i < selector->GetNumBranches() ; ++i) { + + SigSig action(sig_out_val, sig_data_values.extract(offset_data, data_width)); + offset_data += data_width; + + for (unsigned j = 0 ; j < selector->GetNumConditions(i) ; ++j) { + Array left_bound, right_bound ; + selector->GetCondition(i, j, &left_bound, &right_bound); + + SigSpec sel_left = sig_select_values.extract(offset_select, select_width); + offset_select += select_width; + + if (right_bound.Size()) { + SigSpec sel_right = sig_select_values.extract(offset_select, select_width); + offset_select += select_width; + + log_assert(sel_right.is_fully_const() && sel_right.is_fully_def()); + log_assert(sel_left.is_fully_const() && sel_right.is_fully_def()); + + int32_t left = sel_left.as_int(); + int32_t right = sel_right.as_int(); + int width = sel_left.size(); + + for (int32_t i = right; icompare.push_back(RTLIL::Const(i,width)); + cs->actions.push_back(action); + sw->cases.push_back(cs); + } + } + + RTLIL::CaseRule *cs = new RTLIL::CaseRule; + cs->compare.push_back(sel_left); + cs->actions.push_back(action); + sw->cases.push_back(cs); + } + } + RTLIL::CaseRule *cs_default = new RTLIL::CaseRule; + cs_default->actions.push_back(SigSig(sig_out_val, sig_data_default)); + sw->cases.push_back(cs_default); + + return true; + } + + if (inst->Type() == OPER_YOSYSHQ_SET_TAG) + { + RTLIL::SigSpec sig_expr = operatorInport(inst, "expr"); + RTLIL::SigSpec sig_set_mask = operatorInport(inst, "set_mask"); + RTLIL::SigSpec sig_clr_mask = operatorInport(inst, "clr_mask"); + RTLIL::SigSpec sig_o = operatorOutput(inst); + std::string tag = inst->GetAtt("tag") ? verific_unescape(inst->GetAttValue("tag")) : ""; + module->connect(sig_o, module->SetTag(new_verific_id(inst), tag, sig_expr, sig_set_mask, sig_clr_mask)); + return true; + } + if (inst->Type() == OPER_YOSYSHQ_GET_TAG) + { + std::string tag = inst->GetAtt("tag") ? verific_unescape(inst->GetAttValue("tag")) : ""; + module->connect(operatorOutput(inst),module->GetTag(new_verific_id(inst), tag, operatorInput(inst))); + return true; + } + if (inst->Type() == OPER_YOSYSHQ_OVERWRITE_TAG) + { + RTLIL::SigSpec sig_signal = operatorInport(inst, "signal"); + RTLIL::SigSpec sig_set_mask = operatorInport(inst, "set_mask"); + RTLIL::SigSpec sig_clr_mask = operatorInport(inst, "clr_mask"); + std::string tag = inst->GetAtt("tag") ? verific_unescape(inst->GetAttValue("tag")) : ""; + module->addOverwriteTag(new_verific_id(inst), tag, sig_signal, sig_set_mask, sig_clr_mask); + return true; + } + if (inst->Type() == OPER_YOSYSHQ_ORIGINAL_TAG) + { + std::string tag = inst->GetAtt("tag") ? verific_unescape(inst->GetAttValue("tag")) : ""; + module->connect(operatorOutput(inst),module->OriginalTag(new_verific_id(inst), tag, operatorInput(inst))); + return true; + } + if (inst->Type() == OPER_YOSYSHQ_FUTURE_FF) + { + module->connect(operatorOutput(inst),module->FutureFF(new_verific_id(inst), operatorInput(inst))); + return true; + } + #undef IN #undef IN1 #undef IN2 @@ -856,6 +1231,7 @@ void VerificImporter::merge_past_ffs(pool &candidates) for (auto cell : candidates) { + if (cell->type != ID($dff)) continue; SigBit clock = cell->getPort(ID::CLK); bool clock_pol = cell->getParam(ID::CLK_POLARITY).as_bool(); database[make_pair(clock, int(clock_pol))].insert(cell); @@ -880,15 +1256,15 @@ static std::string sha1_if_contain_spaces(std::string str) return str; } -void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set &nl_todo, bool norename) +void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::map &nl_todo, bool norename) { - std::string netlist_name = nl->GetAtt(" \\top") ? nl->CellBaseName() : nl->Owner()->Name(); + std::string netlist_name = nl->GetAtt(" \\top") || is_blackbox(nl) ? nl->CellBaseName() : nl->Owner()->Name(); std::string module_name = netlist_name; if (nl->IsOperator() || nl->IsPrimitive()) { module_name = "$verific$" + module_name; } else { - if (!norename && *nl->Name()) { + if (!norename && *nl->Name() && !is_blackbox(nl)) { module_name += "("; module_name += nl->Name(); module_name += ")"; @@ -914,15 +1290,37 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se } else { log("Importing module %s.\n", RTLIL::id2cstr(module->name)); } + import_attributes(module->attributes, nl, nl); + module->set_string_attribute(ID::hdlname, nl->CellBaseName()); +#ifdef VERIFIC_VHDL_SUPPORT + if (nl->IsFromVhdl()) { + NameSpace name_space(0); + char *architecture_name = name_space.ReName(nl->Name()) ; + module->set_string_attribute(ID(architecture), (architecture_name) ? architecture_name : nl->Name()); + } +#endif + const char *param_name ; + const char *param_value ; + MapIter mi; + FOREACH_PARAMETER_OF_NETLIST(nl, mi, param_name, param_value) { + module->avail_parameters(RTLIL::escape_id(param_name)); + module->parameter_default_values[RTLIL::escape_id(param_name)] = verific_const(param_value); + } SetIter si; - MapIter mi, mi2; + MapIter mi2; Port *port; PortBus *portbus; Net *net; NetBus *netbus; Instance *inst; PortRef *pr; + Att *attr; + + FOREACH_ATTRIBUTE(nl, mi, attr) { + if (!strcmp(attr->Key(), "noblackbox")) + module->set_bool_attribute(ID::blackbox, false); + } FOREACH_PORT_OF_NETLIST(nl, mi, port) { @@ -960,20 +1358,37 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(portbus->Name()), portbus->Size()); wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex()); + wire->upto = portbus->IsUp(); import_attributes(wire->attributes, portbus, nl); - - if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN) + SetIter si ; + Port *port ; + FOREACH_PORT_OF_PORTBUS(portbus, si, port) { + import_attributes(wire->attributes, port->GetNet(), nl); + break; + } + bool portbus_input = portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN; + if (portbus_input) wire->port_input = true; if (portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_OUT) wire->port_output = true; for (int i = portbus->LeftIndex();; i += portbus->IsUp() ? +1 : -1) { if (portbus->ElementAtIndex(i) && portbus->ElementAtIndex(i)->GetNet()) { + bool bit_input = portbus_input; + if (portbus->GetDir() == DIR_NONE) { + Port *p = portbus->ElementAtIndex(i); + bit_input = p->GetDir() == DIR_INOUT || p->GetDir() == DIR_IN; + if (bit_input) + wire->port_input = true; + if (p->GetDir() == DIR_INOUT || p->GetDir() == DIR_OUT) + wire->port_output = true; + } net = portbus->ElementAtIndex(i)->GetNet(); - RTLIL::SigBit bit(wire, i - wire->start_offset); + int bitidx = wire->upto ? (wire->width - 1 - (i - wire->start_offset)) : (i - wire->start_offset); + RTLIL::SigBit bit(wire, bitidx); if (net_map.count(net) == 0) net_map[net] = bit; - else if (wire->port_input) + else if (bit_input) module->connect(net_map_at(net), bit); else module->connect(bit, net_map_at(net)); @@ -998,9 +1413,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se memory->name = RTLIL::escape_id(net->Name()); log_assert(module->count_id(memory->name) == 0); module->memories[memory->name] = memory; + import_attributes(memory->attributes, net, nl); int number_of_bits = net->Size(); - number_of_bits = 1 << ceil_log2(number_of_bits); int bits_in_word = number_of_bits; FOREACH_PORTREF_OF_NET(net, si, pr) { if (pr->GetInst()->Type() == OPER_READ_PORT) { @@ -1135,12 +1550,14 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se RTLIL::Wire *wire = module->addWire(wire_name, netbus->Size()); wire->start_offset = min(netbus->LeftIndex(), netbus->RightIndex()); + wire->upto = netbus->IsUp(); MapIter mibus; FOREACH_NET_OF_NETBUS(netbus, mibus, net) { if (net) import_attributes(wire->attributes, net, nl); break; } + import_attributes(wire->attributes, netbus, nl); RTLIL::Const initval = Const(State::Sx, GetSize(wire)); bool initval_valid = false; @@ -1149,7 +1566,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se { if (netbus->ElementAtIndex(i)) { - int bitidx = i - wire->start_offset; + int bitidx = wire->upto ? (wire->width - 1 - (i - wire->start_offset)) : (i - wire->start_offset); net = netbus->ElementAtIndex(i); RTLIL::SigBit bit(wire, bitidx); @@ -1313,6 +1730,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se cell->parameters[ID::TRANSPARENT] = false; cell->parameters[ID::ABITS] = GetSize(addr); cell->parameters[ID::WIDTH] = GetSize(data); + import_attributes(cell->attributes, inst); cell->setPort(ID::CLK, RTLIL::State::Sx); cell->setPort(ID::EN, RTLIL::State::Sx); cell->setPort(ID::ADDR, addr); @@ -1342,6 +1760,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se cell->parameters[ID::PRIORITY] = 0; cell->parameters[ID::ABITS] = GetSize(addr); cell->parameters[ID::WIDTH] = GetSize(data); + import_attributes(cell->attributes, inst); cell->setPort(ID::EN, RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data))); cell->setPort(ID::CLK, RTLIL::State::S0); cell->setPort(ID::ADDR, addr); @@ -1385,23 +1804,45 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se log_assert(inst->Input1Size() == inst->OutputSize()); - SigSpec sig_d, sig_q, sig_o; - sig_q = module->addWire(new_verific_id(inst), inst->Input1Size()); + unsigned width = inst->Input1Size(); + + SigSpec sig_d, sig_dx, sig_qx, sig_o, sig_ox; + sig_dx = module->addWire(new_verific_id(inst), width * 2); + sig_qx = module->addWire(new_verific_id(inst), width * 2); + sig_ox = module->addWire(new_verific_id(inst), width * 2); - for (int i = int(inst->Input1Size())-1; i >= 0; i--){ + for (int i = int(width)-1; i >= 0; i--){ sig_d.append(net_map_at(inst->GetInput1Bit(i))); sig_o.append(net_map_at(inst->GetOutputBit(i))); } if (verific_verbose) { + for (unsigned i = 0; i < width; i++) { + log(" NEX with A=%s, B=0, Y=%s.\n", + log_signal(sig_d[i]), log_signal(sig_dx[i])); + log(" EQX with A=%s, B=1, Y=%s.\n", + log_signal(sig_d[i]), log_signal(sig_dx[i + width])); + } log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", - log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); + log_signal(sig_dx), log_signal(sig_qx), log_signal(clocking.clock_sig)); log(" XNOR with A=%s, B=%s, Y=%s.\n", - log_signal(sig_d), log_signal(sig_q), log_signal(sig_o)); + log_signal(sig_dx), log_signal(sig_qx), log_signal(sig_ox)); + log(" AND with A=%s, B=%s, Y=%s.\n", + log_signal(sig_ox.extract(0, width)), log_signal(sig_ox.extract(width, width)), log_signal(sig_o)); } - clocking.addDff(new_verific_id(inst), sig_d, sig_q); - module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o); + for (unsigned i = 0; i < width; i++) { + module->addNex(new_verific_id(inst), sig_d[i], State::S0, sig_dx[i]); + module->addEqx(new_verific_id(inst), sig_d[i], State::S1, sig_dx[i + width]); + } + + Const qx_init = Const(State::S1, width); + qx_init.bits.resize(2 * width, State::S0); + + clocking.addDff(new_verific_id(inst), sig_dx, sig_qx, qx_init); + module->addXnor(new_verific_id(inst), sig_dx, sig_qx, sig_ox); + + module->addAnd(new_verific_id(inst), sig_ox.extract(0, width), sig_ox.extract(width, width), sig_o); if (!mode_keep) continue; @@ -1415,17 +1856,25 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se SigSpec sig_d = net_map_at(inst->GetInput1()); SigSpec sig_o = net_map_at(inst->GetOutput()); - SigSpec sig_q = module->addWire(new_verific_id(inst)); + SigSpec sig_dx = module->addWire(new_verific_id(inst), 2); + SigSpec sig_qx = module->addWire(new_verific_id(inst), 2); if (verific_verbose) { + log(" NEX with A=%s, B=0, Y=%s.\n", + log_signal(sig_d), log_signal(sig_dx[0])); + log(" EQX with A=%s, B=1, Y=%s.\n", + log_signal(sig_d), log_signal(sig_dx[1])); log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", - log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); - log(" XNOR with A=%s, B=%s, Y=%s.\n", - log_signal(sig_d), log_signal(sig_q), log_signal(sig_o)); + log_signal(sig_dx), log_signal(sig_qx), log_signal(clocking.clock_sig)); + log(" EQ with A=%s, B=%s, Y=%s.\n", + log_signal(sig_dx), log_signal(sig_qx), log_signal(sig_o)); } - clocking.addDff(new_verific_id(inst), sig_d, sig_q); - module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o); + module->addNex(new_verific_id(inst), sig_d, State::S0, sig_dx[0]); + module->addEqx(new_verific_id(inst), sig_d, State::S1, sig_dx[1]); + + clocking.addDff(new_verific_id(inst), sig_dx, sig_qx, Const(1, 2)); + module->addEq(new_verific_id(inst), sig_dx, sig_qx, sig_o); if (!mode_keep) continue; @@ -1459,20 +1908,29 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se SigBit sig_d = net_map_at(inst->GetInput1()); SigBit sig_o = net_map_at(inst->GetOutput()); SigBit sig_q = module->addWire(new_verific_id(inst)); + SigBit sig_d_no_x = module->addWire(new_verific_id(inst)); - if (verific_verbose) + if (verific_verbose) { + log(" EQX with A=%s, B=%d, Y=%s.\n", + log_signal(sig_d), inst->Type() == PRIM_SVA_ROSE, log_signal(sig_d_no_x)); log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", - log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); + log_signal(sig_d_no_x), log_signal(sig_q), log_signal(clocking.clock_sig)); + log(" EQ with A={%s, %s}, B={0, 1}, Y=%s.\n", + log_signal(sig_q), log_signal(sig_d_no_x), log_signal(sig_o)); + } - clocking.addDff(new_verific_id(inst), sig_d, sig_q); - module->addEq(new_verific_id(inst), {sig_q, sig_d}, Const(inst->Type() == PRIM_SVA_ROSE ? 1 : 2, 2), sig_o); + module->addEqx(new_verific_id(inst), sig_d, inst->Type() == PRIM_SVA_ROSE ? State::S1 : State::S0, sig_d_no_x); + clocking.addDff(new_verific_id(inst), sig_d_no_x, sig_q, State::S0); + module->addEq(new_verific_id(inst), {sig_q, sig_d_no_x}, Const(1, 2), sig_o); if (!mode_keep) continue; } - if (inst->Type() == PRIM_SEDA_INITSTATE) + if (inst->Type() == PRIM_YOSYSHQ_INITSTATE) { + if (verific_verbose) + log(" adding YosysHQ init state\n"); SigBit initstate = module->Initstate(new_verific_id(inst)); SigBit sig_o = net_map_at(inst->GetOutput()); module->connect(sig_o, initstate); @@ -1516,14 +1974,14 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se } import_verific_cells: - nl_todo.insert(inst->View()); + std::string inst_type = is_blackbox(inst->View()) ? inst->View()->CellBaseName() : inst->View()->Owner()->Name(); - std::string inst_type = inst->View()->Owner()->Name(); + nl_todo[inst_type] = inst->View(); if (inst->View()->IsOperator() || inst->View()->IsPrimitive()) { inst_type = "$verific$" + inst_type; } else { - if (*inst->View()->Name()) { + if (*inst->View()->Name() && !is_blackbox(inst->View())) { inst_type += "("; inst_type += inst->View()->Name(); inst_type += ")"; @@ -1532,6 +1990,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se } RTLIL::Cell *cell = module->addCell(inst_name, inst_type); + import_attributes(cell->attributes, inst); if (inst->IsPrimitive() && mode_keep) cell->attributes[ID::keep] = 1; @@ -1541,6 +2000,14 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se if (verific_verbose) log(" ports in verific db:\n"); + const char *param_name ; + const char *param_value ; + if (is_blackbox(inst->View())) { + FOREACH_PARAMETER_OF_INST(inst, mi2, param_name, param_value) { + cell->setParam(RTLIL::escape_id(param_name), verific_const(param_value)); + } + } + FOREACH_PORTREF_OF_INST(inst, mi2, pr) { if (verific_verbose) log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name()); @@ -1630,7 +2097,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se initval[i] = State::Sx; } - if (initval.is_fully_undef()) + if (wire->port_input) { + wire->attributes[ID::defaultvalue] = Const(initval); + wire->attributes.erase(ID::init); + } else if (initval.is_fully_undef()) wire->attributes.erase(ID::init); } } @@ -1647,6 +2117,28 @@ VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_a Instance *inst = net->Driver(); + // Detect condition expression in sva_at_only mode + if (sva_at_only) + do { + Instance *inst_mux = net->Driver(); + if (inst_mux == nullptr || inst_mux->Type() != PRIM_MUX) + break; + + bool pwr1 = inst_mux->GetInput1()->IsPwr(); + bool pwr2 = inst_mux->GetInput2()->IsPwr(); + + if (!pwr1 && !pwr2) + break; + + Net *sva_net = pwr1 ? inst_mux->GetInput2() : inst_mux->GetInput1(); + if (!verific_is_sva_net(importer, sva_net)) + break; + + inst = sva_net->Driver(); + cond_net = inst_mux->GetControl(); + cond_pol = pwr1; + } while (0); + if (inst != nullptr && inst->Type() == PRIM_SVA_AT) { net = inst->GetInput1(); @@ -1729,15 +2221,19 @@ VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_a if (inst_mux == nullptr || inst_mux->Type() != PRIM_MUX) break; - if (!inst_mux->GetInput1()->IsPwr()) + bool pwr1 = inst_mux->GetInput1()->IsPwr(); + bool pwr2 = inst_mux->GetInput2()->IsPwr(); + + if (!pwr1 && !pwr2) break; - Net *sva_net = inst_mux->GetInput2(); + Net *sva_net = pwr1 ? inst_mux->GetInput2() : inst_mux->GetInput1(); if (!verific_is_sva_net(importer, sva_net)) break; body_net = sva_net; cond_net = inst_mux->GetControl(); + cond_pol = pwr1; } while (0); clock_net = net; @@ -1752,30 +2248,62 @@ Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const { log_assert(GetSize(sig_d) == GetSize(sig_q)); - if (GetSize(init_value) != 0) { - log_assert(GetSize(sig_q) == GetSize(init_value)); - if (sig_q.is_wire()) { - sig_q.as_wire()->attributes[ID::init] = init_value; + auto set_init_attribute = [&](SigSpec &s) { + if (GetSize(init_value) == 0) + return; + log_assert(GetSize(s) == GetSize(init_value)); + if (s.is_wire()) { + s.as_wire()->attributes[ID::init] = init_value; } else { - Wire *w = module->addWire(NEW_ID, GetSize(sig_q)); + Wire *w = module->addWire(NEW_ID, GetSize(s)); w->attributes[ID::init] = init_value; - module->connect(sig_q, w); - sig_q = w; + module->connect(s, w); + s = w; } - } + }; if (enable_sig != State::S1) sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); if (disable_sig != State::S0) { - log_assert(gclk == false); log_assert(GetSize(sig_q) == GetSize(init_value)); + + if (gclk) { + Wire *pre_d = module->addWire(NEW_ID, GetSize(sig_d)); + Wire *post_q_w = module->addWire(NEW_ID, GetSize(sig_q)); + + Const initval(State::Sx, GetSize(sig_q)); + int offset = 0; + for (auto c : sig_q.chunks()) { + if (c.wire && c.wire->attributes.count(ID::init)) { + Const val = c.wire->attributes.at(ID::init); + for (int i = 0; i < GetSize(c); i++) + initval[offset+i] = val[c.offset+i]; + } + offset += GetSize(c); + } + + if (!initval.is_fully_undef()) + post_q_w->attributes[ID::init] = initval; + + module->addMux(NEW_ID, sig_d, init_value, disable_sig, pre_d); + module->addMux(NEW_ID, post_q_w, init_value, disable_sig, sig_q); + + SigSpec post_q(post_q_w); + set_init_attribute(post_q); + return module->addFf(name, pre_d, post_q); + } + + set_init_attribute(sig_q); return module->addAdff(name, clock_sig, disable_sig, sig_d, sig_q, init_value, posedge); } - if (gclk) + if (gclk) { + set_init_attribute(sig_q); return module->addFf(name, sig_d, sig_q); + } + set_init_attribute(sig_q); return module->addDff(name, clock_sig, sig_d, sig_q, posedge); } @@ -1784,6 +2312,7 @@ Cell *VerificClocking::addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec s log_assert(gclk == false); log_assert(disable_sig == State::S0); + // FIXME: Adffe if (enable_sig != State::S1) sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); @@ -1795,12 +2324,48 @@ Cell *VerificClocking::addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::Si log_assert(gclk == false); log_assert(disable_sig == State::S0); + // FIXME: Dffsre if (enable_sig != State::S1) sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); return module->addDffsr(name, clock_sig, sig_set, sig_clr, sig_d, sig_q, posedge); } +Cell *VerificClocking::addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::SigSpec sig_adata, SigSpec sig_d, SigSpec sig_q) +{ + log_assert(disable_sig == State::S0); + + // FIXME: Aldffe + if (enable_sig != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); + + if (gclk) { + Wire *pre_d = module->addWire(NEW_ID, GetSize(sig_d)); + Wire *post_q = module->addWire(NEW_ID, GetSize(sig_q)); + + Const initval(State::Sx, GetSize(sig_q)); + int offset = 0; + for (auto c : sig_q.chunks()) { + if (c.wire && c.wire->attributes.count(ID::init)) { + Const val = c.wire->attributes.at(ID::init); + for (int i = 0; i < GetSize(c); i++) + initval[offset+i] = val[c.offset+i]; + } + offset += GetSize(c); + } + + if (!initval.is_fully_undef()) + post_q->attributes[ID::init] = initval; + + module->addMux(NEW_ID, sig_d, sig_adata, sig_aload, pre_d); + module->addMux(NEW_ID, post_q, sig_adata, sig_aload, sig_q); + + return module->addFf(name, pre_d, post_q); + } + + return module->addAldff(name, clock_sig, sig_aload, sig_d, sig_q, sig_adata, posedge); +} + // ================================================================== struct VerificExtNets @@ -1829,7 +2394,7 @@ struct VerificExtNets string name = stringf("___extnets_%d", portname_cnt++); Port *new_port = new Port(name.c_str(), drive_up ? DIR_OUT : DIR_IN); nl->Add(new_port); - net->Connect(new_port); + nl->Buf(net)->Connect(new_port); // create new Net in up Netlist Net *new_net = final_net; @@ -1941,64 +2506,105 @@ struct VerificExtNets } }; -void verific_import(Design *design, const std::map ¶meters, std::string top) +std::string verific_import(Design *design, const std::map ¶meters, std::string top) { verific_sva_fsm_limit = 16; - std::set nl_todo, nl_done; + std::map nl_todo, nl_done; - VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1); VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1); Array *netlists = NULL; Array veri_libs, vhdl_libs; +#ifdef VERIFIC_VHDL_SUPPORT + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1); if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); +#endif if (veri_lib) veri_libs.InsertLast(veri_lib); Map verific_params(STRING_HASH); for (const auto &i : parameters) verific_params.Insert(i.first.c_str(), i.second.c_str()); - InitialAssertionRewriter rw; - rw.RegisterCallBack(); - if (top.empty()) { + +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + VerificExtensions::ElaborateAndRewrite("work", &verific_params); + verific_error_msg.clear(); +#endif netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, &verific_params); } else { - Array veri_modules, vhdl_units; - if (veri_lib) { - VeriModule *veri_module = veri_lib->GetModule(top.c_str(), 1); - if (veri_module) { - veri_modules.InsertLast(veri_module); +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + for (int static_elaborate = 1; static_elaborate >= 0; static_elaborate--) +#endif + { + Array veri_modules, vhdl_units; + + if (veri_lib) { + VeriModule *veri_module = veri_lib->GetModule(top.c_str(), 1); + if (veri_module) { + veri_modules.InsertLast(veri_module); + if (veri_module->IsConfiguration()) { + VeriConfiguration *cfg = (VeriConfiguration*)veri_module; + VeriName *module_name = (VeriName*)cfg->GetTopModuleNames()->GetLast(); + VeriLibrary *lib = veri_module->GetLibrary() ; + if (module_name && module_name->IsHierName()) { + VeriName *prefix = module_name->GetPrefix() ; + const char *lib_name = (prefix) ? prefix->GetName() : 0 ; + if (!Strings::compare("work", lib_name)) lib = veri_file::GetLibrary(lib_name, 1) ; + } + if (lib && module_name) + top = lib->GetModule(module_name->GetName(), 1)->GetName(); + } + } + +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + if (!static_elaborate) +#endif + { + // Also elaborate all root modules since they may contain bind statements + MapIter mi; + FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { + if (!veri_module->IsRootModule()) continue; + veri_modules.InsertLast(veri_module); + } + } } - // Also elaborate all root modules since they may contain bind statements - MapIter mi; - FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { - if (!veri_module->IsRootModule()) continue; - veri_modules.InsertLast(veri_module); +#ifdef VERIFIC_VHDL_SUPPORT + if (vhdl_lib) { + VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str()); + if (vhdl_unit) + vhdl_units.InsertLast(vhdl_unit); } - } +#endif - if (vhdl_lib) { - VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(top.c_str()); - if (vhdl_unit) - vhdl_units.InsertLast(vhdl_unit); - } +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + if (static_elaborate) { + VerificExtensions::ElaborateAndRewrite("work", &veri_modules, &vhdl_units, &verific_params); + verific_error_msg.clear(); + continue; + } +#endif - netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params); + netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, &verific_params); + } } Netlist *nl; int i; + std::string cell_name = top; FOREACH_ARRAY_ITEM(netlists, i, nl) { - if (top.empty() && nl->CellBaseName() != top) + if (!nl) continue; + if (!top.empty() && nl->CellBaseName() != top) continue; nl->AddAtt(new Att(" \\top", NULL)); - nl_todo.insert(nl); + nl_todo.emplace(nl->CellBaseName(), nl); + cell_name = nl->CellBaseName(); } + if (top.empty()) cell_name = top; delete netlists; @@ -2006,31 +2612,49 @@ void verific_import(Design *design, const std::map &par log_error("%s\n", verific_error_msg.c_str()); for (auto nl : nl_todo) - nl->ChangePortBusStructures(1 /* hierarchical */); + nl.second->ChangePortBusStructures(1 /* hierarchical */); VerificExtNets worker; for (auto nl : nl_todo) - worker.run(nl); + worker.run(nl.second); while (!nl_todo.empty()) { - Netlist *nl = *nl_todo.begin(); - if (nl_done.count(nl) == 0) { + auto it = nl_todo.begin(); + Netlist *nl = it->second; + if (nl_done.count(it->first) == 0) { VerificImporter importer(false, false, false, false, false, false, false); - importer.import_netlist(design, nl, nl_todo, nl->Owner()->Name() == top); + nl_done[it->first] = it->second; + importer.import_netlist(design, nl, nl_todo, nl->CellBaseName() == cell_name); } - nl_todo.erase(nl); - nl_done.insert(nl); + nl_todo.erase(it); } +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + VerificExtensions::Reset(); +#endif + hier_tree::DeleteHierarchicalTree(); veri_file::Reset(); +#ifdef VERIFIC_VHDL_SUPPORT vhdl_file::Reset(); +#endif +#ifdef VERIFIC_EDIF_SUPPORT + edif_file::Reset(); +#endif +#ifdef VERIFIC_LIBERTY_SUPPORT + synlib_file::Reset(); +#endif Libset::Reset(); + Message::Reset(); + RuntimeFlags::DeleteAllFlags(); + LineFile::DeleteAllLineFiles(); verific_incdirs.clear(); verific_libdirs.clear(); + verific_libexts.clear(); verific_import_pending = false; if (!verific_error_msg.empty()) log_error("%s\n", verific_error_msg.c_str()); + return top; } YOSYS_NAMESPACE_END @@ -2066,7 +2690,7 @@ struct VerificPass : public Pass { log("\n"); log("Additional -D[=] options may be added after the option indicating\n"); log("the language version (and before file names) to set additional verilog defines.\n"); - log("The macros SYNTHESIS and VERIFIC are defined implicitly.\n"); + log("The macros YOSYS, SYNTHESIS, and VERIFIC are defined implicitly.\n"); log("\n"); log("\n"); log(" verific -formal ..\n"); @@ -2074,11 +2698,65 @@ struct VerificPass : public Pass { log("Like -sv, but define FORMAL instead of SYNTHESIS.\n"); log("\n"); log("\n"); +#ifdef VERIFIC_VHDL_SUPPORT log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} ..\n"); log("\n"); log("Load the specified VHDL files into Verific.\n"); log("\n"); log("\n"); +#endif +#ifdef VERIFIC_EDIF_SUPPORT + log(" verific {-edif} ..\n"); + log("\n"); + log("Load the specified EDIF files into Verific.\n"); + log("\n"); + log("\n"); +#endif +#ifdef VERIFIC_LIBERTY_SUPPORT + log(" verific {-liberty} ..\n"); + log("\n"); + log("Load the specified Liberty files into Verific.\n"); + log("Default library when -work is not present is one specified in liberty file.\n"); + log("To use from SystemVerilog or VHDL use -L to specify liberty library."); + log("\n"); + log(" -lib\n"); + log(" only create empty blackbox modules\n"); + log("\n"); + log("\n"); +#endif + log(" verific {-f|-F} [-vlog95|-vlog2k|-sv2005|-sv2009|\n"); + log(" -sv2012|-sv|-formal] \n"); + log("\n"); + log("Load and execute the specified command file.\n"); + log("Override verilog parsing mode can be set.\n"); + log("The macros YOSYS, SYNTHESIS/FORMAL, and VERIFIC are defined implicitly.\n"); + log("\n"); + log("Command file parser supports following commands in file:\n"); + log(" +define+= - defines macro\n"); + log(" -u - upper case all identifier (makes Verilog parser\n"); + log(" case insensitive)\n"); + log(" -v - register library name (file)\n"); + log(" -y - register library name (directory)\n"); + log(" +incdir+ - specify include dir\n"); + log(" +libext+ - specify library extension\n"); + log(" +liborder+ - add library in ordered list\n"); + log(" +librescan - unresolved modules will be always searched\n"); + log(" starting with the first library specified\n"); + log(" by -y/-v options.\n"); + log(" -f/-file - nested -f option\n"); + log(" -F - nested -F option (relative path)\n"); + log(" parse files:\n"); + log(" \n"); + log(" +systemverilogext+\n"); + log(" +verilog1995ext+\n"); + log(" +verilog2001ext+\n"); + log("\n"); + log(" analysis mode:\n"); + log(" -ams\n"); + log(" +v2k\n"); + log(" -sverilog\n"); + log("\n"); + log("\n"); log(" verific [-work ] {-sv|-vhdl|...} \n"); log("\n"); log("Load the specified Verilog/SystemVerilog/VHDL file into the specified library.\n"); @@ -2102,6 +2780,11 @@ struct VerificPass : public Pass { log("find undefined modules.\n"); log("\n"); log("\n"); + log(" verific -vlog-libext ..\n"); + log("\n"); + log("Add Verilog library extensions, used when searching in library directories.\n"); + log("\n"); + log("\n"); log(" verific -vlog-define [=]..\n"); log("\n"); log("Add Verilog defines.\n"); @@ -2119,12 +2802,14 @@ struct VerificPass : public Pass { log("\n"); log("Set message severity. is the string in square brackets when a message\n"); log("is printed, such as VERI-1209.\n"); + log("Also errors, warnings, infos and comments could be used to set new severity for\n"); + log("all messages of certain type.\n"); log("\n"); log("\n"); - log(" verific -import [options] ..\n"); + log(" verific -import [options] ..\n"); log("\n"); - log("Elaborate the design for the specified top modules, import to Yosys and\n"); - log("reset the internal state of Verific.\n"); + log("Elaborate the design for the specified top modules or configurations, import to\n"); + log("Yosys and reset the internal state of Verific.\n"); log("\n"); log("Import options:\n"); log("\n"); @@ -2141,12 +2826,19 @@ struct VerificPass : public Pass { log(" -extnets\n"); log(" Resolve references to external nets by adding module ports as needed.\n"); log("\n"); + log(" -no-split-complex-ports\n"); + log(" Complex ports (structs or arrays) are not split and remain packed as a single port.\n"); + log("\n"); log(" -autocover\n"); log(" Generate automatic cover statements for all asserts\n"); log("\n"); log(" -fullinit\n"); log(" Keep all register initializations, even those for non-FF registers.\n"); log("\n"); + log(" -cells\n"); + log(" Import all cell definitions from Verific loaded libraries even if they are\n"); + log(" unused in design. Useful with \"-edif\" and \"-liberty\" option.\n"); + log("\n"); log(" -chparam name value \n"); log(" Elaborate the specified top modules (all modules when -all given) using\n"); log(" this parameter value. Modules on which this parameter does not exist will\n"); @@ -2157,6 +2849,9 @@ struct VerificPass : public Pass { log(" -v, -vv\n"); log(" Verbose log messages. (-vv is even more verbose than -v.)\n"); log("\n"); + log(" -pp \n"); + log(" Pretty print design after elaboration to specified file.\n"); + log("\n"); log("The following additional import options are useful for debugging the Verific\n"); log("bindings (for Yosys and/or Verific developers):\n"); log("\n"); @@ -2197,67 +2892,142 @@ struct VerificPass : public Pass { log(" Save output for VHDL design units.\n"); log("\n"); log("\n"); - log(" verific -app ..\n"); - log("\n"); - log("Execute SEDA formal application on loaded Verilog files.\n"); + log(" verific -cfg [ []]\n"); log("\n"); - log("Application options:\n"); + log("Get/set Verific runtime flags.\n"); log("\n"); - log(" -module \n"); - log(" Run formal application only on specified module.\n"); log("\n"); - log(" -blacklist \n"); - log(" Do not run application on modules from files that match the filename\n"); - log(" or filename and line number if provided in such format.\n"); - log(" Parameter can also contain comma separated list of file locations.\n"); +#if defined(YOSYS_ENABLE_VERIFIC) and defined(YOSYSHQ_VERIFIC_EXTENSIONS) + VerificExtensions::Help(); +#endif + log("Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n"); + log("https://www.yosyshq.com/\n"); log("\n"); - log(" -blfile \n"); - log(" Do not run application on locations specified in file, they can represent filename\n"); - log(" or filename and location in file.\n"); - log("\n"); - log("Applications:\n"); + log("Contact office@yosyshq.com for free evaluation\n"); + log("binaries of YosysHQ Tabby CAD Suite.\n"); log("\n"); + } #ifdef YOSYS_ENABLE_VERIFIC - VerificFormalApplications vfa; - log("%s\n",vfa.GetHelp().c_str()); -#else - log(" WARNING: Applications only available in commercial build.\n"); + std::string frontent_rewrite(std::vector &args, int &argidx, std::vector &tmp_files) + { + std::string filename = args[argidx++]; + //Accommodate heredocs with EOT marker spaced out from "<<", e.g. "<< EOT" vs. "< 0 && (buffer[buffer.size() - 1] == '\n' || buffer[buffer.size() - 1] == '\r')) + break; + } + size_t indent = buffer.find_first_not_of(" \t\r\n"); + if (indent != std::string::npos && buffer.compare(indent, eot_marker.size(), eot_marker) == 0) + break; + last_here_document += buffer; + } + filename = make_temp_file(); + tmp_files.push_back(filename); + std::ofstream file(filename); + file << last_here_document; + } else { + rewrite_filename(filename); + } + return filename; + } +#ifdef VERIFIC_VHDL_SUPPORT + msg_type_t prev_1240 ; + msg_type_t prev_1241 ; + + void add_units_to_map(Map &map, std::string work, bool flag_lib) + { + MapIter mi ; + VhdlPrimaryUnit *unit ; + if (!flag_lib) return; + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); + if (vhdl_lib) { + FOREACH_VHDL_PRIMARY_UNIT(vhdl_lib, mi, unit) { + if (!unit) continue; + map.Insert(unit,unit); + } + } + + prev_1240 = Message::GetMessageType("VHDL-1240") ; + prev_1241 = Message::GetMessageType("VHDL-1241") ; + Message::SetMessageType("VHDL-1240", VERIFIC_INFO); + Message::SetMessageType("VHDL-1241", VERIFIC_INFO); + } + + void set_units_to_blackbox(Map &map, std::string work, bool flag_lib) + { + MapIter mi ; + VhdlPrimaryUnit *unit ; + if (!flag_lib) return; + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); + FOREACH_VHDL_PRIMARY_UNIT(vhdl_lib, mi, unit) { + if (!unit) continue; + if (!map.GetValue(unit)) { + unit->SetCompileAsBlackbox(); + } + } + Message::ClearMessageType("VHDL-1240") ; + Message::ClearMessageType("VHDL-1241") ; + if (Message::GetMessageType("VHDL-1240")!=prev_1240) + Message::SetMessageType("VHDL-1240", prev_1240); + if (Message::GetMessageType("VHDL-1241")!=prev_1241) + Message::SetMessageType("VHDL-1241", prev_1241); + + } #endif - log("\n"); - log("\n"); - log(" verific -template ..\n"); - log("\n"); - log("Generate template for specified top module of loaded design.\n"); - log("\n"); - log("Template options:\n"); - log("\n"); - log(" -out\n"); - log(" Specifies output file for generated template, by default output is stdout\n"); - log("\n"); - log(" -chparam name value \n"); - log(" Generate template using this parameter value. Otherwise default parameter\n"); - log(" values will be used for templat generate functionality. This option\n"); - log(" can be specified multiple times to override multiple parameters.\n"); - log(" String values must be passed in double quotes (\").\n"); - log("\n"); - log("Templates:\n"); - log("\n"); -#ifdef YOSYS_ENABLE_VERIFIC - VerificTemplateGenerator vfg; - log("%s\n",vfg.GetHelp().c_str()); -#else - log(" WARNING: Templates only available in commercial build.\n"); - log("\n"); -#endif - log("Use Symbiotic EDA Suite if you need Yosys+Verifc.\n"); - log("https://www.symbioticeda.com/seda-suite\n"); - log("\n"); - log("Contact office@symbioticeda.com for free evaluation\n"); - log("binaries of Symbiotic EDA Suite.\n"); - log("\n"); + + msg_type_t prev_1063; + + void add_modules_to_map(Map &map, std::string work, bool flag_lib) + { + MapIter mi ; + VeriModule *veri_module ; + if (!flag_lib) return; + VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1); + if (veri_lib) { + FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { + if (!veri_module) continue; + map.Insert(veri_module,veri_module); + } + } + + prev_1063 = Message::GetMessageType("VERI-1063") ; + Message::SetMessageType("VERI-1063", VERIFIC_INFO); } -#ifdef YOSYS_ENABLE_VERIFIC + + void set_modules_to_blackbox(Map &map, std::string work, bool flag_lib) + { + MapIter mi ; + VeriModule *veri_module ; + if (!flag_lib) return; + VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1); + FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { + if (!veri_module) continue; + if (!map.GetValue(veri_module)) { + veri_module->SetCompileAsBlackbox(); + } + } + Message::ClearMessageType("VERI-1063") ; + if (Message::GetMessageType("VERI-1063")!=prev_1063) + Message::SetMessageType("VERI-1063", prev_1063); + } + void execute(std::vector args, RTLIL::Design *design) override { static bool set_verific_global_flags = true; @@ -2265,11 +3035,11 @@ struct VerificPass : public Pass { if (check_noverific_env()) log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" - "Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" - "https://www.symbioticeda.com/seda-suite\n" + "Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" + "https://www.yosyshq.com/\n" "\n" - "Contact office@symbioticeda.com for free evaluation\n" - "binaries of Symbiotic EDA Suite.\n"); + "Contact office@yosyshq.com for free evaluation\n" + "binaries of YosysHQ Tabby CAD Suite.\n"); log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n"); @@ -2278,24 +3048,36 @@ struct VerificPass : public Pass { Message::SetConsoleOutput(0); Message::RegisterCallBackMsg(msg_func); + RuntimeFlags::SetVar("db_preserve_user_instances", 1); RuntimeFlags::SetVar("db_preserve_user_nets", 1); + RuntimeFlags::SetVar("db_preserve_x", 1); + RuntimeFlags::SetVar("db_allow_external_nets", 1); RuntimeFlags::SetVar("db_infer_wide_operators", 1); + RuntimeFlags::SetVar("db_infer_set_reset_registers", 0); + + // Properly respect order of read and write for rams + RuntimeFlags::SetVar("db_change_inplace_ram_blocking_write_before_read", 1); RuntimeFlags::SetVar("veri_extract_dualport_rams", 0); RuntimeFlags::SetVar("veri_extract_multiport_rams", 1); + RuntimeFlags::SetVar("veri_allow_any_ram_in_loop", 1); +#ifdef VERIFIC_VHDL_SUPPORT RuntimeFlags::SetVar("vhdl_extract_dualport_rams", 0); RuntimeFlags::SetVar("vhdl_extract_multiport_rams", 1); + RuntimeFlags::SetVar("vhdl_allow_any_ram_in_loop", 1); RuntimeFlags::SetVar("vhdl_support_variable_slice", 1); RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); - RuntimeFlags::SetVar("veri_preserve_assignments", 1); RuntimeFlags::SetVar("vhdl_preserve_assignments", 1); - - RuntimeFlags::SetVar("veri_preserve_comments",1); - //RuntimeFlags::SetVar("vhdl_preserve_comments",1); + //RuntimeFlags::SetVar("vhdl_preserve_comments", 1); + RuntimeFlags::SetVar("vhdl_preserve_drivers", 1); +#endif + RuntimeFlags::SetVar("veri_preserve_assignments", 1); + RuntimeFlags::SetVar("veri_preserve_comments", 1); + RuntimeFlags::SetVar("veri_preserve_drivers", 1); // Workaround for VIPER #13851 RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1); @@ -2306,6 +3088,8 @@ struct VerificPass : public Pass { // https://github.com/YosysHQ/yosys/issues/1055 RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ; + RuntimeFlags::SetVar("verific_produce_verbose_syntax_error_message", 1); + #ifndef DB_PRESERVE_INITIAL_VALUE # warning Verific was built without DB_PRESERVE_INITIAL_VALUE. #endif @@ -2319,6 +3103,7 @@ struct VerificPass : public Pass { const char *release_str = Message::ReleaseString(); time_t release_time = Message::ReleaseDate(); char *release_tmstr = ctime(&release_time); + std::vector tmp_files; if (release_str == nullptr) release_str = "(no release string)"; @@ -2330,6 +3115,9 @@ struct VerificPass : public Pass { int argidx = 1; std::string work = "work"; + bool is_work_set = false; + (void)is_work_set; + veri_file::RegisterCallBackVerificStream(&verific_read_cb); if (GetSize(args) > argidx && (args[argidx] == "-set-error" || args[argidx] == "-set-warning" || args[argidx] == "-set-info" || args[argidx] == "-set-ignore")) @@ -2347,9 +3135,20 @@ struct VerificPass : public Pass { else log_abort(); - for (argidx++; argidx < GetSize(args); argidx++) - Message::SetMessageType(args[argidx].c_str(), new_type); - + for (argidx++; argidx < GetSize(args); argidx++) { + if (Strings::compare(args[argidx].c_str(), "errors")) { + Message::SetMessageType("VERI-1063", new_type); + Message::SetAllMessageType(VERIFIC_ERROR, new_type); + } else if (Strings::compare(args[argidx].c_str(), "warnings")) { + Message::SetAllMessageType(VERIFIC_WARNING, new_type); + } else if (Strings::compare(args[argidx].c_str(), "infos")) { + Message::SetAllMessageType(VERIFIC_INFO, new_type); + } else if (Strings::compare(args[argidx].c_str(), "comments")) { + Message::SetAllMessageType(VERIFIC_COMMENT, new_type); + } else { + Message::SetMessageType(args[argidx].c_str(), new_type); + } + } goto check_error; } @@ -2365,6 +3164,12 @@ struct VerificPass : public Pass { goto check_error; } + if (GetSize(args) > argidx && args[argidx] == "-vlog-libext") { + for (argidx++; argidx < GetSize(args); argidx++) + verific_libexts.push_back(args[argidx]); + goto check_error; + } + if (GetSize(args) > argidx && args[argidx] == "-vlog-define") { for (argidx++; argidx < GetSize(args); argidx++) { string name = args[argidx]; @@ -2389,10 +3194,38 @@ struct VerificPass : public Pass { } veri_file::RemoveAllLOptions(); + for (int i = argidx; i < GetSize(args); i++) + { + if (args[i] == "-work" && i+1 < GetSize(args)) { + work = args[++i]; + is_work_set = true; + continue; + } + if (args[i] == "-L" && i+1 < GetSize(args)) { + ++i; + continue; + } + break; + } + veri_file::AddLOption(work.c_str()); + for (int i = argidx; i < GetSize(args); i++) + { + if (args[i] == "-work" && i+1 < GetSize(args)) { + ++i; + continue; + } + if (args[i] == "-L" && i+1 < GetSize(args)) { + if (args[++i] == work) + veri_file::RemoveAllLOptions(); + continue; + } + break; + } for (; argidx < GetSize(args); argidx++) { if (args[argidx] == "-work" && argidx+1 < GetSize(args)) { work = args[++argidx]; + is_work_set = true; continue; } if (args[argidx] == "-L" && argidx+1 < GetSize(args)) { @@ -2402,6 +3235,65 @@ struct VerificPass : public Pass { break; } + if (GetSize(args) > argidx && (args[argidx] == "-f" || args[argidx] == "-F")) + { + unsigned verilog_mode = veri_file::UNDEFINED; + bool is_formal = false; + const char* filename = nullptr; + + Verific::veri_file::f_file_flags flags = (args[argidx] == "-f") ? veri_file::F_FILE_NONE : veri_file::F_FILE_CAPITAL; + + for (argidx++; argidx < GetSize(args); argidx++) { + if (args[argidx] == "-vlog95") { + verilog_mode = veri_file::VERILOG_95; + continue; + } else if (args[argidx] == "-vlog2k") { + verilog_mode = veri_file::VERILOG_2K; + continue; + } else if (args[argidx] == "-sv2005") { + verilog_mode = veri_file::SYSTEM_VERILOG_2005; + continue; + } else if (args[argidx] == "-sv2009") { + verilog_mode = veri_file::SYSTEM_VERILOG_2009; + continue; + } else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal") { + verilog_mode = veri_file::SYSTEM_VERILOG; + if (args[argidx] == "-formal") is_formal = true; + continue; + } else if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; + } + + if (!filename) { + filename = args[argidx].c_str(); + continue; + } else { + log_cmd_error("Only one filename can be specified.\n"); + } + } + if (!filename) + log_cmd_error("Filname must be specified.\n"); + + unsigned analysis_mode = verilog_mode; // keep default as provided by user if not defined in file + Array *file_names = veri_file::ProcessFFile(filename, flags, analysis_mode); + if (analysis_mode != verilog_mode) + log_warning("Provided verilog mode differs from one specified in file.\n"); + + veri_file::DefineMacro("YOSYS"); + veri_file::DefineMacro("VERIFIC"); + veri_file::DefineMacro(is_formal ? "FORMAL" : "SYNTHESIS"); + + if (!veri_file::AnalyzeMultipleFiles(file_names, analysis_mode, work.c_str(), veri_file::MFCU)) { + verific_error_msg.clear(); + log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n"); + } + + delete file_names; + verific_import_pending = true; + goto check_error; + } + if (GetSize(args) > argidx && (args[argidx] == "-vlog95" || args[argidx] == "-vlog2k" || args[argidx] == "-sv2005" || args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal")) { @@ -2421,6 +3313,7 @@ struct VerificPass : public Pass { else log_abort(); + veri_file::DefineMacro("YOSYS"); veri_file::DefineMacro("VERIFIC"); veri_file::DefineMacro(args[argidx] == "-formal" ? "FORMAL" : "SYNTHESIS"); @@ -2445,159 +3338,187 @@ struct VerificPass : public Pass { veri_file::AddIncludeDir(dir.c_str()); for (auto &dir : verific_libdirs) veri_file::AddYDir(dir.c_str()); - - while (argidx < GetSize(args)) - file_names.Insert(args[argidx++].c_str()); - + for (auto &ext : verific_libexts) + veri_file::AddLibExt(ext.c_str()); + + bool flag_lib = false; + while (argidx < GetSize(args)) { + if (args[argidx] == "-lib") { + flag_lib = true; + argidx++; + continue; + } + if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; + } + std::string filename = frontent_rewrite(args, argidx, tmp_files); + file_names.Insert(strdup(filename.c_str())); + } + Map map(POINTER_HASH); + add_modules_to_map(map, work, flag_lib); if (!veri_file::AnalyzeMultipleFiles(&file_names, verilog_mode, work.c_str(), veri_file::MFCU)) { verific_error_msg.clear(); log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n"); } - + set_modules_to_blackbox(map, work, flag_lib); verific_import_pending = true; goto check_error; } +#ifdef VERIFIC_VHDL_SUPPORT if (GetSize(args) > argidx && args[argidx] == "-vhdl87") { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str()); - for (argidx++; argidx < GetSize(args); argidx++) - if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_87)) - log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str()); + bool flag_lib = false; + for (argidx++; argidx < GetSize(args); argidx++) { + if (args[argidx] == "-lib") { + flag_lib = true; + continue; + } + if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; + } + Map map(POINTER_HASH); + add_units_to_map(map, work, flag_lib); + std::string filename = frontent_rewrite(args, argidx, tmp_files); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_87)) + log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", filename.c_str()); + set_units_to_blackbox(map, work, flag_lib); + } verific_import_pending = true; goto check_error; } if (GetSize(args) > argidx && args[argidx] == "-vhdl93") { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str()); - for (argidx++; argidx < GetSize(args); argidx++) - if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_93)) - log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", args[argidx].c_str()); + bool flag_lib = false; + for (argidx++; argidx < GetSize(args); argidx++) { + if (args[argidx] == "-lib") { + flag_lib = true; + continue; + } + if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; + } + Map map(POINTER_HASH); + add_units_to_map(map, work, flag_lib); + std::string filename = frontent_rewrite(args, argidx, tmp_files); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_93)) + log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", filename.c_str()); + set_units_to_blackbox(map, work, flag_lib); + } verific_import_pending = true; goto check_error; } if (GetSize(args) > argidx && args[argidx] == "-vhdl2k") { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1993").c_str()); - for (argidx++; argidx < GetSize(args); argidx++) - if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_2K)) - log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", args[argidx].c_str()); + bool flag_lib = false; + for (argidx++; argidx < GetSize(args); argidx++) { + if (args[argidx] == "-lib") { + flag_lib = true; + continue; + } + if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; + } + Map map(POINTER_HASH); + add_units_to_map(map, work, flag_lib); + std::string filename = frontent_rewrite(args, argidx, tmp_files); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_2K)) + log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", filename.c_str()); + set_units_to_blackbox(map, work, flag_lib); + } verific_import_pending = true; goto check_error; } if (GetSize(args) > argidx && (args[argidx] == "-vhdl2008" || args[argidx] == "-vhdl")) { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2008").c_str()); - for (argidx++; argidx < GetSize(args); argidx++) - if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_2008)) - log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", args[argidx].c_str()); - verific_import_pending = true; - goto check_error; - } - - if (argidx < GetSize(args) && args[argidx] == "-app") - { - if (!(argidx+1 < GetSize(args))) - cmd_error(args, argidx, "No formal application specified.\n"); - - VerificFormalApplications vfa; - auto apps = vfa.GetApps(); - std::string app = args[++argidx]; - std::vector blacklists; - if (apps.find(app) == apps.end()) - log_cmd_error("Application '%s' does not exist.\n", app.c_str()); - - FormalApplication *application = apps[app]; - application->setLogger([](std::string msg) { log("%s",msg.c_str()); } ); - VeriModule *selected_module = nullptr; - + bool flag_lib = false; for (argidx++; argidx < GetSize(args); argidx++) { - std::string error; - if (application->checkParams(args, argidx, error)) { - if (!error.empty()) - cmd_error(args, argidx, error); + if (args[argidx] == "-lib") { + flag_lib = true; continue; } - - if (args[argidx] == "-module" && argidx < GetSize(args)) { - if (!(argidx+1 < GetSize(args))) - cmd_error(args, argidx+1, "No module name specified.\n"); - std::string module = args[++argidx]; - VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); - selected_module = veri_lib ? veri_lib->GetModule(module.c_str(), 1) : nullptr; - if (!selected_module) { - log_error("Can't find module '%s'.\n", module.c_str()); - } - continue; + if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; } - if (args[argidx] == "-blacklist" && argidx < GetSize(args)) { - if (!(argidx+1 < GetSize(args))) - cmd_error(args, argidx+1, "No blacklist specified.\n"); - - std::string line = args[++argidx]; - std::string p; - while (!(p = next_token(line, ",\t\r\n ")).empty()) - blacklists.push_back(p); + Map map(POINTER_HASH); + add_units_to_map(map, work, flag_lib); + std::string filename = frontent_rewrite(args, argidx, tmp_files); + if (!vhdl_file::Analyze(filename.c_str(), work.c_str(), vhdl_file::VHDL_2008)) + log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", filename.c_str()); + set_units_to_blackbox(map, work, flag_lib); + } + verific_import_pending = true; + goto check_error; + } +#endif +#ifdef VERIFIC_EDIF_SUPPORT + if (GetSize(args) > argidx && args[argidx] == "-edif") { + edif_file edif; + argidx++; + while (argidx < GetSize(args)) { + std::string filename = frontent_rewrite(args, argidx, tmp_files); + if (!edif.Read(filename.c_str())) + log_cmd_error("Reading `%s' in EDIF mode failed.\n", filename.c_str()); + } + goto check_error; + } +#endif +#ifdef VERIFIC_LIBERTY_SUPPORT + if (GetSize(args) > argidx && args[argidx] == "-liberty") { + bool flag_lib = false; + for (argidx++; argidx < GetSize(args); argidx++) { + if (args[argidx] == "-lib") { + flag_lib = true; continue; } - if (args[argidx] == "-blfile" && argidx < GetSize(args)) { - if (!(argidx+1 < GetSize(args))) - cmd_error(args, argidx+1, "No blacklist file specified.\n"); - std::string fn = args[++argidx]; - std::ifstream f(fn); - if (f.fail()) - log_cmd_error("Can't open blacklist file '%s'!\n", fn.c_str()); - - std::string line,p; - while (std::getline(f, line)) { - while (!(p = next_token(line, ",\t\r\n ")).empty()) - blacklists.push_back(p); - } - continue; + if (args[argidx].compare(0, 1, "-") == 0) { + cmd_error(args, argidx, "unknown option"); + goto check_error; } break; } - if (argidx < GetSize(args)) - cmd_error(args, argidx, "unknown option/parameter"); - - application->setBlacklists(&blacklists); - application->setSingleModuleMode(selected_module!=nullptr); - - const char *err = application->validate(); - if (err) - cmd_error(args, argidx, err); - MapIter mi; - VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1); - log("Running formal application '%s'.\n", app.c_str()); - - if (selected_module) { - std::string out; - if (!application->execute(selected_module, out)) - log_error("%s", out.c_str()); - } - else { - VeriModule *module ; - FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, module) { - std::string out; - if (!application->execute(module, out)) { - log_error("%s", out.c_str()); - break; + while (argidx < GetSize(args)) { + std::string filename = frontent_rewrite(args, argidx, tmp_files); + if (!synlib_file::Read(filename.c_str(), is_work_set ? work.c_str() : nullptr)) + log_cmd_error("Reading `%s' in LIBERTY mode failed.\n", filename.c_str()); + SynlibLibrary *lib = synlib_file::GetLastLibraryAnalyzed(); + if (lib && flag_lib) { + MapIter mi ; + Verific::Cell *c ; + FOREACH_CELL_OF_LIBRARY(lib->GetLibrary(),mi,c) { + MapIter ni ; + Netlist *nl; + FOREACH_NETLIST_OF_CELL(c, ni, nl) { + if (nl) + nl->MakeBlackBox(); + } } } } goto check_error; } - +#endif if (argidx < GetSize(args) && args[argidx] == "-pp") { const char* filename = nullptr; const char* module = nullptr; bool mode_vhdl = false; for (argidx++; argidx < GetSize(args); argidx++) { +#ifdef VERIFIC_VHDL_SUPPORT if (args[argidx] == "-vhdl") { mode_vhdl = true; continue; } +#endif if (args[argidx] == "-verilog") { mode_vhdl = false; continue; @@ -2624,104 +3545,26 @@ struct VerificPass : public Pass { log_cmd_error("Filname must be specified.\n"); if (mode_vhdl) +#ifdef VERIFIC_VHDL_SUPPORT vhdl_file::PrettyPrint(filename, module, work.c_str()); +#else + goto check_error; +#endif else veri_file::PrettyPrint(filename, module, work.c_str()); goto check_error; } - if (argidx < GetSize(args) && args[argidx] == "-template") - { - if (!(argidx+1 < GetSize(args))) - cmd_error(args, argidx+1, "No template type specified.\n"); - - VerificTemplateGenerator vfg; - auto gens = vfg.GetGenerators(); - std::string app = args[++argidx]; - if (gens.find(app) == gens.end()) - log_cmd_error("Template generator '%s' does not exist.\n", app.c_str()); - TemplateGenerator *generator = gens[app]; - if (!(argidx+1 < GetSize(args))) - cmd_error(args, argidx+1, "No top module specified.\n"); - generator->setLogger([](std::string msg) { log("%s",msg.c_str()); } ); - - std::string module = args[++argidx]; - VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); - VeriModule *veri_module = veri_lib ? veri_lib->GetModule(module.c_str(), 1) : nullptr; - if (!veri_module) { - log_error("Can't find module/unit '%s'.\n", module.c_str()); - } - - log("Template '%s' is running for module '%s'.\n", app.c_str(),module.c_str()); - - Map parameters(STRING_HASH); - const char *out_filename = nullptr; - - for (argidx++; argidx < GetSize(args); argidx++) { - std::string error; - if (generator->checkParams(args, argidx, error)) { - if (!error.empty()) - cmd_error(args, argidx, error); - continue; - } - - if (args[argidx] == "-chparam" && argidx < GetSize(args)) { - if (!(argidx+1 < GetSize(args))) - cmd_error(args, argidx+1, "No param name specified.\n"); - if (!(argidx+2 < GetSize(args))) - cmd_error(args, argidx+2, "No param value specified.\n"); - - const std::string &key = args[++argidx]; - const std::string &value = args[++argidx]; - unsigned new_insertion = parameters.Insert(key.c_str(), value.c_str(), - 1 /* force_overwrite */); - if (!new_insertion) - log_warning_noprefix("-chparam %s already specified: overwriting.\n", key.c_str()); - continue; - } - - if (args[argidx] == "-out" && argidx < GetSize(args)) { - if (!(argidx+1 < GetSize(args))) - cmd_error(args, argidx+1, "No output file specified.\n"); - out_filename = args[++argidx].c_str(); - continue; - } - - break; - } - if (argidx < GetSize(args)) - cmd_error(args, argidx, "unknown option/parameter"); - - const char *err = generator->validate(); - if (err) - cmd_error(args, argidx, err); - - std::string val; - if (!generator->generate(veri_module, val, ¶meters)) - log_error("%s", val.c_str()); - - FILE *of = stdout; - if (out_filename) { - of = fopen(out_filename, "w"); - if (of == nullptr) - log_error("Can't open '%s' for writing: %s\n", out_filename, strerror(errno)); - log("Writing output to '%s'\n",out_filename); - } - fprintf(of, "%s\n",val.c_str()); - fflush(of); - if (of!=stdout) - fclose(of); - goto check_error; - } - if (GetSize(args) > argidx && args[argidx] == "-import") { - std::set nl_todo, nl_done; + std::map nl_todo, nl_done; bool mode_all = false, mode_gates = false, mode_keep = false; bool mode_nosva = false, mode_names = false, mode_verific = false; bool mode_autocover = false, mode_fullinit = false; - bool flatten = false, extnets = false; + bool flatten = false, extnets = false, mode_cells = false; + bool split_complex_ports = true; string dumpfile; + string ppfile; Map parameters(STRING_HASH); for (argidx++; argidx < GetSize(args); argidx++) { @@ -2737,6 +3580,10 @@ struct VerificPass : public Pass { flatten = true; continue; } + if (args[argidx] == "-no-split-complex-ports") { + split_complex_ports = false; + continue; + } if (args[argidx] == "-extnets") { extnets = true; continue; @@ -2765,11 +3612,15 @@ struct VerificPass : public Pass { mode_fullinit = true; continue; } + if (args[argidx] == "-cells") { + mode_cells = true; + continue; + } if (args[argidx] == "-chparam" && argidx+2 < GetSize(args)) { const std::string &key = args[++argidx]; const std::string &value = args[++argidx]; unsigned new_insertion = parameters.Insert(key.c_str(), value.c_str(), - 1 /* force_overwrite */); + 1 /* force_overwrite */); if (!new_insertion) log_warning_noprefix("-chparam %s already specified: overwriting.\n", key.c_str()); continue; @@ -2790,6 +3641,10 @@ struct VerificPass : public Pass { dumpfile = args[++argidx]; continue; } + if (args[argidx] == "-pp" && argidx+1 < GetSize(args)) { + ppfile = args[++argidx]; + continue; + } break; } @@ -2798,18 +3653,25 @@ struct VerificPass : public Pass { std::set top_mod_names; - InitialAssertionRewriter rw; - rw.RegisterCallBack(); - if (mode_all) { + +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + VerificExtensions::ElaborateAndRewrite(work, ¶meters); + verific_error_msg.clear(); +#endif + if (!ppfile.empty()) + veri_file::PrettyPrint(ppfile.c_str(), nullptr, work.c_str()); + log("Running hier_tree::ElaborateAll().\n"); - VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1); Array veri_libs, vhdl_libs; +#ifdef VERIFIC_VHDL_SUPPORT + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); +#endif if (veri_lib) veri_libs.InsertLast(veri_lib); Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, ¶meters); @@ -2817,7 +3679,7 @@ struct VerificPass : public Pass { int i; FOREACH_ARRAY_ITEM(netlists, i, nl) - nl_todo.insert(nl); + nl_todo.emplace(nl->CellBaseName(), nl); delete netlists; } else @@ -2825,70 +3687,146 @@ struct VerificPass : public Pass { if (argidx == GetSize(args)) cmd_error(args, argidx, "No top module specified.\n"); - VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); - VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); + Array *netlists = nullptr; - Array veri_modules, vhdl_units; - for (; argidx < GetSize(args); argidx++) +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + for (int static_elaborate = 1; static_elaborate >= 0; static_elaborate--) +#endif { - const char *name = args[argidx].c_str(); - top_mod_names.insert(name); - VeriModule *veri_module = veri_lib ? veri_lib->GetModule(name, 1) : nullptr; - if (veri_module) { - log("Adding Verilog module '%s' to elaboration queue.\n", name); - veri_modules.InsertLast(veri_module); - continue; - } + VeriLibrary* veri_lib = veri_file::GetLibrary(work.c_str(), 1); +#ifdef VERIFIC_VHDL_SUPPORT + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); +#endif - VhdlDesignUnit *vhdl_unit = vhdl_lib ? vhdl_lib->GetPrimUnit(name) : nullptr; - if (vhdl_unit) { - log("Adding VHDL unit '%s' to elaboration queue.\n", name); - vhdl_units.InsertLast(vhdl_unit); - continue; + Array veri_modules, vhdl_units; + for (int i = argidx; i < GetSize(args); i++) + { + const char *name = args[i].c_str(); + top_mod_names.insert(name); + + VeriModule *veri_module = veri_lib ? veri_lib->GetModule(name, 1) : nullptr; + if (veri_module) { + if (veri_module->IsConfiguration()) { + log("Adding Verilog configuration '%s' to elaboration queue.\n", name); + veri_modules.InsertLast(veri_module); + + top_mod_names.erase(name); + + VeriConfiguration *cfg = (VeriConfiguration*)veri_module; + VeriName *module_name; + int i; + FOREACH_ARRAY_ITEM(cfg->GetTopModuleNames(), i, module_name) { + VeriLibrary *lib = veri_module->GetLibrary() ; + if (module_name && module_name->IsHierName()) { + VeriName *prefix = module_name->GetPrefix() ; + const char *lib_name = (prefix) ? prefix->GetName() : 0 ; + if (work != lib_name) lib = veri_file::GetLibrary(lib_name, 1) ; + } + if (lib && module_name) + top_mod_names.insert(lib->GetModule(module_name->GetName(), 1)->GetName()); + } + } else { + log("Adding Verilog module '%s' to elaboration queue.\n", name); + veri_modules.InsertLast(veri_module); + } + continue; + } +#ifdef VERIFIC_VHDL_SUPPORT + VhdlDesignUnit *vhdl_unit = vhdl_lib ? vhdl_lib->GetPrimUnit(name) : nullptr; + if (vhdl_unit) { + log("Adding VHDL unit '%s' to elaboration queue.\n", name); + vhdl_units.InsertLast(vhdl_unit); + continue; + } +#endif + log_error("Can't find module/unit '%s'.\n", name); } - log_error("Can't find module/unit '%s'.\n", name); - } +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + if (static_elaborate) { + VerificExtensions::ElaborateAndRewrite(work, &veri_modules, &vhdl_units, ¶meters); + verific_error_msg.clear(); +#endif + if (!ppfile.empty()) + veri_file::PrettyPrint(ppfile.c_str(), nullptr, work.c_str()); - if (veri_lib) { - // Also elaborate all root modules since they may contain bind statements - MapIter mi; - VeriModule *veri_module; - FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { - if (!veri_module->IsRootModule()) continue; - veri_modules.InsertLast(veri_module); +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + continue; + } +#endif + const char *lib_name = nullptr; + SetIter si; + FOREACH_SET_ITEM(veri_file::GetAllLOptions(), si, &lib_name) { + VeriLibrary* veri_lib = veri_file::GetLibrary(lib_name, 0); + if (veri_lib) { + // Also elaborate all root modules since they may contain bind statements + MapIter mi; + VeriModule *veri_module; + FOREACH_VERILOG_MODULE_IN_LIBRARY(veri_lib, mi, veri_module) { + if (!veri_module->IsRootModule()) continue; + veri_modules.InsertLast(veri_module); + } + } } + + log("Running hier_tree::Elaborate().\n"); + netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, ¶meters); } - log("Running hier_tree::Elaborate().\n"); - Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, ¶meters); Netlist *nl; int i; FOREACH_ARRAY_ITEM(netlists, i, nl) { + if (!nl) continue; + if (!top_mod_names.count(nl->CellBaseName())) + continue; nl->AddAtt(new Att(" \\top", NULL)); - nl_todo.insert(nl); + nl_todo.emplace(nl->CellBaseName(), nl); } delete netlists; } + if (mode_cells) { + log("Importing all cells.\n"); + Libset *gls = Libset::Global() ; + MapIter it ; + Library *l ; + FOREACH_LIBRARY_OF_LIBSET(gls,it,l) { + MapIter mi ; + Verific::Cell *c ; + FOREACH_CELL_OF_LIBRARY(l,mi,c) { + if (!mode_verific && (l == Library::Primitives() || l == Library::Operators())) continue; + MapIter ni ; + if (c->NumOfNetlists() == 1) { + c->GetFirstNetlist()->SetName(""); + } + Netlist *nl; + FOREACH_NETLIST_OF_CELL(c, ni, nl) { + if (nl) + nl_todo.emplace(nl->CellBaseName(), nl); + } + } + } + } if (!verific_error_msg.empty()) goto check_error; if (flatten) { for (auto nl : nl_todo) - nl->Flatten(); + nl.second->Flatten(); } if (extnets) { VerificExtNets worker; for (auto nl : nl_todo) - worker.run(nl); + worker.run(nl.second); } - for (auto nl : nl_todo) - nl->ChangePortBusStructures(1 /* hierarchical */); + if (split_complex_ports) { + for (auto nl : nl_todo) + nl.second->ChangePortBusStructures(1 /* hierarchical */); + } if (!dumpfile.empty()) { VeriWrite veri_writer; @@ -2896,28 +3834,118 @@ struct VerificPass : public Pass { } while (!nl_todo.empty()) { - Netlist *nl = *nl_todo.begin(); - if (nl_done.count(nl) == 0) { + auto it = nl_todo.begin(); + Netlist *nl = it->second; + if (nl_done.count(it->first) == 0) { VerificImporter importer(mode_gates, mode_keep, mode_nosva, mode_names, mode_verific, mode_autocover, mode_fullinit); - importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->Owner()->Name())); + nl_done[it->first] = it->second; + importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->CellBaseName())); } - nl_todo.erase(nl); - nl_done.insert(nl); + nl_todo.erase(it); } +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + VerificExtensions::Reset(); +#endif + hier_tree::DeleteHierarchicalTree(); veri_file::Reset(); +#ifdef VERIFIC_VHDL_SUPPORT vhdl_file::Reset(); +#endif +#ifdef VERIFIC_EDIF_SUPPORT + edif_file::Reset(); +#endif +#ifdef VERIFIC_LIBERTY_SUPPORT + synlib_file::Reset(); +#endif Libset::Reset(); + Message::Reset(); + RuntimeFlags::DeleteAllFlags(); + LineFile::DeleteAllLineFiles(); verific_incdirs.clear(); verific_libdirs.clear(); + verific_libexts.clear(); verific_import_pending = false; goto check_error; } + if (argidx < GetSize(args) && args[argidx] == "-cfg") + { + if (argidx+1 == GetSize(args)) { + MapIter mi; + const char *k, *s; + unsigned long v; + pool lines; + FOREACH_MAP_ITEM(RuntimeFlags::GetVarMap(), mi, &k, &v) { + lines.insert(stringf("%s %lu", k, v)); + } + FOREACH_MAP_ITEM(RuntimeFlags::GetStringVarMap(), mi, &k, &s) { + if (s == nullptr) + lines.insert(stringf("%s NULL", k)); + else + lines.insert(stringf("%s \"%s\"", k, s)); + } + lines.sort(); + for (auto &line : lines) + log("verific -cfg %s\n", line.c_str()); + goto check_error; + } + + if (argidx+2 == GetSize(args)) { + const char *k = args[argidx+1].c_str(); + if (RuntimeFlags::HasUnsignedVar(k)) { + log("verific -cfg %s %lu\n", k, RuntimeFlags::GetVar(k)); + goto check_error; + } + if (RuntimeFlags::HasStringVar(k)) { + const char *s = RuntimeFlags::GetStringVar(k); + if (s == nullptr) + log("verific -cfg %s NULL\n", k); + else + log("verific -cfg %s \"%s\"\n", k, s); + goto check_error; + } + log_cmd_error("Can't find Verific Runtime flag '%s'.\n", k); + } + + if (argidx+3 == GetSize(args)) { + const auto &k = args[argidx+1], &v = args[argidx+2]; + if (v == "NULL") { + RuntimeFlags::SetStringVar(k.c_str(), nullptr); + goto check_error; + } + if (v[0] == '"') { + std::string s = v.substr(1, GetSize(v)-2); + RuntimeFlags::SetStringVar(k.c_str(), v.c_str()); + goto check_error; + } + char *endptr; + unsigned long n = strtol(v.c_str(), &endptr, 0); + if (*endptr == 0) { + RuntimeFlags::SetVar(k.c_str(), n); + goto check_error; + } + } + } +#ifdef YOSYSHQ_VERIFIC_EXTENSIONS + if (VerificExtensions::Execute(args, argidx, work, + [this](const std::vector &args, size_t argidx, std::string msg) + { cmd_error(args, argidx, msg); } )) { + goto check_error; + } +#endif + cmd_error(args, argidx, "Missing or unsupported mode parameter.\n"); check_error: + if (tmp_files.size()) { + log("Removing temp files.\n"); + for(auto &fn : tmp_files) { + remove(fn.c_str()); + } + } + if (!verific_error_msg.empty()) log_error("%s\n", verific_error_msg.c_str()); @@ -2926,11 +3954,11 @@ struct VerificPass : public Pass { void execute(std::vector, RTLIL::Design *) override { log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" - "Use Symbiotic EDA Suite if you need Yosys+Verifc.\n" - "https://www.symbioticeda.com/seda-suite\n" + "Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" + "https://www.yosyshq.com/\n" "\n" - "Contact office@symbioticeda.com for free evaluation\n" - "binaries of Symbiotic EDA Suite.\n"); + "Contact office@yosyshq.com for free evaluation\n" + "binaries of YosysHQ Tabby CAD Suite.\n"); } #endif } VerificPass; @@ -2950,11 +3978,34 @@ struct ReadPass : public Pass { log("the language version (and before file names) to set additional verilog defines.\n"); log("\n"); log("\n"); +#ifdef VERIFIC_VHDL_SUPPORT log(" read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} ..\n"); log("\n"); log("Load the specified VHDL files. (Requires Verific.)\n"); log("\n"); log("\n"); +#endif +#ifdef VERIFIC_EDIF_SUPPORT + log(" read {-edif} ..\n"); + log("\n"); + log("Load the specified EDIF files. (Requires Verific.)\n"); + log("\n"); + log("\n"); +#endif + log(" read {-liberty} ..\n"); + log("\n"); + log("Load the specified Liberty files.\n"); + log("\n"); + log(" -lib\n"); + log(" only create empty blackbox modules\n"); + log("\n"); + log("\n"); + log(" read {-f|-F} \n"); + log("\n"); + log("Load and execute the specified command file. (Requires Verific.)\n"); + log("Check verific command for more information about supported commands in file.\n"); + log("\n"); + log("\n"); log(" read -define [=]..\n"); log("\n"); log("Set global Verilog/SystemVerilog defines.\n"); @@ -3031,6 +4082,7 @@ struct ReadPass : public Pass { return; } +#ifdef VERIFIC_VHDL_SUPPORT if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl") { if (use_verific) { args[0] = "verific"; @@ -3040,6 +4092,36 @@ struct ReadPass : public Pass { } return; } +#endif +#ifdef VERIFIC_EDIF_SUPPORT + if (args[1] == "-edif") { + if (use_verific) { + args[0] = "verific"; + Pass::call(design, args); + } else { + cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); + } + return; + } +#endif + if (args[1] == "-liberty") { + if (use_verific) { + args[0] = "verific"; + } else { + args[0] = "read_liberty"; + } + Pass::call(design, args); + return; + } + if (args[1] == "-f" || args[1] == "-F") { + if (use_verific) { + args[0] = "verific"; + Pass::call(design, args); + } else { + cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); + } + return; + } if (args[1] == "-define") { if (use_verific) { diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h index f168a25887f..0b9616e1944 100644 --- a/frontends/verific/verific.h +++ b/frontends/verific/verific.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -26,7 +26,7 @@ YOSYS_NAMESPACE_BEGIN extern int verific_verbose; extern bool verific_import_pending; -extern void verific_import(Design *design, const std::map ¶meters, std::string top = std::string()); +extern std::string verific_import(Design *design, const std::map ¶meters, std::string top = std::string()); extern pool verific_sva_prims; @@ -44,12 +44,14 @@ struct VerificClocking { SigBit disable_sig = State::S0; bool posedge = true; bool gclk = false; + bool cond_pol = true; VerificClocking() { } VerificClocking(VerificImporter *importer, Verific::Net *net, bool sva_at_only = false); RTLIL::Cell *addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const init_value = Const()); RTLIL::Cell *addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec sig_d, SigSpec sig_q, Const arst_value); RTLIL::Cell *addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, SigSpec sig_d, SigSpec sig_q); + RTLIL::Cell *addAldff(IdString name, RTLIL::SigSpec sig_aload, RTLIL::SigSpec sig_adata, SigSpec sig_d, SigSpec sig_q); bool property_matches_sequence(const VerificClocking &seq) const { if (clock_net != seq.clock_net) @@ -81,10 +83,12 @@ struct VerificImporter RTLIL::IdString new_verific_id(Verific::DesignObj *obj); void import_attributes(dict &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr); + RTLIL::SigBit netToSigBit(Verific::Net *net); RTLIL::SigSpec operatorInput(Verific::Instance *inst); RTLIL::SigSpec operatorInput1(Verific::Instance *inst); RTLIL::SigSpec operatorInput2(Verific::Instance *inst); RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname); + RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname); RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool *any_all_nets = nullptr); bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name); @@ -93,7 +97,7 @@ struct VerificImporter void merge_past_ffs_clock(pool &candidates, SigBit clock, bool clock_pol); void merge_past_ffs(pool &candidates); - void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set &nl_todo, bool norename = false); + void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::map &nl_todo, bool norename = false); }; void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst); diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index 632043b6f09..222c7d2e917 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1522,10 +1522,13 @@ struct VerificSvaImporter if (inst == nullptr) return false; - if (clocking.cond_net != nullptr) + if (clocking.cond_net != nullptr) { trig = importer->net_map_at(clocking.cond_net); - else + if (!clocking.cond_pol) + trig = module->Not(NEW_ID, trig); + } else { trig = State::S1; + } if (inst->Type() == PRIM_SVA_S_EVENTUALLY || inst->Type() == PRIM_SVA_EVENTUALLY) { @@ -1587,17 +1590,25 @@ struct VerificSvaImporter SigBit trig = State::S1; - if (clocking.cond_net != nullptr) + if (clocking.cond_net != nullptr) { trig = importer->net_map_at(clocking.cond_net); + if (!clocking.cond_pol) + trig = module->Not(NEW_ID, trig); + } if (inst == nullptr) { - log_assert(trig == State::S1); - - if (accept_p != nullptr) - *accept_p = importer->net_map_at(net); - if (reject_p != nullptr) - *reject_p = module->Not(NEW_ID, importer->net_map_at(net)); + if (trig != State::S1) { + if (accept_p != nullptr) + *accept_p = module->And(NEW_ID, trig, importer->net_map_at(net)); + if (reject_p != nullptr) + *reject_p = module->And(NEW_ID, trig, module->Not(NEW_ID, importer->net_map_at(net))); + } else { + if (accept_p != nullptr) + *accept_p = importer->net_map_at(net); + if (reject_p != nullptr) + *reject_p = module->Not(NEW_ID, importer->net_map_at(net)); + } } else if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION || @@ -1759,6 +1770,11 @@ struct VerificSvaImporter clocking.addDff(NEW_ID, sig_en, sig_en_q, State::S0); } + // accept in disable case + + if (clocking.disable_sig != State::S0) + sig_a_q = module->Or(NEW_ID, sig_a_q, clocking.disable_sig); + // generate fair/live cell RTLIL::Cell *c = nullptr; @@ -1766,7 +1782,7 @@ struct VerificSvaImporter if (mode_assert) c = module->addLive(root_name, sig_a_q, sig_en_q); if (mode_assume) c = module->addFair(root_name, sig_a_q, sig_en_q); - importer->import_attributes(c->attributes, root); + if (c) importer->import_attributes(c->attributes, root); return; } @@ -1811,7 +1827,7 @@ struct VerificSvaImporter if (mode_assume) c = module->addAssume(root_name, sig_a_q, sig_en_q); if (mode_cover) c = module->addCover(root_name, sig_a_q, sig_en_q); - importer->import_attributes(c->attributes, root); + if (c) importer->import_attributes(c->attributes, root); } } catch (ParserErrorException) diff --git a/frontends/verilog/const2ast.cc b/frontends/verilog/const2ast.cc index 230dfadbfb2..a4dfbc7ec7c 100644 --- a/frontends/verilog/const2ast.cc +++ b/frontends/verilog/const2ast.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/frontends/verilog/preproc.cc b/frontends/verilog/preproc.cc index ea23139e23a..e33b0a2c326 100644 --- a/frontends/verilog/preproc.cc +++ b/frontends/verilog/preproc.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -36,6 +36,7 @@ #include "verilog_frontend.h" #include "kernel/log.h" #include +#include #include #include #include @@ -141,6 +142,16 @@ static std::string next_token(bool pass_newline = false) return_char(ch); } } + else if (ch == '\\') + { + while ((ch = next_char()) != 0) { + if (ch < 33 || ch > 126) { + return_char(ch); + break; + } + token += ch; + } + } else if (ch == '/') { if ((ch = next_char()) != 0) { @@ -321,7 +332,6 @@ struct define_body_t define_map_t::define_map_t() { add("YOSYS", "1"); - add(formal_mode ? "FORMAL" : "SYNTHESIS", "1"); } // We must define this destructor here (rather than relying on the default), because we need to @@ -335,6 +345,11 @@ define_map_t::add(const std::string &name, const std::string &txt, const arg_map defines[name] = std::unique_ptr(new define_body_t(txt, args)); } +void define_map_t::add(const std::string &name, const define_body_t &body) +{ + defines[name] = std::unique_ptr(new define_body_t(body)); +} + void define_map_t::merge(const define_map_t &map) { for (const auto &pr : map.defines) { @@ -391,13 +406,16 @@ static void input_file(std::istream &f, std::string filename) // the argument list); false if we finished with ','. static bool read_argument(std::string &dest) { + skip_spaces(); std::vector openers; for (;;) { - skip_spaces(); std::string tok = next_token(true); if (tok == ")") { - if (openers.empty()) + if (openers.empty()) { + while (dest.size() && (dest.back() == ' ' || dest.back() == '\t')) + dest = dest.substr(0, dest.size() - 1); return true; + } if (openers.back() != '(') log_error("Mismatched brackets in macro argument: %c and %c.\n", openers.back(), tok[0]); @@ -438,7 +456,17 @@ static bool read_argument(std::string &dest) } } -static bool try_expand_macro(define_map_t &defines, std::string &tok) +using macro_arg_stack_t = std::stack>; + +static void restore_macro_arg(define_map_t &defines, macro_arg_stack_t ¯o_arg_stack) +{ + log_assert(!macro_arg_stack.empty()); + auto &overwritten_arg = macro_arg_stack.top(); + defines.add(overwritten_arg.first, overwritten_arg.second); + macro_arg_stack.pop(); +} + +static bool try_expand_macro(define_map_t &defines, macro_arg_stack_t ¯o_arg_stack, std::string &tok) { if (tok == "`\"") { std::string literal("\""); @@ -448,7 +476,7 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok) if (ntok == "`\"") { insert_input(literal+"\""); return true; - } else if (!try_expand_macro(defines, ntok)) { + } else if (!try_expand_macro(defines, macro_arg_stack, ntok)) { literal += ntok; } } @@ -475,7 +503,16 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok) std::string name = tok.substr(1); std::string skipped_spaces = skip_spaces(); tok = next_token(false); - if (tok == "(" && body->has_args) { + if (body->has_args) { + if (tok != "(") { + if (tok.size() == 1 && iscntrl(tok[0])) { + char buf[5]; + snprintf(buf, sizeof(buf), "\\x%02x", tok[0]); + tok = buf; + } + log_error("Expected to find '(' to begin macro arguments for '%s', but instead found '%s'\n", + name.c_str(), tok.c_str()); + } std::vector args; bool done = false; while (!done) { @@ -484,6 +521,10 @@ static bool try_expand_macro(define_map_t &defines, std::string &tok) args.push_back(arg); } for (const auto &pr : body->args.get_vals(name, args)) { + if (const define_body_t *existing = defines.find(pr.first)) { + macro_arg_stack.push({pr.first, *existing}); + insert_input("`__restore_macro_arg "); + } defines.add(pr.first, pr.second); } } else { @@ -714,9 +755,19 @@ frontend_verilog_preproc(std::istream &f, defines.merge(pre_defines); defines.merge(global_defines_cache); + macro_arg_stack_t macro_arg_stack; std::vector filename_stack; + // We are inside pass_level levels of satisfied ifdefs, and then within + // fail_level levels of unsatisfied ifdefs. The unsatisfied ones are + // always within satisfied ones — even if some condition within is true, + // the parent condition failing renders it moot. int ifdef_fail_level = 0; - bool in_elseif = false; + int ifdef_pass_level = 0; + // For the outermost unsatisfied ifdef, true iff that ifdef already + // had a satisfied branch, and further elsif/else branches should be + // considered unsatisfied even if the condition is true. + // Meaningless if ifdef_fail_level == 0. + bool ifdef_already_satisfied = false; output_code.clear(); input_buffer.clear(); @@ -732,42 +783,70 @@ frontend_verilog_preproc(std::istream &f, if (tok == "`endif") { if (ifdef_fail_level > 0) ifdef_fail_level--; - if (ifdef_fail_level == 0) - in_elseif = false; + else if (ifdef_pass_level > 0) + ifdef_pass_level--; + else + log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); continue; } if (tok == "`else") { - if (ifdef_fail_level == 0) + if (ifdef_fail_level == 0) { + if (ifdef_pass_level == 0) + log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); + ifdef_pass_level--; ifdef_fail_level = 1; - else if (ifdef_fail_level == 1 && !in_elseif) + ifdef_already_satisfied = true; + } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied) { ifdef_fail_level = 0; + ifdef_pass_level++; + ifdef_already_satisfied = true; + } continue; } if (tok == "`elsif") { skip_spaces(); std::string name = next_token(true); - if (ifdef_fail_level == 0) - ifdef_fail_level = 1, in_elseif = true; - else if (ifdef_fail_level == 1 && defines.find(name)) - ifdef_fail_level = 0, in_elseif = true; + if (ifdef_fail_level == 0) { + if (ifdef_pass_level == 0) + log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); + ifdef_pass_level--; + ifdef_fail_level = 1; + ifdef_already_satisfied = true; + } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied && defines.find(name)) { + ifdef_fail_level = 0; + ifdef_pass_level++; + ifdef_already_satisfied = true; + } continue; } if (tok == "`ifdef") { skip_spaces(); std::string name = next_token(true); - if (ifdef_fail_level > 0 || !defines.find(name)) + if (ifdef_fail_level > 0 || !defines.find(name)) { ifdef_fail_level++; + } else { + ifdef_pass_level++; + ifdef_already_satisfied = true; + } + if (ifdef_fail_level == 1) + ifdef_already_satisfied = false; continue; } if (tok == "`ifndef") { skip_spaces(); std::string name = next_token(true); - if (ifdef_fail_level > 0 || defines.find(name)) + if (ifdef_fail_level > 0 || defines.find(name)) { ifdef_fail_level++; + } else { + ifdef_pass_level++; + ifdef_already_satisfied = true; + } + if (ifdef_fail_level == 1) + ifdef_already_satisfied = false; continue; } @@ -780,7 +859,7 @@ frontend_verilog_preproc(std::istream &f, if (tok == "`include") { skip_spaces(); std::string fn = next_token(true); - while (try_expand_macro(defines, fn)) { + while (try_expand_macro(defines, macro_arg_stack, fn)) { fn = next_token(); } while (1) { @@ -882,17 +961,31 @@ frontend_verilog_preproc(std::istream &f, } if (tok == "`resetall") { + default_nettype_wire = true; + continue; + } + + if (tok == "`undefineall" && sv_mode) { defines.clear(); global_defines_cache.clear(); continue; } - if (try_expand_macro(defines, tok)) + if (tok == "`__restore_macro_arg") { + restore_macro_arg(defines, macro_arg_stack); + continue; + } + + if (try_expand_macro(defines, macro_arg_stack, tok)) continue; output_code.push_back(tok); } + if (ifdef_fail_level > 0 || ifdef_pass_level > 0) { + log_error("Unterminated preprocessor conditional!\n"); + } + std::string output; for (auto &str : output_code) output += str; diff --git a/frontends/verilog/preproc.h b/frontends/verilog/preproc.h index 673d633c0b6..330855a92af 100644 --- a/frontends/verilog/preproc.h +++ b/frontends/verilog/preproc.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -42,6 +42,7 @@ struct define_map_t // Add a definition, overwriting any existing definition for name. void add(const std::string &name, const std::string &txt, const arg_map_t *args = nullptr); + void add(const std::string &name, const define_body_t &body); // Merge in another map of definitions (which take precedence // over anything currently defined). diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 2e9c9b2e2e9..5c59fe3afac 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -61,8 +61,6 @@ static void add_package_types(dict &user_types, std } } } - user_type_stack.clear(); - user_type_stack.push_back(new UserTypeMap()); } struct VerilogFrontend : public Frontend { @@ -84,6 +82,9 @@ struct VerilogFrontend : public Frontend { log(" enable support for SystemVerilog assertions and some Yosys extensions\n"); log(" replace the implicit -D SYNTHESIS with -D FORMAL\n"); log("\n"); + log(" -nosynthesis\n"); + log(" don't add implicit -D SYNTHESIS\n"); + log("\n"); log(" -noassert\n"); log(" ignore assert() statements\n"); log("\n"); @@ -99,6 +100,10 @@ struct VerilogFrontend : public Frontend { log(" -assert-assumes\n"); log(" treat all assume() statements like assert() statements\n"); log("\n"); + log(" -nodisplay\n"); + log(" suppress output from display system tasks ($display et. al).\n"); + log(" This does not affect the output from a later 'sim' command.\n"); + log("\n"); log(" -debug\n"); log(" alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2 -yydebug\n"); log("\n"); @@ -225,8 +230,8 @@ struct VerilogFrontend : public Frontend { log("the syntax of the code, rather than to rely on read_verilog for that.\n"); log("\n"); log("Depending on if read_verilog is run in -formal mode, either the macro\n"); - log("SYNTHESIS or FORMAL is defined automatically. In addition, read_verilog\n"); - log("always defines the macro YOSYS.\n"); + log("SYNTHESIS or FORMAL is defined automatically, unless -nosynthesis is used.\n"); + log("In addition, read_verilog always defines the macro YOSYS.\n"); log("\n"); log("See the Yosys README file for a list of non-standard Verilog features\n"); log("supported by the Yosys Verilog front-end.\n"); @@ -234,6 +239,7 @@ struct VerilogFrontend : public Frontend { } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { + bool flag_nodisplay = false; bool flag_dump_ast1 = false; bool flag_dump_ast2 = false; bool flag_no_dump_ptr = false; @@ -255,6 +261,7 @@ struct VerilogFrontend : public Frontend { bool flag_defer = false; bool flag_noblackbox = false; bool flag_nowb = false; + bool flag_nosynthesis = false; define_map_t defines_map; std::list include_dirs; @@ -282,6 +289,10 @@ struct VerilogFrontend : public Frontend { formal_mode = true; continue; } + if (arg == "-nosynthesis") { + flag_nosynthesis = true; + continue; + } if (arg == "-noassert") { noassert_mode = true; continue; @@ -302,6 +313,10 @@ struct VerilogFrontend : public Frontend { assert_assumes_mode = true; continue; } + if (arg == "-nodisplay") { + flag_nodisplay = true; + continue; + } if (arg == "-debug") { flag_dump_ast1 = true; flag_dump_ast2 = true; @@ -446,6 +461,10 @@ struct VerilogFrontend : public Frontend { } break; } + + if (formal_mode || !flag_nosynthesis) + defines_map.add(formal_mode ? "FORMAL" : "SYNTHESIS", "1"); + extra_args(f, filename, args, argidx); log_header(design, "Executing Verilog-2005 frontend: %s\n", filename.c_str()); @@ -472,6 +491,19 @@ struct VerilogFrontend : public Frontend { // make package typedefs available to parser add_package_types(pkg_user_types, design->verilog_packages); + UserTypeMap global_types_map; + for (auto def : design->verilog_globals) { + if (def->type == AST::AST_TYPEDEF) { + global_types_map[def->str] = def; + } + } + + log_assert(user_type_stack.empty()); + // use previous global typedefs as bottom level of user type stack + user_type_stack.push_back(std::move(global_types_map)); + // add a new empty type map to allow overriding existing global definitions + user_type_stack.push_back(UserTypeMap()); + frontend_verilog_yyset_lineno(1); frontend_verilog_yyrestart(NULL); frontend_verilog_yyparse(); @@ -487,13 +519,17 @@ struct VerilogFrontend : public Frontend { if (flag_nodpi) error_on_dpi_function(current_ast); - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, + AST::process(design, current_ast, flag_nodisplay, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog1, flag_dump_vlog2, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, flag_noblackbox, lib_mode, flag_nowb, flag_noopt, flag_icells, flag_pwires, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; + // only the previous and new global type maps remain + log_assert(user_type_stack.size() == 2); + user_type_stack.clear(); + delete current_ast; current_ast = NULL; diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index aa78810388c..8454e7999e7 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -47,7 +47,7 @@ namespace VERILOG_FRONTEND // names of locally typedef'ed types in a stack typedef std::map UserTypeMap; - extern std::vector user_type_stack; + extern std::vector user_type_stack; // names of package typedef'ed types extern dict pkg_user_types; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index f2241066ff8..8a37343022f 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -37,6 +37,8 @@ #ifdef __clang__ // bison generates code using the 'register' storage class specifier #pragma clang diagnostic ignored "-Wdeprecated-register" +// flex generates weirdly-indented code +#pragma clang diagnostic ignored "-Wmisleading-indentation" #endif #include "kernel/log.h" @@ -103,7 +105,7 @@ static bool isUserType(std::string &s) { // check current scope then outer scopes for a name for (auto it = user_type_stack.rbegin(); it != user_type_stack.rend(); ++it) { - if ((*it)->count(s) > 0) { + if (it->count(s) > 0) { return true; } } @@ -126,6 +128,11 @@ static bool isUserType(std::string &s) %x IMPORT_DPI %x BASED_CONST +UNSIGNED_NUMBER [0-9][0-9_]* +FIXED_POINT_NUMBER_DEC [0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? +FIXED_POINT_NUMBER_NO_DEC [0-9][0-9_]*[eE][-+]?[0-9_]+ +TIME_SCALE_SUFFIX [munpf]?s + %% // Initialise comment_caller to something to avoid a "maybe undefined" // warning from GCC. @@ -234,7 +241,7 @@ static bool isUserType(std::string &s) "automatic" { return TOK_AUTOMATIC; } "unique" { SV_KEYWORD(TOK_UNIQUE); } -"unique0" { SV_KEYWORD(TOK_UNIQUE); } +"unique0" { SV_KEYWORD(TOK_UNIQUE0); } "priority" { SV_KEYWORD(TOK_PRIORITY); } "always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); } @@ -260,6 +267,7 @@ static bool isUserType(std::string &s) "const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); } "checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); } "endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); } +"bind" { if (formal_mode) return TOK_BIND; SV_KEYWORD(TOK_BIND); } "final" { SV_KEYWORD(TOK_FINAL); } "logic" { SV_KEYWORD(TOK_LOGIC); } "var" { SV_KEYWORD(TOK_VAR); } @@ -267,6 +275,8 @@ static bool isUserType(std::string &s) "int" { SV_KEYWORD(TOK_INT); } "byte" { SV_KEYWORD(TOK_BYTE); } "shortint" { SV_KEYWORD(TOK_SHORTINT); } +"longint" { SV_KEYWORD(TOK_LONGINT); } +"void" { SV_KEYWORD(TOK_VOID); } "eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } "s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } @@ -275,8 +285,11 @@ static bool isUserType(std::string &s) "output" { return TOK_OUTPUT; } "inout" { return TOK_INOUT; } "wire" { return TOK_WIRE; } +"tri" { return TOK_WIRE; } "wor" { return TOK_WOR; } +"trior" { return TOK_WOR; } "wand" { return TOK_WAND; } +"triand" { return TOK_WAND; } "reg" { return TOK_REG; } "integer" { return TOK_INTEGER; } "signed" { return TOK_SIGNED; } @@ -290,7 +303,7 @@ static bool isUserType(std::string &s) "union" { SV_KEYWORD(TOK_UNION); } "packed" { SV_KEYWORD(TOK_PACKED); } -[0-9][0-9_]* { +{UNSIGNED_NUMBER} { yylval->string = new std::string(yytext); return TOK_CONSTVAL; } @@ -312,12 +325,12 @@ static bool isUserType(std::string &s) return TOK_BASED_CONSTVAL; } -[0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? { +{FIXED_POINT_NUMBER_DEC} { yylval->string = new std::string(yytext); return TOK_REALVAL; } -[0-9][0-9_]*[eE][-+]?[0-9_]+ { +{FIXED_POINT_NUMBER_NO_DEC} { yylval->string = new std::string(yytext); return TOK_REALVAL; } @@ -373,7 +386,7 @@ and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { supply0 { return TOK_SUPPLY0; } supply1 { return TOK_SUPPLY1; } -"$"(display|write|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { +"$"(display[bho]?|write[bho]?|strobe|monitor|time|realtime|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { yylval->string = new std::string(yytext); return TOK_ID; } @@ -430,8 +443,13 @@ supply1 { return TOK_SUPPLY1; } "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { static bool printed_warning = false; if (!printed_warning) { - log_warning("Found one of those horrible `(synopsys|synthesis) translate_off' comments.\n" - "Yosys does support them but it is recommended to use `ifdef constructs instead!\n"); + log_warning( + "Encountered `translate_off' comment! Such legacy hot " + "comments are supported by Yosys, but are not part of " + "any formal language specification. Using a portable " + "and standards-compliant construct such as `ifdef is " + "recommended!\n" + ); printed_warning = true; } BEGIN(SYNOPSYS_TRANSLATE_OFF); @@ -446,8 +464,13 @@ supply1 { return TOK_SUPPLY1; } full_case { static bool printed_warning = false; if (!printed_warning) { - log_warning("Found one of those horrible `(synopsys|synthesis) full_case' comments.\n" - "Yosys does support them but it is recommended to use Verilog `full_case' attributes instead!\n"); + log_warning( + "Encountered `full_case' comment! Such legacy hot " + "comments are supported by Yosys, but are not part of " + "any formal language specification. Using the Verilog " + "`full_case' attribute or the SystemVerilog `unique' " + "or `unique0' keywords is recommended!\n" + ); printed_warning = true; } return TOK_SYNOPSYS_FULL_CASE; @@ -455,8 +478,13 @@ supply1 { return TOK_SUPPLY1; } parallel_case { static bool printed_warning = false; if (!printed_warning) { - log_warning("Found one of those horrible `(synopsys|synthesis) parallel_case' comments.\n" - "Yosys does support them but it is recommended to use Verilog `parallel_case' attributes instead!\n"); + log_warning( + "Encountered `parallel_case' comment! Such legacy hot " + "comments are supported by Yosys, but are not part of " + "any formal language specification. Using the Verilog " + "`parallel_case' attribute or the SystemVerilog " + "`unique' or `priority' keywords is recommended!\n" + ); printed_warning = true; } return TOK_SYNOPSYS_PARALLEL_CASE; @@ -528,11 +556,18 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { ".*" { return TOK_WILDCARD_CONNECT; } -"|=" { SV_KEYWORD(TOK_OR_ASSIGN); } -"&=" { SV_KEYWORD(TOK_AND_ASSIGN); } -"+=" { SV_KEYWORD(TOK_PLUS_ASSIGN); } +"|=" { SV_KEYWORD(TOK_BIT_OR_ASSIGN); } +"&=" { SV_KEYWORD(TOK_BIT_AND_ASSIGN); } +"+=" { SV_KEYWORD(TOK_ADD_ASSIGN); } "-=" { SV_KEYWORD(TOK_SUB_ASSIGN); } -"^=" { SV_KEYWORD(TOK_XOR_ASSIGN); } +"^=" { SV_KEYWORD(TOK_BIT_XOR_ASSIGN); } +"/=" { SV_KEYWORD(TOK_DIV_ASSIGN); } +"%=" { SV_KEYWORD(TOK_MOD_ASSIGN); } +"*=" { SV_KEYWORD(TOK_MUL_ASSIGN); } +"<<=" { SV_KEYWORD(TOK_SHL_ASSIGN); } +">>=" { SV_KEYWORD(TOK_SHR_ASSIGN); } +"<<<=" { SV_KEYWORD(TOK_SSHL_ASSIGN); } +">>>=" { SV_KEYWORD(TOK_SSHR_ASSIGN); } [-+]?[=*]> { if (!specify_mode) REJECT; @@ -545,6 +580,10 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { return TOK_SPECIFY_AND; } +{UNSIGNED_NUMBER}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; } +{FIXED_POINT_NUMBER_DEC}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; } +{FIXED_POINT_NUMBER_NO_DEC}{TIME_SCALE_SUFFIX} { return TOK_TIME_SCALE; } + "/*" { comment_caller=YY_START; BEGIN(COMMENT); } . /* ignore comment body */ \n /* ignore comment body */ diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 678ce6c8759..15a04eb2885 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,6 +33,8 @@ * */ +%require "3.0" + %{ #include #include @@ -54,7 +56,7 @@ namespace VERILOG_FRONTEND { dict *attr_list, default_attr_list; std::stack *> attr_list_stack; dict *albuf; - std::vector user_type_stack; + std::vector user_type_stack; dict pkg_user_types; std::vector ast_stack; struct AstNode *astbuf1, *astbuf2, *astbuf3; @@ -127,13 +129,22 @@ struct specify_rise_fall { specify_triple fall; }; +static void addWiretypeNode(std::string *name, AstNode *node) +{ + log_assert(node); + node->is_custom_type = true; + node->children.push_back(new AstNode(AST_WIRETYPE)); + node->children.back()->str = *name; + delete name; +} + static void addTypedefNode(std::string *name, AstNode *node) { log_assert(node); auto *tnode = new AstNode(AST_TYPEDEF, node); tnode->str = *name; - auto user_types = user_type_stack.back(); - (*user_types)[*name] = tnode; + auto &user_types = user_type_stack.back(); + user_types[*name] = tnode; if (current_ast_mod && current_ast_mod->type == AST_PACKAGE) { // typedef inside a package so we need the qualified name auto qname = current_ast_mod->str + "::" + (*name).substr(1); @@ -145,8 +156,7 @@ static void addTypedefNode(std::string *name, AstNode *node) static void enterTypeScope() { - auto user_types = new UserTypeMap(); - user_type_stack.push_back(user_types); + user_type_stack.push_back(UserTypeMap()); } static void exitTypeScope() @@ -157,25 +167,8 @@ static void exitTypeScope() static bool isInLocalScope(const std::string *name) { // tests if a name was declared in the current block scope - auto user_types = user_type_stack.back(); - return (user_types->count(*name) > 0); -} - -static AstNode *getTypeDefinitionNode(std::string type_name) -{ - // return the definition nodes from the typedef statement - auto user_types = user_type_stack.back(); - log_assert(user_types->count(type_name) > 0); - auto typedef_node = (*user_types)[type_name]; - log_assert(typedef_node->type == AST_TYPEDEF); - return typedef_node->children[0]; -} - -static AstNode *copyTypeDefinition(std::string type_name) -{ - // return a copy of the template from a typedef definition - auto typedef_node = getTypeDefinitionNode(type_name); - return typedef_node->clone(); + auto &user_types = user_type_stack.back(); + return (user_types.count(*name) > 0); } static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true) @@ -204,18 +197,29 @@ static AstNode *checkRange(AstNode *type_node, AstNode *range_node) range_node = makeRange(type_node->range_left, type_node->range_right, false); } } - if (range_node && range_node->children.size() != 2) { - frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [:], [+:], or [-:]"); + + if (range_node) { + bool valid = true; + if (range_node->type == AST_RANGE) { + valid = range_node->children.size() == 2; + } else { // AST_MULTIRANGE + for (auto child : range_node->children) { + valid = valid && child->children.size() == 2; + } + } + if (!valid) + frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form [:]"); } + return range_node; } static void rewriteRange(AstNode *rangeNode) { if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { - // SV array size [n], rewrite as [n-1:0] - rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true)); - rangeNode->children.push_back(AstNode::mkconst_int(0, false)); + // SV array size [n], rewrite as [0:n-1] + rangeNode->children.push_back(new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true))); + rangeNode->children[0] = AstNode::mkconst_int(0, false); } } @@ -230,6 +234,134 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) node->children.push_back(rangeNode); } +static void checkLabelsMatch(const char *element, const std::string *before, const std::string *after) +{ + if (!before && after) + frontend_verilog_yyerror("%s missing where end label (%s) was given.", + element, after->c_str() + 1); + if (before && after && *before != *after) + frontend_verilog_yyerror("%s (%s) and end label (%s) don't match.", + element, before->c_str() + 1, after->c_str() + 1); +} + +// This transforms a loop like +// for (genvar i = 0; i < 10; i++) begin : blk +// to +// genvar _i; +// for (_i = 0; _i < 10; _i++) begin : blk +// localparam i = _i; +// where `_i` is actually some auto-generated name. +static void rewriteGenForDeclInit(AstNode *loop) +{ + // check if this generate for loop contains an inline declaration + log_assert(loop->type == AST_GENFOR); + AstNode *decl = loop->children[0]; + if (decl->type == AST_ASSIGN_EQ) + return; + log_assert(decl->type == AST_GENVAR); + log_assert(loop->children.size() == 5); + + // identify each component of the loop + AstNode *init = loop->children[1]; + AstNode *cond = loop->children[2]; + AstNode *incr = loop->children[3]; + AstNode *body = loop->children[4]; + log_assert(init->type == AST_ASSIGN_EQ); + log_assert(incr->type == AST_ASSIGN_EQ); + log_assert(body->type == AST_GENBLOCK); + + // create a unique name for the genvar + std::string old_str = decl->str; + std::string new_str = stringf("$genfordecl$%d$%s", autoidx++, old_str.c_str()); + + // rename and move the genvar declaration to the containing description + decl->str = new_str; + loop->children.erase(loop->children.begin()); + log_assert(current_ast_mod != nullptr); + current_ast_mod->children.push_back(decl); + + // create a new localparam with old name so that the items in the loop + // can simply use the old name and shadow it as necessary + AstNode *indirect = new AstNode(AST_LOCALPARAM); + indirect->str = old_str; + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = new_str; + indirect->children.push_back(ident); + + body->children.insert(body->children.begin(), indirect); + + // only perform the renaming for the initialization, guard, and + // incrementation to enable proper shadowing of the synthetic localparam + std::function substitute = [&](AstNode *node) { + if (node->type == AST_IDENTIFIER && node->str == old_str) + node->str = new_str; + for (AstNode *child : node->children) + substitute(child); + }; + substitute(init); + substitute(cond); + substitute(incr); +} + +static void ensureAsgnExprAllowed() +{ + if (!sv_mode) + frontend_verilog_yyerror("Assignments within expressions are only supported in SystemVerilog mode."); + if (ast_stack.back()->type != AST_BLOCK) + frontend_verilog_yyerror("Assignments within expressions are only permitted within procedures."); +} + +// add a pre/post-increment/decrement statement +static const AstNode *addIncOrDecStmt(dict *stmt_attr, AstNode *lhs, + dict *op_attr, AST::AstNodeType op, + YYLTYPE begin, YYLTYPE end) +{ + AstNode *one = AstNode::mkconst_int(1, true); + AstNode *rhs = new AstNode(op, lhs->clone(), one); + if (op_attr != nullptr) + append_attr(rhs, op_attr); + AstNode *stmt = new AstNode(AST_ASSIGN_EQ, lhs, rhs); + SET_AST_NODE_LOC(stmt, begin, end); + if (stmt_attr != nullptr) + append_attr(stmt, stmt_attr); + ast_stack.back()->children.push_back(stmt); + return stmt; +} + +// create a pre/post-increment/decrement expression, and add the corresponding statement +static AstNode *addIncOrDecExpr(AstNode *lhs, dict *attr, AST::AstNodeType op, YYLTYPE begin, YYLTYPE end, bool undo) +{ + ensureAsgnExprAllowed(); + const AstNode *stmt = addIncOrDecStmt(nullptr, lhs, attr, op, begin, end); + log_assert(stmt->type == AST_ASSIGN_EQ); + AstNode *expr = stmt->children[0]->clone(); + if (undo) { + AstNode *minus_one = AstNode::mkconst_int(-1, true, 1); + expr = new AstNode(op, expr, minus_one); + } + SET_AST_NODE_LOC(expr, begin, end); + return expr; +} + +// add a binary operator assignment statement, e.g., a += b +static const AstNode *addAsgnBinopStmt(dict *attr, AstNode *lhs, AST::AstNodeType op, AstNode *rhs, YYLTYPE begin, YYLTYPE end) +{ + SET_AST_NODE_LOC(rhs, end, end); + if (op == AST_SHIFT_LEFT || op == AST_SHIFT_RIGHT || + op == AST_SHIFT_SLEFT || op == AST_SHIFT_SRIGHT) { + rhs = new AstNode(AST_TO_UNSIGNED, rhs); + SET_AST_NODE_LOC(rhs, end, end); + } + rhs = new AstNode(op, lhs->clone(), rhs); + AstNode *stmt = new AstNode(AST_ASSIGN_EQ, lhs, rhs); + SET_AST_NODE_LOC(rhs, begin, end); + SET_AST_NODE_LOC(stmt, begin, end); + ast_stack.back()->children.push_back(stmt); + if (attr != nullptr) + append_attr(stmt, attr); + return lhs; +} + %} %define api.prefix {frontend_verilog_yy} @@ -253,6 +385,8 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) struct specify_rise_fall *specify_rise_fall_ptr; bool boolean; char ch; + int integer; + YOSYS_NAMESPACE_PREFIX AST::AstNodeType ast_node_type; } %token TOK_STRING TOK_ID TOK_CONSTVAL TOK_REALVAL TOK_PRIMITIVE @@ -265,7 +399,7 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP %token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR TOK_WILDCARD_CONNECT %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC -%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_PLUS_ASSIGN TOK_ALWAYS TOK_INITIAL +%token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT %token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC @@ -277,18 +411,25 @@ static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) %token TOK_SUPPLY0 TOK_SUPPLY1 TOK_TO_SIGNED TOK_TO_UNSIGNED %token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_PROPERTY TOK_ENUM TOK_TYPEDEF %token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY -%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY -%token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE TOK_SHORTINT TOK_UNION -%token TOK_OR_ASSIGN TOK_XOR_ASSIGN TOK_AND_ASSIGN TOK_SUB_ASSIGN - -%type range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int -%type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list +%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_UNIQUE0 TOK_PRIORITY +%token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE TOK_SHORTINT TOK_LONGINT TOK_VOID TOK_UNION +%token TOK_BIT_OR_ASSIGN TOK_BIT_AND_ASSIGN TOK_BIT_XOR_ASSIGN TOK_ADD_ASSIGN +%token TOK_SUB_ASSIGN TOK_DIV_ASSIGN TOK_MOD_ASSIGN TOK_MUL_ASSIGN +%token TOK_SHL_ASSIGN TOK_SHR_ASSIGN TOK_SSHL_ASSIGN TOK_SSHR_ASSIGN +%token TOK_BIND TOK_TIME_SCALE + +%type range range_or_multirange non_opt_range non_opt_multirange +%type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list non_io_wire_type io_wire_type %type opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number %type type_name -%type opt_enum_init enum_type struct_type non_wire_data_type -%type opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff +%type opt_enum_init enum_type struct_type enum_struct_type func_return_type typedef_base_type +%type opt_property always_comb_or_latch always_or_always_ff +%type opt_signedness_default_signed opt_signedness_default_unsigned +%type integer_atom_type integer_vector_type %type attr case_attr %type struct_union +%type asgn_binop inc_or_dec_op +%type genvar_identifier %type specify_target %type specify_triple specify_opt_triple @@ -342,6 +483,7 @@ design: typedef_decl design | package design | interface design | + bind_directive design | %empty; attr: @@ -445,7 +587,6 @@ module: port_counter = 0; mod->str = *$4; append_attr(mod, $1); - delete $4; } module_para_opt module_args_opt ';' module_body TOK_ENDMODULE opt_label { if (port_stubs.size() != 0) frontend_verilog_yyerror("Missing details for module port `%s'.", @@ -453,7 +594,10 @@ module: SET_AST_NODE_LOC(ast_stack.back(), @2, @$); ast_stack.pop_back(); log_assert(ast_stack.size() == 1); + checkLabelsMatch("Module name", $4, $11); current_ast_mod = NULL; + delete $4; + delete $11; exitTypeScope(); }; @@ -491,18 +635,19 @@ optional_comma: module_arg_opt_assignment: '=' expr { if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) { - AstNode *wire = new AstNode(AST_IDENTIFIER); - wire->str = ast_stack.back()->children.back()->str; if (ast_stack.back()->children.back()->is_input) { AstNode *n = ast_stack.back()->children.back(); if (n->attributes.count(ID::defaultvalue)) delete n->attributes.at(ID::defaultvalue); n->attributes[ID::defaultvalue] = $2; - } else - if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic) - ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2)))); - else - ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); + } else { + AstNode *wire = new AstNode(AST_IDENTIFIER); + wire->str = ast_stack.back()->children.back()->str; + if (ast_stack.back()->children.back()->is_reg || ast_stack.back()->children.back()->is_logic) + ast_stack.back()->children.push_back(new AstNode(AST_INITIAL, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_LE, wire, $2)))); + else + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); + } } else frontend_verilog_yyerror("SystemVerilog interface in module port list cannot have a default value."); } | @@ -538,13 +683,14 @@ module_arg: ast_stack.back()->children.push_back(astbuf2); delete astbuf1; // really only needed if multiple instances of same type. } module_arg_opt_assignment | - attr wire_type range TOK_ID { + attr wire_type range_or_multirange TOK_ID { AstNode *node = $2; node->str = *$4; SET_AST_NODE_LOC(node, @4, @4); node->port_id = ++port_counter; - if ($3 != NULL) - node->children.push_back($3); + AstNode *range = checkRange(node, $3); + if (range != NULL) + node->children.push_back(range); if (!node->is_input && !node->is_output) frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $4->c_str()); if (node->is_reg && node->is_input && !node->is_output && !sv_mode) @@ -569,7 +715,10 @@ package: append_attr(mod, $1); } ';' package_body TOK_ENDPACKAGE opt_label { ast_stack.pop_back(); + checkLabelsMatch("Package name", $4, $9); current_ast_mod = NULL; + delete $4; + delete $9; exitTypeScope(); }; @@ -577,7 +726,7 @@ package_body: package_body package_body_stmt | %empty; package_body_stmt: - typedef_decl | localparam_decl | param_decl; + typedef_decl | localparam_decl | param_decl | task_func_decl; interface: TOK_INTERFACE { @@ -607,38 +756,99 @@ interface_body: interface_body_stmt: param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | - modport_stmt; + modport_stmt | bind_directive; + +bind_directive: + TOK_BIND { + AstNode *bnode = new AstNode(AST_BIND); + ast_stack.back()->children.push_back(bnode); + ast_stack.push_back(bnode); + } + bind_target { + // bind_target should have added at least one child + log_assert(ast_stack.back()->children.size() >= 1); + } + TOK_ID { + // The single_cell parser in cell_list_no_array uses astbuf1 as + // a sort of template for constructing cells. + astbuf1 = new AstNode(AST_CELL); + astbuf1->children.push_back(new AstNode(AST_CELLTYPE)); + astbuf1->children[0]->str = *$5; + delete $5; + } + cell_parameter_list_opt cell_list_no_array ';' { + // cell_list should have added at least one more child + log_assert(ast_stack.back()->children.size() >= 2); + delete astbuf1; + ast_stack.pop_back(); + }; + +// bind_target matches the target of the bind (everything before +// bind_instantiation in the IEEE 1800 spec). +// +// We can't use the BNF from the spec directly because it's ambiguous: +// something like "bind foo bar_i (.*)" can either be interpreted with "foo" as +// a module or interface identifier (matching bind_target_scope in the spec) or +// by considering foo as a degenerate hierarchical identifier with no '.' +// characters, followed by no bit select (which matches bind_target_instance in +// the spec). +// +// Instead, we resolve everything as an instance name and then deal with the +// ambiguity when converting to RTLIL / in the hierarchy pass. +bind_target: + bind_target_instance opt_bind_target_instance_list; + +// An optional list of target instances for a bind statement, introduced by a +// colon. +opt_bind_target_instance_list: + ':' bind_target_instance_list | + %empty; + +bind_target_instance_list: + bind_target_instance | + bind_target_instance_list ',' bind_target_instance; + +// A single target instance for a bind statement. The top of ast_stack will be +// the bind node where we should add it. +bind_target_instance: + hierarchical_id { + auto *node = new AstNode(AST_IDENTIFIER); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + }; + +mintypmax_expr: + expr { delete $1; } | + expr ':' expr ':' expr { delete $1; delete $3; delete $5; }; non_opt_delay: '#' TOK_ID { delete $2; } | '#' TOK_CONSTVAL { delete $2; } | '#' TOK_REALVAL { delete $2; } | - '#' '(' expr ')' { delete $3; } | - '#' '(' expr ':' expr ':' expr ')' { delete $3; delete $5; delete $7; }; + // our `expr` doesn't have time_scale, so we need the parenthesized variant + '#' TOK_TIME_SCALE | + '#' '(' TOK_TIME_SCALE ')' | + '#' '(' mintypmax_expr ')' | + '#' '(' mintypmax_expr ',' mintypmax_expr ')' | + '#' '(' mintypmax_expr ',' mintypmax_expr ',' mintypmax_expr ')'; delay: non_opt_delay | %empty; -wire_type: - { - astbuf3 = new AstNode(AST_WIRE); - current_wire_rand = false; - current_wire_const = false; - } wire_type_token_list { - $$ = astbuf3; - SET_RULE_LOC(@$, @2, @$); - }; +io_wire_type: + { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + wire_type_token_io wire_type_const_rand opt_wire_type_token wire_type_signedness + { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; -wire_type_token_list: - wire_type_token | - wire_type_token_list wire_type_token | - wire_type_token_io | - hierarchical_type_id { - astbuf3->is_custom_type = true; - astbuf3->children.push_back(new AstNode(AST_WIRETYPE)); - astbuf3->children.back()->str = *$1; - delete $1; - }; +non_io_wire_type: + { astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; } + wire_type_const_rand wire_type_token wire_type_signedness + { $$ = astbuf3; SET_RULE_LOC(@$, @2, @$); }; + +wire_type: + io_wire_type | + non_io_wire_type; wire_type_token_io: TOK_INPUT { @@ -652,29 +862,49 @@ wire_type_token_io: astbuf3->is_output = true; }; -wire_type_token: - TOK_WIRE { +wire_type_signedness: + TOK_SIGNED { astbuf3->is_signed = true; } | + TOK_UNSIGNED { astbuf3->is_signed = false; } | + %empty; + +wire_type_const_rand: + TOK_RAND TOK_CONST { + current_wire_rand = true; + current_wire_const = true; } | - TOK_WOR { - astbuf3->is_wor = true; + TOK_CONST { + current_wire_const = true; } | - TOK_WAND { - astbuf3->is_wand = true; + TOK_RAND { + current_wire_rand = true; } | + %empty; + +opt_wire_type_token: + wire_type_token | %empty; + +wire_type_token: + // nets + net_type { + } | + net_type logic_type { + } | + // regs TOK_REG { astbuf3->is_reg = true; } | - TOK_LOGIC { - astbuf3->is_logic = true; + TOK_VAR TOK_REG { + astbuf3->is_reg = true; } | + // logics TOK_VAR { astbuf3->is_logic = true; } | - TOK_INTEGER { - astbuf3->is_reg = true; - astbuf3->range_left = 31; - astbuf3->range_right = 0; - astbuf3->is_signed = true; + TOK_VAR logic_type { + astbuf3->is_logic = true; + } | + logic_type { + astbuf3->is_logic = true; } | TOK_GENVAR { astbuf3->type = AST_GENVAR; @@ -682,17 +912,40 @@ wire_type_token: astbuf3->is_signed = true; astbuf3->range_left = 31; astbuf3->range_right = 0; + }; + +net_type: + TOK_WOR { + astbuf3->is_wor = true; } | - TOK_SIGNED { - astbuf3->is_signed = true; + TOK_WAND { + astbuf3->is_wand = true; } | - TOK_RAND { - current_wire_rand = true; + TOK_WIRE; + +logic_type: + TOK_LOGIC { } | - TOK_CONST { - current_wire_const = true; + integer_atom_type { + astbuf3->range_left = $1 - 1; + astbuf3->range_right = 0; + astbuf3->is_signed = true; + } | + hierarchical_type_id { + addWiretypeNode($1, astbuf3); }; +integer_atom_type: + TOK_INTEGER { $$ = 32; } | + TOK_INT { $$ = 32; } | + TOK_SHORTINT { $$ = 16; } | + TOK_LONGINT { $$ = 64; } | + TOK_BYTE { $$ = 8; } ; + +integer_vector_type: + TOK_LOGIC { $$ = TOK_LOGIC; } | + TOK_REG { $$ = TOK_REG; } ; + non_opt_range: '[' expr ':' expr ']' { $$ = new AstNode(AST_RANGE); @@ -737,21 +990,17 @@ range_or_multirange: range { $$ = $1; } | non_opt_multirange { $$ = $1; }; -range_or_signed_int: - range { $$ = $1; } - | TOK_INTEGER { $$ = makeRange(); } - ; - module_body: module_body module_body_stmt | /* the following line makes the generate..endgenrate keywords optional */ module_body gen_stmt | + module_body gen_block | module_body ';' | %empty; module_body_stmt: task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt | - enum_decl | struct_decl | + enum_decl | struct_decl | bind_directive | always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; checker_decl: @@ -811,29 +1060,81 @@ task_func_decl: current_function_or_task = NULL; ast_stack.pop_back(); } | - attr TOK_FUNCTION opt_automatic opt_signed range_or_signed_int TOK_ID { + attr TOK_FUNCTION opt_automatic TOK_VOID TOK_ID { + // The difference between void functions and tasks is that + // always_comb's implicit sensitivity list behaves as if functions were + // inlined, but ignores signals read only in tasks. This only matters + // for event based simulation, and for synthesis we can treat a void + // function like a task. + current_function_or_task = new AstNode(AST_TASK); + current_function_or_task->str = *$5; + append_attr(current_function_or_task, $1); + ast_stack.back()->children.push_back(current_function_or_task); + ast_stack.push_back(current_function_or_task); + current_function_or_task_port_id = 1; + delete $5; + } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION { + current_function_or_task = NULL; + ast_stack.pop_back(); + } | + attr TOK_FUNCTION opt_automatic func_return_type TOK_ID { current_function_or_task = new AstNode(AST_FUNCTION); - current_function_or_task->str = *$6; + current_function_or_task->str = *$5; append_attr(current_function_or_task, $1); ast_stack.back()->children.push_back(current_function_or_task); ast_stack.push_back(current_function_or_task); AstNode *outreg = new AstNode(AST_WIRE); - outreg->str = *$6; - outreg->is_signed = $4; + outreg->str = *$5; + outreg->is_signed = false; outreg->is_reg = true; - if ($5 != NULL) { - outreg->children.push_back($5); - outreg->is_signed = $4 || $5->is_signed; - $5->is_signed = false; + if ($4 != NULL) { + outreg->children.push_back($4); + outreg->is_signed = $4->is_signed; + $4->is_signed = false; + outreg->is_custom_type = $4->type == AST_WIRETYPE; } current_function_or_task->children.push_back(outreg); current_function_or_task_port_id = 1; - delete $6; + delete $5; } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION { current_function_or_task = NULL; ast_stack.pop_back(); }; +func_return_type: + hierarchical_type_id { + $$ = new AstNode(AST_WIRETYPE); + $$->str = *$1; + delete $1; + } | + opt_type_vec opt_signedness_default_unsigned { + $$ = makeRange(0, 0, $2); + } | + opt_type_vec opt_signedness_default_unsigned non_opt_range { + $$ = $3; + $$->is_signed = $2; + } | + integer_atom_type opt_signedness_default_signed { + $$ = makeRange($1 - 1, 0, $2); + }; + +opt_type_vec: + %empty + | TOK_REG + | TOK_LOGIC + ; + +opt_signedness_default_signed: + %empty { $$ = true; } + | TOK_SIGNED { $$ = true; } + | TOK_UNSIGNED { $$ = false; } + ; +opt_signedness_default_unsigned: + %empty { $$ = false; } + | TOK_SIGNED { $$ = true; } + | TOK_UNSIGNED { $$ = false; } + ; + dpi_function_arg: TOK_ID TOK_ID { current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); @@ -859,14 +1160,6 @@ opt_automatic: TOK_AUTOMATIC | %empty; -opt_signed: - TOK_SIGNED { - $$ = true; - } | - %empty { - $$ = false; - }; - task_func_args_opt: '(' ')' | %empty | '(' { albuf = nullptr; @@ -883,8 +1176,12 @@ task_func_args: task_func_port | task_func_args ',' task_func_port; task_func_port: - attr wire_type range { + attr wire_type range_or_multirange { + bool prev_was_input = true; + bool prev_was_output = false; if (albuf) { + prev_was_input = astbuf1->is_input; + prev_was_output = astbuf1->is_output; delete astbuf1; if (astbuf2 != NULL) delete astbuf2; @@ -893,6 +1190,12 @@ task_func_port: albuf = $1; astbuf1 = $2; astbuf2 = checkRange(astbuf1, $3); + if (!astbuf1->is_input && !astbuf1->is_output) { + if (!sv_mode) + frontend_verilog_yyerror("task/function argument direction missing"); + astbuf1->is_input = prev_was_input; + astbuf1->is_output = prev_was_output; + } } wire_name | { if (!astbuf1) { @@ -1074,6 +1377,8 @@ specify_item: cell->children.back()->str = "\\DST"; delete $1; + delete limit; + delete limit2; }; specify_opt_triple: @@ -1339,11 +1644,8 @@ param_signed: } | %empty; param_integer: - TOK_INTEGER { - astbuf1->children.push_back(new AstNode(AST_RANGE)); - astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true)); - astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true)); - astbuf1->is_signed = true; + type_atom { + astbuf1->is_reg = false; }; param_real: @@ -1359,15 +1661,19 @@ param_range: }; param_integer_type: param_integer param_signed; -param_range_type: type_vec param_signed param_range; +param_range_type: + type_vec param_signed { + addRange(astbuf1, 0, 0); + } | + type_vec param_signed non_opt_range { + astbuf1->children.push_back($3); + }; param_implicit_type: param_signed param_range; param_type: param_integer_type | param_real | param_range_type | param_implicit_type | hierarchical_type_id { - astbuf1->is_custom_type = true; - astbuf1->children.push_back(new AstNode(AST_WIRETYPE)); - astbuf1->children.back()->str = *$1; + addWiretypeNode($1, astbuf1); }; param_decl: @@ -1392,7 +1698,26 @@ param_decl_list: single_param_decl | param_decl_list ',' single_param_decl; single_param_decl: - TOK_ID '=' expr { + single_param_decl_ident '=' expr { + AstNode *decl = ast_stack.back()->children.back(); + log_assert(decl->type == AST_PARAMETER || decl->type == AST_LOCALPARAM); + delete decl->children[0]; + decl->children[0] = $3; + } | + single_param_decl_ident { + AstNode *decl = ast_stack.back()->children.back(); + if (decl->type != AST_PARAMETER) { + log_assert(decl->type == AST_LOCALPARAM); + frontend_verilog_yyerror("localparam initialization is missing!"); + } + if (!sv_mode) + frontend_verilog_yyerror("Parameter defaults can only be omitted in SystemVerilog mode!"); + delete decl->children[0]; + decl->children.erase(decl->children.begin()); + }; + +single_param_decl_ident: + TOK_ID { AstNode *node; if (astbuf1 == nullptr) { if (!sv_mode) @@ -1403,10 +1728,9 @@ single_param_decl: node = astbuf1->clone(); } node->str = *$1; - delete node->children[0]; - node->children[0] = $3; ast_stack.back()->children.push_back(node); delete $1; + SET_AST_NODE_LOC(node, @1, @1); }; defparam_decl: @@ -1439,28 +1763,30 @@ enum_type: TOK_ENUM { // create the template for the names astbuf1 = new AstNode(AST_ENUM_ITEM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); - } enum_base_type '{' enum_name_list '}' { // create template for the enum vars - auto tnode = astbuf1->clone(); - delete astbuf1; - astbuf1 = tnode; - tnode->type = AST_WIRE; - tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str); - // drop constant but keep any range - delete tnode->children[0]; - tnode->children.erase(tnode->children.begin()); - $$ = astbuf1; } - ; + } enum_base_type '{' enum_name_list optional_comma '}' { + // create template for the enum vars + auto tnode = astbuf1->clone(); + delete astbuf1; + astbuf1 = tnode; + tnode->type = AST_WIRE; + tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str); + // drop constant but keep any range + delete tnode->children[0]; + tnode->children.erase(tnode->children.begin()); + $$ = astbuf1; + }; enum_base_type: type_atom type_signing | type_vec type_signing range { if ($3) astbuf1->children.push_back($3); } | %empty { astbuf1->is_reg = true; addRange(astbuf1); } ; -type_atom: TOK_INTEGER { astbuf1->is_reg = true; addRange(astbuf1); } // 4-state signed - | TOK_INT { astbuf1->is_reg = true; addRange(astbuf1); } // 2-state signed - | TOK_SHORTINT { astbuf1->is_reg = true; addRange(astbuf1, 15, 0); } // 2-state signed - | TOK_BYTE { astbuf1->is_reg = true; addRange(astbuf1, 7, 0); } // 2-state signed - ; +type_atom: + integer_atom_type { + astbuf1->is_reg = true; + astbuf1->is_signed = true; + addRange(astbuf1, $1 - 1, 0); + }; type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned | TOK_LOGIC { astbuf1->is_logic = true; } // unsigned @@ -1520,7 +1846,12 @@ enum_decl: enum_type enum_var_list ';' { delete $1; } // struct or union ////////////////// -struct_decl: struct_type struct_var_list ';' { delete astbuf2; } +struct_decl: + attr struct_type { + append_attr($2, $1); + } struct_var_list ';' { + delete astbuf2; + } ; struct_type: struct_union { astbuf2 = $1; } struct_body { $$ = astbuf2; } @@ -1569,28 +1900,17 @@ struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_toke ; member_type_token: - member_type - | hierarchical_type_id { - // use a clone of the typedef definition nodes - auto template_node = copyTypeDefinition(*$1); - delete $1; - switch (template_node->type) { - case AST_WIRE: - template_node->type = AST_STRUCT_ITEM; - break; - case AST_STRUCT: - case AST_UNION: - break; - default: - frontend_verilog_yyerror("Invalid type for struct member: %s", type2str(template_node->type).c_str()); - } - delete astbuf1; - astbuf1 = template_node; - } - | struct_union { + member_type range_or_multirange { + AstNode *range = checkRange(astbuf1, $2); + if (range) + astbuf1->children.push_back(range); + } + | { + delete astbuf1; + } struct_union { // stash state on ast_stack ast_stack.push_back(astbuf2); - astbuf2 = $1; + astbuf2 = $2; } struct_body { astbuf1 = astbuf2; // recover state @@ -1600,7 +1920,8 @@ member_type_token: ; member_type: type_atom type_signing - | type_vec type_signing range_or_multirange { if ($3) astbuf1->children.push_back($3); } + | type_vec type_signing + | hierarchical_type_id { addWiretypeNode($1, astbuf1); } ; struct_var_list: struct_var @@ -1620,7 +1941,7 @@ struct_var: TOK_ID { auto *var_node = astbuf2->clone(); ///////// wire_decl: - attr wire_type range { + attr wire_type range_or_multirange { albuf = $1; astbuf1 = $2; astbuf2 = checkRange(astbuf1, $3); @@ -1749,7 +2070,13 @@ wire_name: } rewriteAsMemoryNode(node, $2); } - if (current_function_or_task == NULL) { + if (current_function_or_task) { + if (node->is_input || node->is_output) + node->port_id = current_function_or_task_port_id++; + } else if (ast_stack.back()->type == AST_GENBLOCK) { + if (node->is_input || node->is_output) + frontend_verilog_yyerror("Cannot declare module port `%s' within a generate block.", $1->c_str()); + } else { if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { port_stubs[*$1] = ++port_counter; } @@ -1764,9 +2091,6 @@ wire_name: if (node->is_input || node->is_output) frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str()); } - } else { - if (node->is_input || node->is_output) - node->port_id = current_function_or_task_port_id++; } //FIXME: for some reason, TOK_ID has a location which always points to one column *after* the real last column... SET_AST_NODE_LOC(node, @1, @1); @@ -1793,23 +2117,46 @@ type_name: TOK_ID // first time seen ; typedef_decl: - TOK_TYPEDEF wire_type range type_name range_or_multirange ';' { + TOK_TYPEDEF typedef_base_type range_or_multirange type_name range_or_multirange ';' { astbuf1 = $2; astbuf2 = checkRange(astbuf1, $3); if (astbuf2) astbuf1->children.push_back(astbuf2); if ($5 != NULL) { - if (!astbuf2) { + if (!astbuf2 && !astbuf1->is_custom_type) { addRange(astbuf1, 0, 0, false); } rewriteAsMemoryNode(astbuf1, $5); } addTypedefNode($4, astbuf1); } - | TOK_TYPEDEF non_wire_data_type type_name ';' { addTypedefNode($3, $2); } + | TOK_TYPEDEF enum_struct_type type_name ';' { addTypedefNode($3, $2); } ; -non_wire_data_type: +typedef_base_type: + hierarchical_type_id { + $$ = new AstNode(AST_WIRE); + $$->is_logic = true; + addWiretypeNode($1, $$); + } | + integer_vector_type opt_signedness_default_unsigned { + $$ = new AstNode(AST_WIRE); + if ($1 == TOK_REG) { + $$->is_reg = true; + } else { + $$->is_logic = true; + } + $$->is_signed = $2; + } | + integer_atom_type opt_signedness_default_signed { + $$ = new AstNode(AST_WIRE); + $$->is_logic = true; + $$->is_signed = $2; + $$->range_left = $1 - 1; + $$->range_right = 0; + }; + +enum_struct_type: enum_type | struct_type ; @@ -1846,6 +2193,9 @@ cell_list: cell_list ',' single_cell; single_cell: + single_cell_no_array | single_cell_arraylist; + +single_cell_no_array: TOK_ID { astbuf2 = astbuf1->clone(); if (astbuf2->type != AST_PRIMITIVE) @@ -1854,7 +2204,9 @@ single_cell: ast_stack.back()->children.push_back(astbuf2); } '(' cell_port_list ')' { SET_AST_NODE_LOC(astbuf2, @1, @$); - } | + } + +single_cell_arraylist: TOK_ID non_opt_range { astbuf2 = astbuf1->clone(); if (astbuf2->type != AST_PRIMITIVE) @@ -1865,6 +2217,10 @@ single_cell: SET_AST_NODE_LOC(astbuf2, @1, @$); }; +cell_list_no_array: + single_cell_no_array | + cell_list_no_array ',' single_cell_no_array; + prim_list: single_prim | prim_list ',' single_prim; @@ -1972,6 +2328,7 @@ cell_port: if (!sv_mode) frontend_verilog_yyerror("Wildcard port connections are only supported in SystemVerilog mode."); astbuf2->attributes[ID::wildcard_port_conns] = AstNode::mkconst_int(1, false); + free_attr($1); }; always_comb_or_latch: @@ -2140,7 +2497,7 @@ assert: delete $5; } else { AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); - SET_AST_NODE_LOC(node, @1, @6); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2153,7 +2510,7 @@ assert: delete $5; } else { AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5); - SET_AST_NODE_LOC(node, @1, @6); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2166,7 +2523,7 @@ assert: delete $6; } else { AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); - SET_AST_NODE_LOC(node, @1, @7); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2179,7 +2536,7 @@ assert: delete $6; } else { AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6); - SET_AST_NODE_LOC(node, @1, @7); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2189,7 +2546,7 @@ assert: } | opt_sva_label TOK_COVER opt_property '(' expr ')' ';' { AstNode *node = new AstNode(AST_COVER, $5); - SET_AST_NODE_LOC(node, @1, @6); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) { node->str = *$1; delete $1; @@ -2198,7 +2555,7 @@ assert: } | opt_sva_label TOK_COVER opt_property '(' ')' ';' { AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); - SET_AST_NODE_LOC(node, @1, @5); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @5); if ($1 != nullptr) { node->str = *$1; delete $1; @@ -2207,7 +2564,7 @@ assert: } | opt_sva_label TOK_COVER ';' { AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); - SET_AST_NODE_LOC(node, @1, @2); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @2); if ($1 != nullptr) { node->str = *$1; delete $1; @@ -2219,7 +2576,7 @@ assert: delete $5; } else { AstNode *node = new AstNode(AST_ASSUME, $5); - SET_AST_NODE_LOC(node, @1, @6); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2234,7 +2591,7 @@ assert: delete $6; } else { AstNode *node = new AstNode(AST_FAIR, $6); - SET_AST_NODE_LOC(node, @1, @7); + SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); @@ -2325,17 +2682,11 @@ simple_behavioral_stmt: SET_AST_NODE_LOC(node, @2, @5); append_attr(node, $1); } | - attr lvalue TOK_INCREMENT { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_ADD, $2->clone(), AstNode::mkconst_int(1, true))); - ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @2, @3); - append_attr(node, $1); + attr lvalue attr inc_or_dec_op { + addIncOrDecStmt($1, $2, $3, $4, @1, @4); } | - attr lvalue TOK_DECREMENT { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_SUB, $2->clone(), AstNode::mkconst_int(1, true))); - ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @2, @3); - append_attr(node, $1); + attr inc_or_dec_op attr lvalue { + addIncOrDecStmt($1, $4, $3, $2, @1, @4); } | attr lvalue OP_LE delay expr { AstNode *node = new AstNode(AST_ASSIGN_LE, $2, $5); @@ -2343,45 +2694,76 @@ simple_behavioral_stmt: SET_AST_NODE_LOC(node, @2, @5); append_attr(node, $1); } | - attr lvalue TOK_XOR_ASSIGN delay expr { - AstNode *xor_node = new AstNode(AST_BIT_XOR, $2->clone(), $5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, xor_node); - SET_AST_NODE_LOC(xor_node, @2, @5); - SET_AST_NODE_LOC(node, @2, @5); - ast_stack.back()->children.push_back(node); - append_attr(node, $1); - } | - attr lvalue TOK_OR_ASSIGN delay expr { - AstNode *or_node = new AstNode(AST_BIT_OR, $2->clone(), $5); - SET_AST_NODE_LOC(or_node, @2, @5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, or_node); - SET_AST_NODE_LOC(node, @2, @5); - ast_stack.back()->children.push_back(node); - append_attr(node, $1); - } | - attr lvalue TOK_PLUS_ASSIGN delay expr { - AstNode *add_node = new AstNode(AST_ADD, $2->clone(), $5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, add_node); - SET_AST_NODE_LOC(node, @2, @5); - SET_AST_NODE_LOC(add_node, @2, @5); + attr lvalue asgn_binop delay expr { + addAsgnBinopStmt($1, $2, $3, $5, @2, @5); + }; + +asgn_binop: + TOK_BIT_OR_ASSIGN { $$ = AST_BIT_OR; } | + TOK_BIT_AND_ASSIGN { $$ = AST_BIT_AND; } | + TOK_BIT_XOR_ASSIGN { $$ = AST_BIT_XOR; } | + TOK_ADD_ASSIGN { $$ = AST_ADD; } | + TOK_SUB_ASSIGN { $$ = AST_SUB; } | + TOK_DIV_ASSIGN { $$ = AST_DIV; } | + TOK_MOD_ASSIGN { $$ = AST_MOD; } | + TOK_MUL_ASSIGN { $$ = AST_MUL; } | + TOK_SHL_ASSIGN { $$ = AST_SHIFT_LEFT; } | + TOK_SHR_ASSIGN { $$ = AST_SHIFT_RIGHT; } | + TOK_SSHL_ASSIGN { $$ = AST_SHIFT_SLEFT; } | + TOK_SSHR_ASSIGN { $$ = AST_SHIFT_SRIGHT; } ; + +inc_or_dec_op: + // NOTE: These should only be permitted in SV mode, but Yosys has + // allowed them in all modes since support for them was added in 2017. + TOK_INCREMENT { $$ = AST_ADD; } | + TOK_DECREMENT { $$ = AST_SUB; } ; + +for_initialization: + TOK_ID '=' expr { + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = *$1; + AstNode *node = new AstNode(AST_ASSIGN_EQ, ident, $3); ast_stack.back()->children.push_back(node); - append_attr(node, $1); + SET_AST_NODE_LOC(node, @1, @3); + delete $1; } | - attr lvalue TOK_SUB_ASSIGN delay expr { - AstNode *sub_node = new AstNode(AST_SUB, $2->clone(), $5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, sub_node); - SET_AST_NODE_LOC(node, @2, @5); - SET_AST_NODE_LOC(sub_node, @2, @5); - ast_stack.back()->children.push_back(node); - append_attr(node, $1); + non_io_wire_type range TOK_ID { + frontend_verilog_yyerror("For loop variable declaration is missing initialization!"); } | - attr lvalue TOK_AND_ASSIGN delay expr { - AstNode *and_node = new AstNode(AST_BIT_AND, $2->clone(), $5); - AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, and_node); - SET_AST_NODE_LOC(node, @2, @5); - SET_AST_NODE_LOC(and_node, @2, @5); - ast_stack.back()->children.push_back(node); - append_attr(node, $1); + non_io_wire_type range TOK_ID '=' expr { + if (!sv_mode) + frontend_verilog_yyerror("For loop inline variable declaration is only supported in SystemVerilog mode!"); + + // loop variable declaration + AstNode *wire = $1; + AstNode *range = checkRange(wire, $2); + if (range != nullptr) + wire->children.push_back(range); + SET_AST_NODE_LOC(wire, @1, @3); + SET_AST_NODE_LOC(range, @2, @2); + + AstNode *ident = new AstNode(AST_IDENTIFIER); + ident->str = *$3; + wire->str = *$3; + delete $3; + + AstNode *loop = ast_stack.back(); + AstNode *parent = ast_stack.at(ast_stack.size() - 2); + log_assert(parent->children.back() == loop); + + // loop variable initialization + AstNode *asgn = new AstNode(AST_ASSIGN_EQ, ident, $5); + loop->children.push_back(asgn); + SET_AST_NODE_LOC(asgn, @3, @5); + SET_AST_NODE_LOC(ident, @3, @3); + + // inject a wrapping block to declare the loop variable and + // contain the current loop + AstNode *wrapper = new AstNode(AST_BLOCK); + wrapper->str = "$fordecl_block$" + std::to_string(autoidx++); + wrapper->children.push_back(wire); + wrapper->children.push_back(loop); + parent->children.back() = wrapper; // replaces `loop` }; // this production creates the obligatory if-else shift/reduce conflict @@ -2400,6 +2782,7 @@ behavioral_stmt: ast_stack.push_back(node); append_attr(node, $1); } opt_arg_list ';'{ + SET_AST_NODE_LOC(ast_stack.back(), @2, @5); ast_stack.pop_back(); } | attr TOK_MSG_TASKS { @@ -2410,6 +2793,7 @@ behavioral_stmt: ast_stack.push_back(node); append_attr(node, $1); } opt_arg_list ';'{ + SET_AST_NODE_LOC(ast_stack.back(), @2, @5); ast_stack.pop_back(); } | attr TOK_BEGIN { @@ -2423,8 +2807,17 @@ behavioral_stmt: node->str = *$4; } behavioral_stmt_list TOK_END opt_label { exitTypeScope(); - if ($4 != NULL && $8 != NULL && *$4 != *$8) - frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1); + checkLabelsMatch("Begin label", $4, $8); + AstNode *node = ast_stack.back(); + // In SystemVerilog, unnamed blocks with block item declarations + // create an implicit hierarchy scope + if (sv_mode && node->str.empty()) + for (const AstNode* child : node->children) + if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER + || child->type == AST_LOCALPARAM || child->type == AST_TYPEDEF) { + node->str = "$unnamed_block$" + std::to_string(autoidx++); + break; + } SET_AST_NODE_LOC(ast_stack.back(), @2, @8); delete $4; delete $8; @@ -2435,10 +2828,11 @@ behavioral_stmt: ast_stack.back()->children.push_back(node); ast_stack.push_back(node); append_attr(node, $1); - } simple_behavioral_stmt ';' expr { + } for_initialization ';' expr { ast_stack.back()->children.push_back($7); } ';' simple_behavioral_stmt ')' { AstNode *block = new AstNode(AST_BLOCK); + block->str = "$for_loop$" + std::to_string(autoidx++); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { @@ -2505,20 +2899,21 @@ behavioral_stmt: ast_stack.pop_back(); }; -unique_case_attr: - %empty { - $$ = false; +case_attr: + attr { + $$ = $1; } | - TOK_PRIORITY case_attr { - $$ = $2; + attr TOK_UNIQUE0 { + (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + $$ = $1; } | - TOK_UNIQUE case_attr { - $$ = true; - }; - -case_attr: - attr unique_case_attr { - if ($2) (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); + attr TOK_PRIORITY { + (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + $$ = $1; + } | + attr TOK_UNIQUE { + (*$1)[ID::full_case] = AstNode::mkconst_int(1, false); + (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); $$ = $1; }; @@ -2632,6 +3027,7 @@ rvalue: hierarchical_id '[' expr ']' '.' rvalue { $$ = new AstNode(AST_PREFIX, $3, $6); $$->str = *$1; + SET_AST_NODE_LOC($$, @1, @6); delete $1; } | hierarchical_id range { @@ -2688,6 +3084,7 @@ single_arg: module_gen_body: module_gen_body gen_stmt_or_module_body_stmt | + module_gen_body gen_block | %empty; gen_stmt_or_module_body_stmt: @@ -2696,16 +3093,50 @@ gen_stmt_or_module_body_stmt: free_attr($1); }; +genvar_identifier: + TOK_ID { + $$ = new AstNode(AST_IDENTIFIER); + $$->str = *$1; + delete $1; + }; + +genvar_initialization: + TOK_GENVAR genvar_identifier { + frontend_verilog_yyerror("Generate for loop variable declaration is missing initialization!"); + } | + TOK_GENVAR genvar_identifier '=' expr { + if (!sv_mode) + frontend_verilog_yyerror("Generate for loop inline variable declaration is only supported in SystemVerilog mode!"); + AstNode *node = new AstNode(AST_GENVAR); + node->is_reg = true; + node->is_signed = true; + node->range_left = 31; + node->range_right = 0; + node->str = $2->str; + node->children.push_back(checkRange(node, nullptr)); + ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node, @1, @4); + node = new AstNode(AST_ASSIGN_EQ, $2, $4); + ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node, @1, @4); + } | + genvar_identifier '=' expr { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $3); + ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node, @1, @3); + }; + // this production creates the obligatory if-else shift/reduce conflict gen_stmt: TOK_FOR '(' { AstNode *node = new AstNode(AST_GENFOR); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); - } simple_behavioral_stmt ';' expr { + } genvar_initialization ';' expr { ast_stack.back()->children.push_back($6); } ';' simple_behavioral_stmt ')' gen_stmt_block { SET_AST_NODE_LOC(ast_stack.back(), @1, @11); + rewriteGenForDeclInit(ast_stack.back()); ast_stack.pop_back(); } | TOK_IF '(' expr ')' { @@ -2713,12 +3144,7 @@ gen_stmt: ast_stack.back()->children.push_back(node); ast_stack.push_back(node); ast_stack.back()->children.push_back($3); - AstNode *block = new AstNode(AST_GENBLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); - } gen_stmt_block { - ast_stack.pop_back(); - } opt_gen_else { + } gen_stmt_block opt_gen_else { SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | @@ -2731,6 +3157,18 @@ gen_stmt: SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | + TOK_MSG_TASKS { + AstNode *node = new AstNode(AST_TECALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } opt_arg_list ';'{ + SET_AST_NODE_LOC(ast_stack.back(), @1, @3); + ast_stack.pop_back(); + }; + +gen_block: TOK_BEGIN { enterTypeScope(); } opt_label { @@ -2740,22 +3178,14 @@ gen_stmt: ast_stack.push_back(node); } module_gen_body TOK_END opt_label { exitTypeScope(); + checkLabelsMatch("Begin label", $3, $7); delete $3; delete $7; SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); - } | - TOK_MSG_TASKS { - AstNode *node = new AstNode(AST_TECALL); - node->str = *$1; - delete $1; - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - } opt_arg_list ';'{ - SET_AST_NODE_LOC(ast_stack.back(), @1, @3); - ast_stack.pop_back(); }; +// result is wrapped in a genblock only if necessary gen_stmt_block: { AstNode *node = new AstNode(AST_GENBLOCK); @@ -2764,7 +3194,7 @@ gen_stmt_block: } gen_stmt_or_module_body_stmt { SET_AST_NODE_LOC(ast_stack.back(), @2, @2); ast_stack.pop_back(); - }; + } | gen_block; opt_gen_else: TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; @@ -2780,6 +3210,14 @@ expr: $$->children.push_back($6); SET_AST_NODE_LOC($$, @1, @$); append_attr($$, $3); + } | + inc_or_dec_op attr rvalue { + $$ = addIncOrDecExpr($3, $2, $1, @1, @3, false); + } | + // TODO: Attributes are allowed in the middle here, but they create some + // non-trivial conflicts that don't seem worth solving for now. + rvalue inc_or_dec_op { + $$ = addIncOrDecExpr($1, nullptr, $2, @1, @2, true); }; basic_expr: @@ -3067,6 +3505,17 @@ basic_expr: frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); $$ = new AstNode(AST_CAST_SIZE, $1, $4); SET_AST_NODE_LOC($$, @1, @4); + } | + '(' expr '=' expr ')' { + ensureAsgnExprAllowed(); + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $4); + ast_stack.back()->children.push_back(node); + SET_AST_NODE_LOC(node, @2, @4); + $$ = $2->clone(); + } | + '(' expr asgn_binop expr ')' { + ensureAsgnExprAllowed(); + $$ = addAsgnBinopStmt(nullptr, $2, $3, $4, @2, @4)-> clone(); }; concat_list: diff --git a/guidelines/Checklists b/guidelines/Checklists new file mode 100644 index 00000000000..75af12fa9e4 --- /dev/null +++ b/guidelines/Checklists @@ -0,0 +1,116 @@ +Checklist for adding internal cell types +======================================== + +Things to do right away: + + - Add to kernel/celltypes.h (incl. eval() handling for non-mem cells) + - Add to InternalCellChecker::check() in kernel/rtlil.cc + - Add to techlibs/common/simlib.v + - Add to techlibs/common/techmap.v + +Things to do after finalizing the cell interface: + + - Add support to kernel/satgen.h for the new cell type + - Add to docs/source/CHAPTER_CellLib.rst (or just add a fixme to the bottom) + - Maybe add support to the Verilog backend for dumping such cells as expression + + + +Checklist for creating Yosys releases +===================================== + +Update the CHANGELOG file: + + cd ~yosys + gitk & + vi CHANGELOG + + +Update and check documentation: + + cd ~yosys + make docs + - sanity check the figures in docs/images + - if there are any odd things -> investigate + + cd ~yosys + vi README guidelines/* + - is the information provided in those file still up to date + + +Then with default config setting: + + cd ~yosys + make vgtest + + cd ~yosys + ./yosys -p 'proc; show' tests/simple/fiedler-cooley.v + ./yosys -p 'proc; opt; show' tests/simple/fiedler-cooley.v + ./yosys -p 'synth; show' tests/simple/fiedler-cooley.v + ./yosys -p 'synth_xilinx -top up3down5; show' tests/simple/fiedler-cooley.v + + cd ~yosys/examples/cmos + bash testbench.sh + + cd ~yosys/examples/basys3 + bash run.sh + + +Test building plugins with various of the standard passes: + + yosys-config --build test.so equiv_simple.cc + - also check the code examples in guidelines/GettingStarted + + +And if a version of the verific library is currently available: + + cd ~yosys + cat frontends/verific/build_amd64.txt + - follow instructions + + cd frontends/verific + ../../yosys test_navre.ys + + +Finally run all tests with "make config-{clang,gcc}": + + cd ~yosys + make clean + make test + make ystests + make vloghtb + make install + + cd ~yosys-bigsim + make clean + make full + + cd ~vloghammer + make purge gen_issues gen_samples + make SYN_LIST="yosys" SIM_LIST="icarus yosim verilator" REPORT_FULL=1 world + chromium-browser report.html + + +Release: + + - set YOSYS_VER to x.y.z in Makefile + - remove "bumpversion" target from Makefile + - update version string in CHANGELOG + git commit -am "Yosys x.y.z" + + - push tag to github + - post changelog on github + - post short release note on reddit + + +Updating the website: + + cd ~yosys + make install + + cd ~yosys-web + make update_show + git commit -am update + make push + + - Read the Docs updates handled by Jenkins on source change diff --git a/CodeOfConduct b/guidelines/CodeOfConduct similarity index 92% rename from CodeOfConduct rename to guidelines/CodeOfConduct index 4f779977bbe..92decd3b6a5 100644 --- a/CodeOfConduct +++ b/guidelines/CodeOfConduct @@ -55,9 +55,8 @@ further defined and clarified by project maintainers. Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at clifford@clifford.at (and/or -cliffordvienna@gmail.com if you think your mail to the other address got -stuck in the spam filter). All complaints will be reviewed and investigated and +reported by contacting the project team at contact@yosyshq.com and/or +claire@clairexen.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement diff --git a/guidelines/CodingStyle b/guidelines/CodingStyle new file mode 100644 index 00000000000..ee1e1a2b6a1 --- /dev/null +++ b/guidelines/CodingStyle @@ -0,0 +1,34 @@ +Coding Style +============ + + +Formatting of code +------------------ + +- Yosys code is using tabs for indentation. Tabs are 8 characters. + +- A continuation of a statement in the following line is indented by + two additional tabs. + +- Lines are as long as you want them to be. A good rule of thumb is + to break lines at about column 150. + +- Opening braces can be put on the same or next line as the statement + opening the block (if, switch, for, while, do). Put the opening brace + on its own line for larger blocks, especially blocks that contains + blank lines. + +- Otherwise stick to the Linux Kernel Coding Style: + https://www.kernel.org/doc/Documentation/CodingStyle + + +C++ Language +------------- + +Yosys is written in C++11. + +In general Yosys uses "int" instead of "size_t". To avoid compiler +warnings for implicit type casts, always use "GetSize(foobar)" instead +of "foobar.size()". (GetSize() is defined in kernel/yosys.h) + +Use range-based for loops whenever applicable. \ No newline at end of file diff --git a/CodingReadme b/guidelines/GettingStarted similarity index 53% rename from CodingReadme rename to guidelines/GettingStarted index 7d4ded93d2f..110f6318518 100644 --- a/CodingReadme +++ b/guidelines/GettingStarted @@ -1,10 +1,3 @@ - -This file contains some very brief documentation on things like programming APIs. -Also consult the Yosys manual and the section about programming in the presentation. -(Both can be downloaded as PDF from the yosys webpage.) - - ---snip-- only the lines below this mark are included in the yosys manual --snip-- Getting Started =============== @@ -198,8 +191,8 @@ Example Code The following yosys commands are a good starting point if you are looking for examples of how to use the Yosys API: - manual/CHAPTER_Prog/stubnets.cc - manual/PRESENTATION_Prog/my_cmd.cc + docs/source/code_examples/stubnets/stubnets.cc + docs/resources/PRESENTATION_Prog/my_cmd.cc Script Passes @@ -253,304 +246,4 @@ Notes on the existing codebase For historical reasons not all parts of Yosys adhere to the current coding style. When adding code to existing parts of the system, adhere to this guide -for the new code instead of trying to mimic the style of the surrounding code. - - - -Coding Style -============ - - -Formatting of code ------------------- - -- Yosys code is using tabs for indentation. Tabs are 8 characters. - -- A continuation of a statement in the following line is indented by - two additional tabs. - -- Lines are as long as you want them to be. A good rule of thumb is - to break lines at about column 150. - -- Opening braces can be put on the same or next line as the statement - opening the block (if, switch, for, while, do). Put the opening brace - on its own line for larger blocks, especially blocks that contains - blank lines. - -- Otherwise stick to the Linux Kernel Coding Style: - https://www.kernel.org/doc/Documentation/CodingStyle - - -C++ Language -------------- - -Yosys is written in C++11. At the moment only constructs supported by -gcc 4.8 are allowed in Yosys code. This will change in future releases. - -In general Yosys uses "int" instead of "size_t". To avoid compiler -warnings for implicit type casts, always use "GetSize(foobar)" instead -of "foobar.size()". (GetSize() is defined in kernel/yosys.h) - -Use range-based for loops whenever applicable. - - ---snap-- only the lines above this mark are included in the yosys manual --snap-- - - -Creating the Visual Studio Template Project -=========================================== - -1. Create an empty Visual C++ Win32 Console App project - - Microsoft Visual Studio Express 2013 for Windows Desktop - Open New Project Wizard (File -> New Project..) - - Project Name: YosysVS - Solution Name: YosysVS - [X] Create directory for solution - [ ] Add to source control - - [X] Console applications - [X] Empty Project - [ ] SDL checks - -2. Open YosysVS Project Properties - - Select Configuration: All Configurations - - C/C++ -> General -> Additional Include Directories - Add: ..\yosys - - C/C++ -> Preprocessor -> Preprocessor Definitions - Add: _YOSYS_;_CRT_SECURE_NO_WARNINGS - -3. Resulting file system tree: - - YosysVS/ - YosysVS/YosysVS - YosysVS/YosysVS/YosysVS.vcxproj - YosysVS/YosysVS/YosysVS.vcxproj.filters - YosysVS/YosysVS.sdf - YosysVS/YosysVS.sln - YosysVS/YosysVS.v12.suo - -4. Zip YosysVS as YosysVS-Tpl-v1.zip - - - -Checklist for adding internal cell types -======================================== - -Things to do right away: - - - Add to kernel/celltypes.h (incl. eval() handling for non-mem cells) - - Add to InternalCellChecker::check() in kernel/rtlil.cc - - Add to techlibs/common/simlib.v - - Add to techlibs/common/techmap.v - -Things to do after finalizing the cell interface: - - - Add support to kernel/satgen.h for the new cell type - - Add to manual/CHAPTER_CellLib.tex (or just add a fixme to the bottom) - - Maybe add support to the Verilog backend for dumping such cells as expression - - - -Checklist for creating Yosys releases -===================================== - -Update the CHANGELOG file: - - cd ~yosys - gitk & - vi CHANGELOG - - -Update and check documentation: - - cd ~yosys - make update-manual - make manual - - sanity check the figures in the appnotes and presentation - - if there are any odd things -> investigate - - make cosmetic changes to the .tex files if necessary - - cd ~yosys - vi README CodingReadme - - is the information provided in those file still up to date - - -Then with default config setting: - - cd ~yosys - make vgtest - - cd ~yosys - ./yosys -p 'proc; show' tests/simple/fiedler-cooley.v - ./yosys -p 'proc; opt; show' tests/simple/fiedler-cooley.v - ./yosys -p 'synth; show' tests/simple/fiedler-cooley.v - ./yosys -p 'synth_xilinx -top up3down5; show' tests/simple/fiedler-cooley.v - - cd ~yosys/examples/cmos - bash testbench.sh - - cd ~yosys/examples/basys3 - bash run.sh - - -Test building plugins with various of the standard passes: - - yosys-config --build test.so equiv_simple.cc - - also check the code examples in CodingReadme - - -And if a version of the verific library is currently available: - - cd ~yosys - cat frontends/verific/build_amd64.txt - - follow instructions - - cd frontends/verific - ../../yosys test_navre.ys - - -Finally run all tests with "make config-{clang,gcc,gcc-4.8}": - - cd ~yosys - make clean - make test - make ystests - make vloghtb - make install - - cd ~yosys-bigsim - make clean - make full - - cd ~vloghammer - make purge gen_issues gen_samples - make SYN_LIST="yosys" SIM_LIST="icarus yosim verilator" REPORT_FULL=1 world - chromium-browser report.html - - -Release: - - - set YOSYS_VER to x.y.z in Makefile - - remove "bumpversion" target from Makefile - - update version string in CHANGELOG - git commit -am "Yosys x.y.z" - - - push tag to github - - post changelog on github - - post short release note on reddit - - -Updating the website: - - cd ~yosys - make manual - make install - - - update pdf files on the website - - cd ~yosys-web - make update_cmd - make update_show - git commit -am update - make push - - - -Cross-Building for Windows with MXE -=================================== - -Check http://mxe.cc/#requirements and install all missing requirements. - -As root (or other user with write access to /usr/local/src): - - cd /usr/local/src - git clone https://github.com/mxe/mxe.git - cd mxe - - make -j$(nproc) MXE_PLUGIN_DIRS="plugins/tcl.tk" \ - MXE_TARGETS="i686-w64-mingw32.static" \ - gcc tcl readline - -Then as regular user in some directory where you build stuff: - - git clone https://github.com/cliffordwolf/yosys.git yosys-win32 - cd yosys-win32 - make config-mxe - make -j$(nproc) mxebin - - - -How to add unit test -==================== - -Unit test brings some advantages, briefly, we can list some of them (reference -[1](https://en.wikipedia.org/wiki/Unit_testing)): - -* Tests reduce bugs in new features; -* Tests reduce bugs in existing features; -* Tests are good documentation; -* Tests reduce the cost of change; -* Tests allow refactoring; - -With those advantages in mind, it was required to choose a framework which fits -well with C/C++ code. Hence, it was chosen (google test) -[https://github.com/google/googletest], because it is largely used and it is -relatively easy learn. - -Install and configure google test (manually) --------------------------------------------- - -In this section, you will see a brief description of how to install google -test. However, it is strongly recommended that you take a look to the official -repository (https://github.com/google/googletest) and refers to that if you -have any problem to install it. Follow the steps below: - -* Install: cmake and pthread -* Clone google test project from: https://github.com/google/googletest and - enter in the project directory -* Inside project directory, type: - -``` -cmake -DBUILD_SHARED_LIBS=ON . -make -``` - -* After compilation, copy all "*.so" inside directory "googlemock" and - "googlemock/gtest" to "/usr/lib/" -* Done! Now you can compile your tests. - -If you have any problem, go to the official repository to find help. - -Ps.: Some distros already have googletest packed. If your distro supports it, -you can use it instead of compile. - -Create new unit test --------------------- - -If you want to add new unit tests for Yosys, just follow the steps below: - -* Go to directory "yosys/test/unit/" -* In this directory you can find something similar Yosys's directory structure. - To create your unit test file you have to follow this pattern: - fileNameToImplementUnitTest + Test.cc. E.g.: if you want to implement the - unit test for kernel/celledges.cc, you will need to create a file like this: - tests/unit/kernel/celledgesTest.cc; -* Implement your unit test - -Run unit test -------------- - -To compile and run all unit tests, just go to yosys root directory and type: -``` -make unit-test -``` - -If you want to remove all unit test files, type: -``` -make clean-unit-test -``` +for the new code instead of trying to mimic the style of the surrounding code. \ No newline at end of file diff --git a/guidelines/UnitTests b/guidelines/UnitTests new file mode 100644 index 00000000000..d42a63ce587 --- /dev/null +++ b/guidelines/UnitTests @@ -0,0 +1,69 @@ +How to add unit test +==================== + +Unit test brings some advantages, briefly, we can list some of them (reference +[1](https://en.wikipedia.org/wiki/Unit_testing)): + +* Tests reduce bugs in new features; +* Tests reduce bugs in existing features; +* Tests are good documentation; +* Tests reduce the cost of change; +* Tests allow refactoring; + +With those advantages in mind, it was required to choose a framework which fits +well with C/C++ code. Hence, it was chosen (google test) +[https://github.com/google/googletest], because it is largely used and it is +relatively easy learn. + +Install and configure google test (manually) +-------------------------------------------- + +In this section, you will see a brief description of how to install google +test. However, it is strongly recommended that you take a look to the official +repository (https://github.com/google/googletest) and refers to that if you +have any problem to install it. Follow the steps below: + +* Install: cmake and pthread +* Clone google test project from: https://github.com/google/googletest and + enter in the project directory +* Inside project directory, type: + +``` +cmake -DBUILD_SHARED_LIBS=ON . +make +``` + +* After compilation, copy all "*.so" inside directory "googlemock" and + "googlemock/gtest" to "/usr/lib/" +* Done! Now you can compile your tests. + +If you have any problem, go to the official repository to find help. + +Ps.: Some distros already have googletest packed. If your distro supports it, +you can use it instead of compile. + +Create new unit test +-------------------- + +If you want to add new unit tests for Yosys, just follow the steps below: + +* Go to directory "yosys/test/unit/" +* In this directory you can find something similar Yosys's directory structure. + To create your unit test file you have to follow this pattern: + fileNameToImplementUnitTest + Test.cc. E.g.: if you want to implement the + unit test for kernel/celledges.cc, you will need to create a file like this: + tests/unit/kernel/celledgesTest.cc; +* Implement your unit test + +Run unit test +------------- + +To compile and run all unit tests, just go to yosys root directory and type: +``` +make unit-test +``` + +If you want to remove all unit test files, type: +``` +make clean-unit-test +``` diff --git a/guidelines/Windows b/guidelines/Windows new file mode 100644 index 00000000000..2af0620fae9 --- /dev/null +++ b/guidelines/Windows @@ -0,0 +1,83 @@ +Creating the Visual Studio Template Project +=========================================== + +1. Create an empty Visual C++ Win32 Console App project + + Microsoft Visual Studio Express 2013 for Windows Desktop + Open New Project Wizard (File -> New Project..) + + Project Name: YosysVS + Solution Name: YosysVS + [X] Create directory for solution + [ ] Add to source control + + [X] Console applications + [X] Empty Project + [ ] SDL checks + +2. Open YosysVS Project Properties + + Select Configuration: All Configurations + + C/C++ -> General -> Additional Include Directories + Add: ..\yosys + + C/C++ -> Preprocessor -> Preprocessor Definitions + Add: _YOSYS_;_CRT_SECURE_NO_WARNINGS + +3. Resulting file system tree: + + YosysVS/ + YosysVS/YosysVS + YosysVS/YosysVS/YosysVS.vcxproj + YosysVS/YosysVS/YosysVS.vcxproj.filters + YosysVS/YosysVS.sdf + YosysVS/YosysVS.sln + YosysVS/YosysVS.v12.suo + +4. Zip YosysVS as YosysVS-Tpl-v1.zip + +Compiling with Visual Studio +============================ + +Visual Studio builds are not directly supported by build scripts, but they are still possible. + +1. Easy way + + - Go to https://github.com/YosysHQ/yosys/actions/workflows/vs.yml?query=branch%3Amaster + - Click on the most recent completed run + - In Artifacts region find vcxsrc and click on it to download + - Unpack downloaded ZIP file + - Open YosysVS.sln with Visual Studio + +2. Using WSL or MSYS2 + + - Make sure to have make, python3 and git available + - Git clone yosys repository + - Execute ```make vcxsrc YOSYS_VER=latest``` + - File yosys-win32-vcxsrc-latest.zip will be created + - Transfer that file to location visible by Windows application + - Unpack ZIP + - Open YosysVS.sln with Visual Studio + +Cross-Building for Windows with MXE +=================================== + +Check http://mxe.cc/#requirements and install all missing requirements. + +As root (or other user with write access to /usr/local/src): + + cd /usr/local/src + git clone https://github.com/mxe/mxe.git + cd mxe + + make -j$(nproc) MXE_PLUGIN_DIRS="plugins/tcl.tk" \ + MXE_TARGETS="i686-w64-mingw32.static" \ + gcc tcl readline + +Then as regular user in some directory where you build stuff: + + git clone https://github.com/YosysHQ/yosys.git yosys-win32 + cd yosys-win32 + make config-mxe + make -j$(nproc) mxebin diff --git a/kernel/binding.cc b/kernel/binding.cc new file mode 100644 index 00000000000..621f7007bfa --- /dev/null +++ b/kernel/binding.cc @@ -0,0 +1,29 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "binding.h" + +YOSYS_NAMESPACE_BEGIN + +RTLIL::Binding::Binding(RTLIL::IdString target_type, + RTLIL::IdString target_name) + : target_type(target_type), target_name(target_name) +{} + +YOSYS_NAMESPACE_END diff --git a/kernel/binding.h b/kernel/binding.h new file mode 100644 index 00000000000..3b64e76dac3 --- /dev/null +++ b/kernel/binding.h @@ -0,0 +1,60 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef BINDING_H +#define BINDING_H + +#include "kernel/rtlil.h" + +YOSYS_NAMESPACE_BEGIN + +struct RTLIL::Binding +{ + // Represents a bind construct. + // + // The target of the binding is represented by target_type and + // target_name (see comments above the fields). + + Binding(RTLIL::IdString target_type, + RTLIL::IdString target_name); + + virtual ~Binding() {} + + // Return a string describing the binding + virtual std::string describe() const = 0; + +protected: + // May be empty. If not, it's the name of the module or interface to + // bind to. + RTLIL::IdString target_type; + + // If target_type is nonempty (the usual case), this is a hierarchical + // reference to the bind target. If target_type is empty, we have to + // wait until the hierarchy pass to figure out whether this was the name + // of a module/interface type or an instance. + RTLIL::IdString target_name; + + // An attribute name which contains an ID that's unique across binding + // instances (used to ensure we don't apply a binding twice to a module) + RTLIL::IdString attr_name; +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index 894a95ed1ff..7a8eb39f9f2 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/calc.cc b/kernel/calc.cc index d54ccbc10c8..9b02a6e30c8 100644 --- a/kernel/calc.cc +++ b/kernel/calc.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -609,5 +609,109 @@ RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, boo return RTLIL::const_sub(zero, arg1_ext, true, signed1, result_len); } +RTLIL::Const RTLIL::const_mux(const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) +{ + log_assert(arg2.size() == arg1.size()); + if (arg3[0] == State::S0) + return arg1; + else if (arg3[0] == State::S1) + return arg2; + + RTLIL::Const ret = arg1; + for (int i = 0; i < ret.size(); i++) + if (ret[i] != arg2[i]) + ret[i] = State::Sx; + return ret; +} + +RTLIL::Const RTLIL::const_pmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) +{ + if (arg3.is_fully_zero()) + return arg1; + + if (!arg3.is_onehot()) + return RTLIL::Const(State::Sx, arg1.size()); + + for (int i = 0; i < arg3.size(); i++) + if (arg3[i] == State::S1) + return RTLIL::Const(std::vector(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size())); + + log_abort(); // unreachable +} + +RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) +{ + std::vector t = arg1.bits; + + for (int i = GetSize(arg2)-1; i >= 0; i--) + { + RTLIL::State sel = arg2.bits.at(i); + std::vector new_t; + if (sel == State::S0) + new_t = std::vector(t.begin(), t.begin() + GetSize(t)/2); + else if (sel == State::S1) + new_t = std::vector(t.begin() + GetSize(t)/2, t.end()); + else + for (int j = 0; j < GetSize(t)/2; j++) + new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx); + t.swap(new_t); + } + + return t; +} + +RTLIL::Const RTLIL::const_demux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) +{ + int width = GetSize(arg1); + int s_width = GetSize(arg2); + std::vector res; + for (int i = 0; i < (1 << s_width); i++) + { + bool ne = false; + bool x = false; + for (int j = 0; j < s_width; j++) { + bool bit = i & 1 << j; + if (arg2[j] == (bit ? RTLIL::S0 : RTLIL::S1)) + ne = true; + else if (arg2[j] != RTLIL::S0 && arg2[j] != RTLIL::S1) + x = true; + } + if (ne) { + for (int j = 0; j < width; j++) + res.push_back(State::S0); + } else if (x) { + for (int j = 0; j < width; j++) + res.push_back(arg1.bits[j] == State::S0 ? State::S0 : State::Sx); + } else { + for (int j = 0; j < width; j++) + res.push_back(arg1.bits[j]); + } + } + return res; +} + +RTLIL::Const RTLIL::const_bweqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2) +{ + log_assert(arg2.size() == arg1.size()); + RTLIL::Const result(RTLIL::State::S0, arg1.size()); + for (int i = 0; i < arg1.size(); i++) + result[i] = arg1[i] == arg2[i] ? State::S1 : State::S0; + + return result; +} + +RTLIL::Const RTLIL::const_bwmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) +{ + log_assert(arg2.size() == arg1.size()); + log_assert(arg3.size() == arg1.size()); + RTLIL::Const result(RTLIL::State::Sx, arg1.size()); + for (int i = 0; i < arg1.size(); i++) { + if (arg3[i] != State::Sx || arg1[i] == arg2[i]) + result[i] = arg3[i] == State::S1 ? arg2[i] : arg1[i]; + } + + return result; +} + YOSYS_NAMESPACE_END diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index 2c82b1bca78..5dda4503fdd 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -318,7 +318,7 @@ Aig::Aig(Cell *cell) goto optimize; } - if (cell->type.in(ID($mux), ID($_MUX_))) + if (cell->type.in(ID($mux), ID($_MUX_), ID($_NMUX_))) { int S = mk.inport(ID::S); for (int i = 0; i < GetSize(cell->getPort(ID::Y)); i++) { @@ -385,6 +385,26 @@ Aig::Aig(Cell *cell) goto optimize; } + if (cell->type.in(ID($lt), ID($gt), ID($le), ID($ge))) + { + int width = std::max(GetSize(cell->getPort(ID::A)), + GetSize(cell->getPort(ID::B))) + 1; + vector A = mk.inport_vec(ID::A, width); + vector B = mk.inport_vec(ID::B, width); + + if (cell->type.in(ID($gt), ID($ge))) + std::swap(A, B); + + int carry = mk.bool_node(!cell->type.in(ID($le), ID($ge))); + for (auto &n : B) + n = mk.not_gate(n); + vector Y = mk.adder(A, B, carry); + mk.outport(Y.back(), ID::Y); + for (int i = 1; i < GetSize(cell->getPort(ID::Y)); i++) + mk.outport(mk.bool_node(false), ID::Y, i); + goto optimize; + } + if (cell->type == ID($alu)) { int width = GetSize(cell->getPort(ID::Y)); diff --git a/kernel/cellaigs.h b/kernel/cellaigs.h index 1417a614c9e..8f6d69ba62b 100644 --- a/kernel/cellaigs.h +++ b/kernel/cellaigs.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/celledges.cc b/kernel/celledges.cc index 314e7c77e76..2ed0d503605 100644 --- a/kernel/celledges.cc +++ b/kernel/celledges.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -142,6 +142,171 @@ void mux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) } } +void bmux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + int width = GetSize(cell->getPort(ID::Y)); + int a_width = GetSize(cell->getPort(ID::A)); + int s_width = GetSize(cell->getPort(ID::S)); + + for (int i = 0; i < width; i++) + { + for (int k = i; k < a_width; k += width) + db->add_edge(cell, ID::A, k, ID::Y, i, -1); + + for (int k = 0; k < s_width; k++) + db->add_edge(cell, ID::S, k, ID::Y, i, -1); + } +} + +void demux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + int width = GetSize(cell->getPort(ID::Y)); + int a_width = GetSize(cell->getPort(ID::A)); + int s_width = GetSize(cell->getPort(ID::S)); + + for (int i = 0; i < width; i++) + { + db->add_edge(cell, ID::A, i % a_width, ID::Y, i, -1); + for (int k = 0; k < s_width; k++) + db->add_edge(cell, ID::S, k, ID::Y, i, -1); + } +} + +void shift_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) +{ + bool is_signed = cell->getParam(ID::A_SIGNED).as_bool(); + bool is_b_signed = cell->getParam(ID::B_SIGNED).as_bool(); + int a_width = GetSize(cell->getPort(ID::A)); + int b_width = GetSize(cell->getPort(ID::B)); + int y_width = GetSize(cell->getPort(ID::Y)); + + // Behavior of the different shift cells: + // + // $shl, $sshl -- shifts left by the amount on B port, B always unsigned + // $shr, $sshr -- ditto right + // $shift, $shiftx -- shifts right by the amount on B port, B optionally signed + // + // Sign extension (if A signed): + // + // $shl, $shr, $shift -- only sign-extends up to size of Y, then shifts in zeroes + // $sshl, $sshr -- fully sign-extends + // $shiftx -- no sign extension + // + // Because $shl, $sshl only shift left, and $shl sign-extens up to size of Y, they + // are effectively the same. + + // the cap below makes sure we don't overflow in the arithmetic further down, though + // it makes the edge data invalid once a_width approaches the order of 2**30 + // (that ever happening is considered improbable) + int b_width_capped = min(b_width, 30); + + int b_high, b_low; + if (!is_b_signed) { + b_high = (1 << b_width_capped) - 1; + b_low = 0; + } else { + b_high = (1 << (b_width_capped - 1)) - 1; + b_low = -(1 << (b_width_capped - 1)); + } + + for (int i = 0; i < y_width; i++){ + // highest position of Y that can change with the value of B + int b_range_upper = 0; + // 1 + highest position of A that can be moved to Y[i] + int a_range_upper; + // lowest position of A that can be moved to Y[i] + int a_range_lower; + + if (cell->type.in(ID($shl), ID($sshl))) { + b_range_upper = a_width + b_high; + if (is_signed) b_range_upper -= 1; + a_range_lower = max(0, i - b_high); + a_range_upper = min(i+1, a_width); + } else if (cell->type.in(ID($shr), ID($sshr)) || (cell->type.in(ID($shift), ID($shiftx)) && !is_b_signed)) { + b_range_upper = a_width; + a_range_lower = min(i, a_width - 1); + a_range_upper = min(i+1 + b_high, a_width); + } else if (cell->type.in(ID($shift), ID($shiftx)) && is_b_signed) { + // can go both ways depending on sign of B + // 2's complement range is different depending on direction + b_range_upper = a_width - b_low; + a_range_lower = max(0, i + b_low); + if (is_signed) + a_range_lower = min(a_range_lower, a_width - 1); + a_range_upper = min(i+1 + b_high, a_width); + } else { + log_assert(false && "unreachable"); + } + + if (i < b_range_upper) { + for (int k = a_range_lower; k < a_range_upper; k++) + db->add_edge(cell, ID::A, k, ID::Y, i, -1); + } else { + // only influence is through sign extension + if (is_signed) + db->add_edge(cell, ID::A, a_width - 1, ID::Y, i, -1); + } + + for (int k = 0; k < b_width; k++) { + // left shifts + if (cell->type.in(ID($shl), ID($sshl))) { + if (a_width == 1 && is_signed) { + int skip = 1 << (k + 1); + int base = skip -1; + if (i % skip != base && i - a_width + 2 < 1 << b_width) + db->add_edge(cell, ID::B, k, ID::Y, i, -1); + } else if (is_signed) { + if (i - a_width + 2 < 1 << b_width) + db->add_edge(cell, ID::B, k, ID::Y, i, -1); + } else { + if (i - a_width + 1 < 1 << b_width) + db->add_edge(cell, ID::B, k, ID::Y, i, -1); + } + // right shifts + } else if (cell->type.in(ID($shr), ID($sshr)) || (cell->type.in(ID($shift), ID($shiftx)) && !is_b_signed)) { + if (is_signed) { + bool shift_in_bulk = i < a_width - 1; + // can we jump into the zero-padding by toggling B[k]? + bool zpad_jump = (((y_width - i) & ((1 << (k + 1)) - 1)) != 0 \ + && (((y_width - i) & ~(1 << k)) < (1 << b_width))); + + if (shift_in_bulk || (cell->type.in(ID($shr), ID($shift), ID($shiftx)) && zpad_jump)) + db->add_edge(cell, ID::B, k, ID::Y, i, -1); + } else { + if (i < a_width) + db->add_edge(cell, ID::B, k, ID::Y, i, -1); + } + // bidirectional shifts (positive B shifts right, negative left) + } else if (cell->type.in(ID($shift), ID($shiftx)) && is_b_signed) { + if (is_signed) { + if (k != b_width - 1) { + bool r_shift_in_bulk = i < a_width - 1; + // assuming B is positive, can we jump into the upper zero-padding by toggling B[k]? + bool r_zpad_jump = (((y_width - i) & ((1 << (k + 1)) - 1)) != 0 \ + && (((y_width - i) & ~(1 << k)) <= b_high)); + // assuming B is negative, can we influence Y[i] by toggling B[k]? + bool l = a_width - 2 - i >= b_low; + if (a_width == 1) { + // in case of a_width==1 we go into more detailed reasoning + l = l && (~(i - a_width) & ((1 << (k + 1)) - 1)) != 0; + } + if (r_shift_in_bulk || r_zpad_jump || l) + db->add_edge(cell, ID::B, k, ID::Y, i, -1); + } else { + if (y_width - i <= b_high || a_width - 2 - i >= b_low) + db->add_edge(cell, ID::B, k, ID::Y, i, -1); + } + } else { + if (a_width - 1 - i >= b_low) + db->add_edge(cell, ID::B, k, ID::Y, i, -1); + } + } else { + log_assert(false && "unreachable"); + } + } + } +} + PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) @@ -171,11 +336,10 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL return true; } - // FIXME: - // if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) { - // shift_op(this, cell); - // return true; - // } + if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) { + shift_op(this, cell); + return true; + } if (cell->type.in(ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt))) { compare_op(this, cell); @@ -187,8 +351,27 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL return true; } - // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat - // FIXME: $lut $sop $alu $lcu $macc $fa + if (cell->type == ID($bmux)) { + bmux_op(this, cell); + return true; + } + + if (cell->type == ID($demux)) { + demux_op(this, cell); + return true; + } + + // FIXME: $mul $div $mod $divfloor $modfloor $pow $slice $concat $bweqx + // FIXME: $lut $sop $alu $lcu $macc $fa $logic_and $logic_or $bwmux + + // FIXME: $_BUF_ $_NOT_ $_AND_ $_NAND_ $_OR_ $_NOR_ $_XOR_ $_XNOR_ $_ANDNOT_ $_ORNOT_ + // FIXME: $_MUX_ $_NMUX_ $_MUX4_ $_MUX8_ $_MUX16_ $_AOI3_ $_OAI3_ $_AOI4_ $_OAI4_ + + // FIXME: $specify2 $specify3 $specrule ??? + // FIXME: $equiv $set_tag $get_tag $overwrite_tag $original_tag + + if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover), ID($initstate), ID($anyconst), ID($anyseq), ID($allconst), ID($allseq))) + return true; // no-op: these have either no inputs or no outputs return false; } diff --git a/kernel/celledges.h b/kernel/celledges.h index d105e4009d1..d5e374f050c 100644 --- a/kernel/celledges.h +++ b/kernel/celledges.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 944cb301ab2..fde6624e179 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -51,6 +51,7 @@ struct CellTypes setup_internals(); setup_internals_mem(); + setup_internals_anyinit(); setup_stdcells(); setup_stdcells_mem(); } @@ -100,6 +101,14 @@ struct CellTypes setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool(), true); setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool(), true); setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool(), true); + setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool()); + setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool()); + setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}); + setup_type(ID($get_tag), {ID::A}, {ID::Y}); + setup_type(ID($overwrite_tag), {ID::A, ID::SET, ID::CLR}, pool()); + setup_type(ID($original_tag), {ID::A}, {ID::Y}); + setup_type(ID($future_ff), {ID::A}, {ID::Y}); + setup_type(ID($scopeinfo), {}, {}); } void setup_internals_eval() @@ -115,7 +124,8 @@ struct CellTypes ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx), ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt), ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), - ID($logic_and), ID($logic_or), ID($concat), ID($macc) + ID($logic_and), ID($logic_or), ID($concat), ID($macc), + ID($bweqx) }; for (auto type : unary_ops) @@ -124,9 +134,12 @@ struct CellTypes for (auto type : binary_ops) setup_type(type, {ID::A, ID::B}, {ID::Y}, true); - for (auto type : std::vector({ID($mux), ID($pmux)})) + for (auto type : std::vector({ID($mux), ID($pmux), ID($bwmux)})) setup_type(type, {ID::A, ID::B, ID::S}, {ID::Y}, true); + for (auto type : std::vector({ID($bmux), ID($demux)})) + setup_type(type, {ID::A, ID::S}, {ID::Y}, true); + setup_type(ID($lcu), {ID::P, ID::G, ID::CI}, {ID::CO}, true); setup_type(ID($alu), {ID::A, ID::B, ID::CI, ID::BI}, {ID::X, ID::Y, ID::CO}, true); setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true); @@ -142,6 +155,8 @@ struct CellTypes setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q}); setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q}); setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q}); + setup_type(ID($aldff), {ID::CLK, ID::ALOAD, ID::AD, ID::D}, {ID::Q}); + setup_type(ID($aldffe), {ID::CLK, ID::ALOAD, ID::AD, ID::D, ID::EN}, {ID::Q}); setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q}); setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q}); @@ -150,14 +165,23 @@ struct CellTypes setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q}); } + void setup_internals_anyinit() + { + setup_type(ID($anyinit), {ID::D}, {ID::Q}); + } + void setup_internals_mem() { setup_internals_ff(); setup_type(ID($memrd), {ID::CLK, ID::EN, ID::ADDR}, {ID::DATA}); + setup_type(ID($memrd_v2), {ID::CLK, ID::EN, ID::ARST, ID::SRST, ID::ADDR}, {ID::DATA}); setup_type(ID($memwr), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool()); + setup_type(ID($memwr_v2), {ID::CLK, ID::EN, ID::ADDR, ID::DATA}, pool()); setup_type(ID($meminit), {ID::ADDR, ID::DATA}, pool()); + setup_type(ID($meminit_v2), {ID::ADDR, ID::DATA, ID::EN}, pool()); setup_type(ID($mem), {ID::RD_CLK, ID::RD_EN, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}); + setup_type(ID($mem_v2), {ID::RD_CLK, ID::RD_EN, ID::RD_ARST, ID::RD_SRST, ID::RD_ADDR, ID::WR_CLK, ID::WR_EN, ID::WR_ADDR, ID::WR_DATA}, {ID::RD_DATA}); setup_type(ID($fsm), {ID::CLK, ID::ARST, ID::CTRL_IN}, {ID::CTRL_OUT}); } @@ -220,6 +244,15 @@ struct CellTypes for (auto c4 : list_np) setup_type(stringf("$_DFFE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); + for (auto c1 : list_np) + for (auto c2 : list_np) + setup_type(stringf("$_ALDFF_%c%c_", c1, c2), {ID::C, ID::L, ID::AD, ID::D}, {ID::Q}); + + for (auto c1 : list_np) + for (auto c2 : list_np) + for (auto c3 : list_np) + setup_type(stringf("$_ALDFFE_%c%c%c_", c1, c2, c3), {ID::C, ID::L, ID::AD, ID::D, ID::E}, {ID::Q}); + for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) @@ -396,6 +429,21 @@ struct CellTypes return ret; } + if (cell->type == ID($bmux)) + { + return const_bmux(arg1, arg2); + } + + if (cell->type == ID($demux)) + { + return const_demux(arg1, arg2); + } + + if (cell->type == ID($bweqx)) + { + return const_bweqx(arg1, arg2); + } + if (cell->type == ID($lut)) { int width = cell->parameters.at(ID::WIDTH).as_int(); @@ -405,21 +453,7 @@ struct CellTypes t.push_back(State::S0); t.resize(1 << width); - for (int i = width-1; i >= 0; i--) { - RTLIL::State sel = arg1.bits.at(i); - std::vector new_t; - if (sel == State::S0) - new_t = std::vector(t.begin(), t.begin() + GetSize(t)/2); - else if (sel == State::S1) - new_t = std::vector(t.begin() + GetSize(t)/2, t.end()); - else - for (int j = 0; j < GetSize(t)/2; j++) - new_t.push_back(t[j] == t[j + GetSize(t)/2] ? t[j] : RTLIL::Sx); - t.swap(new_t); - } - - log_assert(GetSize(t) == 1); - return t; + return const_bmux(t, arg1); } if (cell->type == ID($sop)) @@ -468,16 +502,12 @@ struct CellTypes static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, bool *errp = nullptr) { - if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) { - RTLIL::Const ret = arg1; - for (size_t i = 0; i < arg3.bits.size(); i++) - if (arg3.bits[i] == RTLIL::State::S1) { - std::vector bits(arg2.bits.begin() + i*arg1.bits.size(), arg2.bits.begin() + (i+1)*arg1.bits.size()); - ret = RTLIL::Const(bits); - } - return ret; - } - + if (cell->type.in(ID($mux), ID($_MUX_))) + return const_mux(arg1, arg2, arg3); + if (cell->type == ID($bwmux)) + return const_bwmux(arg1, arg2, arg3); + if (cell->type == ID($pmux)) + return const_pmux(arg1, arg2, arg3); if (cell->type == ID($_AOI3_)) return eval_not(const_or(const_and(arg1, arg2, false, false, 1), arg3, false, false, 1)); if (cell->type == ID($_OAI3_)) diff --git a/kernel/consteval.h b/kernel/consteval.h index ff8cf86d62b..4c0c26049f4 100644 --- a/kernel/consteval.h +++ b/kernel/consteval.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -135,8 +135,6 @@ struct ConstEval if (cell->hasPort(ID::S)) { sig_s = cell->getPort(ID::S); - if (!eval(sig_s, undef, cell)) - return false; } if (cell->hasPort(ID::A)) @@ -148,9 +146,11 @@ struct ConstEval if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_), ID($_NMUX_))) { std::vector y_candidates; - int count_maybe_set_s_bits = 0; int count_set_s_bits = 0; + if (!eval(sig_s, undef, cell)) + return false; + for (int i = 0; i < sig_s.size(); i++) { RTLIL::State s_bit = sig_s.extract(i, 1).as_const().bits.at(0); @@ -159,9 +159,6 @@ struct ConstEval if (s_bit == RTLIL::State::Sx || s_bit == RTLIL::State::S1) y_candidates.push_back(b_slice); - if (s_bit == RTLIL::State::S1 || s_bit == RTLIL::State::Sx) - count_maybe_set_s_bits++; - if (s_bit == RTLIL::State::S1) count_set_s_bits++; } @@ -198,6 +195,36 @@ struct ConstEval else set(sig_y, y_values.front()); } + else if (cell->type == ID($bmux)) + { + if (!eval(sig_s, undef, cell)) + return false; + + if (sig_s.is_fully_def()) { + int sel = sig_s.as_int(); + int width = GetSize(sig_y); + SigSpec res = sig_a.extract(sel * width, width); + if (!eval(res, undef, cell)) + return false; + set(sig_y, res.as_const()); + } else { + if (!eval(sig_a, undef, cell)) + return false; + set(sig_y, const_bmux(sig_a.as_const(), sig_s.as_const())); + } + } + else if (cell->type == ID($demux)) + { + if (!eval(sig_a, undef, cell)) + return false; + if (sig_a.is_fully_zero()) { + set(sig_y, Const(0, GetSize(sig_y))); + } else { + if (!eval(sig_s, undef, cell)) + return false; + set(sig_y, const_demux(sig_a.as_const(), sig_s.as_const())); + } + } else if (cell->type == ID($fa)) { RTLIL::SigSpec sig_c = cell->getPort(ID::C); diff --git a/kernel/constids.inc b/kernel/constids.inc index 3c2ff9beb20..7db21debb0e 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -11,14 +11,19 @@ X(abc9_mergeability) X(abc9_scc_id) X(abcgroup) X(ABITS) +X(AD) X(ADDR) X(allconst) X(allseq) +X(ALOAD) +X(ALOAD_POLARITY) X(always_comb) X(always_ff) X(always_latch) X(anyconst) X(anyseq) +X(ARGS) +X(ARGS_WIDTH) X(ARST) X(ARST_POLARITY) X(ARST_VALUE) @@ -26,12 +31,15 @@ X(A_SIGNED) X(A_WIDTH) X(B) X(BI) +X(BITS_USED) X(blackbox) X(B_SIGNED) X(bugpoint_keep) X(B_WIDTH) +X(BYTE) X(C) X(cells_not_processed) +X(CE_OVER_SRST) X(CFG_ABITS) X(CFG_DBITS) X(CFG_INIT) @@ -46,6 +54,7 @@ X(CLK_POLARITY) X(CLR) X(CLR_POLARITY) X(CO) +X(COLLISION_X_MASK) X(CONFIG) X(CONFIG_WIDTH) X(CTRL_IN) @@ -79,6 +88,8 @@ X(equiv_merged) X(equiv_region) X(extract_order) X(F) +X(FLAVOR) +X(FORMAT) X(force_downto) X(force_upto) X(fsm_encoding) @@ -95,6 +106,7 @@ X(hdlname) X(hierconn) X(I) X(INIT) +X(INIT_VALUE) X(init) X(initial_top) X(interface_modport) @@ -110,6 +122,8 @@ X(keep_hierarchy) X(L) X(lib_whitebox) X(localparam) +X(logic_block) +X(lram) X(LUT) X(lut_keep) X(M) @@ -127,25 +141,46 @@ X(nomem2reg) X(nomeminit) X(nosync) X(nowrshmsk) +X(no_ram) +X(no_rw_check) X(O) X(OFFSET) X(onehot) X(P) X(parallel_case) X(parameter) +X(PORTID) X(PRIORITY) +X(PRIORITY_MASK) X(Q) X(qwp_position) X(R) +X(ram_block) +X(ram_style) +X(ramstyle) X(RD_ADDR) +X(RD_ARST) +X(RD_ARST_VALUE) +X(RD_CE_OVER_SRST) X(RD_CLK) X(RD_CLK_ENABLE) X(RD_CLK_POLARITY) +X(RD_COLLISION_X_MASK) X(RD_DATA) X(RD_EN) +X(RD_INIT_VALUE) X(RD_PORTS) +X(RD_SRST) +X(RD_SRST_VALUE) +X(RD_TRANSPARENCY_MASK) X(RD_TRANSPARENT) +X(RD_WIDE_CONTINUATION) X(reg) +X(replaced_by_gclk) +X(reprocess_after) +X(rom_block) +X(rom_style) +X(romstyle) X(S) X(SET) X(SET_POLARITY) @@ -161,15 +196,21 @@ X(SRC_WIDTH) X(SRST) X(SRST_POLARITY) X(SRST_VALUE) +X(sta_arrival) X(STATE_BITS) X(STATE_NUM) X(STATE_NUM_LOG2) X(STATE_RST) X(STATE_TABLE) +X(smtlib2_module) +X(smtlib2_comb_expr) X(submod) +X(syn_ramstyle) +X(syn_romstyle) X(S_WIDTH) X(T) X(TABLE) +X(TAG) X(techmap_autopurge) X(_TECHMAP_BITS_CONNMAP_) X(_TECHMAP_CELLNAME_) @@ -195,8 +236,13 @@ X(T_LIMIT_TYP) X(to_delete) X(top) X(TRANS_NUM) +X(TRANSPARENCY_MASK) X(TRANSPARENT) X(TRANS_TABLE) +X(TRG) +X(TRG_ENABLE) +X(TRG_POLARITY) +X(TRG_WIDTH) X(T_RISE_MAX) X(T_RISE_MIN) X(T_RISE_TYP) @@ -220,6 +266,9 @@ X(WR_CLK_POLARITY) X(WR_DATA) X(WR_EN) X(WR_PORTS) +X(WR_PRIORITY_MASK) +X(WR_WIDE_CONTINUATION) X(X) +X(xprop_decoder) X(Y) X(Y_WIDTH) diff --git a/kernel/cost.h b/kernel/cost.h index ea2a4c1f028..b81420af756 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/driver.cc b/kernel/driver.cc index 57ed7b8b452..58da1bc32e3 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,6 +33,10 @@ #include #include #include +#ifndef __STDC_FORMAT_MACROS +# define __STDC_FORMAT_MACROS +#endif +#include #if defined (__linux__) || defined(__FreeBSD__) # include @@ -47,40 +51,58 @@ #if !defined(_WIN32) || defined(__MINGW32__) # include -#else +#endif + +USING_YOSYS_NAMESPACE + char *optarg; -int optind = 1, optcur = 1; +int optind = 1, optcur = 1, optopt = 0; int getopt(int argc, char **argv, const char *optstring) { - if (optind >= argc || argv[optind][0] != '-') + if (optind >= argc) return -1; + if (argv[optind][0] != '-' || argv[optind][1] == 0) { + optopt = 1; + optarg = argv[optind++]; + return optopt; + } + bool takes_arg = false; - int opt = argv[optind][optcur]; + optopt = argv[optind][optcur]; + + if (optopt == '-') { + ++optind; + return -1; + } + for (int i = 0; optstring[i]; i++) - if (opt == optstring[i] && optstring[i + 1] == ':') + if (optopt == optstring[i] && optstring[i + 1] == ':') takes_arg = true; if (!takes_arg) { if (argv[optind][++optcur] == 0) optind++, optcur = 1; - return opt; + return optopt; } if (argv[optind][++optcur]) { optarg = argv[optind++] + optcur; optcur = 1; - return opt; + return optopt; } - optarg = argv[++optind]; - optind++, optcur = 1; - return opt; -} -#endif + if (++optind >= argc) { + fprintf(stderr, "%s: option '-%c' expects an argument\n", argv[0], optopt); + optopt = '?'; + return optopt; + } + optarg = argv[optind]; + optind++, optcur = 1; -USING_YOSYS_NAMESPACE + return optopt; +} #ifdef EMSCRIPTEN # include @@ -118,7 +140,7 @@ int main(int argc, char **argv) if (argc == 2) { // Run the first argument as a script file - run_frontend(argv[1], "script", 0, 0, 0); + run_frontend(argv[1], "script"); } } @@ -192,33 +214,42 @@ void yosys_atexit() #endif } +#if defined(__OpenBSD__) +namespace Yosys { +extern char *yosys_argv0; +extern char yosys_path[PATH_MAX]; +}; +#endif +#ifdef YOSYS_ENABLE_TCL +namespace Yosys { + extern int yosys_tcl_iterp_init(Tcl_Interp *interp); + extern void yosys_tcl_activate_repl(); +}; +#endif + int main(int argc, char **argv) { std::string frontend_command = "auto"; std::string backend_command = "auto"; std::vector vlog_defines; std::vector passes_commands; + std::vector frontend_files; std::vector plugin_filenames; std::string output_filename = ""; std::string scriptfile = ""; std::string depsfile = ""; + std::string topmodule = ""; + std::string perffile = ""; bool scriptfile_tcl = false; - bool got_output_filename = false; bool print_banner = true; bool print_stats = true; bool call_abort = false; bool timing_details = false; + bool run_shell = true; + bool run_tcl_shell = false; bool mode_v = false; bool mode_q = false; -#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) - if (getenv("HOME") != NULL) { - yosys_history_file = stringf("%s/.yosys_history", getenv("HOME")); - read_history(yosys_history_file.c_str()); - yosys_history_offset = where_history(); - } -#endif - if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help"))) { printf("\n"); @@ -267,12 +298,17 @@ int main(int argc, char **argv) printf("\n"); printf(" -s scriptfile\n"); printf(" execute the commands in the script file\n"); +#ifdef YOSYS_ENABLE_TCL printf("\n"); printf(" -c tcl_scriptfile\n"); printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n"); + printf("\n"); + printf(" -C\n"); + printf(" enters TCL interatcive shell mode\n"); +#endif printf("\n"); printf(" -p command\n"); - printf(" execute the commands\n"); + printf(" execute the commands (to chain commands, separate them with semicolon + whitespace: 'cmd1; cmd2')\n"); printf("\n"); printf(" -m module_file\n"); printf(" load the specified module (aka plugin)\n"); @@ -286,6 +322,9 @@ int main(int argc, char **argv) printf(" -A\n"); printf(" will call abort() at the end of the script. for debugging\n"); printf("\n"); + printf(" -r \n"); + printf(" elaborate command line arguments using the specified top module\n"); + printf("\n"); printf(" -D [=]\n"); printf(" set the specified Verilog define (via \"read -define\")\n"); printf("\n"); @@ -340,7 +379,7 @@ int main(int argc, char **argv) } int opt; - while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:x:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:r:D:P:E:x:B:")) != -1) { switch (opt) { @@ -364,6 +403,7 @@ int main(int argc, char **argv) exit(0); case 'S': passes_commands.push_back("synth"); + run_shell = false; break; case 'g': log_force_debug++; @@ -376,19 +416,23 @@ int main(int argc, char **argv) break; case 'H': passes_commands.push_back("help"); + run_shell = false; break; case 'h': passes_commands.push_back(stringf("help %s", optarg)); + run_shell = false; break; case 'b': backend_command = optarg; + run_shell = false; break; case 'p': passes_commands.push_back(optarg); + run_shell = false; break; case 'o': output_filename = optarg; - got_output_filename = true; + run_shell = false; break; case 'l': case 'L': @@ -420,10 +464,12 @@ int main(int argc, char **argv) case 's': scriptfile = optarg; scriptfile_tcl = false; + run_shell = false; break; case 'c': scriptfile = optarg; scriptfile_tcl = true; + run_shell = false; break; case 'W': log_warn_regexes.push_back(YS_REGEX_COMPILE(optarg)); @@ -434,6 +480,9 @@ int main(int argc, char **argv) case 'e': log_werror_regexes.push_back(YS_REGEX_COMPILE(optarg)); break; + case 'r': + topmodule = optarg; + break; case 'D': vlog_defines.push_back(optarg); break; @@ -465,6 +514,15 @@ int main(int argc, char **argv) case 'x': log_experimentals_ignored.insert(optarg); break; + case 'B': + perffile = optarg; + break; + case 'C': + run_tcl_shell = true; + break; + case '\001': + frontend_files.push_back(optarg); + break; default: fprintf(stderr, "Run '%s -h' for help.\n", argv[0]); exit(1); @@ -479,9 +537,45 @@ int main(int argc, char **argv) if (print_banner) yosys_banner(); +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) + std::string state_dir; + #if defined(_WIN32) + if (getenv("HOMEDRIVE") != NULL && getenv("HOMEPATH") != NULL) { + state_dir = stringf("%s%s/.local/state", getenv("HOMEDRIVE"), getenv("HOMEPATH")); + } else { + log_debug("$HOMEDRIVE and/or $HOMEPATH is empty. No history file will be created.\n"); + } + #else + if (getenv("XDG_STATE_HOME") == NULL || getenv("XDG_STATE_HOME")[0] == '\0') { + if (getenv("HOME") != NULL) { + state_dir = stringf("%s/.local/state", getenv("HOME")); + } else { + log_debug("$HOME is empty. No history file will be created.\n"); + } + } else { + state_dir = stringf("%s", getenv("XDG_STATE_HOME")); + } + #endif + + if (!state_dir.empty()) { + std::string yosys_dir = state_dir + "/yosys"; + create_directory(yosys_dir); + + yosys_history_file = yosys_dir + "/history"; + read_history(yosys_history_file.c_str()); + yosys_history_offset = where_history(); + } +#endif + if (print_stats) log_hasher = new SHA1; +#if defined(__OpenBSD__) + // save the executable origin for proc_self_dirname() + yosys_argv0 = argv[0]; + realpath(yosys_argv0, yosys_path); +#endif + #if defined(__linux__) // set stack size to >= 128 MB { @@ -504,11 +598,7 @@ int main(int argc, char **argv) for (auto &fn : plugin_filenames) load_plugin(fn, {}); - if (optind == argc && passes_commands.size() == 0 && scriptfile.empty()) { - if (!got_output_filename) - backend_command = ""; - shell(yosys_design); - } + log_suppressed(); if (!vlog_defines.empty()) { std::string vdef_cmd = "read -define"; @@ -517,26 +607,57 @@ int main(int argc, char **argv) run_pass(vdef_cmd); } - while (optind < argc) - run_frontend(argv[optind++], frontend_command, output_filename == "-" ? &backend_command : NULL); + if (scriptfile.empty() || !scriptfile_tcl) { + // Without a TCL script, arguments following '--' are also treated as frontend files + for (int i = optind; i < argc; ++i) + frontend_files.push_back(argv[i]); + } + + for (auto it = frontend_files.begin(); it != frontend_files.end(); ++it) { + if (run_frontend((*it).c_str(), frontend_command)) + run_shell = false; + } + if (!topmodule.empty()) + run_pass("hierarchy -top " + topmodule); if (!scriptfile.empty()) { if (scriptfile_tcl) { #ifdef YOSYS_ENABLE_TCL - if (Tcl_EvalFile(yosys_get_tcl_interp(), scriptfile.c_str()) != TCL_OK) + int tcl_argc = argc - optind; + std::vector script_args; + Tcl_Interp *interp = yosys_get_tcl_interp(); + for (int i = optind; i < argc; ++i) + script_args.push_back(Tcl_NewStringObj(argv[i], strlen(argv[i]))); + + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(tcl_argc), 0); + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(tcl_argc, script_args.data()), 0); + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(scriptfile.c_str(), scriptfile.length()), 0); + + if (Tcl_EvalFile(interp, scriptfile.c_str()) != TCL_OK) log_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp())); #else log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n"); #endif } else - run_frontend(scriptfile, "script", output_filename == "-" ? &backend_command : NULL); + run_frontend(scriptfile, "script"); } for (auto it = passes_commands.begin(); it != passes_commands.end(); it++) run_pass(*it); - if (!backend_command.empty()) - run_backend(output_filename, backend_command); + if (run_tcl_shell) { +#ifdef YOSYS_ENABLE_TCL + yosys_tcl_activate_repl(); + Tcl_Main(argc, argv, yosys_tcl_iterp_init); +#else + log_error("Can't exectue TCL shell: this version of yosys is not built with TCL support enabled.\n"); +#endif + } else { + if (run_shell) + shell(yosys_design); + else + run_backend(output_filename, backend_command); + } yosys_design->check(); for (auto it : saved_designs) @@ -645,6 +766,29 @@ int main(int argc, char **argv) } log("%s\n", out_count ? "" : " no commands executed"); } + if(!perffile.empty()) + { + FILE *f = fopen(perffile.c_str(), "wt"); + if (f == nullptr) + log_error("Can't open performance log file for writing: %s\n", strerror(errno)); + + fprintf(f, "{\n"); + fprintf(f, " \"generator\": \"%s\",\n", yosys_version_str); + fprintf(f, " \"total_ns\": %" PRIu64 ",\n", total_ns); + fprintf(f, " \"passes\": {"); + + bool first = true; + for (auto it = timedat.rbegin(); it != timedat.rend(); it++) { + if (!first) + fprintf(f, ","); + fprintf(f, "\n \"%s\": {\n", std::get<2>(*it).c_str()); + fprintf(f, " \"runtime_ns\": %" PRIu64 ",\n", std::get<0>(*it)); + fprintf(f, " \"num_calls\": %u\n", std::get<1>(*it)); + fprintf(f, " }"); + first = false; + } + fprintf(f, "\n }\n}\n"); + } } #if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__)) @@ -695,4 +839,3 @@ int main(int argc, char **argv) } #endif /* EMSCRIPTEN */ - diff --git a/kernel/ff.cc b/kernel/ff.cc new file mode 100644 index 00000000000..697ba734221 --- /dev/null +++ b/kernel/ff.cc @@ -0,0 +1,773 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 Marcelina Kościelnicka + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/ff.h" + +USING_YOSYS_NAMESPACE + +FfData::FfData(FfInitVals *initvals, Cell *cell_) : FfData(cell_->module, initvals, cell_->name) +{ + cell = cell_; + sig_q = cell->getPort(ID::Q); + width = GetSize(sig_q); + attributes = cell->attributes; + + if (initvals) + val_init = (*initvals)(sig_q); + + std::string type_str = cell->type.str(); + + if (cell->type.in(ID($anyinit), ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { + if (cell->type.in(ID($anyinit), ID($ff))) { + has_gclk = true; + sig_d = cell->getPort(ID::D); + if (cell->type == ID($anyinit)) { + is_anyinit = true; + log_assert(val_init.is_fully_undef()); + } + } else if (cell->type == ID($sr)) { + // No data input at all. + } else if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { + has_aload = true; + sig_aload = cell->getPort(ID::EN); + pol_aload = cell->getParam(ID::EN_POLARITY).as_bool(); + sig_ad = cell->getPort(ID::D); + } else { + has_clk = true; + sig_clk = cell->getPort(ID::CLK); + pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool(); + sig_d = cell->getPort(ID::D); + } + if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($aldffe), ID($sdffe), ID($sdffce))) { + has_ce = true; + sig_ce = cell->getPort(ID::EN); + pol_ce = cell->getParam(ID::EN_POLARITY).as_bool(); + } + if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) { + has_sr = true; + sig_clr = cell->getPort(ID::CLR); + sig_set = cell->getPort(ID::SET); + pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool(); + pol_set = cell->getParam(ID::SET_POLARITY).as_bool(); + } + if (cell->type.in(ID($aldff), ID($aldffe))) { + has_aload = true; + sig_aload = cell->getPort(ID::ALOAD); + pol_aload = cell->getParam(ID::ALOAD_POLARITY).as_bool(); + sig_ad = cell->getPort(ID::AD); + } + if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) { + has_arst = true; + sig_arst = cell->getPort(ID::ARST); + pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool(); + val_arst = cell->getParam(ID::ARST_VALUE); + } + if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) { + has_srst = true; + sig_srst = cell->getPort(ID::SRST); + pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool(); + val_srst = cell->getParam(ID::SRST_VALUE); + ce_over_srst = cell->type == ID($sdffce); + } + } else if (cell->type == ID($_FF_)) { + is_fine = true; + has_gclk = true; + sig_d = cell->getPort(ID::D); + } else if (type_str.substr(0, 5) == "$_SR_") { + is_fine = true; + has_sr = true; + pol_set = type_str[5] == 'P'; + pol_clr = type_str[6] == 'P'; + sig_set = cell->getPort(ID::S); + sig_clr = cell->getPort(ID::R); + } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[6] == 'P'; + sig_clk = cell->getPort(ID::C); + } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[7] == 'P'; + sig_clk = cell->getPort(ID::C); + has_ce = true; + pol_ce = type_str[8] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[6] == 'P'; + sig_clk = cell->getPort(ID::C); + has_arst = true; + pol_arst = type_str[7] == 'P'; + sig_arst = cell->getPort(ID::R); + val_arst = type_str[8] == '1' ? State::S1 : State::S0; + } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[7] == 'P'; + sig_clk = cell->getPort(ID::C); + has_arst = true; + pol_arst = type_str[8] == 'P'; + sig_arst = cell->getPort(ID::R); + val_arst = type_str[9] == '1' ? State::S1 : State::S0; + has_ce = true; + pol_ce = type_str[10] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 8) == "$_ALDFF_" && type_str.size() == 11) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[8] == 'P'; + sig_clk = cell->getPort(ID::C); + has_aload = true; + pol_aload = type_str[9] == 'P'; + sig_aload = cell->getPort(ID::L); + sig_ad = cell->getPort(ID::AD); + } else if (type_str.substr(0, 9) == "$_ALDFFE_" && type_str.size() == 13) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[9] == 'P'; + sig_clk = cell->getPort(ID::C); + has_aload = true; + pol_aload = type_str[10] == 'P'; + sig_aload = cell->getPort(ID::L); + sig_ad = cell->getPort(ID::AD); + has_ce = true; + pol_ce = type_str[11] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[8] == 'P'; + sig_clk = cell->getPort(ID::C); + has_sr = true; + pol_set = type_str[9] == 'P'; + pol_clr = type_str[10] == 'P'; + sig_set = cell->getPort(ID::S); + sig_clr = cell->getPort(ID::R); + } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[9] == 'P'; + sig_clk = cell->getPort(ID::C); + has_sr = true; + pol_set = type_str[10] == 'P'; + pol_clr = type_str[11] == 'P'; + sig_set = cell->getPort(ID::S); + sig_clr = cell->getPort(ID::R); + has_ce = true; + pol_ce = type_str[12] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[7] == 'P'; + sig_clk = cell->getPort(ID::C); + has_srst = true; + pol_srst = type_str[8] == 'P'; + sig_srst = cell->getPort(ID::R); + val_srst = type_str[9] == '1' ? State::S1 : State::S0; + } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[8] == 'P'; + sig_clk = cell->getPort(ID::C); + has_srst = true; + pol_srst = type_str[9] == 'P'; + sig_srst = cell->getPort(ID::R); + val_srst = type_str[10] == '1' ? State::S1 : State::S0; + has_ce = true; + pol_ce = type_str[11] == 'P'; + sig_ce = cell->getPort(ID::E); + } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { + is_fine = true; + sig_d = cell->getPort(ID::D); + has_clk = true; + pol_clk = type_str[9] == 'P'; + sig_clk = cell->getPort(ID::C); + has_srst = true; + pol_srst = type_str[10] == 'P'; + sig_srst = cell->getPort(ID::R); + val_srst = type_str[11] == '1' ? State::S1 : State::S0; + has_ce = true; + pol_ce = type_str[12] == 'P'; + sig_ce = cell->getPort(ID::E); + ce_over_srst = true; + } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { + is_fine = true; + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[9] == 'P'; + sig_aload = cell->getPort(ID::E); + } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { + is_fine = true; + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[9] == 'P'; + sig_aload = cell->getPort(ID::E); + has_arst = true; + pol_arst = type_str[10] == 'P'; + sig_arst = cell->getPort(ID::R); + val_arst = type_str[11] == '1' ? State::S1 : State::S0; + } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { + is_fine = true; + has_aload = true; + sig_ad = cell->getPort(ID::D); + has_aload = true; + pol_aload = type_str[11] == 'P'; + sig_aload = cell->getPort(ID::E); + has_sr = true; + pol_set = type_str[12] == 'P'; + pol_clr = type_str[13] == 'P'; + sig_set = cell->getPort(ID::S); + sig_clr = cell->getPort(ID::R); + } else { + log_assert(0); + } + if (has_aload && !has_clk && !has_sr && !has_arst && sig_ad.is_fully_const()) { + // Plain D latches with const D treated specially. + has_aload = false; + has_arst = true; + sig_arst = sig_aload; + pol_arst = pol_aload; + val_arst = sig_ad.as_const(); + } +} + +FfData FfData::slice(const std::vector &bits) { + FfData res(module, initvals, NEW_ID); + res.sig_clk = sig_clk; + res.sig_ce = sig_ce; + res.sig_aload = sig_aload; + res.sig_arst = sig_arst; + res.sig_srst = sig_srst; + res.has_clk = has_clk; + res.has_gclk = has_gclk; + res.has_ce = has_ce; + res.has_aload = has_aload; + res.has_arst = has_arst; + res.has_srst = has_srst; + res.has_sr = has_sr; + res.ce_over_srst = ce_over_srst; + res.is_fine = is_fine; + res.is_anyinit = is_anyinit; + res.pol_clk = pol_clk; + res.pol_ce = pol_ce; + res.pol_aload = pol_aload; + res.pol_arst = pol_arst; + res.pol_srst = pol_srst; + res.pol_clr = pol_clr; + res.pol_set = pol_set; + res.attributes = attributes; + for (int i : bits) { + res.sig_q.append(sig_q[i]); + if (has_clk || has_gclk) + res.sig_d.append(sig_d[i]); + if (has_aload) + res.sig_ad.append(sig_ad[i]); + if (has_sr) { + res.sig_clr.append(sig_clr[i]); + res.sig_set.append(sig_set[i]); + } + if (has_arst) + res.val_arst.bits.push_back(val_arst[i]); + if (has_srst) + res.val_srst.bits.push_back(val_srst[i]); + if (initvals) + res.val_init.bits.push_back(val_init[i]); + } + res.width = GetSize(res.sig_q); + return res; +} + +void FfData::add_dummy_ce() { + if (has_ce) + return; + has_ce = true; + pol_ce = true; + sig_ce = State::S1; + ce_over_srst = false; +} + +void FfData::add_dummy_srst() { + if (has_srst) + return; + has_srst = true; + pol_srst = true; + sig_srst = State::S0; + val_srst = Const(State::Sx, width); + ce_over_srst = false; +} + +void FfData::add_dummy_arst() { + if (has_arst) + return; + has_arst = true; + pol_arst = true; + sig_arst = State::S0; + val_arst = Const(State::Sx, width); +} + +void FfData::add_dummy_aload() { + if (has_aload) + return; + has_aload = true; + pol_aload = true; + sig_aload = State::S0; + sig_ad = Const(State::Sx, width); +} + +void FfData::add_dummy_sr() { + if (has_sr) + return; + has_sr = true; + pol_clr = true; + pol_set = true; + sig_clr = Const(State::S0, width); + sig_set = Const(State::S0, width); +} + +void FfData::add_dummy_clk() { + if (has_clk) + return; + has_clk = true; + pol_clk = true; + sig_clk = State::S0; + sig_d = Const(State::Sx, width); +} + +void FfData::arst_to_aload() { + log_assert(has_arst); + log_assert(!has_aload); + pol_aload = pol_arst; + sig_aload = sig_arst; + sig_ad = val_arst; + has_aload = true; + has_arst = false; +} + +void FfData::arst_to_sr() { + log_assert(has_arst); + log_assert(!has_sr); + pol_clr = pol_arst; + pol_set = pol_arst; + sig_clr = Const(pol_arst ? State::S0 : State::S1, width); + sig_set = Const(pol_arst ? State::S0 : State::S1, width); + has_sr = true; + has_arst = false; + for (int i = 0; i < width; i++) { + if (val_arst[i] == State::S1) + sig_set[i] = sig_arst; + else + sig_clr[i] = sig_arst; + } +} + +void FfData::aload_to_sr() { + log_assert(has_aload); + log_assert(!has_sr); + has_sr = true; + has_aload = false; + if (!is_fine) { + pol_clr = false; + pol_set = true; + if (pol_aload) { + sig_clr = module->Mux(NEW_ID, Const(State::S1, width), sig_ad, sig_aload); + sig_set = module->Mux(NEW_ID, Const(State::S0, width), sig_ad, sig_aload); + } else { + sig_clr = module->Mux(NEW_ID, sig_ad, Const(State::S1, width), sig_aload); + sig_set = module->Mux(NEW_ID, sig_ad, Const(State::S0, width), sig_aload); + } + } else { + pol_clr = pol_aload; + pol_set = pol_aload; + if (pol_aload) { + sig_clr = module->AndnotGate(NEW_ID, sig_aload, sig_ad); + sig_set = module->AndGate(NEW_ID, sig_aload, sig_ad); + } else { + sig_clr = module->OrGate(NEW_ID, sig_aload, sig_ad); + sig_set = module->OrnotGate(NEW_ID, sig_aload, sig_ad); + } + } +} + +void FfData::convert_ce_over_srst(bool val) { + if (!has_ce || !has_srst || ce_over_srst == val) + return; + if (val) { + // sdffe to sdffce + if (!is_fine) { + if (pol_ce) { + if (pol_srst) { + sig_ce = module->Or(NEW_ID, sig_ce, sig_srst); + } else { + SigSpec tmp = module->Not(NEW_ID, sig_srst); + sig_ce = module->Or(NEW_ID, sig_ce, tmp); + } + } else { + if (pol_srst) { + SigSpec tmp = module->Not(NEW_ID, sig_srst); + sig_ce = module->And(NEW_ID, sig_ce, tmp); + } else { + sig_ce = module->And(NEW_ID, sig_ce, sig_srst); + } + } + } else { + if (pol_ce) { + if (pol_srst) { + sig_ce = module->OrGate(NEW_ID, sig_ce, sig_srst); + } else { + sig_ce = module->OrnotGate(NEW_ID, sig_ce, sig_srst); + } + } else { + if (pol_srst) { + sig_ce = module->AndnotGate(NEW_ID, sig_ce, sig_srst); + } else { + sig_ce = module->AndGate(NEW_ID, sig_ce, sig_srst); + } + } + } + } else { + // sdffce to sdffe + if (!is_fine) { + if (pol_srst) { + if (pol_ce) { + sig_srst = cell->module->And(NEW_ID, sig_srst, sig_ce); + } else { + SigSpec tmp = module->Not(NEW_ID, sig_ce); + sig_srst = cell->module->And(NEW_ID, sig_srst, tmp); + } + } else { + if (pol_ce) { + SigSpec tmp = module->Not(NEW_ID, sig_ce); + sig_srst = cell->module->Or(NEW_ID, sig_srst, tmp); + } else { + sig_srst = cell->module->Or(NEW_ID, sig_srst, sig_ce); + } + } + } else { + if (pol_srst) { + if (pol_ce) { + sig_srst = cell->module->AndGate(NEW_ID, sig_srst, sig_ce); + } else { + sig_srst = cell->module->AndnotGate(NEW_ID, sig_srst, sig_ce); + } + } else { + if (pol_ce) { + sig_srst = cell->module->OrnotGate(NEW_ID, sig_srst, sig_ce); + } else { + sig_srst = cell->module->OrGate(NEW_ID, sig_srst, sig_ce); + } + } + } + } + ce_over_srst = val; +} + +void FfData::unmap_ce() { + if (!has_ce) + return; + log_assert(has_clk); + if (has_srst && ce_over_srst) + unmap_srst(); + + if (!is_fine) { + if (pol_ce) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_ce); + else + sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_ce); + } else { + if (pol_ce) + sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_ce); + else + sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_ce); + } + has_ce = false; +} + +void FfData::unmap_srst() { + if (!has_srst) + return; + if (has_ce && !ce_over_srst) + unmap_ce(); + + if (!is_fine) { + if (pol_srst) + sig_d = module->Mux(NEW_ID, sig_d, val_srst, sig_srst); + else + sig_d = module->Mux(NEW_ID, val_srst, sig_d, sig_srst); + } else { + if (pol_srst) + sig_d = module->MuxGate(NEW_ID, sig_d, val_srst[0], sig_srst); + else + sig_d = module->MuxGate(NEW_ID, val_srst[0], sig_d, sig_srst); + } + has_srst = false; +} + +Cell *FfData::emit() { + remove(); + if (!width) + return nullptr; + if (!has_aload && !has_clk && !has_gclk && !has_sr) { + if (has_arst) { + // Convert this case to a D latch. + arst_to_aload(); + } else { + // No control inputs left. Turn into a const driver. + module->connect(sig_q, val_init); + return nullptr; + } + } + if (initvals && !is_anyinit) + initvals->set_init(sig_q, val_init); + if (!is_fine) { + if (has_gclk) { + log_assert(!has_clk); + log_assert(!has_ce); + log_assert(!has_aload); + log_assert(!has_arst); + log_assert(!has_srst); + log_assert(!has_sr); + if (is_anyinit) { + cell = module->addAnyinit(name, sig_d, sig_q); + log_assert(val_init.is_fully_undef()); + } else { + cell = module->addFf(name, sig_d, sig_q); + } + } else if (!has_aload && !has_clk) { + log_assert(has_sr); + cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); + } else if (!has_clk) { + log_assert(!has_srst); + if (has_sr) + cell = module->addDlatchsr(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr); + else if (has_arst) + cell = module->addAdlatch(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst, pol_aload, pol_arst); + else + cell = module->addDlatch(name, sig_aload, sig_ad, sig_q, pol_aload); + } else { + if (has_sr) { + if (has_ce) + cell = module->addDffsre(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr); + else + cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); + } else if (has_arst) { + if (has_ce) + cell = module->addAdffe(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_ce, pol_arst); + else + cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst); + } else if (has_aload) { + if (has_ce) + cell = module->addAldffe(name, sig_clk, sig_ce, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_ce, pol_aload); + else + cell = module->addAldff(name, sig_clk, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_aload); + } else if (has_srst) { + if (has_ce) + if (ce_over_srst) + cell = module->addSdffce(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst); + else + cell = module->addSdffe(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_ce, pol_srst); + else + cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst); + } else { + if (has_ce) + cell = module->addDffe(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce); + else + cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk); + } + } + } else { + if (has_gclk) { + log_assert(!has_clk); + log_assert(!has_ce); + log_assert(!has_aload); + log_assert(!has_arst); + log_assert(!has_srst); + log_assert(!has_sr); + log_assert(!is_anyinit); + cell = module->addFfGate(name, sig_d, sig_q); + } else if (!has_aload && !has_clk) { + log_assert(has_sr); + cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); + } else if (!has_clk) { + log_assert(!has_srst); + if (has_sr) + cell = module->addDlatchsrGate(name, sig_aload, sig_set, sig_clr, sig_ad, sig_q, pol_aload, pol_set, pol_clr); + else if (has_arst) + cell = module->addAdlatchGate(name, sig_aload, sig_arst, sig_ad, sig_q, val_arst.as_bool(), pol_aload, pol_arst); + else + cell = module->addDlatchGate(name, sig_aload, sig_ad, sig_q, pol_aload); + } else { + if (has_sr) { + if (has_ce) + cell = module->addDffsreGate(name, sig_clk, sig_ce, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_ce, pol_set, pol_clr); + else + cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); + } else if (has_arst) { + if (has_ce) + cell = module->addAdffeGate(name, sig_clk, sig_ce, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_ce, pol_arst); + else + cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst); + } else if (has_aload) { + if (has_ce) + cell = module->addAldffeGate(name, sig_clk, sig_ce, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_ce, pol_aload); + else + cell = module->addAldffGate(name, sig_clk, sig_aload, sig_d, sig_q, sig_ad, pol_clk, pol_aload); + } else if (has_srst) { + if (has_ce) + if (ce_over_srst) + cell = module->addSdffceGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst); + else + cell = module->addSdffeGate(name, sig_clk, sig_ce, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_ce, pol_srst); + else + cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst); + } else { + if (has_ce) + cell = module->addDffeGate(name, sig_clk, sig_ce, sig_d, sig_q, pol_clk, pol_ce); + else + cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk); + } + } + } + cell->attributes = attributes; + return cell; +} + +void FfData::remove() { + if (cell) { + remove_init(); + module->remove(cell); + cell = nullptr; + } +} + +namespace { + State invert(State s) { + switch (s) { + case State::S0: return State::S1; + case State::S1: return State::S0; + default: return s; + } + } +} + +void FfData::flip_rst_bits(const pool &bits) { + if (!bits.size()) + return; + + remove_init(); + + for (auto bit: bits) { + if (has_arst) + val_arst[bit] = invert(val_arst[bit]); + if (has_srst) + val_srst[bit] = invert(val_srst[bit]); + val_init[bit] = invert(val_init[bit]); + } +} + +void FfData::flip_bits(const pool &bits) { + if (!bits.size()) + return; + + flip_rst_bits(bits); + + Wire *new_q = module->addWire(NEW_ID, width); + + if (has_sr && cell) { + log_warning("Flipping D/Q/init and inserting priority fixup to legalize %s.%s [%s].\n", log_id(module->name), log_id(cell->name), log_id(cell->type)); + } + + if (is_fine) { + if (has_sr) { + bool new_pol_clr = pol_set; + SigSpec new_sig_clr; + if (pol_set) { + if (pol_clr) { + new_sig_clr = module->AndnotGate(NEW_ID, sig_set, sig_clr); + } else { + new_sig_clr = module->AndGate(NEW_ID, sig_set, sig_clr); + } + } else { + if (pol_clr) { + new_sig_clr = module->OrGate(NEW_ID, sig_set, sig_clr); + } else { + new_sig_clr = module->OrnotGate(NEW_ID, sig_set, sig_clr); + } + } + pol_set = pol_clr; + sig_set = sig_clr; + pol_clr = new_pol_clr; + sig_clr = new_sig_clr; + } + if (has_clk || has_gclk) + sig_d = module->NotGate(NEW_ID, sig_d); + if (has_aload) + sig_ad = module->NotGate(NEW_ID, sig_ad); + module->addNotGate(NEW_ID, new_q, sig_q); + } + else + { + if (has_sr) { + SigSpec not_clr; + if (!pol_clr) { + not_clr = sig_clr; + sig_clr = module->Not(NEW_ID, sig_clr); + pol_clr = true; + } else { + not_clr = module->Not(NEW_ID, sig_clr); + } + if (!pol_set) { + sig_set = module->Not(NEW_ID, sig_set); + pol_set = true; + } + + SigSpec masked_set = module->And(NEW_ID, sig_set, not_clr); + for (auto bit: bits) { + sig_set[bit] = sig_clr[bit]; + sig_clr[bit] = masked_set[bit]; + } + } + + Const mask = Const(State::S0, width); + for (auto bit: bits) + mask.bits[bit] = State::S1; + + if (has_clk || has_gclk) + sig_d = module->Xor(NEW_ID, sig_d, mask); + if (has_aload) + sig_ad = module->Xor(NEW_ID, sig_ad, mask); + module->addXor(NEW_ID, new_q, mask, sig_q); + } + + sig_q = new_q; +} diff --git a/kernel/ff.h b/kernel/ff.h index 0aecbaa2aac..e684d3c4314 100644 --- a/kernel/ff.h +++ b/kernel/ff.h @@ -25,460 +25,198 @@ YOSYS_NAMESPACE_BEGIN +// Describes a flip-flop or a latch. +// +// If has_gclk, this is a formal verification FF with implicit global clock: +// Q is simply previous cycle's D. Additionally if is_anyinit is true, this is +// an $anyinit cell which always has an undefined initialization value. Note +// that $anyinit is not considered to be among the FF celltypes, so a pass has +// to explicitly opt-in to process $anyinit cells with FfData. +// +// Otherwise, the FF/latch can have any number of features selected by has_* +// attributes that determine Q's value (in order of decreasing priority): +// +// - on start, register is initialized to val_init +// - if has_sr is present: +// - sig_clr is per-bit async clear, and sets the corresponding bit to 0 +// if active +// - sig_set is per-bit async set, and sets the corresponding bit to 1 +// if active +// - if has_arst is present: +// - sig_arst is whole-reg async reset, and sets the whole register to val_arst +// - if has_aload is present: +// - sig_aload is whole-reg async load (aka latch gate enable), and sets the whole +// register to sig_ad +// - if has_clk is present, and we're currently on a clock edge: +// - if has_ce is present and ce_over_srst is true: +// - ignore clock edge (don't change value) unless sig_ce is active +// - if has_srst is present: +// - sig_srst is whole-reg sync reset and sets the register to val_srst +// - if has_ce is present and ce_over_srst is false: +// - ignore clock edge (don't change value) unless sig_ce is active +// - set whole reg to sig_d +// - if nothing of the above applies, the reg value remains unchanged +// +// Since the yosys FF cell library isn't fully generic, not all combinations +// of the features above can be supported: +// +// - only one of has_srst, has_arst, has_sr can be used +// - if has_clk is used together with has_aload, then has_srst, has_arst, +// has_sr cannot be used +// +// The valid feature combinations are thus: +// +// - has_clk + optional has_ce [dff/dffe] +// - has_clk + optional has_ce + has_arst [adff/adffe] +// - has_clk + optional has_ce + has_aload [aldff/aldffe] +// - has_clk + optional has_ce + has_sr [dffsr/dffsre] +// - has_clk + optional has_ce + has_srst [sdff/sdffe/sdffce] +// - has_aload [dlatch] +// - has_aload + has_arst [adlatch] +// - has_aload + has_sr [dlatchsr] +// - has_sr [sr] +// - has_arst [does not correspond to a native cell, represented as dlatch with const D input] +// - empty set [not a cell — will be emitted as a simple direct connection] + struct FfData { + Module *module; FfInitVals *initvals; + Cell *cell; + IdString name; + // The FF output. SigSpec sig_q; + // The sync data input, present if has_clk or has_gclk. SigSpec sig_d; + // The async data input, present if has_aload. + SigSpec sig_ad; + // The sync clock, present if has_clk. SigSpec sig_clk; - SigSpec sig_en; + // The clock enable, present if has_ce. + SigSpec sig_ce; + // The async load enable, present if has_aload. + SigSpec sig_aload; + // The async reset, preset if has_arst. SigSpec sig_arst; + // The sync reset, preset if has_srst. SigSpec sig_srst; + // The async clear (per-lane), present if has_sr. SigSpec sig_clr; + // The async set (per-lane), present if has_sr. SigSpec sig_set; - bool has_d; + // True if this is a clocked (edge-sensitive) flip-flop. bool has_clk; - bool has_en; + // True if this is a $ff, exclusive with every other has_*. + bool has_gclk; + // True if this FF has a clock enable. Depends on has_clk. + bool has_ce; + // True if this FF has async load function — this includes D latches. + // If this and has_clk are both set, has_arst and has_sr cannot be set. + bool has_aload; + // True if this FF has sync set/reset. Depends on has_clk, exclusive + // with has_arst, has_sr, has_aload. bool has_srst; + // True if this FF has async set/reset. Exclusive with has_srst, + // has_sr. If this and has_clk are both set, has_aload cannot be set. bool has_arst; + // True if this FF has per-bit async set + clear. Exclusive with + // has_srst, has_arst. If this and has_clk are both set, has_aload + // cannot be set. bool has_sr; + // If has_ce and has_srst are both set, determines their relative + // priorities: if true, inactive ce disables srst; if false, srst + // operates independent of ce. bool ce_over_srst; + // True if this FF is a fine cell, false if it is a coarse cell. + // If true, width must be 1. bool is_fine; + // True if this FF is an $anyinit cell. Depends on has_gclk. + bool is_anyinit; + // Polarities, corresponding to sig_*. True means active-high, false + // means active-low. bool pol_clk; - bool pol_en; + bool pol_ce; + bool pol_aload; bool pol_arst; bool pol_srst; bool pol_clr; bool pol_set; + // The value loaded by sig_arst. Const val_arst; + // The value loaded by sig_srst. Const val_srst; + // The initial value at power-up. Const val_init; - Const val_d; - bool d_is_const; + // The FF data width in bits. int width; dict attributes; - FfData(FfInitVals *initvals, Cell *cell = nullptr) : initvals(initvals) { + FfData(Module *module = nullptr, FfInitVals *initvals = nullptr, IdString name = IdString()) : module(module), initvals(initvals), cell(nullptr), name(name) { width = 0; - has_d = true; has_clk = false; - has_en = false; + has_gclk = false; + has_ce = false; + has_aload = false; has_srst = false; has_arst = false; has_sr = false; ce_over_srst = false; is_fine = false; + is_anyinit = false; pol_clk = false; - pol_en = false; + pol_aload = false; + pol_ce = false; pol_arst = false; pol_srst = false; pol_clr = false; pol_set = false; - d_is_const = false; + } - if (!cell) - return; + FfData(FfInitVals *initvals, Cell *cell_); - sig_q = cell->getPort(ID::Q); - width = GetSize(sig_q); - attributes = cell->attributes; + // Returns a FF identical to this one, but only keeping bit indices from the argument. + FfData slice(const std::vector &bits); - if (initvals) - val_init = (*initvals)(sig_q); + void add_dummy_ce(); + void add_dummy_srst(); + void add_dummy_arst(); + void add_dummy_aload(); + void add_dummy_sr(); + void add_dummy_clk(); - std::string type_str = cell->type.str(); + void arst_to_aload(); + void arst_to_sr(); - if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { - if (cell->type == ID($sr)) { - has_d = false; - } else { - sig_d = cell->getPort(ID::D); - } - if (!cell->type.in(ID($ff), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) { - has_clk = true; - sig_clk = cell->getPort(ID::CLK); - pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool(); - } - if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr))) { - has_en = true; - sig_en = cell->getPort(ID::EN); - pol_en = cell->getParam(ID::EN_POLARITY).as_bool(); - } - if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) { - has_sr = true; - sig_clr = cell->getPort(ID::CLR); - sig_set = cell->getPort(ID::SET); - pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool(); - pol_set = cell->getParam(ID::SET_POLARITY).as_bool(); - } - if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) { - has_arst = true; - sig_arst = cell->getPort(ID::ARST); - pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool(); - val_arst = cell->getParam(ID::ARST_VALUE); - } - if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) { - has_srst = true; - sig_srst = cell->getPort(ID::SRST); - pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool(); - val_srst = cell->getParam(ID::SRST_VALUE); - ce_over_srst = cell->type == ID($sdffce); - } - } else if (cell->type == ID($_FF_)) { - is_fine = true; - sig_d = cell->getPort(ID::D); - } else if (type_str.substr(0, 5) == "$_SR_") { - is_fine = true; - has_d = false; - has_sr = true; - pol_set = type_str[5] == 'P'; - pol_clr = type_str[6] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[6] == 'P'; - sig_clk = cell->getPort(ID::C); - } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_en = true; - pol_en = type_str[8] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[6] == 'P'; - sig_clk = cell->getPort(ID::C); - has_arst = true; - pol_arst = type_str[7] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[8] == '1' ? State::S1 : State::S0; - } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_arst = true; - pol_arst = type_str[8] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[9] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[10] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[8] == 'P'; - sig_clk = cell->getPort(ID::C); - has_sr = true; - pol_set = type_str[9] == 'P'; - pol_clr = type_str[10] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[9] == 'P'; - sig_clk = cell->getPort(ID::C); - has_sr = true; - pol_set = type_str[10] == 'P'; - pol_clr = type_str[11] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - has_en = true; - pol_en = type_str[12] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[7] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[8] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[9] == '1' ? State::S1 : State::S0; - } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[8] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[9] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[10] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[11] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_clk = true; - pol_clk = type_str[9] == 'P'; - sig_clk = cell->getPort(ID::C); - has_srst = true; - pol_srst = type_str[10] == 'P'; - sig_srst = cell->getPort(ID::R); - val_srst = type_str[11] == '1' ? State::S1 : State::S0; - has_en = true; - pol_en = type_str[12] == 'P'; - sig_en = cell->getPort(ID::E); - ce_over_srst = true; - } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[9] == 'P'; - sig_en = cell->getPort(ID::E); - } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[9] == 'P'; - sig_en = cell->getPort(ID::E); - has_arst = true; - pol_arst = type_str[10] == 'P'; - sig_arst = cell->getPort(ID::R); - val_arst = type_str[11] == '1' ? State::S1 : State::S0; - } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { - is_fine = true; - sig_d = cell->getPort(ID::D); - has_en = true; - pol_en = type_str[11] == 'P'; - sig_en = cell->getPort(ID::E); - has_sr = true; - pol_set = type_str[12] == 'P'; - pol_clr = type_str[13] == 'P'; - sig_set = cell->getPort(ID::S); - sig_clr = cell->getPort(ID::R); - } else { - log_assert(0); - } - if (has_d && sig_d.is_fully_const()) { - d_is_const = true; - val_d = sig_d.as_const(); - if (has_en && !has_clk && !has_sr && !has_arst) { - // Plain D latches with const D treated specially. - has_en = has_d = false; - has_arst = true; - sig_arst = sig_en; - pol_arst = pol_en; - val_arst = val_d; - } - } - } + void aload_to_sr(); - // Returns a FF identical to this one, but only keeping bit indices from the argument. - FfData slice(const std::vector &bits) { - FfData res(initvals); - res.sig_clk = sig_clk; - res.sig_en = sig_en; - res.sig_arst = sig_arst; - res.sig_srst = sig_srst; - res.has_d = has_d; - res.has_clk = has_clk; - res.has_en = has_en; - res.has_arst = has_arst; - res.has_srst = has_srst; - res.has_sr = has_sr; - res.ce_over_srst = ce_over_srst; - res.is_fine = is_fine; - res.pol_clk = pol_clk; - res.pol_en = pol_en; - res.pol_arst = pol_arst; - res.pol_srst = pol_srst; - res.pol_clr = pol_clr; - res.pol_set = pol_set; - res.attributes = attributes; - for (int i : bits) { - res.sig_q.append(sig_q[i]); - if (has_d) - res.sig_d.append(sig_d[i]); - if (has_sr) { - res.sig_clr.append(sig_clr[i]); - res.sig_set.append(sig_set[i]); - } - if (has_arst) - res.val_arst.bits.push_back(val_arst[i]); - if (has_srst) - res.val_srst.bits.push_back(val_srst[i]); - res.val_init.bits.push_back(val_init[i]); - } - res.width = GetSize(res.sig_q); - // Slicing bits out may cause D to become const. - if (has_d && res.sig_d.is_fully_const()) { - res.d_is_const = true; - res.val_d = res.sig_d.as_const(); - } - return res; - } + // Given a FF with both has_ce and has_srst, sets ce_over_srst to the given value and + // fixes up control signals appropriately to preserve semantics. + void convert_ce_over_srst(bool val); - void unmap_ce(Module *module) { - if (!has_en) - return; - log_assert(has_clk); - if (has_srst && ce_over_srst) - unmap_srst(module); + void unmap_ce(); + void unmap_srst(); - if (!is_fine) { - if (pol_en) - sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_en); - else - sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_en); - } else { - if (pol_en) - sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_en); - else - sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_en); - } - has_en = false; + void unmap_ce_srst() { + unmap_ce(); + unmap_srst(); } - void unmap_srst(Module *module) { - if (!has_srst) - return; - if (has_en && !ce_over_srst) - unmap_ce(module); + Cell *emit(); - if (!is_fine) { - if (pol_srst) - sig_d = module->Mux(NEW_ID, sig_d, val_srst, sig_srst); - else - sig_d = module->Mux(NEW_ID, val_srst, sig_d, sig_srst); - } else { - if (pol_srst) - sig_d = module->MuxGate(NEW_ID, sig_d, val_srst[0], sig_srst); - else - sig_d = module->MuxGate(NEW_ID, val_srst[0], sig_d, sig_srst); - } - has_srst = false; + // Removes init attribute from the Q output, but keeps val_init unchanged. + // It will be automatically reattached on emit. Use this before changing sig_q. + void remove_init() { + if (initvals) + initvals->remove_init(sig_q); } - void unmap_ce_srst(Module *module) { - unmap_ce(module); - unmap_srst(module); - } + void remove(); - Cell *emit(Module *module, IdString name) { - if (!width) - return nullptr; - if (!has_d && !has_sr) { - if (has_arst) { - // Convert this case to a D latch. - has_d = has_en = true; - has_arst = false; - sig_d = val_arst; - sig_en = sig_arst; - pol_en = pol_arst; - } else { - // No control inputs left. Turn into a const driver. - initvals->remove_init(sig_q); - module->connect(sig_q, val_init); - return nullptr; - } - } - initvals->set_init(sig_q, val_init); - Cell *cell; - if (!is_fine) { - if (!has_d) { - log_assert(has_sr); - cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); - } else if (!has_clk && !has_en) { - log_assert(!has_arst); - log_assert(!has_srst); - log_assert(!has_sr); - cell = module->addFf(name, sig_d, sig_q); - } else if (!has_clk) { - log_assert(!has_srst); - if (has_sr) - cell = module->addDlatchsr(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr); - else if (has_arst) - cell = module->addAdlatch(name, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_en, pol_arst); - else - cell = module->addDlatch(name, sig_en, sig_d, sig_q, pol_en); - } else { - if (has_sr) { - if (has_en) - cell = module->addDffsre(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr); - else - cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); - } else if (has_arst) { - if (has_en) - cell = module->addAdffe(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_en, pol_arst); - else - cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst); - } else if (has_srst) { - if (has_en) - if (ce_over_srst) - cell = module->addSdffce(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst); - else - cell = module->addSdffe(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst); - else - cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst); - } else { - if (has_en) - cell = module->addDffe(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en); - else - cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk); - } - } - } else { - if (!has_d) { - log_assert(has_sr); - cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr); - } else if (!has_clk && !has_en) { - log_assert(!has_arst); - log_assert(!has_srst); - log_assert(!has_sr); - cell = module->addFfGate(name, sig_d, sig_q); - } else if (!has_clk) { - log_assert(!has_srst); - if (has_sr) - cell = module->addDlatchsrGate(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr); - else if (has_arst) - cell = module->addAdlatchGate(name, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_en, pol_arst); - else - cell = module->addDlatchGate(name, sig_en, sig_d, sig_q, pol_en); - } else { - if (has_sr) { - if (has_en) - cell = module->addDffsreGate(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr); - else - cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr); - } else if (has_arst) { - if (has_en) - cell = module->addAdffeGate(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_en, pol_arst); - else - cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst); - } else if (has_srst) { - if (has_en) - if (ce_over_srst) - cell = module->addSdffceGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst); - else - cell = module->addSdffeGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst); - else - cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst); - } else { - if (has_en) - cell = module->addDffeGate(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en); - else - cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk); - } - } - } - cell->attributes = attributes; - return cell; - } + // Flip the sense of the given bit slices of the FF: insert inverters on data + // inputs and output, flip the corresponding init/reset bits, swap clr/set + // inputs with proper priority fix. + void flip_bits(const pool &bits); + + void flip_rst_bits(const pool &bits); }; YOSYS_NAMESPACE_END diff --git a/kernel/ffinit.h b/kernel/ffinit.h index 025b0c8626d..9d33ac572d3 100644 --- a/kernel/ffinit.h +++ b/kernel/ffinit.h @@ -28,7 +28,6 @@ YOSYS_NAMESPACE_BEGIN struct FfInitVals { const SigMap *sigmap; - RTLIL::Module *module; dict> initbits; void set(const SigMap *sigmap_, RTLIL::Module *module) diff --git a/kernel/ffmerge.cc b/kernel/ffmerge.cc new file mode 100644 index 00000000000..c6510841391 --- /dev/null +++ b/kernel/ffmerge.cc @@ -0,0 +1,359 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/ffmerge.h" + +USING_YOSYS_NAMESPACE + +bool FfMergeHelper::is_output_unused(RTLIL::SigSpec sig) { + for (auto bit : (*sigmap)(sig)) + if (sigbit_users_count[bit] != 0) + return false; + return true; +} + +bool FfMergeHelper::find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool> &bits) { + ff = FfData(module, initvals, NEW_ID); + sigmap->apply(sig); + + bool found = false; + + for (auto bit : sig) + { + if (bit.wire == NULL || sigbit_users_count[bit] == 0) { + ff.width++; + ff.sig_q.append(bit); + ff.sig_d.append(bit); + ff.sig_clr.append(State::Sx); + ff.sig_set.append(State::Sx); + ff.val_init.bits.push_back(State::Sx); + ff.val_srst.bits.push_back(State::Sx); + ff.val_arst.bits.push_back(State::Sx); + continue; + } + + if (sigbit_users_count[bit] != 1) + return false; + + auto &sinks = dff_sink[bit]; + if (sinks.size() != 1) + return false; + + Cell *cell; + int idx; + std::tie(cell, idx) = *sinks.begin(); + bits.insert(std::make_pair(cell, idx)); + + FfData cur_ff(initvals, cell); + + // Reject latches and $ff. + if (!cur_ff.has_clk) + return false; + + log_assert((*sigmap)(cur_ff.sig_d[idx]) == bit); + + if (!found) { + ff.sig_clk = cur_ff.sig_clk; + ff.sig_ce = cur_ff.sig_ce; + ff.sig_aload = cur_ff.sig_aload; + ff.sig_srst = cur_ff.sig_srst; + ff.sig_arst = cur_ff.sig_arst; + ff.has_clk = cur_ff.has_clk; + ff.has_ce = cur_ff.has_ce; + ff.has_aload = cur_ff.has_aload; + ff.has_srst = cur_ff.has_srst; + ff.has_arst = cur_ff.has_arst; + ff.has_sr = cur_ff.has_sr; + ff.ce_over_srst = cur_ff.ce_over_srst; + ff.pol_clk = cur_ff.pol_clk; + ff.pol_ce = cur_ff.pol_ce; + ff.pol_aload = cur_ff.pol_aload; + ff.pol_arst = cur_ff.pol_arst; + ff.pol_srst = cur_ff.pol_srst; + ff.pol_clr = cur_ff.pol_clr; + ff.pol_set = cur_ff.pol_set; + } else { + if (ff.has_clk != cur_ff.has_clk) + return false; + if (ff.has_ce != cur_ff.has_ce) + return false; + if (ff.has_aload != cur_ff.has_aload) + return false; + if (ff.has_srst != cur_ff.has_srst) + return false; + if (ff.has_arst != cur_ff.has_arst) + return false; + if (ff.has_sr != cur_ff.has_sr) + return false; + if (ff.has_clk) { + if (ff.sig_clk != cur_ff.sig_clk) + return false; + if (ff.pol_clk != cur_ff.pol_clk) + return false; + } + if (ff.has_ce) { + if (ff.sig_ce != cur_ff.sig_ce) + return false; + if (ff.pol_ce != cur_ff.pol_ce) + return false; + } + if (ff.has_aload) { + if (ff.sig_aload != cur_ff.sig_aload) + return false; + if (ff.pol_aload != cur_ff.pol_aload) + return false; + } + if (ff.has_srst) { + if (ff.sig_srst != cur_ff.sig_srst) + return false; + if (ff.pol_srst != cur_ff.pol_srst) + return false; + if (ff.has_ce && ff.ce_over_srst != cur_ff.ce_over_srst) + return false; + } + if (ff.has_arst) { + if (ff.sig_arst != cur_ff.sig_arst) + return false; + if (ff.pol_arst != cur_ff.pol_arst) + return false; + } + if (ff.has_sr) { + if (ff.pol_clr != cur_ff.pol_clr) + return false; + if (ff.pol_set != cur_ff.pol_set) + return false; + } + } + + ff.width++; + ff.sig_d.append(cur_ff.sig_d[idx]); + ff.sig_ad.append(ff.has_aload ? cur_ff.sig_ad[idx] : State::Sx); + ff.sig_q.append(cur_ff.sig_q[idx]); + ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0); + ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0); + ff.val_arst.bits.push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx); + ff.val_srst.bits.push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx); + ff.val_init.bits.push_back(cur_ff.val_init[idx]); + found = true; + } + + return found; +} + +bool FfMergeHelper::find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool> &bits) { + ff = FfData(module, initvals, NEW_ID); + sigmap->apply(sig); + + bool found = false; + + pool const_bits; + + for (auto bit : sig) + { + if (bit.wire == NULL) { + const_bits.insert(ff.width); + ff.width++; + ff.sig_q.append(bit); + ff.sig_d.append(bit); + // These two will be fixed up later. + ff.sig_clr.append(State::Sx); + ff.sig_set.append(State::Sx); + ff.val_init.bits.push_back(bit.data); + ff.val_srst.bits.push_back(bit.data); + ff.val_arst.bits.push_back(bit.data); + continue; + } + + if (!dff_driver.count(bit)) + return false; + + Cell *cell; + int idx; + std::tie(cell, idx) = dff_driver[bit]; + bits.insert(std::make_pair(cell, idx)); + + FfData cur_ff(initvals, cell); + + log_assert((*sigmap)(cur_ff.sig_q[idx]) == bit); + + if (!found) { + ff.sig_clk = cur_ff.sig_clk; + ff.sig_ce = cur_ff.sig_ce; + ff.sig_aload = cur_ff.sig_aload; + ff.sig_srst = cur_ff.sig_srst; + ff.sig_arst = cur_ff.sig_arst; + ff.has_clk = cur_ff.has_clk; + ff.has_gclk = cur_ff.has_gclk; + ff.has_ce = cur_ff.has_ce; + ff.has_aload = cur_ff.has_aload; + ff.has_srst = cur_ff.has_srst; + ff.has_arst = cur_ff.has_arst; + ff.has_sr = cur_ff.has_sr; + ff.ce_over_srst = cur_ff.ce_over_srst; + ff.pol_clk = cur_ff.pol_clk; + ff.pol_ce = cur_ff.pol_ce; + ff.pol_aload = cur_ff.pol_aload; + ff.pol_arst = cur_ff.pol_arst; + ff.pol_srst = cur_ff.pol_srst; + ff.pol_clr = cur_ff.pol_clr; + ff.pol_set = cur_ff.pol_set; + } else { + if (ff.has_gclk != cur_ff.has_gclk) + return false; + if (ff.has_clk != cur_ff.has_clk) + return false; + if (ff.has_ce != cur_ff.has_ce) + return false; + if (ff.has_aload != cur_ff.has_aload) + return false; + if (ff.has_srst != cur_ff.has_srst) + return false; + if (ff.has_arst != cur_ff.has_arst) + return false; + if (ff.has_sr != cur_ff.has_sr) + return false; + if (ff.has_clk) { + if (ff.sig_clk != cur_ff.sig_clk) + return false; + if (ff.pol_clk != cur_ff.pol_clk) + return false; + } + if (ff.has_ce) { + if (ff.sig_ce != cur_ff.sig_ce) + return false; + if (ff.pol_ce != cur_ff.pol_ce) + return false; + } + if (ff.has_aload) { + if (ff.sig_aload != cur_ff.sig_aload) + return false; + if (ff.pol_aload != cur_ff.pol_aload) + return false; + } + if (ff.has_srst) { + if (ff.sig_srst != cur_ff.sig_srst) + return false; + if (ff.pol_srst != cur_ff.pol_srst) + return false; + if (ff.has_ce && ff.ce_over_srst != cur_ff.ce_over_srst) + return false; + } + if (ff.has_arst) { + if (ff.sig_arst != cur_ff.sig_arst) + return false; + if (ff.pol_arst != cur_ff.pol_arst) + return false; + } + if (ff.has_sr) { + if (ff.pol_clr != cur_ff.pol_clr) + return false; + if (ff.pol_set != cur_ff.pol_set) + return false; + } + } + + ff.width++; + ff.sig_d.append((ff.has_clk || ff.has_gclk) ? cur_ff.sig_d[idx] : State::Sx); + ff.sig_ad.append(ff.has_aload ? cur_ff.sig_ad[idx] : State::Sx); + ff.sig_q.append(cur_ff.sig_q[idx]); + ff.sig_clr.append(ff.has_sr ? cur_ff.sig_clr[idx] : State::S0); + ff.sig_set.append(ff.has_sr ? cur_ff.sig_set[idx] : State::S0); + ff.val_arst.bits.push_back(ff.has_arst ? cur_ff.val_arst[idx] : State::Sx); + ff.val_srst.bits.push_back(ff.has_srst ? cur_ff.val_srst[idx] : State::Sx); + ff.val_init.bits.push_back(cur_ff.val_init[idx]); + found = true; + } + + if (found && ff.has_sr) { + for (auto i: const_bits) { + if (ff.sig_d[i] == State::S0) { + ff.sig_set[i] = ff.pol_set ? State::S0 : State::S1; + } else if (ff.sig_d[i] == State::S1) { + ff.sig_clr[i] = ff.pol_clr ? State::S0 : State::S1; + } + } + } + + return found; +} + + +void FfMergeHelper::remove_output_ff(const pool> &bits) { + for (auto &it : bits) { + Cell *cell = it.first; + int idx = it.second; + SigSpec q = cell->getPort(ID::Q); + initvals->remove_init(q[idx]); + dff_driver.erase((*sigmap)(q[idx])); + q[idx] = module->addWire(stringf("$ffmerge_disconnected$%d", autoidx++)); + cell->setPort(ID::Q, q); + } +} + +void FfMergeHelper::mark_input_ff(const pool> &bits) { + for (auto &it : bits) { + Cell *cell = it.first; + int idx = it.second; + if (cell->hasPort(ID::D)) { + SigSpec d = cell->getPort(ID::D); + // The user count was already at least 1 + // (for the D port). Bump it as it is now connected + // to the merged-to cell as well. This suffices for + // it to not be considered for output merging. + sigbit_users_count[d[idx]]++; + } + } +} + +void FfMergeHelper::set(FfInitVals *initvals_, RTLIL::Module *module_) +{ + clear(); + initvals = initvals_; + sigmap = initvals->sigmap; + module = module_; + + for (auto wire : module->wires()) { + if (wire->port_output) + for (auto bit : (*sigmap)(wire)) + sigbit_users_count[bit]++; + } + + for (auto cell : module->cells()) { + if (RTLIL::builtin_ff_cell_types().count(cell->type)) { + if (cell->hasPort(ID::D)) { + SigSpec d = (*sigmap)(cell->getPort(ID::D)); + for (int i = 0; i < GetSize(d); i++) + dff_sink[d[i]].insert(std::make_pair(cell, i)); + } + SigSpec q = (*sigmap)(cell->getPort(ID::Q)); + for (int i = 0; i < GetSize(q); i++) + dff_driver[q[i]] = std::make_pair(cell, i); + } + for (auto &conn : cell->connections()) + if (!cell->known() || cell->input(conn.first)) + for (auto bit : (*sigmap)(conn.second)) + sigbit_users_count[bit]++; + } +} + +void FfMergeHelper::clear() { + dff_driver.clear(); + dff_sink.clear(); + sigbit_users_count.clear(); +} diff --git a/kernel/ffmerge.h b/kernel/ffmerge.h new file mode 100644 index 00000000000..5428da324a5 --- /dev/null +++ b/kernel/ffmerge.h @@ -0,0 +1,141 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef FFMERGE_H +#define FFMERGE_H + +#include "kernel/ffinit.h" +#include "kernel/ff.h" + +YOSYS_NAMESPACE_BEGIN + +// A helper class for passes that want to merge FFs on the input or output +// of a cell into the cell itself. +// +// The procedure is: +// +// 1. Construct this class (at beginning of processing for a given module). +// 2. For every considered cell: +// +// a. Call find_output_ff for every considered output. +// b. Call find_input_ff for every considered input. +// c. Look at the FF description returned (if any) from each call, reject +// results that cannot be merged into given cell for any reason. +// If both inputs and outputs are being merged, take care of FF bits that +// are returned in both input and output results (a FF bit cannot be +// merged to both). Decide on the final set of FF bits to merge. +// d. Call remove_output_ff for every find_output_ff result that will be used +// for merging. This removes the actual FF bits from design and from index. +// e. Call mark_input_ff for every find_input_ff result that will be used +// for merging. This updates the index disallowing further usage of these +// FF bits for output FF merging, if they were eligible before. The actual +// FF bits are still left in the design and can be merged into other inputs. +// If the FF bits are not otherwise used, they will be removed by later +// opt passes. +// f. Merge the FFs into the cell. +// +// Note that, if both inputs and outputs are being considered for merging in +// a single pass, the result may be nondeterministic (depending on cell iteration +// order) because a given FF bit could be eligible for both input and output merge, +// perhaps in different cells. For this reason, it may be a good idea to separate +// input and output merging. + +struct FfMergeHelper +{ + const SigMap *sigmap; + RTLIL::Module *module; + FfInitVals *initvals; + + dict> dff_driver; + dict>> dff_sink; + dict sigbit_users_count; + + // Returns true if all bits in sig are completely unused. + bool is_output_unused(RTLIL::SigSpec sig); + + // Finds the FF to merge into a given cell output. Takes sig, which + // is the current cell output — it will be the sig_d of the found FF. + // If found, returns true, and fills the two output arguments. + // + // For every bit of sig, this function finds a FF bit that has + // the same sig_d, and fills the output FfData according to the FF + // bits found. This function will only consider FF bits that are + // the only user of the given sig bits — if any bit in sig is used + // by anything other than a single FF, this function will return false. + // + // The returned FfData structure does not correspond to any actual FF + // cell in the design — it is the amalgamation of extracted FF bits, + // possibly coming from several FF cells. + // + // If some of the bits in sig have no users at all, this function + // will accept them as well (and fill returned FfData with dummy values + // for the given bit, effectively synthesizing an unused FF bit of the + // appropriate type). However, if all bits in sig are completely + // unused, this function will fail and return false (having no idea + // what kind of FF to produce) — use the above helper if that case + // is important to handle. + // + // Note that this function does not remove the FF bits returned from + // the design — this is so that the caller can decide whether to accept + // this FF for merging or not. If the result is accepted, + // remove_output_ff should be called on the second output argument. + bool find_output_ff(RTLIL::SigSpec sig, FfData &ff, pool> &bits); + + // Like above, but returns a FF to merge into a given cell input. Takes + // sig_q, which is the current cell input — it will search for FFs with + // matching sig_q. + // + // As opposed to find_output_ff, this function doesn't care about usage + // counts, and may return FF bits that also have other fanout. This + // should not be a problem for input FF merging. + // + // As a special case, if some of the bits in sig_q are constant, this + // function will accept them as well, by synthesizing in-place + // a constant-input FF bit (with matching initial value and reset value). + // However, this will not work if the input is all-constant — if the caller + // cares about this case, it needs to check for it explicitely. + bool find_input_ff(RTLIL::SigSpec sig, FfData &ff, pool> &bits); + + // To be called on find_output_ff result that will be merged. This + // marks the given FF bits as used up (and not to be considered for + // further merging as inputs), and reconnects their Q ports to a dummy + // wire (since the wire previously connected there will now be driven + // by the merged-to cell instead). + void remove_output_ff(const pool> &bits); + + // To be called on find_input_ff result that will be merged. This + // marks the given FF bits as used, and disallows merging them as + // outputs. They can, however, still be merged as inputs again + // (perhaps for another cell). + void mark_input_ff(const pool> &bits); + + void set(FfInitVals *initvals_, RTLIL::Module *module_); + + void clear(); + + FfMergeHelper(FfInitVals *initvals, RTLIL::Module *module) { + set(initvals, module); + } + + FfMergeHelper() {} +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/fmt.cc b/kernel/fmt.cc new file mode 100644 index 00000000000..18eb7cb7193 --- /dev/null +++ b/kernel/fmt.cc @@ -0,0 +1,746 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 whitequark + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "libs/bigint/BigUnsigned.hh" +#include "kernel/fmt.h" + +USING_YOSYS_NAMESPACE + +void Fmt::append_string(const std::string &str) { + FmtPart part = {}; + part.type = FmtPart::STRING; + part.str = str; + parts.push_back(part); +} + +void Fmt::parse_rtlil(const RTLIL::Cell *cell) { + std::string fmt = cell->getParam(ID(FORMAT)).decode_string(); + RTLIL::SigSpec args = cell->getPort(ID(ARGS)); + parts.clear(); + + FmtPart part; + for (size_t i = 0; i < fmt.size(); i++) { + if (fmt.substr(i, 2) == "}}") { + part.str += '}'; + ++i; + } else if (fmt.substr(i, 2) == "{{") { + part.str += '{'; + ++i; + } else if (fmt[i] == '}') + log_assert(false && "Unexpected '}' in format string"); + else if (fmt[i] == '{') { + if (!part.str.empty()) { + part.type = FmtPart::STRING; + parts.push_back(part); + part = {}; + } + + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + + size_t arg_size = 0; + for (; i < fmt.size(); i++) { + if (fmt[i] >= '0' && fmt[i] <= '9') { + arg_size *= 10; + arg_size += fmt[i] - '0'; + } else if (fmt[i] == ':') { + ++i; + break; + } else { + log_assert(false && "Unexpected character in format substitution"); + } + } + if (i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + + if ((size_t)args.size() < arg_size) + log_assert(false && "Format part overruns arguments"); + part.sig = args.extract(0, arg_size); + args.remove(0, arg_size); + + if (fmt[i] == '>') + part.justify = FmtPart::RIGHT; + else if (fmt[i] == '<') + part.justify = FmtPart::LEFT; + else + log_assert(false && "Unexpected justification in format substitution"); + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + + if (fmt[i] == '0' || fmt[i] == ' ') + part.padding = fmt[i]; + else + log_assert(false && "Unexpected padding in format substitution"); + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + + for (; i < fmt.size(); i++) { + if (fmt[i] >= '0' && fmt[i] <= '9') { + part.width *= 10; + part.width += fmt[i] - '0'; + continue; + } else if (fmt[i] == 'b') { + part.type = FmtPart::INTEGER; + part.base = 2; + } else if (fmt[i] == 'o') { + part.type = FmtPart::INTEGER; + part.base = 8; + } else if (fmt[i] == 'd') { + part.type = FmtPart::INTEGER; + part.base = 10; + } else if (fmt[i] == 'h') { + part.type = FmtPart::INTEGER; + part.base = 16; + } else if (fmt[i] == 'c') { + part.type = FmtPart::CHARACTER; + } else if (fmt[i] == 't') { + part.type = FmtPart::VLOG_TIME; + } else if (fmt[i] == 'r') { + part.type = FmtPart::VLOG_TIME; + part.realtime = true; + } else { + log_assert(false && "Unexpected character in format substitution"); + } + ++i; + break; + } + if (i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + + if (part.type == FmtPart::INTEGER) { + if (fmt[i] == '+') { + part.plus = true; + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + } + + if (fmt[i] == 'u') + part.signed_ = false; + else if (fmt[i] == 's') + part.signed_ = true; + else + log_assert(false && "Unexpected character in format substitution"); + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + } + + if (fmt[i] != '}') + log_assert(false && "Expected '}' after format substitution"); + + parts.push_back(part); + part = {}; + } else { + part.str += fmt[i]; + } + } + if (!part.str.empty()) { + part.type = FmtPart::STRING; + parts.push_back(part); + } +} + +void Fmt::emit_rtlil(RTLIL::Cell *cell) const { + std::string fmt; + RTLIL::SigSpec args; + + for (auto &part : parts) { + switch (part.type) { + case FmtPart::STRING: + for (char c : part.str) { + if (c == '{') + fmt += "{{"; + else if (c == '}') + fmt += "}}"; + else + fmt += c; + } + break; + + case FmtPart::VLOG_TIME: + log_assert(part.sig.size() == 0); + YS_FALLTHROUGH + case FmtPart::CHARACTER: + log_assert(part.sig.size() % 8 == 0); + YS_FALLTHROUGH + case FmtPart::INTEGER: + args.append(part.sig); + fmt += '{'; + fmt += std::to_string(part.sig.size()); + fmt += ':'; + if (part.justify == FmtPart::RIGHT) + fmt += '>'; + else if (part.justify == FmtPart::LEFT) + fmt += '<'; + else log_abort(); + log_assert(part.width == 0 || part.padding != '\0'); + fmt += part.padding != '\0' ? part.padding : ' '; + if (part.width > 0) + fmt += std::to_string(part.width); + if (part.type == FmtPart::INTEGER) { + switch (part.base) { + case 2: fmt += 'b'; break; + case 8: fmt += 'o'; break; + case 10: fmt += 'd'; break; + case 16: fmt += 'h'; break; + default: log_abort(); + } + if (part.plus) + fmt += '+'; + fmt += part.signed_ ? 's' : 'u'; + } else if (part.type == FmtPart::CHARACTER) { + fmt += 'c'; + } else if (part.type == FmtPart::VLOG_TIME) { + if (part.realtime) + fmt += 'r'; + else + fmt += 't'; + } else log_abort(); + fmt += '}'; + break; + + default: log_abort(); + } + } + + cell->setParam(ID(FORMAT), fmt); + cell->setParam(ID(ARGS_WIDTH), args.size()); + cell->setPort(ID(ARGS), args); +} + +static size_t compute_required_decimal_places(size_t size, bool signed_) +{ + BigUnsigned max; + if (!signed_) + max.setBit(size, true); + else + max.setBit(size - 1, true); + size_t places = 0; + while (!max.isZero()) { + places++; + max /= 10; + } + if (signed_) + places++; + return places; +} + +static size_t compute_required_nondecimal_places(size_t size, unsigned base) +{ + log_assert(base != 10); + BigUnsigned max; + max.setBit(size - 1, true); + size_t places = 0; + while (!max.isZero()) { + places++; + max /= base; + } + return places; +} + +// Only called for integers, either when: +// +// (a) passed without a format string (e.g. "$display(a);"), or +// +// (b) the corresponding format specifier has no leading zero, e.g. "%b", +// "%20h", "%-10d". +// +// In these cases, for binary/octal/hex, we always zero-pad to the size of the +// signal; i.e. whether "%h" or "%10h" or "%-20h" is used, if the corresponding +// signal is 32'h1234, "00001234" will always be a substring of the output. +// +// For case (a), we have no specified width, so there is nothing more to do. +// +// For case (b), because we are only called with no leading zero on the +// specifier, any specified width beyond the signal size is therefore space +// padding, whatever the justification. +// +// For decimal, we do no zero-padding, instead space-padding to the size +// required for the signal's largest value. This is per other Verilog +// implementations, and intuitively makes sense as decimal representations lack +// a discrete mapping of digits to bit groups. Decimals may also show sign and +// must accommodate this, whereas other representations do not. +void Fmt::apply_verilog_automatic_sizing_and_add(FmtPart &part) +{ + if (part.base == 10) { + size_t places = compute_required_decimal_places(part.sig.size(), part.signed_); + part.padding = ' '; + part.width = std::max(part.width, places); + parts.push_back(part); + return; + } + + part.padding = '0'; + + size_t places = compute_required_nondecimal_places(part.sig.size(), part.base); + if (part.width < places) { + part.justify = FmtPart::RIGHT; + part.width = places; + parts.push_back(part); + } else if (part.width == places) { + parts.push_back(part); + } else if (part.width > places) { + auto gap = std::string(part.width - places, ' '); + part.width = places; + + if (part.justify == FmtPart::RIGHT) { + append_string(gap); + parts.push_back(part); + } else { + part.justify = FmtPart::RIGHT; + parts.push_back(part); + append_string(gap); + } + } +} + +void Fmt::parse_verilog(const std::vector &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name) +{ + parts.clear(); + + auto arg = args.begin(); + for (; arg != args.end(); ++arg) { + switch (arg->type) { + case VerilogFmtArg::INTEGER: { + FmtPart part = {}; + part.type = FmtPart::INTEGER; + part.sig = arg->sig; + part.base = default_base; + part.signed_ = arg->signed_; + apply_verilog_automatic_sizing_and_add(part); + break; + } + + case VerilogFmtArg::TIME: { + FmtPart part = {}; + part.type = FmtPart::VLOG_TIME; + part.realtime = arg->realtime; + part.padding = ' '; + part.width = 20; + parts.push_back(part); + break; + } + + case VerilogFmtArg::STRING: { + if (arg == args.begin() || !sformat_like) { + const auto fmtarg = arg; + const std::string &fmt = fmtarg->str; + FmtPart part = {}; + for (size_t i = 0; i < fmt.size(); i++) { + if (fmt[i] != '%') { + part.str += fmt[i]; + } else if (fmt.substr(i, 2) == "%%") { + i++; + part.str += '%'; + } else if (fmt.substr(i, 2) == "%l" || fmt.substr(i, 2) == "%L") { + i++; + part.str += module_name.str(); + } else if (fmt.substr(i, 2) == "%m" || fmt.substr(i, 2) == "%M") { + i++; + part.str += module_name.str(); + } else { + if (!part.str.empty()) { + part.type = FmtPart::STRING; + parts.push_back(part); + part = {}; + } + if (++i == fmt.size()) { + log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); + } + + if (++arg == args.end()) { + log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with fewer arguments than the format specifiers in argument %zu require.\n", task_name.c_str(), fmtarg - args.begin() + 1); + } + part.sig = arg->sig; + part.signed_ = arg->signed_; + + for (; i < fmt.size(); i++) { + if (fmt[i] == '-') { + // left justify; not in IEEE 1800-2017 or verilator but iverilog has it + part.justify = FmtPart::LEFT; + } else if (fmt[i] == '+') { + // always show sign; not in IEEE 1800-2017 or verilator but iverilog has it + part.plus = true; + } else break; + } + if (i == fmt.size()) { + log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); + } + + bool has_leading_zero = false, has_width = false; + for (; i < fmt.size(); i++) { + if (fmt[i] >= '0' && fmt[i] <= '9') { + if (fmt[i] == '0' && !has_width) { + has_leading_zero = true; + } else { + has_width = true; + part.width *= 10; + part.width += fmt[i] - '0'; + } + continue; + } else if (fmt[i] == 'b' || fmt[i] == 'B') { + part.type = FmtPart::INTEGER; + part.base = 2; + } else if (fmt[i] == 'o' || fmt[i] == 'O') { + part.type = FmtPart::INTEGER; + part.base = 8; + } else if (fmt[i] == 'd' || fmt[i] == 'D') { + part.type = FmtPart::INTEGER; + part.base = 10; + } else if (fmt[i] == 'h' || fmt[i] == 'H' || + fmt[i] == 'x' || fmt[i] == 'X') { + // hex digits always printed in lowercase for %h%x as well as %H%X + part.type = FmtPart::INTEGER; + part.base = 16; + } else if (fmt[i] == 'c' || fmt[i] == 'C') { + part.type = FmtPart::CHARACTER; + part.sig.extend_u0(8); + // %10c and %010c not fully defined in IEEE 1800-2017 and do different things in iverilog + } else if (fmt[i] == 's' || fmt[i] == 'S') { + part.type = FmtPart::CHARACTER; + if ((part.sig.size() % 8) != 0) + part.sig.extend_u0((part.sig.size() + 7) / 8 * 8); + // %10s and %010s not fully defined in IEEE 1800-2017 and do the same thing in iverilog + part.padding = ' '; + } else if (fmt[i] == 't' || fmt[i] == 'T') { + if (arg->type == VerilogFmtArg::TIME) { + part.type = FmtPart::VLOG_TIME; + part.realtime = arg->realtime; + if (!has_width && !has_leading_zero) + part.width = 20; + } else { + log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with format character `%c' in argument %zu, but the argument is not $time or $realtime.\n", task_name.c_str(), fmt[i], fmtarg - args.begin() + 1); + } + } else { + log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with unrecognized format character `%c' in argument %zu.\n", task_name.c_str(), fmt[i], fmtarg - args.begin() + 1); + } + break; + } + if (i == fmt.size()) { + log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); + } + + if (part.padding == '\0') + part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' '; + + if (part.type == FmtPart::INTEGER && part.base != 10 && part.plus) + log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); + + if (part.type == FmtPart::INTEGER && !has_leading_zero) + apply_verilog_automatic_sizing_and_add(part); + else + parts.push_back(part); + part = {}; + } + } + if (!part.str.empty()) { + part.type = FmtPart::STRING; + parts.push_back(part); + } + } else { + FmtPart part = {}; + part.type = FmtPart::STRING; + part.str = arg->str; + parts.push_back(part); + } + break; + } + + default: log_abort(); + } + } +} + +std::vector Fmt::emit_verilog() const +{ + std::vector args; + VerilogFmtArg fmt = {}; + fmt.type = VerilogFmtArg::STRING; + + for (auto &part : parts) { + switch (part.type) { + case FmtPart::STRING: + for (char c : part.str) { + if (c == '%') + fmt.str += "%%"; + else + fmt.str += c; + } + break; + + case FmtPart::INTEGER: { + VerilogFmtArg arg = {}; + arg.type = VerilogFmtArg::INTEGER; + arg.sig = part.sig; + arg.signed_ = part.signed_; + args.push_back(arg); + + fmt.str += '%'; + if (part.plus) + fmt.str += '+'; + if (part.justify == FmtPart::LEFT) + fmt.str += '-'; + if (part.width == 0) { + fmt.str += '0'; + } else if (part.width > 0) { + log_assert(part.padding == ' ' || part.padding == '0'); + if (part.base != 10 || part.padding == '0') + fmt.str += '0'; + fmt.str += std::to_string(part.width); + } + switch (part.base) { + case 2: fmt.str += 'b'; break; + case 8: fmt.str += 'o'; break; + case 10: fmt.str += 'd'; break; + case 16: fmt.str += 'h'; break; + default: log_abort(); + } + break; + } + + case FmtPart::CHARACTER: { + VerilogFmtArg arg; + arg.type = VerilogFmtArg::INTEGER; + arg.sig = part.sig; + args.push_back(arg); + + fmt.str += '%'; + if (part.justify == FmtPart::LEFT) + fmt.str += '-'; + if (part.sig.size() == 8) { + if (part.width > 0) { + log_assert(part.padding == '0' || part.padding == ' '); + if (part.padding == '0') + fmt.str += part.padding; + fmt.str += std::to_string(part.width); + } + fmt.str += 'c'; + } else { + log_assert(part.sig.size() % 8 == 0); + if (part.width > 0) { + log_assert(part.padding == ' '); // no zero padding + fmt.str += std::to_string(part.width); + } + fmt.str += 's'; + } + break; + } + + case FmtPart::VLOG_TIME: { + VerilogFmtArg arg; + arg.type = VerilogFmtArg::TIME; + if (part.realtime) + arg.realtime = true; + args.push_back(arg); + + fmt.str += '%'; + if (part.plus) + fmt.str += '+'; + if (part.justify == FmtPart::LEFT) + fmt.str += '-'; + log_assert(part.padding == ' ' || part.padding == '0'); + if (part.padding == '0' && part.width > 0) + fmt.str += '0'; + fmt.str += std::to_string(part.width); + fmt.str += 't'; + break; + } + + default: log_abort(); + } + } + + args.insert(args.begin(), fmt); + return args; +} + +std::string escape_cxx_string(const std::string &input) +{ + std::string output = "\""; + for (auto c : input) { + if (::isprint(c)) { + if (c == '\\') + output.push_back('\\'); + output.push_back(c); + } else { + char l = c & 0xf, h = (c >> 4) & 0xf; + output.append("\\x"); + output.push_back((h < 10 ? '0' + h : 'a' + h - 10)); + output.push_back((l < 10 ? '0' + l : 'a' + l - 10)); + } + } + output.push_back('"'); + if (output.find('\0') != std::string::npos) { + output.insert(0, "std::string {"); + output.append(stringf(", %zu}", input.size())); + } + return output; +} + +void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function emit_sig, const std::string &context) const +{ + os << indent << "std::string buf;\n"; + for (auto &part : parts) { + os << indent << "buf += fmt_part { "; + os << "fmt_part::"; + switch (part.type) { + case FmtPart::STRING: os << "STRING"; break; + case FmtPart::INTEGER: os << "INTEGER"; break; + case FmtPart::CHARACTER: os << "CHARACTER"; break; + case FmtPart::VLOG_TIME: os << "VLOG_TIME"; break; + } + os << ", "; + os << escape_cxx_string(part.str) << ", "; + os << "fmt_part::"; + switch (part.justify) { + case FmtPart::LEFT: os << "LEFT"; break; + case FmtPart::RIGHT: os << "RIGHT"; break; + } + os << ", "; + os << "(char)" << (int)part.padding << ", "; + os << part.width << ", "; + os << part.base << ", "; + os << part.signed_ << ", "; + os << part.plus << ", "; + os << part.realtime; + os << " }.render("; + emit_sig(part.sig); + os << ", " << context << ");\n"; + } + os << indent << "return buf;\n"; +} + +std::string Fmt::render() const +{ + std::string str; + + for (auto &part : parts) { + switch (part.type) { + case FmtPart::STRING: + str += part.str; + break; + + case FmtPart::INTEGER: + case FmtPart::CHARACTER: + case FmtPart::VLOG_TIME: { + std::string buf; + if (part.type == FmtPart::INTEGER) { + RTLIL::Const value = part.sig.as_const(); + + if (part.base != 10) { + size_t minimum_size = 0; + for (size_t index = 0; index < (size_t)value.size(); index++) + if (value[index] != State::S0) + minimum_size = index + 1; + value = value.extract(0, minimum_size); + } + + if (part.base == 2) { + buf = value.as_string(); + } else if (part.base == 8 || part.base == 16) { + size_t step = (part.base == 16) ? 4 : 3; + for (size_t index = 0; index < (size_t)value.size(); index += step) { + RTLIL::Const subvalue = value.extract(index, min(step, value.size() - index)); + bool has_x = false, all_x = true, has_z = false, all_z = true; + for (State bit : subvalue) { + if (bit == State::Sx) + has_x = true; + else + all_x = false; + if (bit == State::Sz) + has_z = true; + else + all_z = false; + } + if (all_x) + buf += 'x'; + else if (all_z) + buf += 'z'; + else if (has_x) + buf += 'X'; + else if (has_z) + buf += 'Z'; + else + buf += "0123456789abcdef"[subvalue.as_int()]; + } + std::reverse(buf.begin(), buf.end()); + } else if (part.base == 10) { + bool has_x = false, all_x = true, has_z = false, all_z = true; + for (State bit : value) { + if (bit == State::Sx) + has_x = true; + else + all_x = false; + if (bit == State::Sz) + has_z = true; + else + all_z = false; + } + if (all_x) + buf += 'x'; + else if (all_z) + buf += 'z'; + else if (has_x) + buf += 'X'; + else if (has_z) + buf += 'Z'; + else { + bool negative = part.signed_ && value[value.size() - 1]; + RTLIL::Const absvalue; + if (negative) + absvalue = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1); + else + absvalue = value; + log_assert(absvalue.is_fully_def()); + if (absvalue.is_fully_zero()) + buf += '0'; + while (!absvalue.is_fully_zero()) { + buf += '0' + RTLIL::const_mod(absvalue, 10, false, false, 4).as_int(); + absvalue = RTLIL::const_div(absvalue, 10, false, false, absvalue.size()); + } + if (negative || part.plus) + buf += negative ? '-' : '+'; + std::reverse(buf.begin(), buf.end()); + } + } else log_abort(); + } else if (part.type == FmtPart::CHARACTER) { + buf = part.sig.as_const().decode_string(); + } else if (part.type == FmtPart::VLOG_TIME) { + // We only render() during initial, so time is always zero. + buf = "0"; + } + + log_assert(part.width == 0 || part.padding != '\0'); + if (part.justify == FmtPart::RIGHT && buf.size() < part.width) { + size_t pad_width = part.width - buf.size(); + if (part.padding == '0' && (!buf.empty() && (buf.front() == '+' || buf.front() == '-'))) { + str += buf.front(); + buf.erase(0, 1); + } + str += std::string(pad_width, part.padding); + } + str += buf; + if (part.justify == FmtPart::LEFT && buf.size() < part.width) + str += std::string(part.width - buf.size(), part.padding); + break; + } + } + } + + return str; +} diff --git a/kernel/fmt.h b/kernel/fmt.h new file mode 100644 index 00000000000..3bedb786ec4 --- /dev/null +++ b/kernel/fmt.h @@ -0,0 +1,107 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2020 whitequark + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef FMT_H +#define FMT_H + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +// Verilog format argument, such as the arguments in: +// $display("foo %d bar %01x", 4'b0, $signed(2'b11)) +struct VerilogFmtArg { + enum { + STRING = 0, + INTEGER = 1, + TIME = 2, + } type; + + // All types + std::string filename; + unsigned first_line; + + // STRING type + std::string str; + + // INTEGER type + RTLIL::SigSpec sig; + bool signed_ = false; + + // TIME type + bool realtime = false; +}; + +// RTLIL format part, such as the substitutions in: +// "foo {4:> 4du} bar {2:<01hs}" +// Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h! +struct FmtPart { + enum { + STRING = 0, + INTEGER = 1, + CHARACTER = 2, + VLOG_TIME = 3, + } type; + + // STRING type + std::string str; + + // INTEGER/CHARACTER types + RTLIL::SigSpec sig; + + // INTEGER/CHARACTER/VLOG_TIME types + enum { + RIGHT = 0, + LEFT = 1, + } justify = RIGHT; + char padding = '\0'; + size_t width = 0; + + // INTEGER type + unsigned base = 10; + bool signed_ = false; + bool plus = false; + + // VLOG_TIME type + bool realtime = false; +}; + +struct Fmt { +public: + std::vector parts; + + void append_string(const std::string &str); + + void parse_rtlil(const RTLIL::Cell *cell); + void emit_rtlil(RTLIL::Cell *cell) const; + + void parse_verilog(const std::vector &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name); + std::vector emit_verilog() const; + + void emit_cxxrtl(std::ostream &os, std::string indent, std::function emit_sig, const std::string &context) const; + + std::string render() const; + +private: + void apply_verilog_automatic_sizing_and_add(FmtPart &part); +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/fstdata.cc b/kernel/fstdata.cc new file mode 100644 index 00000000000..65ae3426cd6 --- /dev/null +++ b/kernel/fstdata.cc @@ -0,0 +1,264 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Miodrag Milanovic + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/fstdata.h" + +USING_YOSYS_NAMESPACE + + +static std::string file_base_name(std::string const & path) +{ + return path.substr(path.find_last_of("/\\") + 1); +} + +FstData::FstData(std::string filename) : ctx(nullptr) +{ + #if !defined(YOSYS_DISABLE_SPAWN) + std::string filename_trim = file_base_name(filename); + if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vcd") == 0) { + filename_trim.erase(filename_trim.size()-4); + tmp_file = stringf("%s/converted_%s.fst", get_base_tmpdir().c_str(), filename_trim.c_str()); + std::string cmd = stringf("vcd2fst %s %s", filename.c_str(), tmp_file.c_str()); + log("Exec: %s\n", cmd.c_str()); + if (run_command(cmd) != 0) + log_cmd_error("Shell command failed!\n"); + filename = tmp_file; + } + #endif + const std::vector g_units = { "s", "ms", "us", "ns", "ps", "fs", "as", "zs" }; + ctx = (fstReaderContext *)fstReaderOpen(filename.c_str()); + if (!ctx) + log_error("Error opening '%s' as FST file\n", filename.c_str()); + int scale = (int)fstReaderGetTimescale(ctx); + timescale = pow(10.0, scale); + timescale_str = ""; + int unit = 0; + int zeros = 0; + if (scale > 0) { + zeros = scale; + } else { + if ((scale % 3) == 0) { + zeros = (-scale % 3); + unit = (-scale / 3); + } else { + zeros = 3 - (-scale % 3); + unit = (-scale / 3) + 1; + } + } + for (int i=0;i') + c = ']'; + } +} + +fstHandle FstData::getHandle(std::string name) { + normalize_brackets(name); + if (name_to_handle.find(name) != name_to_handle.end()) + return name_to_handle[name]; + else + return 0; +}; + +dict FstData::getMemoryHandles(std::string name) { + if (memory_to_handle.find(name) != memory_to_handle.end()) + return memory_to_handle[name]; + else + return dict(); +}; + +static std::string remove_spaces(std::string str) +{ + str.erase(std::remove(str.begin(), str.end(), ' '), str.end()); + return str; +} + +void FstData::extractVarNames() +{ + struct fstHier *h; + std::string fst_scope_name; + + while ((h = fstReaderIterateHier(ctx))) { + switch (h->htyp) { + case FST_HT_SCOPE: { + fst_scope_name = fstReaderPushScope(ctx, h->u.scope.name, NULL); + break; + } + case FST_HT_UPSCOPE: { + fst_scope_name = fstReaderPopScope(ctx); + break; + } + case FST_HT_VAR: { + FstVar var; + var.id = h->u.var.handle; + var.is_alias = h->u.var.is_alias; + var.is_reg = (fstVarType)h->u.var.typ == FST_VT_VCD_REG; + var.name = remove_spaces(h->u.var.name); + var.scope = fst_scope_name; + normalize_brackets(var.scope); + var.width = h->u.var.length; + vars.push_back(var); + if (!var.is_alias) + handle_to_var[h->u.var.handle] = var; + std::string clean_name; + for(size_t i=0;iu.var.name);i++) + { + char c = h->u.var.name[i]; + if(c==' ') break; + clean_name += c; + } + if (clean_name[0]=='\\') + clean_name = clean_name.substr(1); + size_t pos = clean_name.find_last_of("<"); + if (pos != std::string::npos && clean_name.back() == '>') { + std::string mem_cell = clean_name.substr(0, pos); + normalize_brackets(mem_cell); + std::string addr = clean_name.substr(pos+1); + addr.pop_back(); // remove closing bracket + char *endptr; + int mem_addr = strtol(addr.c_str(), &endptr, 16); + if (*endptr) { + log_debug("Error parsing memory address in : %s\n", clean_name.c_str()); + } else { + memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id; + } + } + pos = clean_name.find_last_of("["); + if (pos != std::string::npos && clean_name.back() == ']') { + std::string mem_cell = clean_name.substr(0, pos); + normalize_brackets(mem_cell); + std::string addr = clean_name.substr(pos+1); + addr.pop_back(); // remove closing bracket + char *endptr; + int mem_addr = strtol(addr.c_str(), &endptr, 10); + if (*endptr) { + log_debug("Error parsing memory address in : %s\n", clean_name.c_str()); + } else { + memory_to_handle[var.scope+"."+mem_cell][mem_addr] = var.id; + } + } + normalize_brackets(clean_name); + name_to_handle[var.scope+"."+clean_name] = h->u.var.handle; + break; + } + } + } +} + + +static void reconstruct_clb_varlen_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen) +{ + FstData *ptr = (FstData*)user_data; + ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen); +} + +static void reconstruct_clb_attimes(void *user_data, uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value) +{ + FstData *ptr = (FstData*)user_data; + uint32_t plen = (pnt_value) ? strlen((const char *)pnt_value) : 0; + ptr->reconstruct_callback_attimes(pnt_time, pnt_facidx, pnt_value, plen); +} + +void FstData::reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t /* plen */) +{ + if (pnt_time > end_time || !pnt_value) return; + // if we are past the timestamp + bool is_clock = false; + if (!all_samples) { + for(auto &s : clk_signals) { + if (s==pnt_facidx) { + is_clock=true; + break; + } + } + } + + if (pnt_time > past_time) { + past_data = last_data; + past_time = pnt_time; + } + + if (pnt_time > last_time) { + if (all_samples) { + callback(last_time); + last_time = pnt_time; + } else { + if (is_clock) { + std::string val = std::string((const char *)pnt_value); + std::string prev = past_data[pnt_facidx]; + if ((prev!="1" && val=="1") || (prev!="0" && val=="0")) { + callback(last_time); + last_time = pnt_time; + } + } + } + } + // always update last_data + last_data[pnt_facidx] = std::string((const char *)pnt_value); +} + +void FstData::reconstructAllAtTimes(std::vector &signal, uint64_t start, uint64_t end, CallbackFunction cb) +{ + clk_signals = signal; + callback = cb; + start_time = start; + end_time = end; + last_data.clear(); + last_time = start_time; + past_data.clear(); + past_time = start_time; + all_samples = clk_signals.empty(); + + fstReaderSetUnlimitedTimeRange(ctx); + fstReaderSetFacProcessMaskAll(ctx); + fstReaderIterBlocks2(ctx, reconstruct_clb_attimes, reconstruct_clb_varlen_attimes, this, nullptr); + if (last_time!=end_time) { + past_data = last_data; + callback(last_time); + } + past_data = last_data; + callback(end_time); +} + +std::string FstData::valueOf(fstHandle signal) +{ + if (past_data.find(signal) == past_data.end()) + log_error("Signal id %d not found\n", (int)signal); + return past_data[signal]; +} diff --git a/kernel/fstdata.h b/kernel/fstdata.h new file mode 100644 index 00000000000..f5cf1d48d8e --- /dev/null +++ b/kernel/fstdata.h @@ -0,0 +1,84 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Miodrag Milanovic + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef FSTDATA_H +#define FSTDATA_H + +#include "kernel/yosys.h" +#include "libs/fst/fstapi.h" + +YOSYS_NAMESPACE_BEGIN + +typedef std::function CallbackFunction; +struct fst_end_of_data_exception { }; + +struct FstVar +{ + fstHandle id; + std::string name; + bool is_alias; + bool is_reg; + std::string scope; + int width; +}; + +class FstData +{ + public: + FstData(std::string filename); + ~FstData(); + + uint64_t getStartTime(); + uint64_t getEndTime(); + + std::vector& getVars() { return vars; }; + + void reconstruct_callback_attimes(uint64_t pnt_time, fstHandle pnt_facidx, const unsigned char *pnt_value, uint32_t plen); + void reconstructAllAtTimes(std::vector &signal, uint64_t start_time, uint64_t end_time, CallbackFunction cb); + + std::string valueOf(fstHandle signal); + fstHandle getHandle(std::string name); + dict getMemoryHandles(std::string name); + double getTimescale() { return timescale; } + const char *getTimescaleString() { return timescale_str.c_str(); } +private: + void extractVarNames(); + + struct fstReaderContext *ctx; + std::vector vars; + std::map handle_to_var; + std::map name_to_handle; + std::map> memory_to_handle; + std::map last_data; + uint64_t last_time; + std::map past_data; + uint64_t past_time; + double timescale; + std::string timescale_str; + uint64_t start_time; + uint64_t end_time; + CallbackFunction callback; + std::vector clk_signals; + bool all_samples; + std::string tmp_file; +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/hashlib.h b/kernel/hashlib.h index a523afadd08..e8ddddd3357 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -6,7 +6,7 @@ // means. // ------------------------------------------------------- -// Written by Clifford Wolf in 2014 +// Written by Claire Xenia Wolf in 2014 // ------------------------------------------------------- #ifndef HASHLIB_H @@ -17,6 +17,8 @@ #include #include +#include + namespace hashlib { const int hashtable_size_trigger = 2; @@ -66,6 +68,12 @@ struct hash_int_ops { } }; +template<> struct hash_ops : hash_int_ops +{ + static inline unsigned int hash(bool a) { + return a ? 1 : 0; + } +}; template<> struct hash_ops : hash_int_ops { static inline unsigned int hash(int32_t a) { @@ -78,6 +86,18 @@ template<> struct hash_ops : hash_int_ops return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); } }; +template<> struct hash_ops : hash_int_ops +{ + static inline unsigned int hash(uint32_t a) { + return a; + } +}; +template<> struct hash_ops : hash_int_ops +{ + static inline unsigned int hash(uint64_t a) { + return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); + } +}; template<> struct hash_ops { static inline bool cmp(const std::string &a, const std::string &b) { @@ -183,7 +203,7 @@ inline int hashtable_size(int min_size) if (p >= min_size) return p; if (sizeof(int) == 4) - throw std::length_error("hash table exceeded maximum size. use a ILP64 abi for larger tables."); + throw std::length_error("hash table exceeded maximum size.\nDesign is likely too large for yosys to handle, if possible try not to flatten the design."); for (auto p : zero_and_some_primes) if (100129 * p > min_size) return 100129 * p; @@ -353,7 +373,7 @@ class dict } public: - class const_iterator : public std::iterator> + class const_iterator { friend class dict; protected: @@ -361,6 +381,11 @@ class dict int index; const_iterator(const dict *ptr, int index) : ptr(ptr), index(index) { } public: + typedef std::forward_iterator_tag iterator_category; + typedef std::pair value_type; + typedef ptrdiff_t difference_type; + typedef std::pair* pointer; + typedef std::pair& reference; const_iterator() { } const_iterator operator++() { index--; return *this; } const_iterator operator+=(int amt) { index -= amt; return *this; } @@ -371,7 +396,7 @@ class dict const std::pair *operator->() const { return &ptr->entries[index].udata; } }; - class iterator : public std::iterator> + class iterator { friend class dict; protected: @@ -379,6 +404,11 @@ class dict int index; iterator(dict *ptr, int index) : ptr(ptr), index(index) { } public: + typedef std::forward_iterator_tag iterator_category; + typedef std::pair value_type; + typedef ptrdiff_t difference_type; + typedef std::pair* pointer; + typedef std::pair& reference; iterator() { } iterator operator++() { index--; return *this; } iterator operator+=(int amt) { index -= amt; return *this; } @@ -392,7 +422,7 @@ class dict operator const_iterator() const { return const_iterator(ptr, index); } }; - dict() + constexpr dict() { } @@ -782,7 +812,7 @@ class pool } public: - class const_iterator : public std::iterator + class const_iterator { friend class pool; protected: @@ -790,6 +820,11 @@ class pool int index; const_iterator(const pool *ptr, int index) : ptr(ptr), index(index) { } public: + typedef std::forward_iterator_tag iterator_category; + typedef K value_type; + typedef ptrdiff_t difference_type; + typedef K* pointer; + typedef K& reference; const_iterator() { } const_iterator operator++() { index--; return *this; } bool operator==(const const_iterator &other) const { return index == other.index; } @@ -798,7 +833,7 @@ class pool const K *operator->() const { return &ptr->entries[index].udata; } }; - class iterator : public std::iterator + class iterator { friend class pool; protected: @@ -806,6 +841,11 @@ class pool int index; iterator(pool *ptr, int index) : ptr(ptr), index(index) { } public: + typedef std::forward_iterator_tag iterator_category; + typedef K value_type; + typedef ptrdiff_t difference_type; + typedef K* pointer; + typedef K& reference; iterator() { } iterator operator++() { index--; return *this; } bool operator==(const iterator &other) const { return index == other.index; } @@ -817,7 +857,7 @@ class pool operator const_iterator() const { return const_iterator(ptr, index); } }; - pool() + constexpr pool() { } @@ -976,7 +1016,7 @@ class pool return !operator==(other); } - bool hash() const { + unsigned int hash() const { unsigned int hashval = mkhash_init; for (auto &it : entries) hashval ^= ops.hash(it.udata); @@ -1003,7 +1043,7 @@ class idict pool database; public: - class const_iterator : public std::iterator + class const_iterator { friend class idict; protected: @@ -1011,6 +1051,11 @@ class idict int index; const_iterator(const idict &container, int index) : container(container), index(index) { } public: + typedef std::forward_iterator_tag iterator_category; + typedef K value_type; + typedef ptrdiff_t difference_type; + typedef K* pointer; + typedef K& reference; const_iterator() { } const_iterator operator++() { index++; return *this; } bool operator==(const const_iterator &other) const { return index == other.index; } @@ -1019,6 +1064,10 @@ class idict const K *operator->() const { return &container[index]; } }; + constexpr idict() + { + } + int operator()(const K &key) { int hash = database.do_hash(key); @@ -1089,6 +1138,10 @@ class mfp public: typedef typename idict::const_iterator const_iterator; + constexpr mfp() + { + } + int operator()(const K &key) const { int i = database(key); diff --git a/kernel/json.cc b/kernel/json.cc new file mode 100644 index 00000000000..73874626722 --- /dev/null +++ b/kernel/json.cc @@ -0,0 +1,172 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/json.h" + +USING_YOSYS_NAMESPACE + +void PrettyJson::emit_to_log() +{ + struct LogTarget : public Target { + void emit(const char *data) override { log("%s", data); } + }; + + targets.push_back(std::unique_ptr(new LogTarget)); +} + +void PrettyJson::append_to_string(std::string &target) +{ + struct AppendStringTarget : public Target { + std::string ⌖ + AppendStringTarget(std::string &target) : target(target) {} + void emit(const char *data) override { target += data; } + }; + + targets.push_back(std::unique_ptr(new AppendStringTarget(target))); +} + +bool PrettyJson::write_to_file(const std::string &path) +{ + struct WriteFileTarget : public Target { + std::ofstream target; + void emit(const char *data) override { target << data; } + void flush() override { target.flush(); } + }; + + auto target = std::unique_ptr(new WriteFileTarget); + target->target.open(path); + if (target->target.fail()) + return false; + targets.push_back(std::unique_ptr(target.release())); + return true; +} + +void PrettyJson::line(bool space_if_inline) +{ + if (compact_depth != INT_MAX) { + if (space_if_inline) + raw(" "); + return; + } + int indent = state.size() - (state.empty() ? 0 : state.back() == VALUE); + newline_indent.resize(1 + 2 * indent, ' '); + raw(newline_indent.c_str()); +} + +void PrettyJson::raw(const char *raw_json) +{ + for (auto &target : targets) + target->emit(raw_json); +} + +void PrettyJson::flush() +{ + for (auto &target : targets) + target->flush(); +} + +void PrettyJson::begin_object() +{ + begin_value(); + raw("{"); + state.push_back(OBJECT_FIRST); +} + +void PrettyJson::begin_array() +{ + begin_value(); + raw("["); + state.push_back(ARRAY_FIRST); +} + +void PrettyJson::end_object() +{ + Scope top_scope = state.back(); + state.pop_back(); + if (top_scope == OBJECT) + line(false); + else + log_assert(top_scope == OBJECT_FIRST); + raw("}"); + end_value(); +} + +void PrettyJson::end_array() +{ + Scope top_scope = state.back(); + state.pop_back(); + if (top_scope == ARRAY) + line(false); + else + log_assert(top_scope == ARRAY_FIRST); + raw("]"); + end_value(); +} + +void PrettyJson::name(const char *name) +{ + if (state.back() == OBJECT_FIRST) { + state.back() = OBJECT; + line(false); + } else { + raw(","); + line(); + } + raw(Json(name).dump().c_str()); + raw(": "); + state.push_back(VALUE); +} + +void PrettyJson::begin_value() +{ + if (state.back() == ARRAY_FIRST) { + line(false); + state.back() = ARRAY; + } else if (state.back() == ARRAY) { + raw(","); + line(); + } else { + log_assert(state.back() == VALUE); + state.pop_back(); + } +} + +void PrettyJson::end_value() +{ + if (state.empty()) { + raw("\n"); + flush(); + } + if (GetSize(state) < compact_depth) + compact_depth = INT_MAX; +} + +void PrettyJson::value_json(const Json &value) +{ + begin_value(); + raw(value.dump().c_str()); + end_value(); +} + +void PrettyJson::entry_json(const char *name, const Json &value) +{ + this->name(name); + this->value(value); +} + diff --git a/kernel/json.h b/kernel/json.h new file mode 100644 index 00000000000..c9aa0e04550 --- /dev/null +++ b/kernel/json.h @@ -0,0 +1,104 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef JSON_H +#define JSON_H + +#include "kernel/yosys.h" +#include "libs/json11/json11.hpp" +#include + +YOSYS_NAMESPACE_BEGIN + +using json11::Json; + +class PrettyJson +{ + enum Scope { + VALUE, + OBJECT_FIRST, + OBJECT, + ARRAY_FIRST, + ARRAY, + }; + + struct Target { + virtual void emit(const char *data) = 0; + virtual void flush() {}; + virtual ~Target() {}; + }; + + std::string newline_indent = "\n"; + std::vector> targets; + std::vector state = {VALUE}; + int compact_depth = INT_MAX; +public: + + void emit_to_log(); + void append_to_string(std::string &target); + bool write_to_file(const std::string &path); + + bool active() { return !targets.empty(); } + + void compact() { compact_depth = GetSize(state); } + + void line(bool space_if_inline = true); + void raw(const char *raw_json); + void flush(); + void begin_object(); + void begin_array(); + void end_object(); + void end_array(); + void name(const char *name); + void begin_value(); + void end_value(); + void value_json(const Json &value); + void value(unsigned int value) { value_json(Json((int)value)); } + template + void value(T &&value) { value_json(Json(std::forward(value))); }; + + void entry_json(const char *name, const Json &value); + void entry(const char *name, unsigned int value) { entry_json(name, Json((int)value)); } + template + void entry(const char *name, T &&value) { entry_json(name, Json(std::forward(value))); }; + + template + void object(const T &&values) + { + begin_object(); + for (auto &item : values) + entry(item.first, item.second); + end_object(); + } + + template + void array(const T &&values) + { + begin_object(); + for (auto &item : values) + value(item); + end_object(); + } +}; + + + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/log.cc b/kernel/log.cc index c7ae873bc82..9a61e8f08b3 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -40,8 +40,9 @@ YOSYS_NAMESPACE_BEGIN std::vector log_files; std::vector log_streams; +std::vector log_scratchpads; std::map> log_hdump; -std::vector log_warn_regexes, log_nowarn_regexes, log_werror_regexes; +std::vector log_warn_regexes, log_nowarn_regexes, log_werror_regexes; dict log_expect_log, log_expect_warning, log_expect_error; std::set log_warnings, log_experimentals, log_experimentals_ignored; int log_warnings_count = 0; @@ -58,6 +59,7 @@ bool log_quiet_warnings = false; int log_verbose_level; string log_last_error; void (*log_error_atexit)() = NULL; +void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int left_line, unsigned int left_col, unsigned int right_line, unsigned int right_col, const char *msg) = NULL; int log_make_debug = 0; int log_force_debug = 0; @@ -71,8 +73,6 @@ int string_buf_index = -1; static struct timeval initial_tv = { 0, 0 }; static bool next_print_log = false; static int log_newline_count = 0; -static bool check_expected_logs = true; -static bool display_error_log_msg = true; static void log_id_cache_clear() { @@ -147,6 +147,12 @@ void logv(const char *format, va_list ap) if (format[0] && format[strlen(format)-1] == '\n') next_print_log = true; + // Special case to detect newlines in Python log output, since + // the binding always calls `log("%s", payload)` and the newline + // is then in the first formatted argument + if (!strcmp(format, "%s") && str.back() == '\n') + next_print_log = true; + for (auto f : log_files) fputs(time_str.c_str(), f); @@ -160,6 +166,11 @@ void logv(const char *format, va_list ap) for (auto f : log_streams) *f << str; + RTLIL::Design *design = yosys_get_design(); + if (design != nullptr) + for (auto &scratchpad : log_scratchpads) + design->scratchpad[scratchpad].append(str); + static std::string linebuffer; static bool log_warn_regex_recusion_guard = false; @@ -177,11 +188,11 @@ void logv(const char *format, va_list ap) if (!linebuffer.empty() && linebuffer.back() == '\n') { for (auto &re : log_warn_regexes) - if (YS_REGEX_NS::regex_search(linebuffer, re)) + if (std::regex_search(linebuffer, re)) log_warning("Found log message matching -W regex:\n%s", str.c_str()); for (auto &item : log_expect_log) - if (YS_REGEX_NS::regex_search(linebuffer, item.second.pattern)) + if (std::regex_search(linebuffer, item.second.pattern)) item.second.current_count++; linebuffer.clear(); @@ -238,7 +249,7 @@ static void logv_warning_with_prefix(const char *prefix, bool suppressed = false; for (auto &re : log_nowarn_regexes) - if (YS_REGEX_NS::regex_search(message, re)) + if (std::regex_search(message, re)) suppressed = true; if (suppressed) @@ -251,12 +262,12 @@ static void logv_warning_with_prefix(const char *prefix, log_make_debug = 0; for (auto &re : log_werror_regexes) - if (YS_REGEX_NS::regex_search(message, re)) + if (std::regex_search(message, re)) log_error("%s", message.c_str()); bool warning_match = false; for (auto &item : log_expect_warning) - if (YS_REGEX_NS::regex_search(message, item.second.pattern)) { + if (std::regex_search(message, item.second.pattern)) { item.second.current_count++; warning_match = true; } @@ -339,23 +350,24 @@ static void logv_error_with_prefix(const char *prefix, f = stderr; log_last_error = vstringf(format, ap); - if (display_error_log_msg) - log("%s%s", prefix, log_last_error.c_str()); + log("%s%s", prefix, log_last_error.c_str()); log_flush(); log_make_debug = bak_log_make_debug; - if (log_error_atexit) - log_error_atexit(); - for (auto &item : log_expect_error) - if (YS_REGEX_NS::regex_search(log_last_error, item.second.pattern)) + if (std::regex_search(log_last_error, item.second.pattern)) item.second.current_count++; - if (check_expected_logs) - log_check_expected(); + log_check_expected(); + + if (log_error_atexit) + log_error_atexit(); YS_DEBUGTRAP_IF_DEBUGGING; + const char *e = getenv("YOSYS_ABORT_ON_LOG_ERROR"); + if (e && atoi(e)) + abort(); #ifdef EMSCRIPTEN log_files = backup_log_files; @@ -372,14 +384,20 @@ void logv_error(const char *format, va_list ap) logv_error_with_prefix("ERROR: ", format, ap); } +void logv_file_error(const string &filename, int lineno, + const char *format, va_list ap) +{ + std::string prefix = stringf("%s:%d: ERROR: ", + filename.c_str(), lineno); + logv_error_with_prefix(prefix.c_str(), format, ap); +} + void log_file_error(const string &filename, int lineno, const char *format, ...) { va_list ap; va_start(ap, format); - std::string prefix = stringf("%s:%d: ERROR: ", - filename.c_str(), lineno); - logv_error_with_prefix(prefix.c_str(), format, ap); + logv_file_error(filename, lineno, format, ap); } void log(const char *format, ...) @@ -631,7 +649,7 @@ const char *log_const(const RTLIL::Const &value, bool autoint) } } -const char *log_id(RTLIL::IdString str) +const char *log_id(const RTLIL::IdString &str) { log_id_cache.push_back(strdup(str.c_str())); const char *p = log_id_cache.back(); @@ -667,9 +685,14 @@ void log_wire(RTLIL::Wire *wire, std::string indent) void log_check_expected() { - check_expected_logs = false; + // copy out all of the expected logs so that they cannot be re-checked + // or match against themselves + dict expect_log, expect_warning, expect_error; + std::swap(expect_warning, log_expect_warning); + std::swap(expect_log, log_expect_log); + std::swap(expect_error, log_expect_error); - for (auto &item : log_expect_warning) { + for (auto &item : expect_warning) { if (item.second.current_count == 0) { log_warn_regexes.clear(); log_error("Expected warning pattern '%s' not found !\n", item.first.c_str()); @@ -681,7 +704,7 @@ void log_check_expected() } } - for (auto &item : log_expect_log) { + for (auto &item : expect_log) { if (item.second.current_count == 0) { log_warn_regexes.clear(); log_error("Expected log pattern '%s' not found !\n", item.first.c_str()); @@ -693,10 +716,11 @@ void log_check_expected() } } - for (auto &item : log_expect_error) + for (auto &item : expect_error) if (item.second.current_count == item.second.expected_count) { log_warn_regexes.clear(); log("Expected error pattern '%s' found !!!\n", item.first.c_str()); + yosys_shutdown(); #ifdef EMSCRIPTEN throw 0; #elif defined(_MSC_VER) @@ -705,7 +729,6 @@ void log_check_expected() _Exit(0); #endif } else { - display_error_log_msg = false; log_warn_regexes.clear(); log_error("Expected error pattern '%s' not found !\n", item.first.c_str()); } diff --git a/kernel/log.h b/kernel/log.h index 8981c4cde1e..e4f06c69d19 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -24,31 +24,14 @@ #include -// In GCC 4.8 std::regex is not working correctlty, in order to make features -// using regular expressions to work replacement regex library is used -#if defined(__GNUC__) && !defined( __clang__) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 8) - #include - #define YS_REGEX_TYPE boost::xpressive::sregex - #define YS_REGEX_MATCH_TYPE boost::xpressive::smatch - #define YS_REGEX_NS boost::xpressive - #define YS_REGEX_COMPILE(param) boost::xpressive::sregex::compile(param, \ - boost::xpressive::regex_constants::nosubs | \ - boost::xpressive::regex_constants::optimize) - #define YS_REGEX_COMPILE_WITH_SUBS(param) boost::xpressive::sregex::compile(param, \ - boost::xpressive::regex_constants::optimize) -# else - #include - #define YS_REGEX_TYPE std::regex - #define YS_REGEX_MATCH_TYPE std::smatch - #define YS_REGEX_NS std - #define YS_REGEX_COMPILE(param) std::regex(param, \ - std::regex_constants::nosubs | \ - std::regex_constants::optimize | \ - std::regex_constants::egrep) - #define YS_REGEX_COMPILE_WITH_SUBS(param) std::regex(param, \ - std::regex_constants::optimize | \ - std::regex_constants::egrep) -#endif +#include +#define YS_REGEX_COMPILE(param) std::regex(param, \ + std::regex_constants::nosubs | \ + std::regex_constants::optimize | \ + std::regex_constants::egrep) +#define YS_REGEX_COMPILE_WITH_SUBS(param) std::regex(param, \ + std::regex_constants::optimize | \ + std::regex_constants::egrep) #if defined(_WIN32) # include @@ -113,8 +96,9 @@ struct log_cmd_error_exception { }; extern std::vector log_files; extern std::vector log_streams; +extern std::vector log_scratchpads; extern std::map> log_hdump; -extern std::vector log_warn_regexes, log_nowarn_regexes, log_werror_regexes; +extern std::vector log_warn_regexes, log_nowarn_regexes, log_werror_regexes; extern std::set log_warnings, log_experimentals, log_experimentals_ignored; extern int log_warnings_count; extern int log_warnings_count_noexpect; @@ -140,12 +124,16 @@ void logv_header(RTLIL::Design *design, const char *format, va_list ap); void logv_warning(const char *format, va_list ap); void logv_warning_noprefix(const char *format, va_list ap); [[noreturn]] void logv_error(const char *format, va_list ap); +[[noreturn]] void logv_file_error(const string &filename, int lineno, const char *format, va_list ap); void log(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3)); void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); +void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int left_line, unsigned int left_col, unsigned int right_line, unsigned int right_col, const char *msg)); +extern void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int left_line, unsigned int left_col, unsigned int right_line, unsigned int right_col, const char *msg); + // Log with filename to report a problem in a source file. void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); void log_file_info(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); @@ -203,11 +191,11 @@ void log_flush(); struct LogExpectedItem { - LogExpectedItem(const YS_REGEX_TYPE &pat, int expected) : + LogExpectedItem(const std::regex &pat, int expected) : pattern(pat), expected_count(expected), current_count(0) {} LogExpectedItem() : expected_count(0), current_count(0) {} - YS_REGEX_TYPE pattern; + std::regex pattern; int expected_count; int current_count; }; @@ -217,7 +205,7 @@ void log_check_expected(); const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true); const char *log_const(const RTLIL::Const &value, bool autoint = true); -const char *log_id(RTLIL::IdString id); +const char *log_id(const RTLIL::IdString &id); template static inline const char *log_id(T *obj, const char *nullstr = nullptr) { if (nullstr && obj == nullptr) @@ -373,6 +361,11 @@ void log_dump_val_worker(RTLIL::IdString v); void log_dump_val_worker(RTLIL::SigSpec v); void log_dump_val_worker(RTLIL::State v); +template static inline void log_dump_val_worker(dict &v); +template static inline void log_dump_val_worker(pool &v); +template static inline void log_dump_val_worker(std::vector &v); +template static inline void log_dump_val_worker(T *ptr); + template static inline void log_dump_val_worker(dict &v) { log("{"); @@ -399,6 +392,18 @@ static inline void log_dump_val_worker(pool &v) { log(" }"); } +template +static inline void log_dump_val_worker(std::vector &v) { + log("{"); + bool first = true; + for (auto &it : v) { + log(first ? " " : ", "); + log_dump_val_worker(it); + first = false; + } + log(" }"); +} + template static inline void log_dump_val_worker(T *ptr) { log("%p", ptr); } diff --git a/kernel/macc.h b/kernel/macc.h index d216e6772e1..e4e1ebf52ab 100644 --- a/kernel/macc.h +++ b/kernel/macc.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/mem.cc b/kernel/mem.cc index 0301a913c42..01c866770f5 100644 --- a/kernel/mem.cc +++ b/kernel/mem.cc @@ -18,6 +18,7 @@ */ #include "kernel/mem.h" +#include "kernel/ff.h" USING_YOSYS_NAMESPACE @@ -52,6 +53,67 @@ void Mem::remove() { } void Mem::emit() { + check(); + std::vector rd_left; + for (int i = 0; i < GetSize(rd_ports); i++) { + auto &port = rd_ports[i]; + if (port.removed) { + if (port.cell) { + module->remove(port.cell); + } + } else { + rd_left.push_back(i); + } + } + std::vector wr_left; + for (int i = 0; i < GetSize(wr_ports); i++) { + auto &port = wr_ports[i]; + if (port.removed) { + if (port.cell) { + module->remove(port.cell); + } + } else { + wr_left.push_back(i); + } + } + std::vector init_left; + for (int i = 0; i < GetSize(inits); i++) { + auto &init = inits[i]; + if (init.removed) { + if (init.cell) { + module->remove(init.cell); + } + } else { + init_left.push_back(i); + } + } + for (int i = 0; i < GetSize(rd_left); i++) + if (i != rd_left[i]) + std::swap(rd_ports[i], rd_ports[rd_left[i]]); + rd_ports.resize(GetSize(rd_left)); + for (int i = 0; i < GetSize(wr_left); i++) + if (i != wr_left[i]) + std::swap(wr_ports[i], wr_ports[wr_left[i]]); + wr_ports.resize(GetSize(wr_left)); + for (int i = 0; i < GetSize(init_left); i++) + if (i != init_left[i]) + std::swap(inits[i], inits[init_left[i]]); + inits.resize(GetSize(init_left)); + + for (auto &port : rd_ports) { + for (int i = 0; i < GetSize(wr_left); i++) { + port.transparency_mask[i] = port.transparency_mask[wr_left[i]]; + port.collision_x_mask[i] = port.collision_x_mask[wr_left[i]]; + } + port.transparency_mask.resize(GetSize(wr_left)); + port.collision_x_mask.resize(GetSize(wr_left)); + } + for (auto &port : wr_ports) { + for (int i = 0; i < GetSize(wr_left); i++) + port.priority_mask[i] = port.priority_mask[wr_left[i]]; + port.priority_mask.resize(GetSize(wr_left)); + } + if (packed) { if (mem) { module->memories.erase(mem->name); @@ -61,85 +123,138 @@ void Mem::emit() { if (!cell) { if (memid.empty()) memid = NEW_ID; - cell = module->addCell(memid, ID($mem)); + cell = module->addCell(memid, ID($mem_v2)); } + cell->type = ID($mem_v2); cell->attributes = attributes; cell->parameters[ID::MEMID] = Const(memid.str()); cell->parameters[ID::WIDTH] = Const(width); cell->parameters[ID::OFFSET] = Const(start_offset); cell->parameters[ID::SIZE] = Const(size); - cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_ports)); - cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_ports)); - Const rd_clk_enable, rd_clk_polarity, rd_transparent; - Const wr_clk_enable, wr_clk_polarity; + Const rd_wide_continuation, rd_clk_enable, rd_clk_polarity, rd_transparency_mask, rd_collision_x_mask; + Const wr_wide_continuation, wr_clk_enable, wr_clk_polarity, wr_priority_mask; + Const rd_ce_over_srst, rd_arst_value, rd_srst_value, rd_init_value; SigSpec rd_clk, rd_en, rd_addr, rd_data; SigSpec wr_clk, wr_en, wr_addr, wr_data; + SigSpec rd_arst, rd_srst; int abits = 0; for (auto &port : rd_ports) abits = std::max(abits, GetSize(port.addr)); for (auto &port : wr_ports) abits = std::max(abits, GetSize(port.addr)); cell->parameters[ID::ABITS] = Const(abits); + std::vector wr_port_xlat; + for (int i = 0; i < GetSize(wr_ports); i++) + for (int j = 0; j < (1 << wr_ports[i].wide_log2); j++) + wr_port_xlat.push_back(i); for (auto &port : rd_ports) { + for (auto attr: port.attributes) + if (!cell->has_attribute(attr.first)) + cell->attributes.insert(attr); if (port.cell) { module->remove(port.cell); port.cell = nullptr; } - rd_clk_enable.bits.push_back(State(port.clk_enable)); - rd_clk_polarity.bits.push_back(State(port.clk_polarity)); - rd_transparent.bits.push_back(State(port.transparent)); - rd_clk.append(port.clk); - log_assert(GetSize(port.clk) == 1); - rd_en.append(port.en); - log_assert(GetSize(port.en) == 1); - SigSpec addr = port.addr; - addr.extend_u0(abits, false); - rd_addr.append(addr); - log_assert(GetSize(addr) == abits); + for (int sub = 0; sub < (1 << port.wide_log2); sub++) + { + rd_wide_continuation.bits.push_back(State(sub != 0)); + rd_clk_enable.bits.push_back(State(port.clk_enable)); + rd_clk_polarity.bits.push_back(State(port.clk_polarity)); + rd_ce_over_srst.bits.push_back(State(port.ce_over_srst)); + rd_clk.append(port.clk); + rd_arst.append(port.arst); + rd_srst.append(port.srst); + rd_en.append(port.en); + SigSpec addr = port.sub_addr(sub); + addr.extend_u0(abits, false); + rd_addr.append(addr); + log_assert(GetSize(addr) == abits); + for (auto idx : wr_port_xlat) { + rd_transparency_mask.bits.push_back(State(bool(port.transparency_mask[idx]))); + rd_collision_x_mask.bits.push_back(State(bool(port.collision_x_mask[idx]))); + } + } rd_data.append(port.data); - log_assert(GetSize(port.data) == width); + for (auto &bit : port.arst_value) + rd_arst_value.bits.push_back(bit); + for (auto &bit : port.srst_value) + rd_srst_value.bits.push_back(bit); + for (auto &bit : port.init_value) + rd_init_value.bits.push_back(bit); } if (rd_ports.empty()) { + rd_wide_continuation = State::S0; rd_clk_enable = State::S0; rd_clk_polarity = State::S0; - rd_transparent = State::S0; + rd_ce_over_srst = State::S0; + rd_arst_value = State::S0; + rd_srst_value = State::S0; + rd_init_value = State::S0; + } + if (rd_ports.empty() || wr_ports.empty()) { + rd_transparency_mask = State::S0; + rd_collision_x_mask = State::S0; } + cell->parameters[ID::RD_PORTS] = Const(GetSize(rd_clk)); cell->parameters[ID::RD_CLK_ENABLE] = rd_clk_enable; cell->parameters[ID::RD_CLK_POLARITY] = rd_clk_polarity; - cell->parameters[ID::RD_TRANSPARENT] = rd_transparent; + cell->parameters[ID::RD_TRANSPARENCY_MASK] = rd_transparency_mask; + cell->parameters[ID::RD_COLLISION_X_MASK] = rd_collision_x_mask; + cell->parameters[ID::RD_WIDE_CONTINUATION] = rd_wide_continuation; + cell->parameters[ID::RD_CE_OVER_SRST] = rd_ce_over_srst; + cell->parameters[ID::RD_ARST_VALUE] = rd_arst_value; + cell->parameters[ID::RD_SRST_VALUE] = rd_srst_value; + cell->parameters[ID::RD_INIT_VALUE] = rd_init_value; + cell->parameters.erase(ID::RD_TRANSPARENT); cell->setPort(ID::RD_CLK, rd_clk); cell->setPort(ID::RD_EN, rd_en); + cell->setPort(ID::RD_ARST, rd_arst); + cell->setPort(ID::RD_SRST, rd_srst); cell->setPort(ID::RD_ADDR, rd_addr); cell->setPort(ID::RD_DATA, rd_data); for (auto &port : wr_ports) { + for (auto attr: port.attributes) + if (!cell->has_attribute(attr.first)) + cell->attributes.insert(attr); if (port.cell) { module->remove(port.cell); port.cell = nullptr; } - wr_clk_enable.bits.push_back(State(port.clk_enable)); - wr_clk_polarity.bits.push_back(State(port.clk_polarity)); - wr_clk.append(port.clk); - log_assert(GetSize(port.clk) == 1); + for (int sub = 0; sub < (1 << port.wide_log2); sub++) + { + wr_wide_continuation.bits.push_back(State(sub != 0)); + wr_clk_enable.bits.push_back(State(port.clk_enable)); + wr_clk_polarity.bits.push_back(State(port.clk_polarity)); + wr_clk.append(port.clk); + for (auto idx : wr_port_xlat) + wr_priority_mask.bits.push_back(State(bool(port.priority_mask[idx]))); + SigSpec addr = port.sub_addr(sub); + addr.extend_u0(abits, false); + wr_addr.append(addr); + log_assert(GetSize(addr) == abits); + } wr_en.append(port.en); - log_assert(GetSize(port.en) == width); - SigSpec addr = port.addr; - addr.extend_u0(abits, false); - wr_addr.append(addr); - log_assert(GetSize(addr) == abits); wr_data.append(port.data); - log_assert(GetSize(port.data) == width); } if (wr_ports.empty()) { + wr_wide_continuation = State::S0; wr_clk_enable = State::S0; wr_clk_polarity = State::S0; + wr_priority_mask = State::S0; } + cell->parameters[ID::WR_PORTS] = Const(GetSize(wr_clk)); cell->parameters[ID::WR_CLK_ENABLE] = wr_clk_enable; cell->parameters[ID::WR_CLK_POLARITY] = wr_clk_polarity; + cell->parameters[ID::WR_PRIORITY_MASK] = wr_priority_mask; + cell->parameters[ID::WR_WIDE_CONTINUATION] = wr_wide_continuation; cell->setPort(ID::WR_CLK, wr_clk); cell->setPort(ID::WR_EN, wr_en); cell->setPort(ID::WR_ADDR, wr_addr); cell->setPort(ID::WR_DATA, wr_data); for (auto &init : inits) { + for (auto attr: init.attributes) + if (!cell->has_attribute(attr.first)) + cell->attributes.insert(attr); if (init.cell) { module->remove(init.cell); init.cell = nullptr; @@ -161,30 +276,46 @@ void Mem::emit() { mem->width = width; mem->start_offset = start_offset; mem->size = size; + mem->attributes = attributes; for (auto &port : rd_ports) { if (!port.cell) - port.cell = module->addCell(NEW_ID, ID($memrd)); + port.cell = module->addCell(NEW_ID, ID($memrd_v2)); + port.cell->type = ID($memrd_v2); + port.cell->attributes = port.attributes; port.cell->parameters[ID::MEMID] = memid.str(); port.cell->parameters[ID::ABITS] = GetSize(port.addr); - port.cell->parameters[ID::WIDTH] = width; + port.cell->parameters[ID::WIDTH] = width << port.wide_log2; port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable; port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity; - port.cell->parameters[ID::TRANSPARENT] = port.transparent; + port.cell->parameters[ID::CE_OVER_SRST] = port.ce_over_srst; + port.cell->parameters[ID::ARST_VALUE] = port.arst_value; + port.cell->parameters[ID::SRST_VALUE] = port.srst_value; + port.cell->parameters[ID::INIT_VALUE] = port.init_value; + port.cell->parameters[ID::TRANSPARENCY_MASK] = port.transparency_mask; + port.cell->parameters[ID::COLLISION_X_MASK] = port.collision_x_mask; + port.cell->parameters.erase(ID::TRANSPARENT); port.cell->setPort(ID::CLK, port.clk); port.cell->setPort(ID::EN, port.en); + port.cell->setPort(ID::ARST, port.arst); + port.cell->setPort(ID::SRST, port.srst); port.cell->setPort(ID::ADDR, port.addr); port.cell->setPort(ID::DATA, port.data); } int idx = 0; for (auto &port : wr_ports) { if (!port.cell) - port.cell = module->addCell(NEW_ID, ID($memwr)); + port.cell = module->addCell(NEW_ID, ID($memwr_v2)); + port.cell->type = ID($memwr_v2); + port.cell->attributes = port.attributes; + if (port.cell->parameters.count(ID::PRIORITY)) + port.cell->parameters.erase(ID::PRIORITY); port.cell->parameters[ID::MEMID] = memid.str(); port.cell->parameters[ID::ABITS] = GetSize(port.addr); - port.cell->parameters[ID::WIDTH] = width; + port.cell->parameters[ID::WIDTH] = width << port.wide_log2; port.cell->parameters[ID::CLK_ENABLE] = port.clk_enable; port.cell->parameters[ID::CLK_POLARITY] = port.clk_polarity; - port.cell->parameters[ID::PRIORITY] = idx++; + port.cell->parameters[ID::PORTID] = idx++; + port.cell->parameters[ID::PRIORITY_MASK] = port.priority_mask; port.cell->setPort(ID::CLK, port.clk); port.cell->setPort(ID::EN, port.en); port.cell->setPort(ID::ADDR, port.addr); @@ -192,8 +323,12 @@ void Mem::emit() { } idx = 0; for (auto &init : inits) { + bool v2 = !init.en.is_fully_ones(); if (!init.cell) - init.cell = module->addCell(NEW_ID, ID($meminit)); + init.cell = module->addCell(NEW_ID, v2 ? ID($meminit_v2) : ID($meminit)); + else + init.cell->type = v2 ? ID($meminit_v2) : ID($meminit); + init.cell->attributes = init.attributes; init.cell->parameters[ID::MEMID] = memid.str(); init.cell->parameters[ID::ABITS] = GetSize(init.addr); init.cell->parameters[ID::WIDTH] = width; @@ -201,42 +336,188 @@ void Mem::emit() { init.cell->parameters[ID::PRIORITY] = idx++; init.cell->setPort(ID::ADDR, init.addr); init.cell->setPort(ID::DATA, init.data); + if (v2) + init.cell->setPort(ID::EN, init.en); + else + init.cell->unsetPort(ID::EN); } } } -void Mem::remove_wr_port(int idx) { - if (wr_ports[idx].cell) { - module->remove(wr_ports[idx].cell); - } - wr_ports.erase(wr_ports.begin() + idx); +void Mem::clear_inits() { + for (auto &init : inits) + init.removed = true; } -void Mem::remove_rd_port(int idx) { - if (rd_ports[idx].cell) { - module->remove(rd_ports[idx].cell); +void Mem::coalesce_inits() { + // start address -> end address + std::map chunks; + // Figure out chunk boundaries. + for (auto &init : inits) { + if (init.removed) + continue; + bool valid = false; + for (auto bit : init.en) + if (bit == State::S1) + valid = true; + if (!valid) { + init.removed = true; + continue; + } + int addr = init.addr.as_int(); + int addr_e = addr + GetSize(init.data) / width; + auto it_e = chunks.upper_bound(addr_e); + auto it = it_e; + while (it != chunks.begin()) { + --it; + if (it->second < addr) { + ++it; + break; + } + } + if (it == it_e) { + // No overlapping inits — add this one to index. + chunks[addr] = addr_e; + } else { + // We have an overlap — all chunks in the [it, it_e) + // range will be merged with this init. + if (it->first < addr) + addr = it->first; + auto it_last = it_e; + it_last--; + if (it_last->second > addr_e) + addr_e = it_last->second; + chunks.erase(it, it_e); + chunks[addr] = addr_e; + } + } + // Group inits by the chunk they belong to. + dict> inits_by_chunk; + for (int i = 0; i < GetSize(inits); i++) { + auto &init = inits[i]; + if (init.removed) + continue; + auto it = chunks.upper_bound(init.addr.as_int()); + --it; + inits_by_chunk[it->first].push_back(i); + int addr = init.addr.as_int(); + int addr_e = addr + GetSize(init.data) / width; + log_assert(addr >= it->first && addr_e <= it->second); + } + // Process each chunk. + for (auto &it : inits_by_chunk) { + int caddr = it.first; + int caddr_e = chunks[caddr]; + auto &chunk_inits = it.second; + if (GetSize(chunk_inits) == 1) { + auto &init = inits[chunk_inits[0]]; + if (!init.en.is_fully_ones()) { + for (int i = 0; i < GetSize(init.data); i++) + if (init.en[i % width] != State::S1) + init.data[i] = State::Sx; + init.en = Const(State::S1, width); + } + continue; + } + Const cdata(State::Sx, (caddr_e - caddr) * width); + for (int idx : chunk_inits) { + auto &init = inits[idx]; + int offset = (init.addr.as_int() - caddr) * width; + log_assert(offset >= 0); + log_assert(offset + GetSize(init.data) <= GetSize(cdata)); + for (int i = 0; i < GetSize(init.data); i++) + if (init.en[i % width] == State::S1) + cdata.bits[i+offset] = init.data.bits[i]; + init.removed = true; + } + MemInit new_init; + new_init.addr = caddr; + new_init.data = cdata; + new_init.en = Const(State::S1, width); + inits.push_back(new_init); } - rd_ports.erase(rd_ports.begin() + idx); -} - -void Mem::clear_inits() { - for (auto &init : inits) - if (init.cell) - module->remove(init.cell); - inits.clear(); } Const Mem::get_init_data() const { Const init_data(State::Sx, width * size); for (auto &init : inits) { + if (init.removed) + continue; int offset = (init.addr.as_int() - start_offset) * width; for (int i = 0; i < GetSize(init.data); i++) - if (0 <= i+offset && i+offset < GetSize(init_data)) + if (0 <= i+offset && i+offset < GetSize(init_data) && init.en[i % width] == State::S1) init_data.bits[i+offset] = init.data.bits[i]; } return init_data; } +void Mem::check() { + int max_wide_log2 = 0; + for (auto &port : rd_ports) { + if (port.removed) + continue; + log_assert(GetSize(port.clk) == 1); + log_assert(GetSize(port.en) == 1); + log_assert(GetSize(port.arst) == 1); + log_assert(GetSize(port.srst) == 1); + log_assert(GetSize(port.addr) >= port.wide_log2); + log_assert(GetSize(port.data) == (width << port.wide_log2)); + log_assert(GetSize(port.init_value) == (width << port.wide_log2)); + log_assert(GetSize(port.arst_value) == (width << port.wide_log2)); + log_assert(GetSize(port.srst_value) == (width << port.wide_log2)); + if (!port.clk_enable) { + log_assert(port.en == State::S1); + log_assert(port.arst == State::S0); + log_assert(port.srst == State::S0); + } + for (int j = 0; j < port.wide_log2; j++) { + log_assert(port.addr[j] == State::S0); + } + max_wide_log2 = std::max(max_wide_log2, port.wide_log2); + log_assert(GetSize(port.transparency_mask) == GetSize(wr_ports)); + log_assert(GetSize(port.collision_x_mask) == GetSize(wr_ports)); + for (int j = 0; j < GetSize(wr_ports); j++) { + auto &wport = wr_ports[j]; + if ((port.transparency_mask[j] || port.collision_x_mask[j]) && !wport.removed) { + log_assert(port.clk_enable); + log_assert(wport.clk_enable); + log_assert(port.clk == wport.clk); + log_assert(port.clk_polarity == wport.clk_polarity); + } + log_assert(!port.transparency_mask[j] || !port.collision_x_mask[j]); + } + } + for (int i = 0; i < GetSize(wr_ports); i++) { + auto &port = wr_ports[i]; + if (port.removed) + continue; + log_assert(GetSize(port.clk) == 1); + log_assert(GetSize(port.en) == (width << port.wide_log2)); + log_assert(GetSize(port.data) == (width << port.wide_log2)); + log_assert(GetSize(port.addr) >= port.wide_log2); + for (int j = 0; j < port.wide_log2; j++) { + log_assert(port.addr[j] == State::S0); + } + max_wide_log2 = std::max(max_wide_log2, port.wide_log2); + log_assert(GetSize(port.priority_mask) == GetSize(wr_ports)); + for (int j = 0; j < GetSize(wr_ports); j++) { + auto &wport = wr_ports[j]; + if (port.priority_mask[j] && !wport.removed) { + log_assert(j < i); + log_assert(port.clk_enable == wport.clk_enable); + if (port.clk_enable) { + log_assert(port.clk == wport.clk); + log_assert(port.clk_polarity == wport.clk_polarity); + } + } + } + } + int mask = (1 << max_wide_log2) - 1; + log_assert(!(start_offset & mask)); + log_assert(!(size & mask)); + log_assert(width != 0); +} + namespace { struct MemIndex { @@ -245,11 +526,11 @@ namespace { dict> inits; MemIndex (Module *module) { for (auto cell: module->cells()) { - if (cell->type == ID($memwr)) + if (cell->type.in(ID($memwr), ID($memwr_v2))) wr_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell); - else if (cell->type == ID($memrd)) + else if (cell->type.in(ID($memrd), ID($memrd_v2))) rd_ports[cell->parameters.at(ID::MEMID).decode_string()].insert(cell); - else if (cell->type == ID($meminit)) + else if (cell->type.in(ID($meminit), ID($meminit_v2))) inits[cell->parameters.at(ID::MEMID).decode_string()].insert(cell); } } @@ -260,25 +541,56 @@ namespace { res.packed = false; res.mem = mem; res.attributes = mem->attributes; + std::vector rd_transparent; + std::vector wr_portid; if (index.rd_ports.count(mem->name)) { for (auto cell : index.rd_ports.at(mem->name)) { MemRd mrd; + bool is_compat = cell->type == ID($memrd); mrd.cell = cell; mrd.attributes = cell->attributes; mrd.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool(); mrd.clk_polarity = cell->parameters.at(ID::CLK_POLARITY).as_bool(); - mrd.transparent = cell->parameters.at(ID::TRANSPARENT).as_bool(); mrd.clk = cell->getPort(ID::CLK); mrd.en = cell->getPort(ID::EN); mrd.addr = cell->getPort(ID::ADDR); mrd.data = cell->getPort(ID::DATA); + mrd.wide_log2 = ceil_log2(GetSize(mrd.data) / mem->width); + bool transparent = false; + if (is_compat) { + transparent = cell->parameters.at(ID::TRANSPARENT).as_bool(); + mrd.ce_over_srst = false; + mrd.arst_value = Const(State::Sx, mem->width << mrd.wide_log2); + mrd.srst_value = Const(State::Sx, mem->width << mrd.wide_log2); + mrd.init_value = Const(State::Sx, mem->width << mrd.wide_log2); + mrd.srst = State::S0; + mrd.arst = State::S0; + if (!mrd.clk_enable) { + // Fix some patterns that we'll allow for backwards compatibility, + // but don't want to see moving forwards: async transparent + // ports (inherently meaningless) and async ports without + // const 1 tied to EN bit (which may mean a latch in the future). + transparent = false; + if (mrd.en == State::Sx) + mrd.en = State::S1; + } + } else { + mrd.ce_over_srst = cell->parameters.at(ID::CE_OVER_SRST).as_bool(); + mrd.arst_value = cell->parameters.at(ID::ARST_VALUE); + mrd.srst_value = cell->parameters.at(ID::SRST_VALUE); + mrd.init_value = cell->parameters.at(ID::INIT_VALUE); + mrd.arst = cell->getPort(ID::ARST); + mrd.srst = cell->getPort(ID::SRST); + } res.rd_ports.push_back(mrd); + rd_transparent.push_back(transparent); } } if (index.wr_ports.count(mem->name)) { std::vector> ports; for (auto cell : index.wr_ports.at(mem->name)) { MemWr mwr; + bool is_compat = cell->type == ID($memwr); mwr.cell = cell; mwr.attributes = cell->attributes; mwr.clk_enable = cell->parameters.at(ID::CLK_ENABLE).as_bool(); @@ -287,11 +599,37 @@ namespace { mwr.en = cell->getPort(ID::EN); mwr.addr = cell->getPort(ID::ADDR); mwr.data = cell->getPort(ID::DATA); - ports.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), mwr)); + mwr.wide_log2 = ceil_log2(GetSize(mwr.data) / mem->width); + ports.push_back(std::make_pair(cell->parameters.at(is_compat ? ID::PRIORITY : ID::PORTID).as_int(), mwr)); } std::sort(ports.begin(), ports.end(), [](const std::pair &a, const std::pair &b) { return a.first < b.first; }); - for (auto &it : ports) + for (auto &it : ports) { res.wr_ports.push_back(it.second); + wr_portid.push_back(it.first); + } + for (int i = 0; i < GetSize(res.wr_ports); i++) { + auto &port = res.wr_ports[i]; + bool is_compat = port.cell->type == ID($memwr); + if (is_compat) { + port.priority_mask.resize(GetSize(res.wr_ports)); + for (int j = 0; j < i; j++) { + auto &oport = res.wr_ports[j]; + if (port.clk_enable != oport.clk_enable) + continue; + if (port.clk_enable && port.clk != oport.clk) + continue; + if (port.clk_enable && port.clk_polarity != oport.clk_polarity) + continue; + port.priority_mask[j] = true; + } + } else { + Const orig_prio_mask = port.cell->parameters.at(ID::PRIORITY_MASK); + for (int orig_portid : wr_portid) { + bool has_prio = orig_portid < GetSize(orig_prio_mask) && orig_prio_mask[orig_portid] == State::S1; + port.priority_mask.push_back(has_prio); + } + } + } } if (index.inits.count(mem->name)) { std::vector> inits; @@ -307,12 +645,50 @@ namespace { log_error("Non-constant data %s in memory initialization %s.\n", log_signal(data), log_id(cell)); init.addr = addr.as_const(); init.data = data.as_const(); + if (cell->type == ID($meminit_v2)) { + auto en = cell->getPort(ID::EN); + if (!en.is_fully_const()) + log_error("Non-constant enable %s in memory initialization %s.\n", log_signal(en), log_id(cell)); + init.en = en.as_const(); + } else { + init.en = RTLIL::Const(State::S1, mem->width); + } inits.push_back(std::make_pair(cell->parameters.at(ID::PRIORITY).as_int(), init)); } std::sort(inits.begin(), inits.end(), [](const std::pair &a, const std::pair &b) { return a.first < b.first; }); for (auto &it : inits) res.inits.push_back(it.second); } + for (int i = 0; i < GetSize(res.rd_ports); i++) { + auto &port = res.rd_ports[i]; + bool is_compat = port.cell->type == ID($memrd); + if (is_compat) { + port.transparency_mask.resize(GetSize(res.wr_ports)); + port.collision_x_mask.resize(GetSize(res.wr_ports)); + if (!rd_transparent[i]) + continue; + if (!port.clk_enable) + continue; + for (int j = 0; j < GetSize(res.wr_ports); j++) { + auto &wport = res.wr_ports[j]; + if (!wport.clk_enable) + continue; + if (port.clk != wport.clk) + continue; + if (port.clk_polarity != wport.clk_polarity) + continue; + port.transparency_mask[j] = true; + } + } else { + Const orig_trans_mask = port.cell->parameters.at(ID::TRANSPARENCY_MASK); + Const orig_cx_mask = port.cell->parameters.at(ID::COLLISION_X_MASK); + for (int orig_portid : wr_portid) { + port.transparency_mask.push_back(orig_portid < GetSize(orig_trans_mask) && orig_trans_mask[orig_portid] == State::S1); + port.collision_x_mask.push_back(orig_portid < GetSize(orig_cx_mask) && orig_cx_mask[orig_portid] == State::S1); + } + } + } + res.check(); return res; } @@ -322,6 +698,7 @@ namespace { cell->parameters.at(ID::OFFSET).as_int(), cell->parameters.at(ID::SIZE).as_int() ); + bool is_compat = cell->type == ID($mem); int abits = cell->parameters.at(ID::ABITS).as_int(); res.packed = true; res.cell = cell; @@ -343,32 +720,112 @@ namespace { MemInit minit; minit.addr = res.start_offset + pos; minit.data = init.extract(pos * res.width, (epos - pos) * res.width, State::Sx); + minit.en = RTLIL::Const(State::S1, res.width); res.inits.push_back(minit); pos = epos; } } } - for (int i = 0; i < cell->parameters.at(ID::RD_PORTS).as_int(); i++) { + int n_rd_ports = cell->parameters.at(ID::RD_PORTS).as_int(); + int n_wr_ports = cell->parameters.at(ID::WR_PORTS).as_int(); + Const rd_wide_continuation = is_compat ? Const(State::S0, n_rd_ports) : cell->parameters.at(ID::RD_WIDE_CONTINUATION); + Const wr_wide_continuation = is_compat ? Const(State::S0, n_wr_ports) : cell->parameters.at(ID::WR_WIDE_CONTINUATION); + for (int i = 0, ni; i < n_rd_ports; i = ni) { + ni = i + 1; + while (ni < n_rd_ports && rd_wide_continuation[ni] == State::S1) + ni++; MemRd mrd; + mrd.wide_log2 = ceil_log2(ni - i); + log_assert(ni - i == (1 << mrd.wide_log2)); mrd.clk_enable = cell->parameters.at(ID::RD_CLK_ENABLE).extract(i, 1).as_bool(); mrd.clk_polarity = cell->parameters.at(ID::RD_CLK_POLARITY).extract(i, 1).as_bool(); - mrd.transparent = cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool(); mrd.clk = cell->getPort(ID::RD_CLK).extract(i, 1); mrd.en = cell->getPort(ID::RD_EN).extract(i, 1); mrd.addr = cell->getPort(ID::RD_ADDR).extract(i * abits, abits); - mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, res.width); + mrd.data = cell->getPort(ID::RD_DATA).extract(i * res.width, (ni - i) * res.width); + if (is_compat) { + mrd.ce_over_srst = false; + mrd.arst_value = Const(State::Sx, res.width << mrd.wide_log2); + mrd.srst_value = Const(State::Sx, res.width << mrd.wide_log2); + mrd.init_value = Const(State::Sx, res.width << mrd.wide_log2); + mrd.arst = State::S0; + mrd.srst = State::S0; + } else { + mrd.ce_over_srst = cell->parameters.at(ID::RD_CE_OVER_SRST).extract(i, 1).as_bool(); + mrd.arst_value = cell->parameters.at(ID::RD_ARST_VALUE).extract(i * res.width, (ni - i) * res.width); + mrd.srst_value = cell->parameters.at(ID::RD_SRST_VALUE).extract(i * res.width, (ni - i) * res.width); + mrd.init_value = cell->parameters.at(ID::RD_INIT_VALUE).extract(i * res.width, (ni - i) * res.width); + mrd.arst = cell->getPort(ID::RD_ARST).extract(i, 1); + mrd.srst = cell->getPort(ID::RD_SRST).extract(i, 1); + } + if (!is_compat) { + Const transparency_mask = cell->parameters.at(ID::RD_TRANSPARENCY_MASK).extract(i * n_wr_ports, n_wr_ports); + Const collision_x_mask = cell->parameters.at(ID::RD_COLLISION_X_MASK).extract(i * n_wr_ports, n_wr_ports); + for (int j = 0; j < n_wr_ports; j++) + if (wr_wide_continuation[j] != State::S1) { + mrd.transparency_mask.push_back(transparency_mask[j] == State::S1); + mrd.collision_x_mask.push_back(collision_x_mask[j] == State::S1); + } + } res.rd_ports.push_back(mrd); } - for (int i = 0; i < cell->parameters.at(ID::WR_PORTS).as_int(); i++) { + for (int i = 0, ni; i < n_wr_ports; i = ni) { + ni = i + 1; + while (ni < n_wr_ports && wr_wide_continuation[ni] == State::S1) + ni++; MemWr mwr; + mwr.wide_log2 = ceil_log2(ni - i); + log_assert(ni - i == (1 << mwr.wide_log2)); mwr.clk_enable = cell->parameters.at(ID::WR_CLK_ENABLE).extract(i, 1).as_bool(); mwr.clk_polarity = cell->parameters.at(ID::WR_CLK_POLARITY).extract(i, 1).as_bool(); mwr.clk = cell->getPort(ID::WR_CLK).extract(i, 1); - mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, res.width); + mwr.en = cell->getPort(ID::WR_EN).extract(i * res.width, (ni - i) * res.width); mwr.addr = cell->getPort(ID::WR_ADDR).extract(i * abits, abits); - mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, res.width); + mwr.data = cell->getPort(ID::WR_DATA).extract(i * res.width, (ni - i) * res.width); + if (!is_compat) { + Const priority_mask = cell->parameters.at(ID::WR_PRIORITY_MASK).extract(i * n_wr_ports, n_wr_ports); + for (int j = 0; j < n_wr_ports; j++) + if (wr_wide_continuation[j] != State::S1) + mwr.priority_mask.push_back(priority_mask[j] == State::S1); + } res.wr_ports.push_back(mwr); } + if (is_compat) { + for (int i = 0; i < GetSize(res.wr_ports); i++) { + auto &port = res.wr_ports[i]; + port.priority_mask.resize(GetSize(res.wr_ports)); + for (int j = 0; j < i; j++) { + auto &oport = res.wr_ports[j]; + if (port.clk_enable != oport.clk_enable) + continue; + if (port.clk_enable && port.clk != oport.clk) + continue; + if (port.clk_enable && port.clk_polarity != oport.clk_polarity) + continue; + port.priority_mask[j] = true; + } + } + for (int i = 0; i < GetSize(res.rd_ports); i++) { + auto &port = res.rd_ports[i]; + port.transparency_mask.resize(GetSize(res.wr_ports)); + port.collision_x_mask.resize(GetSize(res.wr_ports)); + if (!cell->parameters.at(ID::RD_TRANSPARENT).extract(i, 1).as_bool()) + continue; + if (!port.clk_enable) + continue; + for (int j = 0; j < GetSize(res.wr_ports); j++) { + auto &wport = res.wr_ports[j]; + if (!wport.clk_enable) + continue; + if (port.clk != wport.clk) + continue; + if (port.clk_polarity != wport.clk_polarity) + continue; + port.transparency_mask[j] = true; + } + } + } + res.check(); return res; } @@ -381,7 +838,7 @@ std::vector Mem::get_all_memories(Module *module) { res.push_back(mem_from_memory(module, it.second, index)); } for (auto cell: module->cells()) { - if (cell->type == ID($mem)) + if (cell->type.in(ID($mem), ID($mem_v2))) res.push_back(mem_from_cell(cell)); } return res; @@ -395,13 +852,13 @@ std::vector Mem::get_selected_memories(Module *module) { res.push_back(mem_from_memory(module, it.second, index)); } for (auto cell: module->selected_cells()) { - if (cell->type == ID($mem)) + if (cell->type.in(ID($mem), ID($mem_v2))) res.push_back(mem_from_cell(cell)); } return res; } -Cell *Mem::extract_rdff(int idx) { +Cell *Mem::extract_rdff(int idx, FfInitVals *initvals) { MemRd &port = rd_ports[idx]; if (!port.clk_enable) @@ -409,28 +866,816 @@ Cell *Mem::extract_rdff(int idx) { Cell *c; - if (port.transparent) + // There are two ways to handle rdff extraction when transparency is involved: + // + // - if all of the following conditions are true, put the FF on address input: + // + // - the port has no clock enable, no reset, and no initial value + // - the port is transparent wrt all write ports (implying they also share + // the clock domain) + // + // - otherwise, put the FF on the data output, and make bypass paths for + // all write ports wrt which this port is transparent + bool trans_use_addr = true; + for (int i = 0; i < GetSize(wr_ports); i++) + if (!port.transparency_mask[i] && !wr_ports[i].removed) + trans_use_addr = false; + + // If there are no write ports at all, we could possibly use either way; do data + // FF in this case. + if (GetSize(wr_ports) == 0) + trans_use_addr = false; + + if (port.en != State::S1 || port.srst != State::S0 || port.arst != State::S0 || !port.init_value.is_fully_undef()) + trans_use_addr = false; + + if (trans_use_addr) { - SigSpec sig_q = module->addWire(stringf("%s$rdreg[%d]$q", memid.c_str(), idx), GetSize(port.addr)); - SigSpec sig_d = port.addr; - port.addr = sig_q; - c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true); + // Do not put a register in front of constant address bits — this is both + // unnecessary and will break wide ports. + int width = 0; + for (int i = 0; i < GetSize(port.addr); i++) + if (port.addr[i].wire) + width++; + + if (width) + { + SigSpec sig_q = module->addWire(stringf("$%s$rdreg[%d]$q", memid.c_str(), idx), width); + SigSpec sig_d; + + int pos = 0; + for (int i = 0; i < GetSize(port.addr); i++) + if (port.addr[i].wire) { + sig_d.append(port.addr[i]); + port.addr[i] = sig_q[pos++]; + } + + c = module->addDff(stringf("$%s$rdreg[%d]", memid.c_str(), idx), port.clk, sig_d, sig_q, port.clk_polarity); + } else { + c = nullptr; + } } else { - SigSpec sig_d = module->addWire(stringf("%s$rdreg[%d]$d", memid.c_str(), idx), width); - SigSpec sig_q = port.data; - port.data = sig_d; - c = module->addDffe(stringf("%s$rdreg[%d]", memid.c_str(), idx), port.clk, port.en, sig_d, sig_q, port.clk_polarity, true); + log_assert(port.arst == State::S0 || port.srst == State::S0); + + SigSpec async_d = module->addWire(stringf("$%s$rdreg[%d]$d", memid.c_str(), idx), GetSize(port.data)); + SigSpec sig_d = async_d; + + for (int i = 0; i < GetSize(wr_ports); i++) { + auto &wport = wr_ports[i]; + if (wport.removed) + continue; + if (port.transparency_mask[i] || port.collision_x_mask[i]) { + log_assert(wport.clk_enable); + log_assert(wport.clk == port.clk); + log_assert(wport.clk_enable == port.clk_enable); + int min_wide_log2 = std::min(port.wide_log2, wport.wide_log2); + int max_wide_log2 = std::max(port.wide_log2, wport.wide_log2); + bool wide_write = wport.wide_log2 > port.wide_log2; + for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) { + SigSpec raddr = port.addr; + SigSpec waddr = wport.addr; + if (wide_write) + waddr = wport.sub_addr(sub); + else + raddr = port.sub_addr(sub); + SigSpec addr_eq; + if (raddr != waddr) + addr_eq = module->Eq(stringf("$%s$rdtransen[%d][%d][%d]$d", memid.c_str(), idx, i, sub), raddr, waddr); + int pos = 0; + int ewidth = width << min_wide_log2; + int wsub = wide_write ? sub : 0; + int rsub = wide_write ? 0 : sub; + while (pos < ewidth) { + int epos = pos; + while (epos < ewidth && wport.en[epos + wsub * width] == wport.en[pos + wsub * width]) + epos++; + SigSpec cur = sig_d.extract(pos + rsub * width, epos-pos); + SigSpec other = port.transparency_mask[i] ? wport.data.extract(pos + wsub * width, epos-pos) : Const(State::Sx, epos-pos); + SigSpec cond; + if (raddr != waddr) + cond = module->And(stringf("$%s$rdtransgate[%d][%d][%d][%d]$d", memid.c_str(), idx, i, sub, pos), wport.en[pos + wsub * width], addr_eq); + else + cond = wport.en[pos + wsub * width]; + SigSpec merged = module->Mux(stringf("$%s$rdtransmux[%d][%d][%d][%d]$d", memid.c_str(), idx, i, sub, pos), cur, other, cond); + sig_d.replace(pos + rsub * width, merged); + pos = epos; + } + } + } + } + + IdString name = stringf("$%s$rdreg[%d]", memid.c_str(), idx); + FfData ff(module, initvals, name); + ff.width = GetSize(port.data); + ff.has_clk = true; + ff.sig_clk = port.clk; + ff.pol_clk = port.clk_polarity; + if (port.en != State::S1) { + ff.has_ce = true; + ff.pol_ce = true; + ff.sig_ce = port.en; + } + if (port.arst != State::S0) { + ff.has_arst = true; + ff.pol_arst = true; + ff.sig_arst = port.arst; + ff.val_arst = port.arst_value; + } + if (port.srst != State::S0) { + ff.has_srst = true; + ff.pol_srst = true; + ff.sig_srst = port.srst; + ff.val_srst = port.srst_value; + ff.ce_over_srst = ff.has_ce && port.ce_over_srst; + } + ff.sig_d = sig_d; + ff.sig_q = port.data; + ff.val_init = port.init_value; + port.data = async_d; + c = ff.emit(); } - log("Extracted %s FF from read port %d of %s.%s: %s\n", port.transparent ? "addr" : "data", + if (c) + log("Extracted %s FF from read port %d of %s.%s: %s\n", trans_use_addr ? "addr" : "data", idx, log_id(module), log_id(memid), log_id(c)); port.en = State::S1; port.clk = State::S0; + port.arst = State::S0; + port.srst = State::S0; port.clk_enable = false; port.clk_polarity = true; + port.ce_over_srst = false; + port.arst_value = Const(State::Sx, GetSize(port.data)); + port.srst_value = Const(State::Sx, GetSize(port.data)); + port.init_value = Const(State::Sx, GetSize(port.data)); + + for (int i = 0; i < GetSize(wr_ports); i++) { + port.transparency_mask[i] = false; + port.collision_x_mask[i] = false; + } return c; } + +void Mem::narrow() { + // NOTE: several passes depend on this function not modifying + // the design at all until (and unless) emit() is called. + // Be careful to preserve this. + std::vector new_rd_ports; + std::vector new_wr_ports; + std::vector> new_rd_map; + std::vector> new_wr_map; + for (int i = 0; i < GetSize(rd_ports); i++) { + auto &port = rd_ports[i]; + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { + new_rd_map.push_back(std::make_pair(i, sub)); + } + } + for (int i = 0; i < GetSize(wr_ports); i++) { + auto &port = wr_ports[i]; + for (int sub = 0; sub < (1 << port.wide_log2); sub++) { + new_wr_map.push_back(std::make_pair(i, sub)); + } + } + for (auto &it : new_rd_map) { + MemRd &orig = rd_ports[it.first]; + MemRd port = orig; + if (it.second != 0) + port.cell = nullptr; + if (port.wide_log2) { + port.data = port.data.extract(it.second * width, width); + port.init_value = port.init_value.extract(it.second * width, width); + port.arst_value = port.arst_value.extract(it.second * width, width); + port.srst_value = port.srst_value.extract(it.second * width, width); + port.addr = port.sub_addr(it.second); + port.wide_log2 = 0; + } + port.transparency_mask.clear(); + port.collision_x_mask.clear(); + for (auto &it2 : new_wr_map) + port.transparency_mask.push_back(orig.transparency_mask[it2.first]); + for (auto &it2 : new_wr_map) + port.collision_x_mask.push_back(orig.collision_x_mask[it2.first]); + new_rd_ports.push_back(port); + } + for (auto &it : new_wr_map) { + MemWr &orig = wr_ports[it.first]; + MemWr port = orig; + if (it.second != 0) + port.cell = nullptr; + if (port.wide_log2) { + port.data = port.data.extract(it.second * width, width); + port.en = port.en.extract(it.second * width, width); + port.addr = port.sub_addr(it.second); + port.wide_log2 = 0; + } + port.priority_mask.clear(); + for (auto &it2 : new_wr_map) + port.priority_mask.push_back(orig.priority_mask[it2.first]); + new_wr_ports.push_back(port); + } + std::swap(rd_ports, new_rd_ports); + std::swap(wr_ports, new_wr_ports); +} + +void Mem::emulate_priority(int idx1, int idx2, FfInitVals *initvals) +{ + auto &port1 = wr_ports[idx1]; + auto &port2 = wr_ports[idx2]; + if (!port2.priority_mask[idx1]) + return; + for (int i = 0; i < GetSize(rd_ports); i++) { + auto &rport = rd_ports[i]; + if (rport.removed) + continue; + if (rport.transparency_mask[idx1] && !(rport.transparency_mask[idx2] || rport.collision_x_mask[idx2])) + emulate_transparency(idx1, i, initvals); + } + int min_wide_log2 = std::min(port1.wide_log2, port2.wide_log2); + int max_wide_log2 = std::max(port1.wide_log2, port2.wide_log2); + bool wide1 = port1.wide_log2 > port2.wide_log2; + for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) { + SigSpec addr1 = port1.addr; + SigSpec addr2 = port2.addr; + if (wide1) + addr1 = port1.sub_addr(sub); + else + addr2 = port2.sub_addr(sub); + SigSpec addr_eq = module->Eq(NEW_ID, addr1, addr2); + int ewidth = width << min_wide_log2; + int sub1 = wide1 ? sub : 0; + int sub2 = wide1 ? 0 : sub; + dict, SigBit> cache; + for (int pos = 0; pos < ewidth; pos++) { + SigBit &en1 = port1.en[pos + sub1 * width]; + SigBit &en2 = port2.en[pos + sub2 * width]; + std::pair key(en1, en2); + if (cache.count(key)) { + en1 = cache[key]; + } else { + SigBit active2 = module->And(NEW_ID, addr_eq, en2); + SigBit nactive2 = module->Not(NEW_ID, active2); + en1 = cache[key] = module->And(NEW_ID, en1, nactive2); + } + } + } + port2.priority_mask[idx1] = false; +} + +void Mem::emulate_transparency(int widx, int ridx, FfInitVals *initvals) { + auto &wport = wr_ports[widx]; + auto &rport = rd_ports[ridx]; + log_assert(rport.transparency_mask[widx]); + // If other write ports have priority over this one, emulate their transparency too. + for (int i = GetSize(wr_ports) - 1; i > widx; i--) { + if (wr_ports[i].removed) + continue; + if (rport.transparency_mask[i] && wr_ports[i].priority_mask[widx]) + emulate_transparency(i, ridx, initvals); + } + int min_wide_log2 = std::min(rport.wide_log2, wport.wide_log2); + int max_wide_log2 = std::max(rport.wide_log2, wport.wide_log2); + bool wide_write = wport.wide_log2 > rport.wide_log2; + // The write data FF doesn't need full reset/init behavior, as it'll be masked by + // the mux whenever this would be relevant. It does, however, need to have the same + // clock enable signal as the read port. + SigSpec wdata_q = module->addWire(NEW_ID, GetSize(wport.data)); + module->addDffe(NEW_ID, rport.clk, rport.en, wport.data, wdata_q, rport.clk_polarity, true); + for (int sub = 0; sub < (1 << max_wide_log2); sub += (1 << min_wide_log2)) { + SigSpec raddr = rport.addr; + SigSpec waddr = wport.addr; + for (int j = min_wide_log2; j < max_wide_log2; j++) + if (wide_write) + waddr = wport.sub_addr(sub); + else + raddr = rport.sub_addr(sub); + SigSpec addr_eq; + if (raddr != waddr) + addr_eq = module->Eq(NEW_ID, raddr, waddr); + int pos = 0; + int ewidth = width << min_wide_log2; + int wsub = wide_write ? sub : 0; + int rsub = wide_write ? 0 : sub; + SigSpec rdata_a = module->addWire(NEW_ID, ewidth); + while (pos < ewidth) { + int epos = pos; + while (epos < ewidth && wport.en[epos + wsub * width] == wport.en[pos + wsub * width]) + epos++; + SigSpec cond; + if (raddr != waddr) + cond = module->And(NEW_ID, wport.en[pos + wsub * width], addr_eq); + else + cond = wport.en[pos + wsub * width]; + SigSpec cond_q = module->addWire(NEW_ID); + // The FF for storing the bypass enable signal must be carefully + // constructed to preserve the overall init/reset/enable behavior + // of the whole port. + FfData ff(module, initvals, NEW_ID); + ff.width = 1; + ff.sig_q = cond_q; + ff.sig_d = cond; + ff.has_clk = true; + ff.sig_clk = rport.clk; + ff.pol_clk = rport.clk_polarity; + if (rport.en != State::S1) { + ff.has_ce = true; + ff.sig_ce = rport.en; + ff.pol_ce = true; + } + if (rport.arst != State::S0) { + ff.has_arst = true; + ff.sig_arst = rport.arst; + ff.pol_arst = true; + ff.val_arst = State::S0; + } + if (rport.srst != State::S0) { + ff.has_srst = true; + ff.sig_srst = rport.srst; + ff.pol_srst = true; + ff.val_srst = State::S0; + ff.ce_over_srst = rport.ce_over_srst; + } + if (!rport.init_value.is_fully_undef()) + ff.val_init = State::S0; + else + ff.val_init = State::Sx; + ff.emit(); + // And the final bypass mux. + SigSpec cur = rdata_a.extract(pos, epos-pos); + SigSpec other = wdata_q.extract(pos + wsub * width, epos-pos); + SigSpec dest = rport.data.extract(pos + rsub * width, epos-pos); + module->addMux(NEW_ID, cur, other, cond_q, dest); + pos = epos; + } + rport.data.replace(rsub * width, rdata_a); + } + rport.transparency_mask[widx] = false; + rport.collision_x_mask[widx] = true; +} + +void Mem::prepare_wr_merge(int idx1, int idx2, FfInitVals *initvals) { + log_assert(idx1 < idx2); + auto &port1 = wr_ports[idx1]; + auto &port2 = wr_ports[idx2]; + // If port 2 has priority over a port before port 1, make port 1 have priority too. + for (int i = 0; i < idx1; i++) + if (port2.priority_mask[i]) + port1.priority_mask[i] = true; + // If port 2 has priority over a port after port 1, emulate it. + for (int i = idx1 + 1; i < idx2; i++) + if (port2.priority_mask[i] && !wr_ports[i].removed) + emulate_priority(i, idx2, initvals); + // If some port had priority over port 2, make it have priority over the merged port too. + for (int i = idx2 + 1; i < GetSize(wr_ports); i++) { + auto &oport = wr_ports[i]; + if (oport.priority_mask[idx2]) + oport.priority_mask[idx1] = true; + } + // Make sure all read ports have identical collision/transparency behavior wrt both + // ports. + for (int i = 0; i < GetSize(rd_ports); i++) { + auto &rport = rd_ports[i]; + if (rport.removed) + continue; + // If collision already undefined with both ports, it's fine. + if (rport.collision_x_mask[idx1] && rport.collision_x_mask[idx2]) + continue; + // If one port has undefined collision, change it to the behavior + // of the other port. + if (rport.collision_x_mask[idx1]) { + rport.collision_x_mask[idx1] = false; + rport.transparency_mask[idx1] = rport.transparency_mask[idx2]; + continue; + } + if (rport.collision_x_mask[idx2]) { + rport.collision_x_mask[idx2] = false; + rport.transparency_mask[idx2] = rport.transparency_mask[idx1]; + continue; + } + // If transparent with both ports, also fine. + if (rport.transparency_mask[idx1] && rport.transparency_mask[idx2]) + continue; + // If transparent with only one, emulate it, and remove the collision-X + // flag that emulate_transparency will set (to align with the other port). + if (rport.transparency_mask[idx1]) { + emulate_transparency(idx1, i, initvals); + rport.collision_x_mask[idx1] = false; + continue; + } + if (rport.transparency_mask[idx2]) { + emulate_transparency(idx2, i, initvals); + rport.collision_x_mask[idx2] = false; + continue; + } + // If we got here, it's transparent with neither port, which is fine. + } +} + +void Mem::prepare_rd_merge(int idx1, int idx2, FfInitVals *initvals) { + auto &port1 = rd_ports[idx1]; + auto &port2 = rd_ports[idx2]; + // Note that going through write ports in order is important, since + // emulating transparency of a write port can change transparency + // mask for higher-numbered ports (due to transitive transparency + // emulation needed because of write port priority). + for (int i = 0; i < GetSize(wr_ports); i++) { + if (wr_ports[i].removed) + continue; + // Both ports undefined, OK. + if (port1.collision_x_mask[i] && port2.collision_x_mask[i]) + continue; + // Only one port undefined — change its behavior + // to align with the other port. + if (port1.collision_x_mask[i]) { + port1.collision_x_mask[i] = false; + port1.transparency_mask[i] = port2.transparency_mask[i]; + continue; + } + if (port2.collision_x_mask[i]) { + port2.collision_x_mask[i] = false; + port2.transparency_mask[i] = port1.transparency_mask[i]; + continue; + } + // Both ports transparent, OK. + if (port1.transparency_mask[i] && port2.transparency_mask[i]) + continue; + // Only one port transparent — emulate transparency + // on the other. + if (port1.transparency_mask[i]) { + emulate_transparency(i, idx1, initvals); + port1.collision_x_mask[i] = false; + continue; + } + if (port2.transparency_mask[i]) { + emulate_transparency(i, idx2, initvals); + port2.collision_x_mask[i] = false; + continue; + } + // No ports transparent, OK. + } + +} + +void Mem::widen_prep(int wide_log2) { + // Make sure start_offset and size are aligned to the port width, + // adjust if necessary. + int mask = ((1 << wide_log2) - 1); + int delta = start_offset & mask; + start_offset -= delta; + size += delta; + if (size & mask) { + size |= mask; + size++; + } +} + +void Mem::widen_wr_port(int idx, int wide_log2) { + widen_prep(wide_log2); + auto &port = wr_ports[idx]; + log_assert(port.wide_log2 <= wide_log2); + if (port.wide_log2 < wide_log2) { + SigSpec new_data, new_en; + SigSpec addr_lo = port.addr.extract(0, wide_log2); + for (int sub = 0; sub < (1 << wide_log2); sub += (1 << port.wide_log2)) + { + Const cur_addr_lo(sub, wide_log2); + if (addr_lo == cur_addr_lo) { + // Always writes to this subword. + new_data.append(port.data); + new_en.append(port.en); + } else if (addr_lo.is_fully_const()) { + // Never writes to this subword. + new_data.append(Const(State::Sx, GetSize(port.data))); + new_en.append(Const(State::S0, GetSize(port.data))); + } else { + // May or may not write to this subword. + new_data.append(port.data); + SigSpec addr_eq = module->Eq(NEW_ID, addr_lo, cur_addr_lo); + SigSpec en = module->Mux(NEW_ID, Const(State::S0, GetSize(port.data)), port.en, addr_eq); + new_en.append(en); + } + } + port.addr.replace(port.wide_log2, Const(State::S0, wide_log2 - port.wide_log2)); + port.data = new_data; + port.en = new_en; + port.wide_log2 = wide_log2; + } +} + +void Mem::emulate_rden(int idx, FfInitVals *initvals) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + emulate_rd_ce_over_srst(idx); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + Wire *prev_data = module->addWire(NEW_ID, GetSize(port.data)); + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + FfData ff_data(module, initvals, NEW_ID); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = port.en; + ff_sel.sig_q = sel; + ff_data.width = GetSize(port.data); + ff_data.has_clk = true; + ff_data.sig_clk = port.clk; + ff_data.pol_clk = port.clk_polarity; + ff_data.sig_d = port.data; + ff_data.sig_q = prev_data; + if (!port.init_value.is_fully_undef()) { + ff_sel.val_init = State::S0; + ff_data.val_init = port.init_value; + port.init_value = Const(State::Sx, GetSize(port.data)); + } else { + ff_sel.val_init = State::Sx; + ff_data.val_init = Const(State::Sx, GetSize(port.data)); + } + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.val_arst = State::S0; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_data.has_arst = true; + ff_data.val_arst = port.arst_value; + ff_data.sig_arst = port.arst; + ff_data.pol_arst = true; + port.arst = State::S0; + } + if (port.srst != State::S0) { + log_assert(!port.ce_over_srst); + ff_sel.has_srst = true; + ff_sel.val_srst = State::S0; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + ff_sel.ce_over_srst = false; + ff_data.has_srst = true; + ff_data.val_srst = port.srst_value; + ff_data.sig_srst = port.srst; + ff_data.pol_srst = true; + ff_data.ce_over_srst = false; + port.srst = State::S0; + } + ff_sel.emit(); + ff_data.emit(); + module->addMux(NEW_ID, prev_data, new_data, sel, port.data); + port.data = new_data; + port.en = State::S1; +} + +void Mem::emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals) { + auto &port = rd_ports[idx]; + if (emu_init && !port.init_value.is_fully_undef()) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + ff_sel.val_init = State::S0; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + if (emu_arst && port.arst_value == port.init_value) { + // If we're going to emulate async reset anyway, and the reset + // value is the same as init value, reuse the same mux. + ff_sel.val_arst = State::S0; + port.arst = State::S0; + } else { + ff_sel.val_arst = State::S1; + } + } + if (port.srst != State::S0) { + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + if (emu_srst && port.srst_value == port.init_value) { + ff_sel.val_srst = State::S0; + port.srst = State::S0; + } else { + ff_sel.val_srst = State::S1; + } + } + ff_sel.emit(); + module->addMux(NEW_ID, port.init_value, new_data, sel, port.data); + port.data = new_data; + port.init_value = Const(State::Sx, GetSize(port.data)); + } + if (emu_arst && port.arst != State::S0) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + if (port.init_value.is_fully_undef()) + ff_sel.val_init = State::Sx; + else + ff_sel.val_init = State::S1; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_sel.val_arst = State::S0; + if (port.srst != State::S0) { + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + if (emu_srst && port.srst_value == port.arst_value) { + ff_sel.val_srst = State::S0; + port.srst = State::S0; + } else { + ff_sel.val_srst = State::S1; + } + } + ff_sel.emit(); + module->addMux(NEW_ID, port.arst_value, new_data, sel, port.data); + port.data = new_data; + port.arst = State::S0; + } + if (emu_srst && port.srst != State::S0) { + Wire *sel = module->addWire(NEW_ID); + FfData ff_sel(module, initvals, NEW_ID); + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + ff_sel.width = 1; + ff_sel.has_clk = true; + ff_sel.sig_clk = port.clk; + ff_sel.pol_clk = port.clk_polarity; + ff_sel.sig_d = State::S1; + ff_sel.sig_q = sel; + if (port.init_value.is_fully_undef()) + ff_sel.val_init = State::Sx; + else + ff_sel.val_init = State::S1; + if (port.en != State::S1) { + ff_sel.has_ce = true; + ff_sel.sig_ce = port.en; + ff_sel.pol_ce = true; + ff_sel.ce_over_srst = port.ce_over_srst; + } + ff_sel.has_srst = true; + ff_sel.sig_srst = port.srst; + ff_sel.pol_srst = true; + ff_sel.val_srst = State::S0; + if (port.arst != State::S0) { + ff_sel.has_arst = true; + ff_sel.sig_arst = port.arst; + ff_sel.pol_arst = true; + ff_sel.val_arst = State::S1; + } + ff_sel.emit(); + module->addMux(NEW_ID, port.srst_value, new_data, sel, port.data); + port.data = new_data; + port.srst = State::S0; + } +} + +void Mem::emulate_rd_ce_over_srst(int idx) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + if (port.en == State::S1 || port.srst == State::S0 || !port.ce_over_srst) { + port.ce_over_srst = false; + return; + } + port.ce_over_srst = false; + port.srst = module->And(NEW_ID, port.en, port.srst); +} + +void Mem::emulate_rd_srst_over_ce(int idx) { + auto &port = rd_ports[idx]; + log_assert(port.clk_enable); + if (port.en == State::S1 || port.srst == State::S0 || port.ce_over_srst) { + port.ce_over_srst = true; + return; + } + port.ce_over_srst = true; + port.en = module->Or(NEW_ID, port.en, port.srst); +} + +bool Mem::emulate_read_first_ok() { + if (wr_ports.empty()) + return false; + SigSpec clk = wr_ports[0].clk; + bool clk_polarity = wr_ports[0].clk_polarity; + for (auto &port: wr_ports) { + if (!port.clk_enable) + return false; + if (port.clk != clk) + return false; + if (port.clk_polarity != clk_polarity) + return false; + } + bool found_read_first = false; + for (auto &port: rd_ports) { + if (!port.clk_enable) + return false; + if (port.clk != clk) + return false; + if (port.clk_polarity != clk_polarity) + return false; + // No point doing this operation if there is no read-first relationship + // in the first place. + for (int j = 0; j < GetSize(wr_ports); j++) + if (!port.transparency_mask[j] && !port.collision_x_mask[j]) + found_read_first = true; + } + return found_read_first; +} + +void Mem::emulate_read_first(FfInitVals *initvals) { + log_assert(emulate_read_first_ok()); + for (int i = 0; i < GetSize(rd_ports); i++) + for (int j = 0; j < GetSize(wr_ports); j++) + if (rd_ports[i].transparency_mask[j]) + emulate_transparency(j, i, initvals); + for (int i = 0; i < GetSize(rd_ports); i++) + for (int j = 0; j < GetSize(wr_ports); j++) { + log_assert(!rd_ports[i].transparency_mask[j]); + rd_ports[i].collision_x_mask[j] = false; + rd_ports[i].transparency_mask[j] = true; + } + for (auto &port: wr_ports) { + Wire *new_data = module->addWire(NEW_ID, GetSize(port.data)); + Wire *new_addr = module->addWire(NEW_ID, GetSize(port.addr)); + auto compressed = port.compress_en(); + Wire *new_en = module->addWire(NEW_ID, GetSize(compressed.first)); + FfData ff_data(module, initvals, NEW_ID); + FfData ff_addr(module, initvals, NEW_ID); + FfData ff_en(module, initvals, NEW_ID); + ff_data.width = GetSize(port.data); + ff_data.has_clk = true; + ff_data.sig_clk = port.clk; + ff_data.pol_clk = port.clk_polarity; + ff_data.sig_d = port.data; + ff_data.sig_q = new_data;; + ff_data.val_init = Const(State::Sx, ff_data.width); + ff_data.emit(); + ff_addr.width = GetSize(port.addr); + ff_addr.has_clk = true; + ff_addr.sig_clk = port.clk; + ff_addr.pol_clk = port.clk_polarity; + ff_addr.sig_d = port.addr; + ff_addr.sig_q = new_addr;; + ff_addr.val_init = Const(State::Sx, ff_addr.width); + ff_addr.emit(); + ff_en.width = GetSize(compressed.first); + ff_en.has_clk = true; + ff_en.sig_clk = port.clk; + ff_en.pol_clk = port.clk_polarity; + ff_en.sig_d = compressed.first; + ff_en.sig_q = new_en;; + if (inits.empty()) + ff_en.val_init = Const(State::Sx, ff_en.width); + else + ff_en.val_init = Const(State::S0, ff_en.width); + ff_en.emit(); + port.data = new_data; + port.addr = new_addr; + port.en = port.decompress_en(compressed.second, new_en); + } +} + +std::pair> MemWr::compress_en() { + SigSpec sig = en[0]; + std::vector swizzle; + SigBit prev_bit = en[0]; + int idx = 0; + for (auto &bit: en) { + if (bit != prev_bit) { + sig.append(bit); + prev_bit = bit; + idx++; + } + swizzle.push_back(idx); + } + log_assert(idx + 1 == GetSize(sig)); + return {sig, swizzle}; +} + +SigSpec MemWr::decompress_en(const std::vector &swizzle, SigSpec sig) { + SigSpec res; + for (int i: swizzle) + res.append(sig[i]); + return res; +} diff --git a/kernel/mem.h b/kernel/mem.h index 6d727e71d1a..8c484274ccf 100644 --- a/kernel/mem.h +++ b/kernel/mem.h @@ -21,38 +21,76 @@ #define MEM_H #include "kernel/yosys.h" +#include "kernel/ffinit.h" YOSYS_NAMESPACE_BEGIN -struct MemRd { - dict attributes; +struct MemRd : RTLIL::AttrObject { + bool removed; Cell *cell; - bool clk_enable, clk_polarity; - bool transparent; - SigSpec clk, en, addr, data; - MemRd() : cell(nullptr) {} + int wide_log2; + bool clk_enable, clk_polarity, ce_over_srst; + Const arst_value, srst_value, init_value; + // One bit for every write port, true iff simultanous read on this + // port and write on the other port will bypass the written data + // to this port's output (default behavior is to read old value). + // Can only be set for write ports that have the same clock domain. + std::vector transparency_mask; + // One bit for every write port, true iff simultanous read on this + // port and write on the other port will return an all-X (don't care) + // value. Mutually exclusive with transparency_mask. + // Can only be set for write ports that have the same clock domain. + // For optimization purposes, this will also be set if we can + // determine that the two ports can never be active simultanously + // (making the above vacuously true). + std::vector collision_x_mask; + SigSpec clk, en, arst, srst, addr, data; + + MemRd() : removed(false), cell(nullptr), wide_log2(0), clk_enable(false), clk_polarity(true), ce_over_srst(false), clk(State::Sx), en(State::S1), arst(State::S0), srst(State::S0) {} + + // Returns the address of given subword index accessed by this port. + SigSpec sub_addr(int sub) { + SigSpec res = addr; + for (int i = 0; i < wide_log2; i++) + res[i] = State(sub >> i & 1); + return res; + } }; -struct MemWr { - dict attributes; +struct MemWr : RTLIL::AttrObject { + bool removed; Cell *cell; + int wide_log2; bool clk_enable, clk_polarity; + std::vector priority_mask; SigSpec clk, en, addr, data; - MemWr() : cell(nullptr) {} + + MemWr() : removed(false), cell(nullptr) {} + + // Returns the address of given subword index accessed by this port. + SigSpec sub_addr(int sub) { + SigSpec res = addr; + for (int i = 0; i < wide_log2; i++) + res[i] = State(sub >> i & 1); + return res; + } + + std::pair> compress_en(); + SigSpec decompress_en(const std::vector &swizzle, SigSpec sig); }; -struct MemInit { - dict attributes; +struct MemInit : RTLIL::AttrObject { + bool removed; Cell *cell; Const addr; Const data; - MemInit() : cell(nullptr) {} + Const en; + MemInit() : removed(false), cell(nullptr) {} }; -struct Mem { +struct Mem : RTLIL::AttrObject { Module *module; IdString memid; - dict attributes; bool packed; RTLIL::Memory *mem; Cell *cell; @@ -61,15 +99,128 @@ struct Mem { std::vector rd_ports; std::vector wr_ports; + // Removes this memory from the module. The data in helper structures + // is unaffected except for the cell/mem fields. void remove(); + + // Commits all changes in helper structures into the module — ports and + // inits marked as removed are actually removed, new ports/inits create + // new cells, modified port/inits are commited into their existing + // cells. Note that this reindexes the ports and inits array (actually + // removing the ports/inits marked as removed). void emit(); - void remove_wr_port(int idx); - void remove_rd_port(int idx); + + // Marks all inits as removed. void clear_inits(); + + // Coalesces inits: whenever two inits have overlapping or touching + // address ranges, they are combined into one, with the higher-priority + // one's data overwriting the other. Running this results in + // an inits list equivalent to the original, in which all entries + // cover disjoint (and non-touching) address ranges, and all enable + // masks are all-1. + void coalesce_inits(); + + // Checks consistency of this memory and all its ports/inits, using + // log_assert. + void check(); + + // Gathers all initialization data into a single big const covering + // the whole memory. For all non-initialized bits, Sx will be returned. Const get_init_data() const; + + // Constructs and returns the helper structures for all memories + // in a module. static std::vector get_all_memories(Module *module); + + // Constructs and returns the helper structures for all selected + // memories in a module. static std::vector get_selected_memories(Module *module); - Cell *extract_rdff(int idx); + + // Converts a synchronous read port into an asynchronous one by + // extracting the data (or, in some rare cases, address) register + // into a separate cell, together with any soft-transparency + // logic necessary to preserve its semantics. Returns the created + // register cell, if any. Note that in some rare cases this function + // may succeed and perform a conversion without creating a new + // register — a nullptr result doesn't imply nothing was done. + Cell *extract_rdff(int idx, FfInitVals *initvals); + + // Splits all wide ports in this memory into equivalent narrow ones. + // This function performs no modifications at all to the actual + // netlist unless and until emit() is called. + void narrow(); + + // If write port idx2 currently has priority over write port idx1, + // inserts extra logic on idx1's enable signal to disable writes + // when idx2 is writing to the same address, then removes the priority + // from the priority mask. If there is a memory port that is + // transparent with idx1, but not with idx2, that port is converted + // to use soft transparency logic. + void emulate_priority(int idx1, int idx2, FfInitVals *initvals); + + // Creates soft-transparency logic on read port ridx, bypassing the + // data from write port widx. Should only be called when ridx is + // transparent wrt widx in the first place. Once we're done, the + // transparency_mask bit will be cleared, and the collision_x_mask + // bit will be set instead (since whatever value is read will be + // replaced by the soft transparency logic). + void emulate_transparency(int widx, int ridx, FfInitVals *initvals); + + // Prepares for merging write port idx2 into idx1 (where idx1 < idx2). + // Specifically, takes care of priority masks: any priority relations + // that idx2 had are replicated onto idx1, unless they conflict with + // priorities already present on idx1, in which case emulate_priority + // is called. Likewise, ensures transparency and undefined collision + // masks of all read ports have the same values for both ports, + // calling emulate_transparency if necessary. + void prepare_wr_merge(int idx1, int idx2, FfInitVals *initvals); + + // Prepares for merging read port idx2 into idx1. + // Specifically, makes sure the transparency and undefined collision + // masks of both ports are equal, by changing undefined behavior + // of one port to the other's defined behavior, or by calling + // emulate_transparency if necessary. + void prepare_rd_merge(int idx1, int idx2, FfInitVals *initvals); + + // Prepares the memory for widening a port to a given width. This + // involves ensuring that start_offset and size are aligned to the + // target width. + void widen_prep(int wide_log2); + + // Widens a write port up to a given width. The newly port is + // equivalent to the original, made by replicating enable/data bits + // and masking enable bits with decoders on the low part of the + // original address. + void widen_wr_port(int idx, int wide_log2); + + // Emulates a sync read port's enable functionality in soft logic, + // changing the actual read port's enable to be always-on. + void emulate_rden(int idx, FfInitVals *initvals); + + // Emulates a sync read port's initial/reset value functionality in + // soft logic, removing it from the actual read port. + void emulate_reset(int idx, bool emu_init, bool emu_arst, bool emu_srst, FfInitVals *initvals); + + // Given a read port with ce_over_srst set, converts it to a port + // with ce_over_srst unset without changing its behavior by adding + // emulation logic. + void emulate_rd_ce_over_srst(int idx); + + // Given a read port with ce_over_srst unset, converts it to a port + // with ce_over_srst set without changing its behavior by adding + // emulation logic. + void emulate_rd_srst_over_ce(int idx); + + // Returns true iff emulate_read_first makes sense to call. + bool emulate_read_first_ok(); + + // Emulates all read-first read-write port relationships in terms of + // all-transparent ports, by delaying all write ports by one cycle. + // This can only be used when all read ports and all write ports are + // in the same clock domain. + void emulate_read_first(FfInitVals *initvals); + Mem(Module *module, IdString memid, int width, int start_offset, int size) : module(module), memid(memid), packed(false), mem(nullptr), cell(nullptr), width(width), start_offset(start_offset), size(size) {} }; diff --git a/kernel/modtools.h b/kernel/modtools.h index 29c510059ff..34a23b37981 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -380,9 +380,11 @@ struct ModWalker } } - ModWalker(RTLIL::Design *design) : design(design), module(NULL) + ModWalker(RTLIL::Design *design, RTLIL::Module *module = nullptr) : design(design), module(NULL) { - ct.setup(design); + ct.setup(design); + if (module) + setup(module); } void setup(RTLIL::Module *module, CellTypes *filter_ct = NULL) @@ -395,6 +397,8 @@ struct ModWalker signal_consumers.clear(); signal_inputs.clear(); signal_outputs.clear(); + cell_inputs.clear(); + cell_outputs.clear(); for (auto &it : module->wires_) add_wire(it.second); @@ -405,7 +409,6 @@ struct ModWalker // get_* methods -- single RTLIL::SigBit - template inline bool get_drivers(pool &result, RTLIL::SigBit bit) const { bool found = false; @@ -417,7 +420,6 @@ struct ModWalker return found; } - template inline bool get_consumers(pool &result, RTLIL::SigBit bit) const { bool found = false; @@ -429,7 +431,6 @@ struct ModWalker return found; } - template inline bool get_inputs(pool &result, RTLIL::SigBit bit) const { bool found = false; @@ -438,7 +439,6 @@ struct ModWalker return found; } - template inline bool get_outputs(pool &result, RTLIL::SigBit bit) const { bool found = false; diff --git a/kernel/qcsat.cc b/kernel/qcsat.cc new file mode 100644 index 00000000000..aaee984fbaa --- /dev/null +++ b/kernel/qcsat.cc @@ -0,0 +1,102 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/qcsat.h" + +USING_YOSYS_NAMESPACE + +std::vector QuickConeSat::importSig(SigSpec sig) +{ + sig = modwalker.sigmap(sig); + for (auto bit : sig) + bits_queue.insert(bit); + return satgen.importSigSpec(sig); +} + +int QuickConeSat::importSigBit(SigBit bit) +{ + bit = modwalker.sigmap(bit); + bits_queue.insert(bit); + return satgen.importSigBit(bit); +} + +void QuickConeSat::prepare() +{ + while (!bits_queue.empty()) + { + pool portbits; + modwalker.get_drivers(portbits, bits_queue); + + for (auto bit : bits_queue) + if (bit.wire && bit.wire->get_bool_attribute(ID::onehot) && !imported_onehot.count(bit.wire)) + { + std::vector bits = satgen.importSigSpec(bit.wire); + for (int i : bits) + for (int j : bits) + if (i != j) + ez->assume(ez->NOT(i), j); + imported_onehot.insert(bit.wire); + } + + bits_queue.clear(); + + for (auto &pbit : portbits) + { + if (imported_cells.count(pbit.cell)) + continue; + if (cell_complexity(pbit.cell) > max_cell_complexity) + continue; + if (max_cell_outs && GetSize(modwalker.cell_outputs[pbit.cell]) > max_cell_outs) + continue; + auto &inputs = modwalker.cell_inputs[pbit.cell]; + bits_queue.insert(inputs.begin(), inputs.end()); + satgen.importCell(pbit.cell); + imported_cells.insert(pbit.cell); + } + + if (max_cell_count && GetSize(imported_cells) > max_cell_count) + break; + } +} + +int QuickConeSat::cell_complexity(RTLIL::Cell *cell) +{ + if (cell->type.in(ID($concat), ID($slice), ID($pos), ID($_BUF_))) + return 0; + if (cell->type.in(ID($not), ID($and), ID($or), ID($xor), ID($xnor), + ID($reduce_and), ID($reduce_or), ID($reduce_xor), + ID($reduce_xnor), ID($reduce_bool), + ID($logic_not), ID($logic_and), ID($logic_or), + ID($eq), ID($ne), ID($eqx), ID($nex), ID($fa), + ID($mux), ID($pmux), ID($bmux), ID($demux), ID($lut), ID($sop), + ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), + ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), + ID($_MUX_), ID($_NMUX_), ID($_MUX4_), ID($_MUX8_), ID($_MUX16_), + ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) + return 1; + if (cell->type.in(ID($neg), ID($add), ID($sub), ID($alu), ID($lcu), + ID($lt), ID($le), ID($gt), ID($ge))) + return 2; + if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx))) + return 3; + if (cell->type.in(ID($mul), ID($macc), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow))) + return 4; + // Unknown cell. + return 5; +} diff --git a/kernel/qcsat.h b/kernel/qcsat.h new file mode 100644 index 00000000000..e4d3c3c5dc6 --- /dev/null +++ b/kernel/qcsat.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2021 Marcelina Kościelnicka + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef QCSAT_H +#define QCSAT_H + +#include "kernel/satgen.h" +#include "kernel/modtools.h" + +YOSYS_NAMESPACE_BEGIN + +// This is a helper class meant for easy construction of quick SAT queries +// to a combinatorial input cone of some set of signals, meant for SAT-based +// optimizations. Various knobs are provided to set just how much of the +// cone should be included in the model — since this class is meant for +// optimization, it should not be a correctness problem when some cells are +// skipped and the solver spuriously returns SAT with a solution that +// cannot exist in reality due to skipped constraints (ie. only UNSAT results +// from this class should be considered binding). +struct QuickConeSat { + ModWalker &modwalker; + ezSatPtr ez; + SatGen satgen; + + // The effort level knobs. + + // The maximum "complexity level" of cells that will be imported. + // - 1: bitwise operations, muxes, equality comparisons, lut, sop, fa + // - 2: addition, subtraction, greater/less than comparisons, lcu + // - 3: shifts + // - 4: multiplication, division, power + int max_cell_complexity = 2; + // The maximum number of cells to import, or 0 for no limit. + int max_cell_count = 0; + // If non-0, skip importing cells with more than this number of output bits. + int max_cell_outs = 0; + + // Internal state. + pool imported_cells; + pool imported_onehot; + pool bits_queue; + + QuickConeSat(ModWalker &modwalker) : modwalker(modwalker), ez(), satgen(ez.get(), &modwalker.sigmap) {} + + // Imports a signal into the SAT solver, queues its input cone to be + // imported in the next prepare() call. + std::vector importSig(SigSpec sig); + int importSigBit(SigBit bit); + + // Imports the input cones of all previously importSig'd signals into + // the SAT solver. + void prepare(); + + // Returns the "complexity level" of a given cell. + static int cell_complexity(RTLIL::Cell *cell); +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/register.cc b/kernel/register.cc index 34735a60854..80bc44901b2 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -108,7 +108,8 @@ Pass::Pass(std::string name, std::string short_help) : pass_name(name), short_he void Pass::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name) && !replace_existing_pass()) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); pass_register[pass_name] = this; } @@ -445,10 +446,12 @@ Frontend::Frontend(std::string name, std::string short_help) : void Frontend::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name) && !replace_existing_pass()) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); pass_register[pass_name] = this; - log_assert(frontend_register.count(frontend_name) == 0); + if (frontend_register.count(frontend_name) && !replace_existing_pass()) + log_error("Unable to register frontend '%s', frontend already exists!\n", frontend_name.c_str()); frontend_register[frontend_name] = this; } @@ -526,10 +529,11 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vectoropen(filename.c_str(), bin_input ? std::ifstream::binary : std::ifstream::in); yosys_input_files.insert(filename); - if (ff->fail()) + if (ff->fail()) { delete ff; - else - f = ff; + ff = nullptr; + } + f = ff; if (f != NULL) { // Check for gzip magic unsigned char magic[3]; @@ -626,10 +630,12 @@ Backend::Backend(std::string name, std::string short_help) : void Backend::run_register() { - log_assert(pass_register.count(pass_name) == 0); + if (pass_register.count(pass_name)) + log_error("Unable to register pass '%s', pass already exists!\n", pass_name.c_str()); pass_register[pass_name] = this; - log_assert(backend_register.count(backend_name) == 0); + if (backend_register.count(backend_name)) + log_error("Unable to register backend '%s', backend already exists!\n", backend_name.c_str()); backend_register[backend_name] = this; } @@ -765,61 +771,97 @@ struct HelpPass : public Pass { log(" help + .... print verilog code for given cell type\n"); log("\n"); } - void escape_tex(std::string &tex) - { - for (size_t pos = 0; (pos = tex.find('_', pos)) != std::string::npos; pos += 2) - tex.replace(pos, 1, "\\_"); - for (size_t pos = 0; (pos = tex.find('$', pos)) != std::string::npos; pos += 2) - tex.replace(pos, 1, "\\$"); - } - void write_tex(FILE *f, std::string cmd, std::string title, std::string text) + void write_rst(std::string cmd, std::string title, std::string text) { - size_t begin = text.find_first_not_of("\n"), end = text.find_last_not_of("\n"); - if (begin != std::string::npos && end != std::string::npos && begin < end) - text = text.substr(begin, end-begin+1); - std::string cmd_unescaped = cmd; - escape_tex(cmd); - escape_tex(title); - fprintf(f, "\\section{%s -- %s}\n", cmd.c_str(), title.c_str()); - fprintf(f, "\\label{cmd:%s}\n", cmd_unescaped.c_str()); - fprintf(f, "\\begin{lstlisting}[numbers=left,frame=single]\n"); - fprintf(f, "%s\n\\end{lstlisting}\n\n", text.c_str()); - } - void escape_html(std::string &html) - { - size_t pos = 0; - while ((pos = html.find_first_of("<>&", pos)) != std::string::npos) - switch (html[pos]) { - case '<': - html.replace(pos, 1, "<"); - pos += 4; - break; - case '>': - html.replace(pos, 1, ">"); - pos += 4; - break; - case '&': - html.replace(pos, 1, "&"); - pos += 5; - break; + FILE *f = fopen(stringf("docs/source/cmd/%s.rst", cmd.c_str()).c_str(), "wt"); + // make header + size_t char_len = cmd.length() + 3 + title.length(); + std::string title_line = "\n"; + title_line.insert(0, char_len, '='); + fprintf(f, "%s", title_line.c_str()); + fprintf(f, "%s - %s\n", cmd.c_str(), title.c_str()); + fprintf(f, "%s\n", title_line.c_str()); + fprintf(f, ".. raw:: latex\n\n \\begin{comment}\n\n"); + + // render html + fprintf(f, ".. cmd:def:: %s\n", cmd.c_str()); + fprintf(f, " :title: %s\n\n", title.c_str()); + std::stringstream ss; + std::string textcp = text; + ss << text; + bool IsUsage = true; + int blank_count = 0; + size_t def_strip_count = 0; + bool WasDefinition = false; + for (std::string line; std::getline(ss, line, '\n');) { + // find position of first non space character + std::size_t first_pos = line.find_first_not_of(" \t"); + std::size_t last_pos = line.find_last_not_of(" \t"); + if (first_pos == std::string::npos) { + // skip formatting empty lines + if (!WasDefinition) + fputc('\n', f); + blank_count += 1; + continue; } - } - void write_html(FILE *idxf, std::string cmd, std::string title, std::string text) - { - FILE *f = fopen(stringf("cmd_%s.in", cmd.c_str()).c_str(), "wt"); - fprintf(idxf, "
  • ", cmd.c_str()); - escape_html(cmd); - escape_html(title); - escape_html(text); - - fprintf(idxf, "%s %s\n", cmd.c_str(), title.c_str()); - - fprintf(f, "@cmd_header %s@\n", cmd.c_str()); - fprintf(f, "

    %s - %s

    \n", cmd.c_str(), title.c_str()); - fprintf(f, "
    %s
    \n", text.c_str()); - fprintf(f, "@footer@\n"); + // strip leading and trailing whitespace + std::string stripped_line = line.substr(first_pos, last_pos - first_pos +1); + bool IsDefinition = stripped_line[0] == '-'; + IsDefinition &= stripped_line[1] != ' ' && stripped_line[1] != '>'; + bool IsDedent = def_strip_count && first_pos <= def_strip_count; + bool IsIndent = first_pos == 2 || first_pos == 4; + if (cmd.compare(0, 7, "verific") == 0) + // verific.cc has strange and different formatting from the rest + IsIndent = false; + + // another usage block + bool NewUsage = stripped_line.find(cmd) == 0; + + if (IsUsage) { + if (stripped_line.compare(0, 4, "See ") == 0) { + // description refers to another function + fprintf(f, "\n %s\n", stripped_line.c_str()); + } else { + // usage should be the first line of help output + fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; + } + IsUsage = false; + } else if (IsIndent && NewUsage && (blank_count >= 2 || WasDefinition)) { + // another usage block + fprintf(f, "\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; + def_strip_count = 0; + } else if (IsIndent && IsDefinition && (blank_count || WasDefinition)) { + // format definition term + fprintf(f, "\n\n .. code:: yoscrypt\n\n %s\n\n ", stripped_line.c_str()); + WasDefinition = true; + def_strip_count = first_pos; + } else { + if (IsDedent) { + fprintf(f, "\n\n ::\n"); + def_strip_count = first_pos; + } else if (WasDefinition) { + fprintf(f, " ::\n"); + WasDefinition = false; + } + fprintf(f, "\n %s", line.substr(def_strip_count, std::string::npos).c_str()); + } + blank_count = 0; + } + fputc('\n', f); + + // render latex + fprintf(f, ".. raw:: latex\n\n \\end{comment}\n\n"); + fprintf(f, ".. only:: latex\n\n"); + fprintf(f, " ::\n\n"); + std::stringstream ss2; + ss2 << textcp; + for (std::string line; std::getline(ss2, line, '\n');) { + fprintf(f, " %s\n", line.c_str()); + } fclose(f); } void execute(std::vector args, RTLIL::Design*) override @@ -864,26 +906,7 @@ struct HelpPass : public Pass { return; } // this option is undocumented as it is for internal use only - else if (args[1] == "-write-tex-command-reference-manual") { - FILE *f = fopen("command-reference-manual.tex", "wt"); - fprintf(f, "%% Generated using the yosys 'help -write-tex-command-reference-manual' command.\n\n"); - for (auto &it : pass_register) { - std::ostringstream buf; - log_streams.push_back(&buf); - it.second->help(); - if (it.second->experimental_flag) { - log("\n"); - log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); - log("\n"); - } - log_streams.pop_back(); - write_tex(f, it.first, it.second->short_help, buf.str()); - } - fclose(f); - } - // this option is undocumented as it is for internal use only - else if (args[1] == "-write-web-command-reference-manual") { - FILE *f = fopen("templates/cmd_index.in", "wt"); + else if (args[1] == "-write-rst-command-reference-manual") { for (auto &it : pass_register) { std::ostringstream buf; log_streams.push_back(&buf); @@ -894,9 +917,8 @@ struct HelpPass : public Pass { log("\n"); } log_streams.pop_back(); - write_html(f, it.first, it.second->short_help, buf.str()); + write_rst(it.first, it.second->short_help, buf.str()); } - fclose(f); } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); @@ -969,4 +991,44 @@ struct MinisatSatSolver : public SatSolver { } } MinisatSatSolver; +struct LicensePass : public Pass { + LicensePass() : Pass("license", "print license terms") { } + void help() override + { + log("\n"); + log(" license\n"); + log("\n"); + log("This command produces the following notice.\n"); + notice(); + } + void execute(std::vector, RTLIL::Design*) override + { + notice(); + } + void notice() + { + log("\n"); + log(" /----------------------------------------------------------------------------\\\n"); + log(" | |\n"); + log(" | yosys -- Yosys Open SYnthesis Suite |\n"); + log(" | |\n"); + log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf |\n"); + log(" | |\n"); + log(" | Permission to use, copy, modify, and/or distribute this software for any |\n"); + log(" | purpose with or without fee is hereby granted, provided that the above |\n"); + log(" | copyright notice and this permission notice appear in all copies. |\n"); + log(" | |\n"); + log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n"); + log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n"); + log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n"); + log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n"); + log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n"); + log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n"); + log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n"); + log(" | |\n"); + log(" \\----------------------------------------------------------------------------/\n"); + log("\n"); + } +} LicensePass; + YOSYS_NAMESPACE_END diff --git a/kernel/register.h b/kernel/register.h index 5cd8490824b..08ce4b28787 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -70,6 +70,7 @@ struct Pass virtual void on_register(); virtual void on_shutdown(); + virtual bool replace_existing_pass() const { return false; } }; struct ScriptPass : Pass diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index a9f5856160f..8781b6a8946 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include "kernel/yosys.h" #include "kernel/macc.h" #include "kernel/celltypes.h" +#include "kernel/binding.h" #include "frontends/verilog/verilog_frontend.h" #include "frontends/verilog/preproc.h" #include "backends/rtlil/rtlil_backend.h" @@ -29,6 +30,7 @@ YOSYS_NAMESPACE_BEGIN +bool RTLIL::IdString::destruct_guard_ok = false; RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard; std::vector RTLIL::IdString::global_id_storage_; dict RTLIL::IdString::global_id_index_; @@ -57,6 +59,8 @@ const pool &RTLIL::builtin_ff_cell_types() { ID($dffsre), ID($adff), ID($adffe), + ID($aldff), + ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), @@ -117,6 +121,18 @@ const pool &RTLIL::builtin_ff_cell_types() { ID($_DFFE_PP0P_), ID($_DFFE_PP1N_), ID($_DFFE_PP1P_), + ID($_ALDFF_NN_), + ID($_ALDFF_NP_), + ID($_ALDFF_PN_), + ID($_ALDFF_PP_), + ID($_ALDFFE_NNN_), + ID($_ALDFFE_NNP_), + ID($_ALDFFE_NPN_), + ID($_ALDFFE_NPP_), + ID($_ALDFFE_PNN_), + ID($_ALDFFE_PNP_), + ID($_ALDFFE_PPN_), + ID($_ALDFFE_PPP_), ID($_SDFF_NN0_), ID($_SDFF_NN1_), ID($_SDFF_NP0_), @@ -184,14 +200,10 @@ const pool &RTLIL::builtin_ff_cell_types() { return res; } -RTLIL::Const::Const() -{ - flags = RTLIL::CONST_FLAG_NONE; -} - -RTLIL::Const::Const(std::string str) +RTLIL::Const::Const(const std::string &str) { flags = RTLIL::CONST_FLAG_STRING; + bits.reserve(str.size() * 8); for (int i = str.size()-1; i >= 0; i--) { unsigned char ch = str[i]; for (int j = 0; j < 8; j++) { @@ -204,6 +216,7 @@ RTLIL::Const::Const(std::string str) RTLIL::Const::Const(int val, int width) { flags = RTLIL::CONST_FLAG_NONE; + bits.reserve(width); for (int i = 0; i < width; i++) { bits.push_back((val & 1) != 0 ? State::S1 : State::S0); val = val >> 1; @@ -213,6 +226,7 @@ RTLIL::Const::Const(int val, int width) RTLIL::Const::Const(RTLIL::State bit, int width) { flags = RTLIL::CONST_FLAG_NONE; + bits.reserve(width); for (int i = 0; i < width; i++) bits.push_back(bit); } @@ -220,17 +234,11 @@ RTLIL::Const::Const(RTLIL::State bit, int width) RTLIL::Const::Const(const std::vector &bits) { flags = RTLIL::CONST_FLAG_NONE; + this->bits.reserve(bits.size()); for (const auto &b : bits) this->bits.emplace_back(b ? State::S1 : State::S0); } -RTLIL::Const::Const(const RTLIL::Const &c) -{ - flags = c.flags; - for (const auto &b : c.bits) - this->bits.push_back(b); -} - bool RTLIL::Const::operator <(const RTLIL::Const &other) const { if (bits.size() != other.bits.size()) @@ -305,18 +313,33 @@ RTLIL::Const RTLIL::Const::from_string(const std::string &str) std::string RTLIL::Const::decode_string() const { - std::string string; - string.reserve(GetSize(bits)/8); - for (int i = 0; i < GetSize(bits); i += 8) { + const int n = GetSize(bits); + const int n_over_8 = n / 8; + std::string s; + s.reserve(n_over_8); + int i = n_over_8 * 8; + if (i < n) { + char ch = 0; + for (int j = 0; j < (n - i); j++) { + if (bits[i + j] == RTLIL::State::S1) { + ch |= 1 << j; + } + } + if (ch != 0) + s.append({ch}); + } + i -= 8; + for (; i >= 0; i -= 8) { char ch = 0; - for (int j = 0; j < 8 && i + j < int (bits.size()); j++) - if (bits[i + j] == RTLIL::State::S1) + for (int j = 0; j < 8; j++) { + if (bits[i + j] == RTLIL::State::S1) { ch |= 1 << j; + } + } if (ch != 0) - string.append({ch}); + s.append({ch}); } - std::reverse(string.begin(), string.end()); - return string; + return s; } bool RTLIL::Const::is_fully_zero() const @@ -363,12 +386,43 @@ bool RTLIL::Const::is_fully_undef() const return true; } -bool RTLIL::AttrObject::has_attribute(RTLIL::IdString id) const +bool RTLIL::Const::is_fully_undef_x_only() const +{ + cover("kernel.rtlil.const.is_fully_undef_x_only"); + + for (const auto &bit : bits) + if (bit != RTLIL::State::Sx) + return false; + + return true; +} + +bool RTLIL::Const::is_onehot(int *pos) const +{ + cover("kernel.rtlil.const.is_onehot"); + + bool found = false; + for (int i = 0; i < GetSize(*this); i++) { + auto &bit = bits[i]; + if (bit != RTLIL::State::S0 && bit != RTLIL::State::S1) + return false; + if (bit == RTLIL::State::S1) { + if (found) + return false; + if (pos) + *pos = i; + found = true; + } + } + return found; +} + +bool RTLIL::AttrObject::has_attribute(const RTLIL::IdString &id) const { return attributes.count(id); } -void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) +void RTLIL::AttrObject::set_bool_attribute(const RTLIL::IdString &id, bool value) { if (value) attributes[id] = RTLIL::Const(1); @@ -376,7 +430,7 @@ void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id, bool value) attributes.erase(id); } -bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const +bool RTLIL::AttrObject::get_bool_attribute(const RTLIL::IdString &id) const { const auto it = attributes.find(id); if (it == attributes.end()) @@ -384,7 +438,7 @@ bool RTLIL::AttrObject::get_bool_attribute(RTLIL::IdString id) const return it->second.as_bool(); } -void RTLIL::AttrObject::set_string_attribute(RTLIL::IdString id, string value) +void RTLIL::AttrObject::set_string_attribute(const RTLIL::IdString& id, string value) { if (value.empty()) attributes.erase(id); @@ -392,7 +446,7 @@ void RTLIL::AttrObject::set_string_attribute(RTLIL::IdString id, string value) attributes[id] = value; } -string RTLIL::AttrObject::get_string_attribute(RTLIL::IdString id) const +string RTLIL::AttrObject::get_string_attribute(const RTLIL::IdString &id) const { std::string value; const auto it = attributes.find(id); @@ -401,7 +455,7 @@ string RTLIL::AttrObject::get_string_attribute(RTLIL::IdString id) const return value; } -void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool &data) +void RTLIL::AttrObject::set_strpool_attribute(const RTLIL::IdString& id, const pool &data) { string attrval; for (const auto &s : data) { @@ -412,7 +466,7 @@ void RTLIL::AttrObject::set_strpool_attribute(RTLIL::IdString id, const pool &data) +void RTLIL::AttrObject::add_strpool_attribute(const RTLIL::IdString& id, const pool &data) { pool union_data = get_strpool_attribute(id); union_data.insert(data.begin(), data.end()); @@ -420,7 +474,7 @@ void RTLIL::AttrObject::add_strpool_attribute(RTLIL::IdString id, const pool RTLIL::AttrObject::get_strpool_attribute(RTLIL::IdString id) const +pool RTLIL::AttrObject::get_strpool_attribute(const RTLIL::IdString &id) const { pool data; if (attributes.count(id) != 0) @@ -445,7 +499,36 @@ vector RTLIL::AttrObject::get_hdlname_attribute() const return split_tokens(get_string_attribute(ID::hdlname), " "); } -bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const +void RTLIL::AttrObject::set_intvec_attribute(const RTLIL::IdString& id, const vector &data) +{ + std::stringstream attrval; + for (auto &i : data) { + if (attrval.tellp() > 0) + attrval << " "; + attrval << i; + } + attributes[id] = RTLIL::Const(attrval.str()); +} + +vector RTLIL::AttrObject::get_intvec_attribute(const RTLIL::IdString &id) const +{ + vector data; + auto it = attributes.find(id); + if (it != attributes.end()) + for (const auto &s : split_tokens(attributes.at(id).decode_string())) { + char *end = nullptr; + errno = 0; + long value = strtol(s.c_str(), &end, 10); + if (end != s.c_str() + s.size()) + log_cmd_error("Literal for intvec attribute has invalid format"); + if (errno == ERANGE || value < INT_MIN || value > INT_MAX) + log_cmd_error("Literal for intvec attribute is out of range"); + data.push_back(value); + } + return data; +} + +bool RTLIL::Selection::selected_module(const RTLIL::IdString &mod_name) const { if (full_selection) return true; @@ -456,7 +539,7 @@ bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const return false; } -bool RTLIL::Selection::selected_whole_module(RTLIL::IdString mod_name) const +bool RTLIL::Selection::selected_whole_module(const RTLIL::IdString &mod_name) const { if (full_selection) return true; @@ -465,7 +548,7 @@ bool RTLIL::Selection::selected_whole_module(RTLIL::IdString mod_name) const return false; } -bool RTLIL::Selection::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const +bool RTLIL::Selection::selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const { if (full_selection) return true; @@ -551,8 +634,10 @@ RTLIL::Design::Design() RTLIL::Design::~Design() { - for (auto it = modules_.begin(); it != modules_.end(); ++it) - delete it->second; + for (auto &pr : modules_) + delete pr.second; + for (auto n : bindings_) + delete n; for (auto n : verilog_packages) delete n; for (auto n : verilog_globals) @@ -575,7 +660,12 @@ RTLIL::ObjRange RTLIL::Design::modules() return RTLIL::ObjRange(&modules_, &refcount_modules_); } -RTLIL::Module *RTLIL::Design::module(RTLIL::IdString name) +RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) +{ + return modules_.count(name) ? modules_.at(name) : NULL; +} + +const RTLIL::Module *RTLIL::Design::module(const RTLIL::IdString& name) const { return modules_.count(name) ? modules_.at(name) : NULL; } @@ -611,9 +701,16 @@ void RTLIL::Design::add(RTLIL::Module *module) } } +void RTLIL::Design::add(RTLIL::Binding *binding) +{ + log_assert(binding != nullptr); + bindings_.push_back(binding); +} + RTLIL::Module *RTLIL::Design::addModule(RTLIL::IdString name) { - log_assert(modules_.count(name) == 0); + if (modules_.count(name) != 0) + log_error("Attempted to add new module named '%s', but a module by that name already exists\n", name.c_str()); log_assert(refcount_modules_ == 0); RTLIL::Module *module = new RTLIL::Module; @@ -750,7 +847,7 @@ void RTLIL::Design::optimize() it.second.optimize(this); } -bool RTLIL::Design::selected_module(RTLIL::IdString mod_name) const +bool RTLIL::Design::selected_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -759,7 +856,7 @@ bool RTLIL::Design::selected_module(RTLIL::IdString mod_name) const return selection_stack.back().selected_module(mod_name); } -bool RTLIL::Design::selected_whole_module(RTLIL::IdString mod_name) const +bool RTLIL::Design::selected_whole_module(const RTLIL::IdString& mod_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -768,7 +865,7 @@ bool RTLIL::Design::selected_whole_module(RTLIL::IdString mod_name) const return selection_stack.back().selected_whole_module(mod_name); } -bool RTLIL::Design::selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const +bool RTLIL::Design::selected_member(const RTLIL::IdString& mod_name, const RTLIL::IdString& memb_name) const { if (!selected_active_module.empty() && mod_name != selected_active_module) return false; @@ -807,12 +904,12 @@ std::vector RTLIL::Design::selected_whole_modules() const return result; } -std::vector RTLIL::Design::selected_whole_modules_warn() const +std::vector RTLIL::Design::selected_whole_modules_warn(bool include_wb) const { std::vector result; result.reserve(modules_.size()); for (auto &it : modules_) - if (it.second->get_blackbox_attribute()) + if (it.second->get_blackbox_attribute(include_wb)) continue; else if (selected_whole_module(it.first)) result.push_back(it.second); @@ -838,14 +935,16 @@ RTLIL::Module::Module() RTLIL::Module::~Module() { - for (auto it = wires_.begin(); it != wires_.end(); ++it) - delete it->second; - for (auto it = memories.begin(); it != memories.end(); ++it) - delete it->second; - for (auto it = cells_.begin(); it != cells_.end(); ++it) - delete it->second; - for (auto it = processes.begin(); it != processes.end(); ++it) - delete it->second; + for (auto &pr : wires_) + delete pr.second; + for (auto &pr : memories) + delete pr.second; + for (auto &pr : cells_) + delete pr.second; + for (auto &pr : processes) + delete pr.second; + for (auto binding : bindings_) + delete binding; #ifdef WITH_PYTHON RTLIL::Module::get_all_modules()->erase(hashidx_); #endif @@ -885,9 +984,14 @@ void RTLIL::Module::makeblackbox() set_bool_attribute(ID::blackbox); } -void RTLIL::Module::reprocess_module(RTLIL::Design *, const dict &) +void RTLIL::Module::expand_interfaces(RTLIL::Design *, const dict &) { - log_error("Cannot reprocess_module module `%s' !\n", id2cstr(name)); + log_error("Class doesn't support expand_interfaces (module: `%s')!\n", id2cstr(name)); +} + +bool RTLIL::Module::reprocess_if_necessary(RTLIL::Design *) +{ + return false; } RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dict &, bool mayfail) @@ -905,7 +1009,7 @@ RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, const dictname.c_str(), cell->type.c_str(), __FILE__, linenr, buf.str().c_str()); } - int param(RTLIL::IdString name) + int param(const RTLIL::IdString& name) { auto it = cell->parameters.find(name); if (it == cell->parameters.end()) @@ -939,7 +1043,7 @@ namespace { return it->second.as_int(); } - int param_bool(RTLIL::IdString name) + int param_bool(const RTLIL::IdString& name) { int v = param(name); if (GetSize(cell->parameters.at(name)) > 32) @@ -949,7 +1053,7 @@ namespace { return v; } - int param_bool(RTLIL::IdString name, bool expected) + int param_bool(const RTLIL::IdString& name, bool expected) { int v = param_bool(name); if (v != expected) @@ -957,14 +1061,20 @@ namespace { return v; } - void param_bits(RTLIL::IdString name, int width) + void param_bits(const RTLIL::IdString& name, int width) { param(name); if (GetSize(cell->parameters.at(name).bits) != width) error(__LINE__); } - void port(RTLIL::IdString name, int width) + std::string param_string(const RTLIL::IdString &name) + { + param(name); + return cell->parameters.at(name).decode_string(); + } + + void port(const RTLIL::IdString& name, int width) { auto it = cell->connections_.find(name); if (it == cell->connections_.end()) @@ -1166,6 +1276,22 @@ namespace { return; } + if (cell->type == ID($bmux)) { + port(ID::A, param(ID::WIDTH) << param(ID::S_WIDTH)); + port(ID::S, param(ID::S_WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } + + if (cell->type == ID($demux)) { + port(ID::A, param(ID::WIDTH)); + port(ID::S, param(ID::S_WIDTH)); + port(ID::Y, param(ID::WIDTH) << param(ID::S_WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($lut)) { param(ID::LUT); port(ID::A, param(ID::WIDTH)); @@ -1300,6 +1426,32 @@ namespace { return; } + if (cell->type == ID($aldff)) { + param_bool(ID::CLK_POLARITY); + param_bool(ID::ALOAD_POLARITY); + port(ID::CLK, 1); + port(ID::ALOAD, 1); + port(ID::D, param(ID::WIDTH)); + port(ID::AD, param(ID::WIDTH)); + port(ID::Q, param(ID::WIDTH)); + check_expected(); + return; + } + + if (cell->type == ID($aldffe)) { + param_bool(ID::CLK_POLARITY); + param_bool(ID::EN_POLARITY); + param_bool(ID::ALOAD_POLARITY); + port(ID::CLK, 1); + port(ID::EN, 1); + port(ID::ALOAD, 1); + port(ID::D, param(ID::WIDTH)); + port(ID::AD, param(ID::WIDTH)); + port(ID::Q, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($dlatch)) { param_bool(ID::EN_POLARITY); port(ID::EN, 1); @@ -1366,6 +1518,26 @@ namespace { return; } + if (cell->type == ID($memrd_v2)) { + param(ID::MEMID); + param_bool(ID::CLK_ENABLE); + param_bool(ID::CLK_POLARITY); + param(ID::TRANSPARENCY_MASK); + param(ID::COLLISION_X_MASK); + param_bool(ID::CE_OVER_SRST); + param_bits(ID::ARST_VALUE, param(ID::WIDTH)); + param_bits(ID::SRST_VALUE, param(ID::WIDTH)); + param_bits(ID::INIT_VALUE, param(ID::WIDTH)); + port(ID::CLK, 1); + port(ID::EN, 1); + port(ID::ARST, 1); + port(ID::SRST, 1); + port(ID::ADDR, param(ID::ABITS)); + port(ID::DATA, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($memwr)) { param(ID::MEMID); param_bool(ID::CLK_ENABLE); @@ -1379,6 +1551,20 @@ namespace { return; } + if (cell->type == ID($memwr_v2)) { + param(ID::MEMID); + param_bool(ID::CLK_ENABLE); + param_bool(ID::CLK_POLARITY); + param(ID::PORTID); + param(ID::PRIORITY_MASK); + port(ID::CLK, 1); + port(ID::EN, param(ID::WIDTH)); + port(ID::ADDR, param(ID::ABITS)); + port(ID::DATA, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($meminit)) { param(ID::MEMID); param(ID::PRIORITY); @@ -1388,6 +1574,16 @@ namespace { return; } + if (cell->type == ID($meminit_v2)) { + param(ID::MEMID); + param(ID::PRIORITY); + port(ID::ADDR, param(ID::ABITS)); + port(ID::DATA, param(ID::WIDTH) * param(ID::WORDS)); + port(ID::EN, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($mem)) { param(ID::MEMID); param(ID::SIZE); @@ -1410,6 +1606,38 @@ namespace { return; } + if (cell->type == ID($mem_v2)) { + param(ID::MEMID); + param(ID::SIZE); + param(ID::OFFSET); + param(ID::INIT); + param_bits(ID::RD_CLK_ENABLE, max(1, param(ID::RD_PORTS))); + param_bits(ID::RD_CLK_POLARITY, max(1, param(ID::RD_PORTS))); + param_bits(ID::RD_TRANSPARENCY_MASK, max(1, param(ID::RD_PORTS) * param(ID::WR_PORTS))); + param_bits(ID::RD_COLLISION_X_MASK, max(1, param(ID::RD_PORTS) * param(ID::WR_PORTS))); + param_bits(ID::RD_WIDE_CONTINUATION, max(1, param(ID::RD_PORTS))); + param_bits(ID::RD_CE_OVER_SRST, max(1, param(ID::RD_PORTS))); + param_bits(ID::RD_ARST_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH)); + param_bits(ID::RD_SRST_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH)); + param_bits(ID::RD_INIT_VALUE, param(ID::RD_PORTS) * param(ID::WIDTH)); + param_bits(ID::WR_CLK_ENABLE, max(1, param(ID::WR_PORTS))); + param_bits(ID::WR_CLK_POLARITY, max(1, param(ID::WR_PORTS))); + param_bits(ID::WR_WIDE_CONTINUATION, max(1, param(ID::WR_PORTS))); + param_bits(ID::WR_PRIORITY_MASK, max(1, param(ID::WR_PORTS) * param(ID::WR_PORTS))); + port(ID::RD_CLK, param(ID::RD_PORTS)); + port(ID::RD_EN, param(ID::RD_PORTS)); + port(ID::RD_ARST, param(ID::RD_PORTS)); + port(ID::RD_SRST, param(ID::RD_PORTS)); + port(ID::RD_ADDR, param(ID::RD_PORTS) * param(ID::ABITS)); + port(ID::RD_DATA, param(ID::RD_PORTS) * param(ID::WIDTH)); + port(ID::WR_CLK, param(ID::WR_PORTS)); + port(ID::WR_EN, param(ID::WR_PORTS) * param(ID::WIDTH)); + port(ID::WR_ADDR, param(ID::WR_PORTS) * param(ID::ABITS)); + port(ID::WR_DATA, param(ID::WR_PORTS) * param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($tribuf)) { port(ID::A, param(ID::WIDTH)); port(ID::Y, param(ID::WIDTH)); @@ -1418,6 +1646,23 @@ namespace { return; } + if (cell->type == ID($bweqx)) { + port(ID::A, param(ID::WIDTH)); + port(ID::B, param(ID::WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } + + if (cell->type == ID($bwmux)) { + port(ID::A, param(ID::WIDTH)); + port(ID::B, param(ID::WIDTH)); + port(ID::S, param(ID::WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type.in(ID($assert), ID($assume), ID($live), ID($fair), ID($cover))) { port(ID::A, 1); port(ID::EN, 1); @@ -1437,6 +1682,13 @@ namespace { return; } + if (cell->type.in(ID($anyinit))) { + port(ID::D, param(ID::WIDTH)); + port(ID::Q, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type == ID($equiv)) { port(ID::A, 1); port(ID::B, 1); @@ -1489,6 +1741,43 @@ namespace { return; } + if (cell->type == ID($print)) { + param(ID(FORMAT)); + param_bool(ID::TRG_ENABLE); + param(ID::TRG_POLARITY); + param(ID::PRIORITY); + port(ID::EN, 1); + port(ID::TRG, param(ID::TRG_WIDTH)); + port(ID::ARGS, param(ID::ARGS_WIDTH)); + check_expected(); + return; + } + + if (cell->type == ID($check)) { + std::string flavor = param_string(ID(FLAVOR)); + if (!(flavor == "assert" || flavor == "assume" || flavor == "live" || flavor == "fair" || flavor == "cover")) + error(__LINE__); + param(ID(FORMAT)); + param_bool(ID::TRG_ENABLE); + param(ID::TRG_POLARITY); + param(ID::PRIORITY); + port(ID::A, 1); + port(ID::EN, 1); + port(ID::TRG, param(ID::TRG_WIDTH)); + port(ID::ARGS, param(ID::ARGS_WIDTH)); + check_expected(); + return; + } + + if (cell->type == ID($scopeinfo)) { + param(ID::TYPE); + check_expected(); + std::string scope_type = cell->getParam(ID::TYPE).decode_string(); + if (scope_type != "module" && scope_type != "struct") + error(__LINE__); + return; + } + if (cell->type == ID($_BUF_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_NOT_)) { port(ID::A,1); port(ID::Y,1); check_expected(); return; } if (cell->type == ID($_AND_)) { port(ID::A,1); port(ID::B,1); port(ID::Y,1); check_expected(); return; } @@ -1535,6 +1824,15 @@ namespace { ID($_DFFE_PP0N_), ID($_DFFE_PP0P_), ID($_DFFE_PP1N_), ID($_DFFE_PP1P_))) { port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::R,1); port(ID::E,1); check_expected(); return; } + if (cell->type.in( + ID($_ALDFF_NN_), ID($_ALDFF_NP_), ID($_ALDFF_PN_), ID($_ALDFF_PP_))) + { port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::L,1); port(ID::AD,1); check_expected(); return; } + + if (cell->type.in( + ID($_ALDFFE_NNN_), ID($_ALDFFE_NNP_), ID($_ALDFFE_NPN_), ID($_ALDFFE_NPP_), + ID($_ALDFFE_PNN_), ID($_ALDFFE_PNP_), ID($_ALDFFE_PPN_), ID($_ALDFFE_PPP_))) + { port(ID::D,1); port(ID::Q,1); port(ID::C,1); port(ID::L,1); port(ID::AD,1); port(ID::E,1); check_expected(); return; } + if (cell->type.in( ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) @@ -1576,6 +1874,40 @@ namespace { ID($_DLATCHSR_PNN_), ID($_DLATCHSR_PNP_), ID($_DLATCHSR_PPN_), ID($_DLATCHSR_PPP_))) { port(ID::E,1); port(ID::S,1); port(ID::R,1); port(ID::D,1); port(ID::Q,1); check_expected(); return; } + if (cell->type.in(ID($set_tag))) { + param(ID::WIDTH); + param(ID::TAG); + port(ID::A, param(ID::WIDTH)); + port(ID::SET, param(ID::WIDTH)); + port(ID::CLR, param(ID::WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type.in(ID($get_tag),ID($original_tag))) { + param(ID::WIDTH); + param(ID::TAG); + port(ID::A, param(ID::WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type.in(ID($overwrite_tag))) { + param(ID::WIDTH); + param(ID::TAG); + port(ID::A, param(ID::WIDTH)); + port(ID::SET, param(ID::WIDTH)); + port(ID::CLR, param(ID::WIDTH)); + check_expected(); + return; + } + if (cell->type.in(ID($future_ff))) { + param(ID::WIDTH); + port(ID::A, param(ID::WIDTH)); + port(ID::Y, param(ID::WIDTH)); + check_expected(); + return; + } error(__LINE__); } }; @@ -1633,6 +1965,8 @@ void RTLIL::Module::check() log_assert(!it2.first.empty()); } + pool packed_memids; + for (auto &it : cells_) { log_assert(this == it.second->module); log_assert(it.first == it.second->name); @@ -1640,7 +1974,7 @@ void RTLIL::Module::check() log_assert(!it.second->type.empty()); for (auto &it2 : it.second->connections()) { log_assert(!it2.first.empty()); - it2.second.check(); + it2.second.check(this); } for (auto &it2 : it.second->attributes) log_assert(!it2.first.empty()); @@ -1648,6 +1982,14 @@ void RTLIL::Module::check() log_assert(!it2.first.empty()); InternalCellChecker checker(this, it.second); checker.check(); + if (it.second->has_memid()) { + log_assert(memories.count(it.second->parameters.at(ID::MEMID).decode_string())); + } else if (it.second->is_mem_cell()) { + IdString memid = it.second->parameters.at(ID::MEMID).decode_string(); + log_assert(!memories.count(memid)); + log_assert(!packed_memids.count(memid)); + packed_memids.insert(memid); + } } for (auto &it : processes) { @@ -1686,8 +2028,8 @@ void RTLIL::Module::check() for (auto &it : connections_) { log_assert(it.first.size() == it.second.size()); log_assert(!it.first.has_const()); - it.first.check(); - it.second.check(); + it.first.check(this); + it.second.check(this); } for (auto &it : attributes) @@ -1813,6 +2155,20 @@ void RTLIL::Module::add(RTLIL::Cell *cell) cell->module = this; } +void RTLIL::Module::add(RTLIL::Process *process) +{ + log_assert(!process->name.empty()); + log_assert(count_id(process->name) == 0); + processes[process->name] = process; + process->module = this; +} + +void RTLIL::Module::add(RTLIL::Binding *binding) +{ + log_assert(binding != nullptr); + bindings_.push_back(binding); +} + void RTLIL::Module::remove(const pool &wires) { log_assert(refcount_wires_ == 0); @@ -1826,23 +2182,16 @@ void RTLIL::Module::remove(const pool &wires) sig.pack(); for (auto &c : sig.chunks_) if (c.wire != NULL && wires_p->count(c.wire)) { - c.wire = module->addWire(NEW_ID, c.width); + c.wire = module->addWire(stringf("$delete_wire$%d", autoidx++), c.width); c.offset = 0; } } void operator()(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) { - log_assert(GetSize(lhs) == GetSize(rhs)); - lhs.unpack(); - rhs.unpack(); - for (int i = 0; i < GetSize(lhs); i++) { - RTLIL::SigBit &lhs_bit = lhs.bits_[i]; - RTLIL::SigBit &rhs_bit = rhs.bits_[i]; - if ((lhs_bit.wire != nullptr && wires_p->count(lhs_bit.wire)) || (rhs_bit.wire != nullptr && wires_p->count(rhs_bit.wire))) { - lhs_bit = State::Sx; - rhs_bit = State::Sx; - } - } + // If a deleted wire occurs on the lhs or rhs we just remove that part + // of the assignment + lhs.remove2(*wires_p, &rhs); + rhs.remove2(*wires_p, &lhs); } }; @@ -1869,6 +2218,13 @@ void RTLIL::Module::remove(RTLIL::Cell *cell) delete cell; } +void RTLIL::Module::remove(RTLIL::Process *process) +{ + log_assert(processes.count(process->name) != 0); + processes.erase(process->name); + delete process; +} + void RTLIL::Module::rename(RTLIL::Wire *wire, RTLIL::IdString new_name) { log_assert(wires_[wire->name] == wire); @@ -2094,11 +2450,19 @@ RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name, const RTLIL::Memor return mem; } +RTLIL::Process *RTLIL::Module::addProcess(RTLIL::IdString name) +{ + RTLIL::Process *proc = new RTLIL::Process; + proc->name = name; + add(proc); + return proc; +} + RTLIL::Process *RTLIL::Module::addProcess(RTLIL::IdString name, const RTLIL::Process *other) { RTLIL::Process *proc = other->clone(); proc->name = name; - processes[name] = proc; + add(proc); return proc; } @@ -2216,9 +2580,48 @@ DEF_METHOD(Sshr, sig_a.size(), ID($sshr)) return sig_y; \ } DEF_METHOD(Mux, ID($mux), 0) +DEF_METHOD(Bwmux, ID($bwmux), 0) DEF_METHOD(Pmux, ID($pmux), 1) #undef DEF_METHOD +#define DEF_METHOD(_func, _type, _demux) \ + RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src) { \ + RTLIL::Cell *cell = addCell(name, _type); \ + cell->parameters[ID::WIDTH] = _demux ? sig_a.size() : sig_y.size(); \ + cell->parameters[ID::S_WIDTH] = sig_s.size(); \ + cell->setPort(ID::A, sig_a); \ + cell->setPort(ID::S, sig_s); \ + cell->setPort(ID::Y, sig_y); \ + cell->set_src_attribute(src); \ + return cell; \ + } \ + RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src) { \ + RTLIL::SigSpec sig_y = addWire(NEW_ID, _demux ? sig_a.size() << sig_s.size() : sig_a.size() >> sig_s.size()); \ + add ## _func(name, sig_a, sig_s, sig_y, src); \ + return sig_y; \ + } +DEF_METHOD(Bmux, ID($bmux), 0) +DEF_METHOD(Demux, ID($demux), 1) +#undef DEF_METHOD + +#define DEF_METHOD(_func, _type) \ + RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, const std::string &src) { \ + RTLIL::Cell *cell = addCell(name, _type); \ + cell->parameters[ID::WIDTH] = sig_a.size(); \ + cell->setPort(ID::A, sig_a); \ + cell->setPort(ID::B, sig_b); \ + cell->setPort(ID::Y, sig_y); \ + cell->set_src_attribute(src); \ + return cell; \ + } \ + RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src) { \ + RTLIL::SigSpec sig_y = addWire(NEW_ID, sig_a.size()); \ + add ## _func(name, sig_a, sig_s, sig_y, src); \ + return sig_y; \ + } +DEF_METHOD(Bweqx, ID($bweqx)) +#undef DEF_METHOD + #define DEF_METHOD_2(_func, _type, _P1, _P2) \ RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigBit &sig1, const RTLIL::SigBit &sig2, const std::string &src) { \ RTLIL::Cell *cell = addCell(name, _type); \ @@ -2313,6 +2716,19 @@ RTLIL::Cell* RTLIL::Module::addPow(RTLIL::IdString name, const RTLIL::SigSpec &s return cell; } +RTLIL::Cell* RTLIL::Module::addFa(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_c, const RTLIL::SigSpec &sig_x, const RTLIL::SigSpec &sig_y, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($fa)); + cell->parameters[ID::WIDTH] = sig_a.size(); + cell->setPort(ID::A, sig_a); + cell->setPort(ID::B, sig_b); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::X, sig_x); + cell->setPort(ID::Y, sig_y); + cell->set_src_attribute(src); + return cell; +} + RTLIL::Cell* RTLIL::Module::addSlice(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, RTLIL::Const offset, const std::string &src) { RTLIL::Cell *cell = addCell(name, ID($slice)); @@ -2533,6 +2949,40 @@ RTLIL::Cell* RTLIL::Module::addAdffe(RTLIL::IdString name, const RTLIL::SigSpec return cell; } +RTLIL::Cell* RTLIL::Module::addAldff(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool aload_polarity, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($aldff)); + cell->parameters[ID::CLK_POLARITY] = clk_polarity; + cell->parameters[ID::ALOAD_POLARITY] = aload_polarity; + cell->parameters[ID::WIDTH] = sig_q.size(); + cell->setPort(ID::CLK, sig_clk); + cell->setPort(ID::ALOAD, sig_aload); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::AD, sig_ad); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addAldffe(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool en_polarity, bool aload_polarity, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($aldffe)); + cell->parameters[ID::CLK_POLARITY] = clk_polarity; + cell->parameters[ID::EN_POLARITY] = en_polarity; + cell->parameters[ID::ALOAD_POLARITY] = aload_polarity; + cell->parameters[ID::WIDTH] = sig_q.size(); + cell->setPort(ID::CLK, sig_clk); + cell->setPort(ID::EN, sig_en); + cell->setPort(ID::ALOAD, sig_aload); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::AD, sig_ad); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + RTLIL::Cell* RTLIL::Module::addSdff(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity, bool srst_polarity, const std::string &src) { @@ -2723,6 +3173,33 @@ RTLIL::Cell* RTLIL::Module::addAdffeGate(RTLIL::IdString name, const RTLIL::SigS return cell; } +RTLIL::Cell* RTLIL::Module::addAldffGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool aload_polarity, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, stringf("$_ALDFF_%c%c_", clk_polarity ? 'P' : 'N', aload_polarity ? 'P' : 'N')); + cell->setPort(ID::C, sig_clk); + cell->setPort(ID::L, sig_aload); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::AD, sig_ad); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addAldffeGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity, bool en_polarity, bool aload_polarity, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, stringf("$_ALDFFE_%c%c%c_", clk_polarity ? 'P' : 'N', aload_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort(ID::C, sig_clk); + cell->setPort(ID::L, sig_aload); + cell->setPort(ID::E, sig_en); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::AD, sig_ad); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + RTLIL::Cell* RTLIL::Module::addSdffGate(RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool srst_value, bool clk_polarity, bool srst_polarity, const std::string &src) { @@ -2796,6 +3273,16 @@ RTLIL::Cell* RTLIL::Module::addDlatchsrGate(RTLIL::IdString name, const RTLIL::S return cell; } +RTLIL::Cell* RTLIL::Module::addAnyinit(RTLIL::IdString name, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($anyinit)); + cell->parameters[ID::WIDTH] = sig_q.size(); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->set_src_attribute(src); + return cell; +} + RTLIL::SigSpec RTLIL::Module::Anyconst(RTLIL::IdString name, int width, const std::string &src) { RTLIL::SigSpec sig = addWire(NEW_ID, width); @@ -2845,6 +3332,80 @@ RTLIL::SigSpec RTLIL::Module::Initstate(RTLIL::IdString name, const std::string return sig; } +RTLIL::SigSpec RTLIL::Module::SetTag(RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_c, const std::string &src) +{ + RTLIL::SigSpec sig = addWire(NEW_ID, sig_a.size()); + Cell *cell = addCell(name, ID($set_tag)); + cell->parameters[ID::WIDTH] = sig_a.size(); + cell->parameters[ID::TAG] = tag; + cell->setPort(ID::A, sig_a); + cell->setPort(ID::SET, sig_s); + cell->setPort(ID::CLR, sig_c); + cell->setPort(ID::Y, sig); + cell->set_src_attribute(src); + return sig; +} + +RTLIL::Cell* RTLIL::Module::addSetTag(RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_c, const RTLIL::SigSpec &sig_y, const std::string &src) +{ + Cell *cell = addCell(name, ID($set_tag)); + cell->parameters[ID::WIDTH] = sig_a.size(); + cell->parameters[ID::TAG] = tag; + cell->setPort(ID::A, sig_a); + cell->setPort(ID::SET, sig_s); + cell->setPort(ID::CLR, sig_c); + cell->setPort(ID::Y, sig_y); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::SigSpec RTLIL::Module::GetTag(RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const std::string &src) +{ + RTLIL::SigSpec sig = addWire(NEW_ID, sig_a.size()); + Cell *cell = addCell(name, ID($get_tag)); + cell->parameters[ID::WIDTH] = sig_a.size(); + cell->parameters[ID::TAG] = tag; + cell->setPort(ID::A, sig_a); + cell->setPort(ID::Y, sig); + cell->set_src_attribute(src); + return sig; +} + +RTLIL::Cell* RTLIL::Module::addOverwriteTag(RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_c, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, ID($overwrite_tag)); + cell->parameters[ID::WIDTH] = sig_a.size(); + cell->parameters[ID::TAG] = tag; + cell->setPort(ID::A, sig_a); + cell->setPort(ID::SET, sig_s); + cell->setPort(ID::CLR, sig_c); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::SigSpec RTLIL::Module::OriginalTag(RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const std::string &src) +{ + RTLIL::SigSpec sig = addWire(NEW_ID, sig_a.size()); + Cell *cell = addCell(name, ID($original_tag)); + cell->parameters[ID::WIDTH] = sig_a.size(); + cell->parameters[ID::TAG] = tag; + cell->setPort(ID::A, sig_a); + cell->setPort(ID::Y, sig); + cell->set_src_attribute(src); + return sig; +} + +RTLIL::SigSpec RTLIL::Module::FutureFF(RTLIL::IdString name, const RTLIL::SigSpec &sig_e, const std::string &src) +{ + RTLIL::SigSpec sig = addWire(NEW_ID, sig_e.size()); + Cell *cell = addCell(name, ID($future_ff)); + cell->parameters[ID::WIDTH] = sig_e.size(); + cell->setPort(ID::A, sig_e); + cell->setPort(ID::Y, sig); + cell->set_src_attribute(src); + return sig; +} + RTLIL::Wire::Wire() { static unsigned int hashidx_count = 123456789; @@ -2894,6 +3455,13 @@ RTLIL::Memory::Memory() #endif } +RTLIL::Process::Process() : module(nullptr) +{ + static unsigned int hashidx_count = 123456789; + hashidx_count = mkhash_xorshift(hashidx_count); + hashidx_ = hashidx_count; +} + RTLIL::Cell::Cell() : module(nullptr) { static unsigned int hashidx_count = 123456789; @@ -2923,12 +3491,12 @@ std::map *RTLIL::Cell::get_all_cells(void) } #endif -bool RTLIL::Cell::hasPort(RTLIL::IdString portname) const +bool RTLIL::Cell::hasPort(const RTLIL::IdString& portname) const { return connections_.count(portname) != 0; } -void RTLIL::Cell::unsetPort(RTLIL::IdString portname) +void RTLIL::Cell::unsetPort(const RTLIL::IdString& portname) { RTLIL::SigSpec signal; auto conn_it = connections_.find(portname); @@ -2951,7 +3519,7 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname) } } -void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) +void RTLIL::Cell::setPort(const RTLIL::IdString& portname, RTLIL::SigSpec signal) { auto r = connections_.insert(portname); auto conn_it = r.first; @@ -2973,7 +3541,7 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) conn_it->second = std::move(signal); } -const RTLIL::SigSpec &RTLIL::Cell::getPort(RTLIL::IdString portname) const +const RTLIL::SigSpec &RTLIL::Cell::getPort(const RTLIL::IdString& portname) const { return connections_.at(portname); } @@ -2992,7 +3560,7 @@ bool RTLIL::Cell::known() const return false; } -bool RTLIL::Cell::input(RTLIL::IdString portname) const +bool RTLIL::Cell::input(const RTLIL::IdString& portname) const { if (yosys_celltypes.cell_known(type)) return yosys_celltypes.cell_input(type, portname); @@ -3004,7 +3572,7 @@ bool RTLIL::Cell::input(RTLIL::IdString portname) const return false; } -bool RTLIL::Cell::output(RTLIL::IdString portname) const +bool RTLIL::Cell::output(const RTLIL::IdString& portname) const { if (yosys_celltypes.cell_known(type)) return yosys_celltypes.cell_output(type, portname); @@ -3016,22 +3584,22 @@ bool RTLIL::Cell::output(RTLIL::IdString portname) const return false; } -bool RTLIL::Cell::hasParam(RTLIL::IdString paramname) const +bool RTLIL::Cell::hasParam(const RTLIL::IdString& paramname) const { return parameters.count(paramname) != 0; } -void RTLIL::Cell::unsetParam(RTLIL::IdString paramname) +void RTLIL::Cell::unsetParam(const RTLIL::IdString& paramname) { parameters.erase(paramname); } -void RTLIL::Cell::setParam(RTLIL::IdString paramname, RTLIL::Const value) +void RTLIL::Cell::setParam(const RTLIL::IdString& paramname, RTLIL::Const value) { parameters[paramname] = std::move(value); } -const RTLIL::Const &RTLIL::Cell::getParam(RTLIL::IdString paramname) const +const RTLIL::Const &RTLIL::Cell::getParam(const RTLIL::IdString& paramname) const { const auto &it = parameters.find(paramname); if (it != parameters.end()) @@ -3065,14 +3633,21 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) type.begins_with("$verific$") || type.begins_with("$array:") || type.begins_with("$extern:")) return; - if (type == ID($mux) || type == ID($pmux)) { + if (type == ID($mux) || type == ID($pmux) || type == ID($bmux)) { parameters[ID::WIDTH] = GetSize(connections_[ID::Y]); - if (type == ID($pmux)) + if (type != ID($mux)) parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]); check(); return; } + if (type == ID($demux)) { + parameters[ID::WIDTH] = GetSize(connections_[ID::A]); + parameters[ID::S_WIDTH] = GetSize(connections_[ID::S]); + check(); + return; + } + if (type == ID($lut) || type == ID($sop)) { parameters[ID::WIDTH] = GetSize(connections_[ID::A]); return; @@ -3119,59 +3694,14 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) check(); } -RTLIL::SigChunk::SigChunk() -{ - wire = NULL; - width = 0; - offset = 0; -} - -RTLIL::SigChunk::SigChunk(const RTLIL::Const &value) -{ - wire = NULL; - data = value.bits; - width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire) +bool RTLIL::Cell::has_memid() const { - log_assert(wire != nullptr); - this->wire = wire; - this->width = wire->width; - this->offset = 0; + return type.in(ID($memwr), ID($memwr_v2), ID($memrd), ID($memrd_v2), ID($meminit), ID($meminit_v2)); } -RTLIL::SigChunk::SigChunk(RTLIL::Wire *wire, int offset, int width) +bool RTLIL::Cell::is_mem_cell() const { - log_assert(wire != nullptr); - this->wire = wire; - this->width = width; - this->offset = offset; -} - -RTLIL::SigChunk::SigChunk(const std::string &str) -{ - wire = NULL; - data = RTLIL::Const(str).bits; - width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(int val, int width) -{ - wire = NULL; - data = RTLIL::Const(val, width).bits; - this->width = GetSize(data); - offset = 0; -} - -RTLIL::SigChunk::SigChunk(RTLIL::State bit, int width) -{ - wire = NULL; - data = RTLIL::Const(bit, width).bits; - this->width = GetSize(data); - offset = 0; + return type.in(ID($mem), ID($mem_v2)) || has_memid(); } RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit) @@ -3185,13 +3715,11 @@ RTLIL::SigChunk::SigChunk(const RTLIL::SigBit &bit) width = 1; } -RTLIL::SigChunk::SigChunk(const RTLIL::SigChunk &sigchunk) -{ - *this = sigchunk; -} - RTLIL::SigChunk RTLIL::SigChunk::extract(int offset, int length) const { + log_assert(offset >= 0); + log_assert(length >= 0); + log_assert(offset + length <= width); RTLIL::SigChunk ret; if (wire) { ret.wire = wire; @@ -3235,17 +3763,6 @@ bool RTLIL::SigChunk::operator !=(const RTLIL::SigChunk &other) const return true; } -RTLIL::SigSpec::SigSpec() -{ - width_ = 0; - hash_ = 0; -} - -RTLIL::SigSpec::SigSpec(const RTLIL::SigSpec &other) -{ - *this = other; -} - RTLIL::SigSpec::SigSpec(std::initializer_list parts) { cover("kernel.rtlil.sigspec.init.list"); @@ -3260,23 +3777,30 @@ RTLIL::SigSpec::SigSpec(std::initializer_list parts) append(*it--); } -RTLIL::SigSpec &RTLIL::SigSpec::operator=(const RTLIL::SigSpec &other) +RTLIL::SigSpec::SigSpec(const RTLIL::Const &value) { - cover("kernel.rtlil.sigspec.assign"); + cover("kernel.rtlil.sigspec.init.const"); - width_ = other.width_; - hash_ = other.hash_; - chunks_ = other.chunks_; - bits_ = other.bits_; - return *this; + if (GetSize(value) != 0) { + chunks_.emplace_back(value); + width_ = chunks_.back().width; + } else { + width_ = 0; + } + hash_ = 0; + check(); } -RTLIL::SigSpec::SigSpec(const RTLIL::Const &value) +RTLIL::SigSpec::SigSpec(RTLIL::Const &&value) { - cover("kernel.rtlil.sigspec.init.const"); + cover("kernel.rtlil.sigspec.init.const.move"); - chunks_.emplace_back(value); - width_ = chunks_.back().width; + if (GetSize(value) != 0) { + chunks_.emplace_back(std::move(value)); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3285,8 +3809,26 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigChunk &chunk) { cover("kernel.rtlil.sigspec.init.chunk"); - chunks_.emplace_back(chunk); - width_ = chunks_.back().width; + if (chunk.width != 0) { + chunks_.emplace_back(chunk); + width_ = chunks_.back().width; + } else { + width_ = 0; + } + hash_ = 0; + check(); +} + +RTLIL::SigSpec::SigSpec(RTLIL::SigChunk &&chunk) +{ + cover("kernel.rtlil.sigspec.init.chunk.move"); + + if (chunk.width != 0) { + chunks_.emplace_back(std::move(chunk)); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3295,8 +3837,12 @@ RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire) { cover("kernel.rtlil.sigspec.init.wire"); - chunks_.emplace_back(wire); - width_ = chunks_.back().width; + if (wire->width != 0) { + chunks_.emplace_back(wire); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3305,8 +3851,12 @@ RTLIL::SigSpec::SigSpec(RTLIL::Wire *wire, int offset, int width) { cover("kernel.rtlil.sigspec.init.wire_part"); - chunks_.emplace_back(wire, offset, width); - width_ = chunks_.back().width; + if (width != 0) { + chunks_.emplace_back(wire, offset, width); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3315,8 +3865,12 @@ RTLIL::SigSpec::SigSpec(const std::string &str) { cover("kernel.rtlil.sigspec.init.str"); - chunks_.emplace_back(str); - width_ = chunks_.back().width; + if (str.size() != 0) { + chunks_.emplace_back(str); + width_ = chunks_.back().width; + } else { + width_ = 0; + } hash_ = 0; check(); } @@ -3325,7 +3879,8 @@ RTLIL::SigSpec::SigSpec(int val, int width) { cover("kernel.rtlil.sigspec.init.int"); - chunks_.emplace_back(val, width); + if (width != 0) + chunks_.emplace_back(val, width); width_ = width; hash_ = 0; check(); @@ -3335,7 +3890,8 @@ RTLIL::SigSpec::SigSpec(RTLIL::State bit, int width) { cover("kernel.rtlil.sigspec.init.state"); - chunks_.emplace_back(bit, width); + if (width != 0) + chunks_.emplace_back(bit, width); width_ = width; hash_ = 0; check(); @@ -3345,11 +3901,13 @@ RTLIL::SigSpec::SigSpec(const RTLIL::SigBit &bit, int width) { cover("kernel.rtlil.sigspec.init.bit"); - if (bit.wire == NULL) - chunks_.emplace_back(bit.data, width); - else - for (int i = 0; i < width; i++) - chunks_.push_back(bit); + if (width != 0) { + if (bit.wire == NULL) + chunks_.emplace_back(bit.data, width); + else + for (int i = 0; i < width; i++) + chunks_.push_back(bit); + } width_ = width; hash_ = 0; check(); @@ -3528,13 +4086,17 @@ void RTLIL::SigSpec::replace(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec unpack(); other->unpack(); + dict pattern_to_with; for (int i = 0; i < GetSize(pattern.bits_); i++) { if (pattern.bits_[i].wire != NULL) { - for (int j = 0; j < GetSize(bits_); j++) { - if (bits_[j] == pattern.bits_[i]) { - other->bits_[j] = with.bits_[i]; - } - } + pattern_to_with.emplace(pattern.bits_[i], i); + } + } + + for (int j = 0; j < GetSize(bits_); j++) { + auto it = pattern_to_with.find(bits_[j]); + if (it != pattern_to_with.end()) { + other->bits_[j] = with.bits_[it->second]; } } @@ -3703,6 +4265,34 @@ void RTLIL::SigSpec::remove2(const std::set &pattern, RTLIL::SigS check(); } +void RTLIL::SigSpec::remove2(const pool &pattern, RTLIL::SigSpec *other) +{ + if (other) + cover("kernel.rtlil.sigspec.remove_other"); + else + cover("kernel.rtlil.sigspec.remove"); + + unpack(); + + if (other != NULL) { + log_assert(width_ == other->width_); + other->unpack(); + } + + for (int i = GetSize(bits_) - 1; i >= 0; i--) { + if (bits_[i].wire != NULL && pattern.count(bits_[i].wire)) { + bits_.erase(bits_.begin() + i); + width_--; + if (other != NULL) { + other->bits_.erase(other->bits_.begin() + i); + other->width_--; + } + } + } + + check(); +} + RTLIL::SigSpec RTLIL::SigSpec::extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other) const { if (other) @@ -3794,7 +4384,13 @@ void RTLIL::SigSpec::remove_const() width_ = 0; for (auto &chunk : chunks_) if (chunk.wire != NULL) { - new_chunks.push_back(chunk); + if (!new_chunks.empty() && + new_chunks.back().wire == chunk.wire && + new_chunks.back().offset + new_chunks.back().width == chunk.offset) { + new_chunks.back().width += chunk.width; + } else { + new_chunks.push_back(chunk); + } width_ += chunk.width; } @@ -3836,6 +4432,9 @@ void RTLIL::SigSpec::remove(int offset, int length) RTLIL::SigSpec RTLIL::SigSpec::extract(int offset, int length) const { + log_assert(offset >= 0); + log_assert(length >= 0); + log_assert(offset + length <= width_); unpack(); cover("kernel.rtlil.sigspec.extract_pos"); return std::vector(bits_.begin() + offset, bits_.begin() + offset + length); @@ -3941,7 +4540,7 @@ RTLIL::SigSpec RTLIL::SigSpec::repeat(int num) const } #ifndef NDEBUG -void RTLIL::SigSpec::check() const +void RTLIL::SigSpec::check(Module *mod) const { if (width_ > 64) { @@ -3954,6 +4553,7 @@ void RTLIL::SigSpec::check() const int w = 0; for (size_t i = 0; i < chunks_.size(); i++) { const RTLIL::SigChunk &chunk = chunks_[i]; + log_assert(chunk.width != 0); if (chunk.wire == NULL) { if (i > 0) log_assert(chunks_[i-1].wire != NULL); @@ -3966,6 +4566,8 @@ void RTLIL::SigSpec::check() const log_assert(chunk.width >= 0); log_assert(chunk.offset + chunk.width <= chunk.wire->width); log_assert(chunk.data.size() == 0); + if (mod != nullptr) + log_assert(chunk.wire->module == mod); } w += chunk.width; } @@ -3976,6 +4578,12 @@ void RTLIL::SigSpec::check() const { cover("kernel.rtlil.sigspec.check.unpacked"); + if (mod != nullptr) { + for (size_t i = 0; i < bits_.size(); i++) + if (bits_[i].wire != nullptr) + log_assert(bits_[i].wire->module == mod); + } + log_assert(width_ == GetSize(bits_)); log_assert(chunks_.empty()); } @@ -4164,6 +4772,19 @@ bool RTLIL::SigSpec::has_marked_bits() const return false; } +bool RTLIL::SigSpec::is_onehot(int *pos) const +{ + cover("kernel.rtlil.sigspec.is_onehot"); + + pack(); + if (!is_fully_const()) + return false; + log_assert(GetSize(chunks_) <= 1); + if (width_) + return RTLIL::Const(chunks_[0].data).is_onehot(pos); + return false; +} + bool RTLIL::SigSpec::as_bool() const { cover("kernel.rtlil.sigspec.as_bool"); @@ -4537,6 +5158,7 @@ RTLIL::SyncRule *RTLIL::SyncRule::clone() const new_syncrule->type = type; new_syncrule->signal = signal; new_syncrule->actions = actions; + new_syncrule->mem_write_actions = mem_write_actions; return new_syncrule; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index a03e8933c59..40422dce56d 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -69,8 +69,10 @@ namespace RTLIL struct SigSpec; struct CaseRule; struct SwitchRule; + struct MemWriteAction; struct SyncRule; struct Process; + struct Binding; typedef std::pair SigSig; @@ -83,10 +85,10 @@ namespace RTLIL // the global id string cache + static bool destruct_guard_ok; // POD, will be initialized to zero static struct destruct_guard_t { - bool ok; // POD, will be initialized to zero - destruct_guard_t() { ok = true; } - ~destruct_guard_t() { ok = false; } + destruct_guard_t() { destruct_guard_ok = true; } + ~destruct_guard_t() { destruct_guard_ok = false; } } destruct_guard; static std::vector global_id_storage_; @@ -145,7 +147,7 @@ namespace RTLIL static int get_reference(const char *p) { - log_assert(destruct_guard.ok); + log_assert(destruct_guard_ok); if (!p[0]) return 0; @@ -165,7 +167,8 @@ namespace RTLIL log_assert(p[0] == '$' || p[0] == '\\'); log_assert(p[1] != 0); for (const char *c = p; *c; c++) - log_assert((unsigned)*c > (unsigned)' '); + if ((unsigned)*c <= (unsigned)' ') + log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p); #ifndef YOSYS_NO_IDS_REFCNT if (global_free_idx_list_.empty()) { @@ -222,7 +225,7 @@ namespace RTLIL { // put_reference() may be called from destructors after the destructor of // global_refcount_storage_ has been run. in this case we simply do nothing. - if (!destruct_guard.ok || !idx) + if (!destruct_guard_ok || !idx) return; #ifdef YOSYS_XTRACE_GET_PUT @@ -305,10 +308,14 @@ namespace RTLIL bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; } char operator[](size_t i) const { - const char *p = c_str(); + const char *p = c_str(); +#ifndef NDEBUG for (; i != 0; i--, p++) log_assert(*p != 0); return *p; +#else + return *(p + i); +#endif } std::string substr(size_t pos = 0, size_t len = std::string::npos) const { @@ -334,6 +341,10 @@ namespace RTLIL return compare(size()-len, len, suffix) == 0; } + bool contains(const char* str) const { + return strstr(c_str(), str); + } + size_t size() const { return strlen(c_str()); } @@ -376,7 +387,7 @@ namespace RTLIL bool in(const std::string &rhs) const { return *this == rhs; } bool in(const pool &rhs) const { return rhs.count(*this) != 0; } - bool isPublic() { return begins_with("\\"); } + bool isPublic() const { return begins_with("\\"); } }; namespace ID { @@ -407,11 +418,11 @@ namespace RTLIL return str.substr(1); } - static inline std::string unescape_id(RTLIL::IdString str) { + static inline std::string unescape_id(const RTLIL::IdString &str) { return unescape_id(str.str()); } - static inline const char *id2cstr(RTLIL::IdString str) { + static inline const char *id2cstr(const RTLIL::IdString &str) { return log_id(str); } @@ -428,11 +439,26 @@ namespace RTLIL }; struct sort_by_id_str { - bool operator()(RTLIL::IdString a, RTLIL::IdString b) const { + bool operator()(const RTLIL::IdString &a, const RTLIL::IdString &b) const { return strcmp(a.c_str(), b.c_str()) < 0; } }; + static inline std::string encode_filename(const std::string &filename) + { + std::stringstream val; + if (!std::any_of(filename.begin(), filename.end(), [](char c) { + return static_cast(c) < 33 || static_cast(c) > 126; + })) return filename; + for (unsigned char const c : filename) { + if (c < 33 || c > 126) + val << stringf("$%02x", c); + else + val << c; + } + return val.str(); + } + // see calc.cc for the implementation of this functions RTLIL::Const const_not (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); RTLIL::Const const_and (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); @@ -478,6 +504,14 @@ namespace RTLIL RTLIL::Const const_pos (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); RTLIL::Const const_neg (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len); + RTLIL::Const const_mux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); + RTLIL::Const const_pmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); + RTLIL::Const const_bmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); + RTLIL::Const const_demux (const RTLIL::Const &arg1, const RTLIL::Const &arg2); + + RTLIL::Const const_bweqx (const RTLIL::Const &arg1, const RTLIL::Const &arg2); + RTLIL::Const const_bwmux (const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3); + // This iterator-range-pair is used for Design::modules(), Module::wires() and Module::cells(). // It maintains a reference counter that is used to make sure that the container is not modified while being iterated over. @@ -625,13 +659,13 @@ struct RTLIL::Const int flags; std::vector bits; - Const(); - Const(std::string str); + Const() : flags(RTLIL::CONST_FLAG_NONE) {} + Const(const std::string &str); Const(int val, int width = 32); Const(RTLIL::State bit, int width = 1); Const(const std::vector &bits) : bits(bits) { flags = CONST_FLAG_NONE; } Const(const std::vector &bits); - Const(const RTLIL::Const &c); + Const(const RTLIL::Const &c) = default; RTLIL::Const &operator =(const RTLIL::Const &other) = default; bool operator <(const RTLIL::Const &other) const; @@ -656,6 +690,8 @@ struct RTLIL::Const bool is_fully_ones() const; bool is_fully_def() const; bool is_fully_undef() const; + bool is_fully_undef_x_only() const; + bool is_onehot(int *pos = nullptr) const; inline RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const { RTLIL::Const ret; @@ -676,7 +712,7 @@ struct RTLIL::Const inline unsigned int hash() const { unsigned int h = mkhash_init; for (auto b : bits) - mkhash(h, b); + h = mkhash(h, b); return h; } }; @@ -685,21 +721,21 @@ struct RTLIL::AttrObject { dict attributes; - bool has_attribute(RTLIL::IdString id) const; + bool has_attribute(const RTLIL::IdString &id) const; - void set_bool_attribute(RTLIL::IdString id, bool value=true); - bool get_bool_attribute(RTLIL::IdString id) const; + void set_bool_attribute(const RTLIL::IdString &id, bool value=true); + bool get_bool_attribute(const RTLIL::IdString &id) const; bool get_blackbox_attribute(bool ignore_wb=false) const { return get_bool_attribute(ID::blackbox) || (!ignore_wb && get_bool_attribute(ID::whitebox)); } - void set_string_attribute(RTLIL::IdString id, string value); - string get_string_attribute(RTLIL::IdString id) const; + void set_string_attribute(const RTLIL::IdString& id, string value); + string get_string_attribute(const RTLIL::IdString &id) const; - void set_strpool_attribute(RTLIL::IdString id, const pool &data); - void add_strpool_attribute(RTLIL::IdString id, const pool &data); - pool get_strpool_attribute(RTLIL::IdString id) const; + void set_strpool_attribute(const RTLIL::IdString& id, const pool &data); + void add_strpool_attribute(const RTLIL::IdString& id, const pool &data); + pool get_strpool_attribute(const RTLIL::IdString &id) const; void set_src_attribute(const std::string &src) { set_string_attribute(ID::src, src); @@ -710,6 +746,9 @@ struct RTLIL::AttrObject void set_hdlname_attribute(const vector &hierarchy); vector get_hdlname_attribute() const; + + void set_intvec_attribute(const RTLIL::IdString& id, const vector &data); + vector get_intvec_attribute(const RTLIL::IdString &id) const; }; struct RTLIL::SigChunk @@ -718,19 +757,19 @@ struct RTLIL::SigChunk std::vector data; // only used if wire == NULL, LSB at index 0 int width, offset; - SigChunk(); - SigChunk(const RTLIL::Const &value); - SigChunk(RTLIL::Wire *wire); - SigChunk(RTLIL::Wire *wire, int offset, int width = 1); - SigChunk(const std::string &str); - SigChunk(int val, int width = 32); - SigChunk(RTLIL::State bit, int width = 1); + SigChunk() : wire(nullptr), width(0), offset(0) {} + SigChunk(const RTLIL::Const &value) : wire(nullptr), data(value.bits), width(GetSize(data)), offset(0) {} + SigChunk(RTLIL::Const &&value) : wire(nullptr), data(std::move(value.bits)), width(GetSize(data)), offset(0) {} + SigChunk(RTLIL::Wire *wire) : wire(wire), width(GetSize(wire)), offset(0) {} + SigChunk(RTLIL::Wire *wire, int offset, int width = 1) : wire(wire), width(width), offset(offset) {} + SigChunk(const std::string &str) : SigChunk(RTLIL::Const(str)) {} + SigChunk(int val, int width = 32) : SigChunk(RTLIL::Const(val, width)) {} + SigChunk(RTLIL::State bit, int width = 1) : SigChunk(RTLIL::Const(bit, width)) {} SigChunk(const RTLIL::SigBit &bit); - SigChunk(const RTLIL::SigChunk &sigchunk); - RTLIL::SigChunk &operator =(const RTLIL::SigChunk &other) = default; RTLIL::SigChunk extract(int offset, int length) const; inline int size() const { return width; } + inline bool is_wire() const { return wire != NULL; } bool operator <(const RTLIL::SigChunk &other) const; bool operator ==(const RTLIL::SigChunk &other) const; @@ -747,7 +786,7 @@ struct RTLIL::SigBit SigBit(); SigBit(RTLIL::State bit); - SigBit(bool bit); + explicit SigBit(bool bit); SigBit(RTLIL::Wire *wire); SigBit(RTLIL::Wire *wire, int offset); SigBit(const RTLIL::SigChunk &chunk); @@ -756,14 +795,22 @@ struct RTLIL::SigBit SigBit(const RTLIL::SigBit &sigbit) = default; RTLIL::SigBit &operator =(const RTLIL::SigBit &other) = default; + inline bool is_wire() const { return wire != NULL; } + bool operator <(const RTLIL::SigBit &other) const; bool operator ==(const RTLIL::SigBit &other) const; bool operator !=(const RTLIL::SigBit &other) const; unsigned int hash() const; }; -struct RTLIL::SigSpecIterator : public std::iterator +struct RTLIL::SigSpecIterator { + typedef std::input_iterator_tag iterator_category; + typedef RTLIL::SigBit value_type; + typedef ptrdiff_t difference_type; + typedef RTLIL::SigBit* pointer; + typedef RTLIL::SigBit& reference; + RTLIL::SigSpec *sig_p; int index; @@ -773,8 +820,14 @@ struct RTLIL::SigSpecIterator : public std::iterator +struct RTLIL::SigSpecConstIterator { + typedef std::input_iterator_tag iterator_category; + typedef RTLIL::SigBit value_type; + typedef ptrdiff_t difference_type; + typedef RTLIL::SigBit* pointer; + typedef RTLIL::SigBit& reference; + const RTLIL::SigSpec *sig_p; int index; @@ -810,13 +863,13 @@ struct RTLIL::SigSpec friend struct RTLIL::Module; public: - SigSpec(); - SigSpec(const RTLIL::SigSpec &other); + SigSpec() : width_(0), hash_(0) {} SigSpec(std::initializer_list parts); - RTLIL::SigSpec &operator=(const RTLIL::SigSpec &other); SigSpec(const RTLIL::Const &value); + SigSpec(RTLIL::Const &&value); SigSpec(const RTLIL::SigChunk &chunk); + SigSpec(RTLIL::SigChunk &&chunk); SigSpec(RTLIL::Wire *wire); SigSpec(RTLIL::Wire *wire, int offset, int width = 1); SigSpec(const std::string &str); @@ -827,22 +880,7 @@ struct RTLIL::SigSpec SigSpec(const std::vector &bits); SigSpec(const pool &bits); SigSpec(const std::set &bits); - SigSpec(bool bit); - - SigSpec(RTLIL::SigSpec &&other) { - width_ = other.width_; - hash_ = other.hash_; - chunks_ = std::move(other.chunks_); - bits_ = std::move(other.bits_); - } - - const RTLIL::SigSpec &operator=(RTLIL::SigSpec &&other) { - width_ = other.width_; - hash_ = other.hash_; - chunks_ = std::move(other.chunks_); - bits_ = std::move(other.bits_); - return *this; - } + explicit SigSpec(bool bit); size_t get_hash() const { if (!hash_) hash(); @@ -886,6 +924,7 @@ struct RTLIL::SigSpec void remove(const pool &pattern, RTLIL::SigSpec *other) const; void remove2(const pool &pattern, RTLIL::SigSpec *other); void remove2(const std::set &pattern, RTLIL::SigSpec *other); + void remove2(const pool &pattern, RTLIL::SigSpec *other); void remove(int offset, int length = 1); void remove_const(); @@ -895,6 +934,9 @@ struct RTLIL::SigSpec RTLIL::SigSpec extract(int offset, int length = 1) const; RTLIL::SigSpec extract_end(int offset) const { return extract(offset, width_ - offset); } + RTLIL::SigBit lsb() const { log_assert(width_); return (*this)[0]; }; + RTLIL::SigBit msb() const { log_assert(width_); return (*this)[width_ - 1]; }; + void append(const RTLIL::SigSpec &signal); inline void append(Wire *wire) { append(RTLIL::SigSpec(wire)); } inline void append(const RTLIL::SigChunk &chunk) { append(RTLIL::SigSpec(chunk)); } @@ -925,6 +967,7 @@ struct RTLIL::SigSpec bool is_fully_undef() const; bool has_const() const; bool has_marked_bits() const; + bool is_onehot(int *pos = nullptr) const; bool as_bool() const; int as_int(bool is_signed = false) const; @@ -953,9 +996,9 @@ struct RTLIL::SigSpec unsigned int hash() const { if (!hash_) updhash(); return hash_; }; #ifndef NDEBUG - void check() const; + void check(Module *mod = nullptr) const; #else - void check() const { } + void check(Module *mod = nullptr) const { (void)mod; } #endif }; @@ -967,9 +1010,9 @@ struct RTLIL::Selection Selection(bool full = true) : full_selection(full) { } - bool selected_module(RTLIL::IdString mod_name) const; - bool selected_whole_module(RTLIL::IdString mod_name) const; - bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const; + bool selected_module(const RTLIL::IdString &mod_name) const; + bool selected_whole_module(const RTLIL::IdString &mod_name) const; + bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; void optimize(RTLIL::Design *design); template void select(T1 *module) { @@ -1022,6 +1065,8 @@ struct RTLIL::Design int refcount_modules_; dict modules_; + std::vector bindings_; + std::vector verilog_packages, verilog_globals; std::unique_ptr verilog_defines; @@ -1033,14 +1078,17 @@ struct RTLIL::Design ~Design(); RTLIL::ObjRange modules(); - RTLIL::Module *module(RTLIL::IdString name); + RTLIL::Module *module(const RTLIL::IdString &name); + const RTLIL::Module *module(const RTLIL::IdString &name) const; RTLIL::Module *top_module(); - bool has(RTLIL::IdString id) const { + bool has(const RTLIL::IdString &id) const { return modules_.count(id) != 0; } void add(RTLIL::Module *module); + void add(RTLIL::Binding *binding); + RTLIL::Module *addModule(RTLIL::IdString name); void remove(RTLIL::Module *module); void rename(RTLIL::Module *module, RTLIL::IdString new_name); @@ -1059,9 +1107,9 @@ struct RTLIL::Design void check(); void optimize(); - bool selected_module(RTLIL::IdString mod_name) const; - bool selected_whole_module(RTLIL::IdString mod_name) const; - bool selected_member(RTLIL::IdString mod_name, RTLIL::IdString memb_name) const; + bool selected_module(const RTLIL::IdString &mod_name) const; + bool selected_whole_module(const RTLIL::IdString &mod_name) const; + bool selected_member(const RTLIL::IdString &mod_name, const RTLIL::IdString &memb_name) const; bool selected_module(RTLIL::Module *mod) const; bool selected_whole_module(RTLIL::Module *mod) const; @@ -1103,7 +1151,7 @@ struct RTLIL::Design std::vector selected_modules() const; std::vector selected_whole_modules() const; - std::vector selected_whole_modules_warn() const; + std::vector selected_whole_modules_warn(bool include_wb = false) const; #ifdef WITH_PYTHON static std::map *get_all_designs(void); #endif @@ -1117,6 +1165,7 @@ struct RTLIL::Module : public RTLIL::AttrObject protected: void add(RTLIL::Wire *wire); void add(RTLIL::Cell *cell); + void add(RTLIL::Process *process); public: RTLIL::Design *design; @@ -1127,7 +1176,9 @@ struct RTLIL::Module : public RTLIL::AttrObject dict wires_; dict cells_; - std::vector connections_; + + std::vector connections_; + std::vector bindings_; RTLIL::IdString name; idict avail_parameters; @@ -1139,8 +1190,9 @@ struct RTLIL::Module : public RTLIL::AttrObject virtual ~Module(); virtual RTLIL::IdString derive(RTLIL::Design *design, const dict ¶meters, bool mayfail = false); virtual RTLIL::IdString derive(RTLIL::Design *design, const dict ¶meters, const dict &interfaces, const dict &modports, bool mayfail = false); - virtual size_t count_id(RTLIL::IdString id); - virtual void reprocess_module(RTLIL::Design *design, const dict &local_interfaces); + virtual size_t count_id(const RTLIL::IdString& id); + virtual void expand_interfaces(RTLIL::Design *design, const dict &local_interfaces); + virtual bool reprocess_if_necessary(RTLIL::Design *design); virtual void sort(); virtual void check(); @@ -1173,11 +1225,20 @@ struct RTLIL::Module : public RTLIL::AttrObject return design->selected_member(name, member->name); } - RTLIL::Wire* wire(RTLIL::IdString id) { + RTLIL::Wire* wire(const RTLIL::IdString &id) { auto it = wires_.find(id); return it == wires_.end() ? nullptr : it->second; } - RTLIL::Cell* cell(RTLIL::IdString id) { + RTLIL::Cell* cell(const RTLIL::IdString &id) { + auto it = cells_.find(id); + return it == cells_.end() ? nullptr : it->second; + } + + const RTLIL::Wire* wire(const RTLIL::IdString &id) const{ + auto it = wires_.find(id); + return it == wires_.end() ? nullptr : it->second; + } + const RTLIL::Cell* cell(const RTLIL::IdString &id) const { auto it = cells_.find(id); return it == cells_.end() ? nullptr : it->second; } @@ -1185,9 +1246,12 @@ struct RTLIL::Module : public RTLIL::AttrObject RTLIL::ObjRange wires() { return RTLIL::ObjRange(&wires_, &refcount_wires_); } RTLIL::ObjRange cells() { return RTLIL::ObjRange(&cells_, &refcount_cells_); } + void add(RTLIL::Binding *binding); + // Removing wires is expensive. If you have to remove wires, remove them all at once. void remove(const pool &wires); void remove(RTLIL::Cell *cell); + void remove(RTLIL::Process *process); void rename(RTLIL::Wire *wire, RTLIL::IdString new_name); void rename(RTLIL::Cell *cell, RTLIL::IdString new_name); @@ -1207,6 +1271,7 @@ struct RTLIL::Module : public RTLIL::AttrObject RTLIL::Memory *addMemory(RTLIL::IdString name, const RTLIL::Memory *other); + RTLIL::Process *addProcess(RTLIL::IdString name); RTLIL::Process *addProcess(RTLIL::IdString name, const RTLIL::Process *other); // The add* methods create a cell and return the created cell. All signals must exist in advance. @@ -1253,12 +1318,19 @@ struct RTLIL::Module : public RTLIL::AttrObject RTLIL::Cell* addModFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addPow (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool a_signed = false, bool b_signed = false, const std::string &src = ""); + RTLIL::Cell* addFa (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_c, const RTLIL::SigSpec &sig_x, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::Cell* addLogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addLogicAnd (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addLogicOr (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = ""); RTLIL::Cell* addMux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); RTLIL::Cell* addPmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::Cell* addBmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::Cell* addDemux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + + RTLIL::Cell* addBweqx (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::Cell* addBwmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_y, const std::string &src = ""); RTLIL::Cell* addSlice (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, RTLIL::Const offset, const std::string &src = ""); RTLIL::Cell* addConcat (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, const std::string &src = ""); @@ -1279,6 +1351,8 @@ struct RTLIL::Module : public RTLIL::AttrObject RTLIL::Cell* addDffsre (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool clk_polarity = true, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); RTLIL::Cell* addAdff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const arst_value, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = ""); RTLIL::Cell* addAdffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const arst_value, bool clk_polarity = true, bool en_polarity = true, bool arst_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAldff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool aload_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAldffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool en_polarity = true, bool aload_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool srst_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool en_polarity = true, bool srst_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdffce (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const srst_value, bool clk_polarity = true, bool en_polarity = true, bool srst_polarity = true, const std::string &src = ""); @@ -1316,6 +1390,10 @@ struct RTLIL::Module : public RTLIL::AttrObject bool arst_value = false, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = ""); RTLIL::Cell* addAdffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool arst_value = false, bool clk_polarity = true, bool en_polarity = true, bool arst_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAldffGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool aload_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAldffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_aload, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, + const RTLIL::SigSpec &sig_ad, bool clk_polarity = true, bool en_polarity = true, bool aload_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdffGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool srst_value = false, bool clk_polarity = true, bool srst_polarity = true, const std::string &src = ""); RTLIL::Cell* addSdffeGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_srst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, @@ -1328,11 +1406,12 @@ struct RTLIL::Module : public RTLIL::AttrObject RTLIL::Cell* addDlatchsrGate (RTLIL::IdString name, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); + RTLIL::Cell* addAnyinit(RTLIL::IdString name, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const std::string &src = ""); + // The methods without the add* prefix create a cell and an output signal. They return the newly created output signal. RTLIL::SigSpec Not (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); RTLIL::SigSpec Pos (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); - RTLIL::SigSpec Bu0 (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); RTLIL::SigSpec Neg (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = ""); RTLIL::SigSpec And (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = ""); @@ -1379,6 +1458,11 @@ struct RTLIL::Module : public RTLIL::AttrObject RTLIL::SigSpec Mux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = ""); RTLIL::SigSpec Pmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = ""); + RTLIL::SigSpec Bmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src = ""); + RTLIL::SigSpec Demux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const std::string &src = ""); + + RTLIL::SigSpec Bweqx (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const std::string &src = ""); + RTLIL::SigSpec Bwmux (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_s, const std::string &src = ""); RTLIL::SigBit BufGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const std::string &src = ""); RTLIL::SigBit NotGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const std::string &src = ""); @@ -1403,6 +1487,13 @@ struct RTLIL::Module : public RTLIL::AttrObject RTLIL::SigSpec Allseq (RTLIL::IdString name, int width = 1, const std::string &src = ""); RTLIL::SigSpec Initstate (RTLIL::IdString name, const std::string &src = ""); + RTLIL::SigSpec SetTag (RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_c, const std::string &src = ""); + RTLIL::Cell* addSetTag (RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_c, const RTLIL::SigSpec &sig_y, const std::string &src = ""); + RTLIL::SigSpec GetTag (RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const std::string &src = ""); + RTLIL::Cell* addOverwriteTag (RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_s, const RTLIL::SigSpec &sig_c, const std::string &src = ""); + RTLIL::SigSpec OriginalTag (RTLIL::IdString name, const std::string &tag, const RTLIL::SigSpec &sig_a, const std::string &src = ""); + RTLIL::SigSpec FutureFF (RTLIL::IdString name, const RTLIL::SigSpec &sig_e, const std::string &src = ""); + #ifdef WITH_PYTHON static std::map *get_all_modules(void); #endif @@ -1434,6 +1525,10 @@ struct RTLIL::Wire : public RTLIL::AttrObject #endif }; +inline int GetSize(RTLIL::Wire *wire) { + return wire->width; +} + struct RTLIL::Memory : public RTLIL::AttrObject { unsigned int hashidx_; @@ -1472,22 +1567,22 @@ struct RTLIL::Cell : public RTLIL::AttrObject dict parameters; // access cell ports - bool hasPort(RTLIL::IdString portname) const; - void unsetPort(RTLIL::IdString portname); - void setPort(RTLIL::IdString portname, RTLIL::SigSpec signal); - const RTLIL::SigSpec &getPort(RTLIL::IdString portname) const; + bool hasPort(const RTLIL::IdString &portname) const; + void unsetPort(const RTLIL::IdString &portname); + void setPort(const RTLIL::IdString &portname, RTLIL::SigSpec signal); + const RTLIL::SigSpec &getPort(const RTLIL::IdString &portname) const; const dict &connections() const; // information about cell ports bool known() const; - bool input(RTLIL::IdString portname) const; - bool output(RTLIL::IdString portname) const; + bool input(const RTLIL::IdString &portname) const; + bool output(const RTLIL::IdString &portname) const; // access cell parameters - bool hasParam(RTLIL::IdString paramname) const; - void unsetParam(RTLIL::IdString paramname); - void setParam(RTLIL::IdString paramname, RTLIL::Const value); - const RTLIL::Const &getParam(RTLIL::IdString paramname) const; + bool hasParam(const RTLIL::IdString ¶mname) const; + void unsetParam(const RTLIL::IdString ¶mname); + void setParam(const RTLIL::IdString ¶mname, RTLIL::Const value); + const RTLIL::Const &getParam(const RTLIL::IdString ¶mname) const; void sort(); void check(); @@ -1504,6 +1599,9 @@ struct RTLIL::Cell : public RTLIL::AttrObject #ifdef WITH_PYTHON static std::map *get_all_cells(void); #endif + + bool has_memid() const; + bool is_mem_cell() const; }; struct RTLIL::CaseRule : public RTLIL::AttrObject @@ -1513,7 +1611,6 @@ struct RTLIL::CaseRule : public RTLIL::AttrObject std::vector switches; ~CaseRule(); - void optimize(); bool empty() const; @@ -1536,11 +1633,21 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject RTLIL::SwitchRule *clone() const; }; +struct RTLIL::MemWriteAction : RTLIL::AttrObject +{ + RTLIL::IdString memid; + RTLIL::SigSpec address; + RTLIL::SigSpec data; + RTLIL::SigSpec enable; + RTLIL::Const priority_mask; +}; + struct RTLIL::SyncRule { RTLIL::SyncType type; RTLIL::SigSpec signal; std::vector actions; + std::vector mem_write_actions; template void rewrite_sigspecs(T &functor); template void rewrite_sigspecs2(T &functor); @@ -1549,12 +1656,21 @@ struct RTLIL::SyncRule struct RTLIL::Process : public RTLIL::AttrObject { + unsigned int hashidx_; + unsigned int hash() const { return hashidx_; } + +protected: + // use module->addProcess() and module->remove() to create or destroy processes + friend struct RTLIL::Module; + Process(); + ~Process(); + +public: RTLIL::IdString name; + RTLIL::Module *module; RTLIL::CaseRule root_case; std::vector syncs; - ~Process(); - template void rewrite_sigspecs(T &functor); template void rewrite_sigspecs2(T &functor); RTLIL::Process *clone() const; @@ -1688,6 +1804,11 @@ void RTLIL::SyncRule::rewrite_sigspecs(T &functor) functor(it.first); functor(it.second); } + for (auto &it : mem_write_actions) { + functor(it.address); + functor(it.data); + functor(it.enable); + } } template @@ -1697,6 +1818,11 @@ void RTLIL::SyncRule::rewrite_sigspecs2(T &functor) for (auto &it : actions) { functor(it.first, it.second); } + for (auto &it : mem_write_actions) { + functor(it.address); + functor(it.data); + functor(it.enable); + } } template diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 2a54e78ec23..baaf22d1faf 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -223,7 +223,33 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } - if (cell->type.in(ID($_MUX_), ID($mux), ID($_NMUX_))) + if (cell->type == ID($bweqx)) + { + std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); + std::vector b = importDefSigSpec(cell->getPort(ID::B), timestep); + std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); + + std::vector bweqx = ez->vec_not(ez->vec_xor(a, b)); + + if (model_undef) + { + std::vector undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); + std::vector undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep); + std::vector undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + + std::vector both_undef = ez->vec_and(undef_a, undef_b); + std::vector both_def = ez->vec_and(ez->vec_not(undef_a), ez->vec_not(undef_b)); + + bweqx = ez->vec_or(both_undef, ez->vec_and(both_def, bweqx)); + + for (int yx : undef_y) + ez->assume(ez->NOT(yx)); + } + ez->assume(ez->vec_eq(bweqx, y)); + return true; + } + + if (cell->type.in(ID($_MUX_), ID($mux), ID($_NMUX_), ID($bwmux))) { std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); std::vector b = importDefSigSpec(cell->getPort(ID::B), timestep); @@ -233,6 +259,8 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector yy = model_undef ? ez->vec_var(y.size()) : y; if (cell->type == ID($_NMUX_)) ez->assume(ez->vec_eq(ez->vec_not(ez->vec_ite(s.at(0), b, a)), yy)); + else if (cell->type == ID($bwmux)) + ez->assume(ez->vec_eq(ez->vec_ite(s, b, a), yy)); else ez->assume(ez->vec_eq(ez->vec_ite(s.at(0), b, a), yy)); @@ -245,13 +273,117 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector unequal_ab = ez->vec_not(ez->vec_iff(a, b)); std::vector undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a, undef_b)); - std::vector yX = ez->vec_ite(undef_s.at(0), undef_ab, ez->vec_ite(s.at(0), undef_b, undef_a)); + std::vector yX; + if (cell->type == ID($bwmux)) + yX = ez->vec_ite(undef_s, undef_ab, ez->vec_ite(s, undef_b, undef_a)); + else + yX = ez->vec_ite(undef_s.at(0), undef_ab, ez->vec_ite(s.at(0), undef_b, undef_a)); ez->assume(ez->vec_eq(yX, undef_y)); undefGating(y, yy, undef_y); } return true; } + if (cell->type == ID($bmux)) + { + std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); + std::vector s = importDefSigSpec(cell->getPort(ID::S), timestep); + std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); + std::vector undef_a, undef_s, undef_y; + + if (model_undef) + { + undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); + undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep); + undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + } + + if (GetSize(s) == 0) { + ez->vec_set(a, y); + if (model_undef) + ez->vec_set(undef_a, undef_y); + } else { + for (int i = GetSize(s)-1; i >= 0; i--) + { + std::vector out = (i == 0) ? y : ez->vec_var(a.size() / 2); + std::vector yy = model_undef ? ez->vec_var(out.size()) : out; + + std::vector a0(a.begin(), a.begin() + a.size() / 2); + std::vector a1(a.begin() + a.size() / 2, a.end()); + ez->assume(ez->vec_eq(ez->vec_ite(s.at(i), a1, a0), yy)); + + if (model_undef) + { + std::vector undef_out = (i == 0) ? undef_y : ez->vec_var(a.size() / 2); + std::vector undef_a0(undef_a.begin(), undef_a.begin() + a.size() / 2); + std::vector undef_a1(undef_a.begin() + a.size() / 2, undef_a.end()); + std::vector unequal_ab = ez->vec_not(ez->vec_iff(a0, a1)); + std::vector undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a0, undef_a1)); + std::vector yX = ez->vec_ite(undef_s.at(i), undef_ab, ez->vec_ite(s.at(i), undef_a1, undef_a0)); + ez->assume(ez->vec_eq(yX, undef_out)); + undefGating(out, yy, undef_out); + + undef_a = undef_out; + } + + a = out; + } + } + return true; + } + + if (cell->type == ID($demux)) + { + std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); + std::vector s = importDefSigSpec(cell->getPort(ID::S), timestep); + std::vector y = importDefSigSpec(cell->getPort(ID::Y), timestep); + std::vector yy = model_undef ? ez->vec_var(y.size()) : y; + std::vector undef_a, undef_s, undef_y; + + if (model_undef) + { + undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); + undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep); + undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + } + + if (GetSize(s) == 0) { + ez->vec_set(a, y); + if (model_undef) + ez->vec_set(undef_a, undef_y); + } else { + for (int i = 0; i < (1 << GetSize(s)); i++) + { + std::vector ss; + for (int j = 0; j < GetSize(s); j++) { + if (i & 1 << j) + ss.push_back(s[j]); + else + ss.push_back(ez->NOT(s[j])); + } + int sss = ez->expression(ezSAT::OpAnd, ss); + + for (int j = 0; j < GetSize(a); j++) { + ez->SET(ez->AND(sss, a[j]), yy.at(i * GetSize(a) + j)); + } + + if (model_undef) + { + int s0 = ez->expression(ezSAT::OpOr, ez->vec_and(ez->vec_not(ss), ez->vec_not(undef_s))); + int us = ez->AND(ez->NOT(s0), ez->expression(ezSAT::OpOr, undef_s)); + for (int j = 0; j < GetSize(a); j++) { + int a0 = ez->AND(ez->NOT(a[j]), ez->NOT(undef_a[j])); + int yX = ez->AND(ez->OR(us, undef_a[j]), ez->NOT(ez->OR(s0, a0))); + ez->SET(yX, undef_y.at(i * GetSize(a) + j)); + } + } + } + if (model_undef) + undefGating(y, yy, undef_y); + } + return true; + } + if (cell->type == ID($pmux)) { std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); @@ -275,29 +407,24 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep); std::vector undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); - int maybe_a = ez->CONST_TRUE; + int all_undef = ez->CONST_FALSE; + int found_active = ez->CONST_FALSE; - std::vector bits_set = std::vector(undef_y.size(), ez->CONST_FALSE); - std::vector bits_clr = std::vector(undef_y.size(), ez->CONST_FALSE); + std::vector undef_tmp = undef_a; for (size_t i = 0; i < s.size(); i++) { - std::vector part_of_b(b.begin()+i*a.size(), b.begin()+(i+1)*a.size()); std::vector part_of_undef_b(undef_b.begin()+i*a.size(), undef_b.begin()+(i+1)*a.size()); - int maybe_s = ez->OR(s.at(i), undef_s.at(i)); - int sure_s = ez->AND(s.at(i), ez->NOT(undef_s.at(i))); - - maybe_a = ez->AND(maybe_a, ez->NOT(sure_s)); - - bits_set = ez->vec_ite(maybe_s, ez->vec_or(bits_set, ez->vec_or(part_of_b, part_of_undef_b)), bits_set); - bits_clr = ez->vec_ite(maybe_s, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(part_of_b), part_of_undef_b)), bits_clr); + undef_tmp = ez->vec_ite(s.at(i), part_of_undef_b, undef_tmp); + all_undef = ez->OR(all_undef, undef_s.at(i)); + all_undef = ez->OR(all_undef, ez->AND(s.at(i), found_active)); + found_active = ez->OR(found_active, s.at(i)); } - bits_set = ez->vec_ite(maybe_a, ez->vec_or(bits_set, ez->vec_or(bits_set, ez->vec_or(a, undef_a))), bits_set); - bits_clr = ez->vec_ite(maybe_a, ez->vec_or(bits_clr, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(a), undef_a))), bits_clr); + undef_tmp = ez->vec_or(undef_tmp, std::vector(a.size(), all_undef)); - ez->assume(ez->vec_eq(ez->vec_not(ez->vec_xor(bits_set, bits_clr)), undef_y)); + ez->assume(ez->vec_eq(undef_tmp, undef_y)); undefGating(y, yy, undef_y); } return true; @@ -1076,17 +1203,21 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } - if (timestep > 0 && RTLIL::builtin_ff_cell_types().count(cell->type)) + if (timestep > 0 && (RTLIL::builtin_ff_cell_types().count(cell->type) || cell->type == ID($anyinit))) { FfData ff(nullptr, cell); // Latches and FFs with async inputs are not supported — use clk2fflogic or async2sync first. - if (!ff.has_d || ff.has_arst || ff.has_sr || (ff.has_en && !ff.has_clk)) + if (ff.has_aload || ff.has_arst || ff.has_sr) return false; if (timestep == 1) { initial_state.add((*sigmap)(cell->getPort(ID::Q))); + if (model_undef && def_formal) { + std::vector undef_q = importUndefSigSpec(cell->getPort(ID::Q), timestep); + ez->assume(ez->NOT(ez->vec_reduce_or(undef_q))); + } } else { @@ -1094,7 +1225,7 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector undef_d; if (model_undef) undef_d = importUndefSigSpec(cell->getPort(ID::D), timestep-1); - if (ff.has_srst && ff.has_en && ff.ce_over_srst) { + if (ff.has_srst && ff.has_ce && ff.ce_over_srst) { int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0); std::vector rval = importDefSigSpec(ff.val_srst, timestep-1); int undef_srst; @@ -1108,21 +1239,21 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) else std::tie(d, undef_d) = mux(srst, undef_srst, rval, undef_rval, d, undef_d); } - if (ff.has_en) { - int en = importDefSigSpec(ff.sig_en, timestep-1).at(0); + if (ff.has_ce) { + int ce = importDefSigSpec(ff.sig_ce, timestep-1).at(0); std::vector old_q = importDefSigSpec(ff.sig_q, timestep-1); - int undef_en; + int undef_ce; std::vector undef_old_q; if (model_undef) { - undef_en = importUndefSigSpec(ff.sig_en, timestep-1).at(0); + undef_ce = importUndefSigSpec(ff.sig_ce, timestep-1).at(0); undef_old_q = importUndefSigSpec(ff.sig_q, timestep-1); } - if (ff.pol_en) - std::tie(d, undef_d) = mux(en, undef_en, old_q, undef_old_q, d, undef_d); + if (ff.pol_ce) + std::tie(d, undef_d) = mux(ce, undef_ce, old_q, undef_old_q, d, undef_d); else - std::tie(d, undef_d) = mux(en, undef_en, d, undef_d, old_q, undef_old_q); + std::tie(d, undef_d) = mux(ce, undef_ce, d, undef_d, old_q, undef_old_q); } - if (ff.has_srst && !(ff.has_en && ff.ce_over_srst)) { + if (ff.has_srst && !(ff.has_ce && ff.ce_over_srst)) { int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0); std::vector rval = importDefSigSpec(ff.val_srst, timestep-1); int undef_srst; @@ -1154,13 +1285,18 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) if (cell->type == ID($anyconst)) { - if (timestep < 2) + if (timestep < 2) { + if (model_undef && def_formal) { + std::vector undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep); + ez->assume(ez->NOT(ez->vec_reduce_or(undef_y))); + } return true; + } std::vector d = importDefSigSpec(cell->getPort(ID::Y), timestep-1); std::vector q = importDefSigSpec(cell->getPort(ID::Y), timestep); - std::vector qq = model_undef ? ez->vec_var(q.size()) : q; + std::vector qq = (model_undef && !def_formal) ? ez->vec_var(q.size()) : q; ez->assume(ez->vec_eq(d, qq)); if (model_undef) @@ -1168,14 +1304,24 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) std::vector undef_d = importUndefSigSpec(cell->getPort(ID::Y), timestep-1); std::vector undef_q = importUndefSigSpec(cell->getPort(ID::Y), timestep); - ez->assume(ez->vec_eq(undef_d, undef_q)); - undefGating(q, qq, undef_q); + if (def_formal) { + for (auto &undef_q_bit : undef_q) + ez->SET(ez->CONST_FALSE, undef_q_bit); + } else { + ez->assume(ez->vec_eq(undef_d, undef_q)); + undefGating(q, qq, undef_q); + } } return true; } if (cell->type == ID($anyseq)) { + if (model_undef && def_formal) { + std::vector undef_q = importUndefSigSpec(cell->getPort(ID::Y), timestep); + for (auto &undef_q_bit : undef_q) + ez->SET(ez->CONST_FALSE, undef_q_bit); + } return true; } @@ -1233,6 +1379,11 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } + if (cell->type == ID($scopeinfo)) + { + return true; + } + // Unsupported internal cell types: $pow $fsm $mem* // .. and all sequential cells with asynchronous inputs return false; diff --git a/kernel/satgen.h b/kernel/satgen.h index cf2db733f87..8a89ff9dba4 100644 --- a/kernel/satgen.h +++ b/kernel/satgen.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -73,6 +73,7 @@ struct SatGen std::map, bool> initstates; bool ignore_div_by_zero; bool model_undef; + bool def_formal = false; SatGen(ezSAT *ez, SigMap *sigmap, std::string prefix = std::string()) : ez(ez), sigmap(sigmap), prefix(prefix), ignore_div_by_zero(false), model_undef(false) diff --git a/kernel/scopeinfo.cc b/kernel/scopeinfo.cc new file mode 100644 index 00000000000..7ed9ebf33f7 --- /dev/null +++ b/kernel/scopeinfo.cc @@ -0,0 +1,129 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/scopeinfo.h" + +YOSYS_NAMESPACE_BEGIN + +template void ModuleHdlnameIndex::index_items(I begin, I end, Filter filter) +{ + for (; begin != end; ++begin) { + auto const &item = *begin; + + if (!filter(item)) + continue; + std::vector path = parse_hdlname(item); + if (!path.empty()) + lookup.emplace(item, tree.insert(path, item)); + } +} + +void ModuleHdlnameIndex::index() +{ + index_wires(); + index_cells(); +} + +void ModuleHdlnameIndex::index_wires() +{ + auto wires = module->wires(); + index_items(wires.begin(), wires.end(), [](Wire *) { return true; }); +} + +void ModuleHdlnameIndex::index_cells() +{ + auto cells = module->cells(); + index_items(cells.begin(), cells.end(), [](Cell *) { return true; }); +} + +void ModuleHdlnameIndex::index_scopeinfo_cells() +{ + auto cells = module->cells(); + index_items(cells.begin(), cells.end(), [](Cell *cell) { return cell->type == ID($scopeinfo); }); +} + +std::vector ModuleHdlnameIndex::scope_sources(Cursor cursor) +{ + std::vector result; + + for (; !cursor.is_root(); cursor = cursor.parent()) { + if (!cursor.has_entry()) { + result.push_back(""); + result.push_back(""); + continue; + } + Cell *cell = cursor.entry().cell(); + if (cell == nullptr || cell->type != ID($scopeinfo)) { + result.push_back(""); + result.push_back(""); + continue; + } + result.push_back(scopeinfo_get_attribute(cell, ScopeinfoAttrs::Module, ID::src).decode_string()); + result.push_back(scopeinfo_get_attribute(cell, ScopeinfoAttrs::Cell, ID::src).decode_string()); + } + + result.push_back(module->get_src_attribute()); + + std::reverse(result.begin(), result.end()); + + return result; +} + +static const char *attr_prefix(ScopeinfoAttrs attrs) +{ + switch (attrs) { + case ScopeinfoAttrs::Cell: + return "\\cell_"; + case ScopeinfoAttrs::Module: + return "\\module_"; + default: + log_abort(); + } +} + +bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id) +{ + log_assert(scopeinfo->type == ID($scopeinfo)); + return scopeinfo->has_attribute(attr_prefix(attrs) + RTLIL::unescape_id(id)); +} + +RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id) +{ + log_assert(scopeinfo->type == ID($scopeinfo)); + auto found = scopeinfo->attributes.find(attr_prefix(attrs) + RTLIL::unescape_id(id)); + if (found == scopeinfo->attributes.end()) + return RTLIL::Const(); + return found->second; +} + +dict scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs) +{ + dict attributes; + + const char *prefix = attr_prefix(attrs); + int prefix_len = strlen(prefix); + + for (auto const &entry : scopeinfo->attributes) + if (entry.first.begins_with(prefix)) + attributes.emplace(RTLIL::escape_id(entry.first.c_str() + prefix_len), entry.second); + + return attributes; +} + +YOSYS_NAMESPACE_END diff --git a/kernel/scopeinfo.h b/kernel/scopeinfo.h new file mode 100644 index 00000000000..71af70344ce --- /dev/null +++ b/kernel/scopeinfo.h @@ -0,0 +1,432 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef SCOPEINFO_H +#define SCOPEINFO_H + +#include +#include + +#include "kernel/yosys.h" +#include "kernel/celltypes.h" + +YOSYS_NAMESPACE_BEGIN + +template +class IdTree +{ +public: + struct Cursor; + +protected: + IdTree *parent = nullptr; + IdString scope_name; + int depth = 0; + + pool names; + dict entries; +public: // XXX + dict> subtrees; + + template + static Cursor do_insert(IdTree *tree, P begin, P end, T_ref &&value) + { + log_assert(begin != end && "path must be non-empty"); + while (true) { + IdString name = *begin; + ++begin; + log_assert(!name.empty()); + tree->names.insert(name); + if (begin == end) { + tree->entries.emplace(name, std::forward(value)); + return Cursor(tree, name); + } + auto &unique = tree->subtrees[name]; + if (!unique) { + unique.reset(new IdTree); + unique->scope_name = name; + unique->parent = tree; + unique->depth = tree->depth + 1; + } + tree = unique.get(); + } + } + +public: + IdTree() = default; + IdTree(const IdTree &) = delete; + IdTree(IdTree &&) = delete; + + // A cursor remains valid as long as the (sub-)IdTree it points at is alive + struct Cursor + { + friend class IdTree; + protected: + public: + IdTree *target; + IdString scope_name; + + Cursor() : target(nullptr) {} + Cursor(IdTree *target, IdString scope_name) : target(target), scope_name(scope_name) { + if (scope_name.empty()) + log_assert(target->parent == nullptr); + } + + Cursor do_first_child() { + IdTree *tree = nullptr; + if (scope_name.empty()) { + tree = target; + } else { + auto found = target->subtrees.find(scope_name); + if (found != target->subtrees.end()) { + tree = found->second.get(); + } else { + return Cursor(); + } + } + if (tree->names.empty()) { + return Cursor(); + } + return Cursor(tree, *tree->names.begin()); + } + + Cursor do_next_sibling() { + if (scope_name.empty()) + return Cursor(); + auto found = target->names.find(scope_name); + if (found == target->names.end()) + return Cursor(); + ++found; + if (found == target->names.end()) + return Cursor(); + return Cursor(target, *found); + } + + Cursor do_parent() { + if (scope_name.empty()) + return Cursor(); + if (target->parent != nullptr) + return Cursor(target->parent, target->scope_name); + return Cursor(target, IdString()); + } + + Cursor do_next_preorder() { + Cursor current = *this; + Cursor next = current.do_first_child(); + if (next.valid()) + return next; + while (current.valid()) { + if (next.valid()) + return next; + next = current.do_next_sibling(); + if (next.valid()) + return next; + current = current.do_parent(); + } + return current; + } + + Cursor do_child(IdString name) { + IdTree *tree = nullptr; + if (scope_name.empty()) { + tree = target; + } else { + auto found = target->subtrees.find(scope_name); + if (found != target->subtrees.end()) { + tree = found->second.get(); + } else { + return Cursor(); + } + } + auto found = tree->names.find(name); + if (found == tree->names.end()) { + return Cursor(); + } + return Cursor(tree, *found); + } + + public: + bool operator==(const Cursor &other) const { + return target == other.target && scope_name == other.scope_name; + } + bool operator!=(const Cursor &other) const { + return !(*this == other); + } + + bool valid() const { + return target != nullptr; + } + + int depth() const { + log_assert(valid()); + return target->depth + !scope_name.empty(); + } + + bool is_root() const { + return target != nullptr && scope_name.empty(); + } + + bool has_entry() const { + log_assert(valid()); + return !scope_name.empty() && target->entries.count(scope_name); + } + + T &entry() { + log_assert(!scope_name.empty()); + return target->entries.at(scope_name); + } + + void assign_path_to(std::vector &out_path) { + log_assert(valid()); + out_path.clear(); + if (scope_name.empty()) + return; + out_path.push_back(scope_name); + IdTree *current = target; + while (current->parent) { + out_path.push_back(current->scope_name); + current = current->parent; + } + std::reverse(out_path.begin(), out_path.end()); + } + + std::vector path() { + std::vector result; + assign_path_to(result); + return result; + } + + std::string path_str() { + std::string result; + for (const auto &item : path()) { + if (!result.empty()) + result.push_back(' '); + result += RTLIL::unescape_id(item); + } + return result; + } + + Cursor first_child() { + log_assert(valid()); + return do_first_child(); + } + + Cursor next_preorder() { + log_assert(valid()); + return do_next_preorder(); + } + + Cursor parent() { + log_assert(valid()); + return do_parent(); + } + + Cursor child(IdString name) { + log_assert(valid()); + return do_child(name); + } + + Cursor common_ancestor(Cursor other) { + Cursor current = *this; + + while (current != other) { + if (!current.valid() || !other.valid()) + return Cursor(); + int delta = current.depth() - other.depth(); + if (delta >= 0) + current = current.do_parent(); + if (delta <= 0) + other = other.do_parent(); + } + return current; + } + }; + + template + Cursor insert(P begin, P end, const T &value) { + return do_insert(this, begin, end, value); + } + + template + Cursor insert(P begin, P end, T &&value) { + return do_insert(this, begin, end, std::move(value)); + } + + template + Cursor insert(const P &path, const T &value) { + return do_insert(this, path.begin(), path.end(), value); + } + + template + Cursor insert(const P &path, T &&value) { + return do_insert(this, path.begin(), path.end(), std::move(value)); + } + + Cursor cursor() { + return parent ? Cursor(this->parent, this->scope_name) : Cursor(this, IdString()); + } + + template + Cursor cursor(P begin, P end) { + Cursor current = cursor(); + for (; begin != end; ++begin) { + current = current.do_child(*begin); + if (!current.valid()) + break; + } + return current; + } + + template + Cursor cursor(const P &path) { + return cursor(path.begin(), path.end()); + } +}; + + +struct ModuleItem { + enum class Type { + Wire, + Cell, + }; + Type type; + void *ptr; + + ModuleItem(Wire *wire) : type(Type::Wire), ptr(wire) {} + ModuleItem(Cell *cell) : type(Type::Cell), ptr(cell) {} + + bool is_wire() const { return type == Type::Wire; } + bool is_cell() const { return type == Type::Cell; } + + Wire *wire() const { return type == Type::Wire ? static_cast(ptr) : nullptr; } + Cell *cell() const { return type == Type::Cell ? static_cast(ptr) : nullptr; } + + bool operator==(const ModuleItem &other) const { return ptr == other.ptr && type == other.type; } + unsigned int hash() const { return (uintptr_t)ptr; } +}; + +static inline void log_dump_val_worker(typename IdTree::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); } + +template +static inline void log_dump_val_worker(const typename std::unique_ptr &cursor ) { log("unique %p", cursor.get()); } + +template +std::vector parse_hdlname(const O* object) +{ + std::vector path; + if (!object->name.isPublic()) + return path; + for (auto const &item : object->get_hdlname_attribute()) + path.push_back("\\" + item); + if (path.empty()) + path.push_back(object->name); + return path; +} + +template +std::pair, IdString> parse_scopename(const O* object) +{ + std::vector path; + IdString trailing = object->name; + if (object->name.isPublic()) { + for (auto const &item : object->get_hdlname_attribute()) + path.push_back("\\" + item); + if (!path.empty()) { + trailing = path.back(); + path.pop_back(); + } + } else { + for (auto const &item : split_tokens(object->get_string_attribute(ID(scopename)), " ")) + path.push_back("\\" + item); + + } + return {path, trailing}; +} + +struct ModuleHdlnameIndex { + typedef IdTree::Cursor Cursor; + + RTLIL::Module *module; + IdTree tree; + dict lookup; + + ModuleHdlnameIndex(RTLIL::Module *module) : module(module) {} + +private: + template + void index_items(I begin, I end, Filter filter); + +public: + // Index all wires and cells of the module + void index(); + + // Index all wires of the module + void index_wires(); + + // Index all cells of the module + void index_cells(); + + // Index only the $scopeinfo cells of the module. + // This is sufficient when using `containing_scope`. + void index_scopeinfo_cells(); + + + // Return the cursor for the containing scope of some RTLIL object (Wire/Cell/...) + template + std::pair containing_scope(O *object) { + auto pair = parse_scopename(object); + return {tree.cursor(pair.first), pair.second}; + } + + // Return a vector of source locations starting from the indexed module to + // the scope represented by the cursor. The vector alternates module and + // module item source locations, using empty strings for missing src + // attributes. + std::vector scope_sources(Cursor cursor); + + // Return a vector of source locations starting from the indexed module to + // the passed RTLIL object (Wire/Cell/...). The vector alternates module + // and module item source locations, using empty strings for missing src + // attributes. + template + std::vector sources(O *object) { + auto pair = parse_scopename(object); + std::vector result = scope_sources(tree.cursor(pair.first)); + result.push_back(object->get_src_attribute()); + return result; + } +}; + +enum class ScopeinfoAttrs { + Module, + Cell, +}; + +// Check whether the flattened module or flattened cell corresponding to a $scopeinfo cell had a specific attribute. +bool scopeinfo_has_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id); + +// Get a specific attribute from the flattened module or flattened cell corresponding to a $scopeinfo cell. +RTLIL::Const scopeinfo_get_attribute(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs, const RTLIL::IdString &id); + +// Get all attribute from the flattened module or flattened cell corresponding to a $scopeinfo cell. +dict scopeinfo_attributes(const RTLIL::Cell *scopeinfo, ScopeinfoAttrs attrs); + +YOSYS_NAMESPACE_END + +#endif diff --git a/kernel/sigtools.h b/kernel/sigtools.h index c631fa481da..4ea43d74364 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/kernel/timinginfo.h b/kernel/timinginfo.h index d818e580b96..e7e4eab6e2e 100644 --- a/kernel/timinginfo.h +++ b/kernel/timinginfo.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * (C) 2020 Eddie Hung * * Permission to use, copy, modify, and/or distribute this software for any @@ -49,9 +49,9 @@ struct TimingInfo struct ModuleTiming { - RTLIL::IdString type; dict comb; - dict arrival, required; + dict> arrival, required; + bool has_inputs; }; dict data; @@ -88,10 +88,10 @@ struct TimingInfo auto src = cell->getPort(ID::SRC); auto dst = cell->getPort(ID::DST); for (const auto &c : src.chunks()) - if (!c.wire->port_input) + if (!c.wire || !c.wire->port_input) log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src)); for (const auto &c : dst.chunks()) - if (!c.wire->port_output) + if (!c.wire || !c.wire->port_output) log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst)); int rise_max = cell->getParam(ID::T_RISE_MAX).as_int(); int fall_max = cell->getParam(ID::T_FALL_MAX).as_int(); @@ -120,11 +120,10 @@ struct TimingInfo } } else if (cell->type == ID($specify3)) { - auto src = cell->getPort(ID::SRC); + auto src = cell->getPort(ID::SRC).as_bit(); auto dst = cell->getPort(ID::DST); - for (const auto &c : src.chunks()) - if (!c.wire->port_input) - log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src)); + if (!src.wire || !src.wire->port_input) + log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src)); for (const auto &c : dst.chunks()) if (!c.wire->port_output) log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module output.\n", log_id(module), log_id(cell), log_signal(dst)); @@ -136,34 +135,49 @@ struct TimingInfo max = 0; } for (const auto &d : dst) { - auto &v = t.arrival[NameBit(d)]; - v = std::max(v, max); + auto r = t.arrival.insert(NameBit(d)); + auto &v = r.first->second; + if (r.second || v.first < max) { + v.first = max; + v.second = NameBit(src); + } } } else if (cell->type == ID($specrule)) { - auto type = cell->getParam(ID::TYPE).decode_string(); - if (type != "$setup" && type != "$setuphold") + IdString type = cell->getParam(ID::TYPE).decode_string(); + if (type != ID($setup) && type != ID($setuphold)) continue; auto src = cell->getPort(ID::SRC); - auto dst = cell->getPort(ID::DST); + auto dst = cell->getPort(ID::DST).as_bit(); for (const auto &c : src.chunks()) - if (!c.wire->port_input) + if (!c.wire || !c.wire->port_input) log_error("Module '%s' contains specify cell '%s' where SRC '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(src)); - for (const auto &c : dst.chunks()) - if (!c.wire->port_input) - log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst)); + if (!dst.wire || !dst.wire->port_input) + log_error("Module '%s' contains specify cell '%s' where DST '%s' is not a module input.\n", log_id(module), log_id(cell), log_signal(dst)); int max = cell->getParam(ID::T_LIMIT_MAX).as_int(); if (max < 0) { log_warning("Module '%s' contains specify cell '%s' with T_LIMIT_MAX < 0 which is currently unsupported. Clamping to 0.\n", log_id(module), log_id(cell)); max = 0; } for (const auto &s : src) { - auto &v = t.required[NameBit(s)]; - v = std::max(v, max); + auto r = t.required.insert(NameBit(s)); + auto &v = r.first->second; + if (r.second || v.first < max) { + v.first = max; + v.second = NameBit(dst); + } } } } + for (auto port_name : module->ports) { + auto wire = module->wire(port_name); + if (wire->port_input) { + t.has_inputs = true; + break; + } + } + return t; } diff --git a/kernel/utils.h b/kernel/utils.h index 8942905fe35..8fa223824da 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -128,41 +128,103 @@ struct stackmap // A simple class for topological sorting // ------------------------------------------------ -template> -struct TopoSort +template , typename OPS = hash_ops> class TopoSort { - bool analyze_loops, found_loops; - std::map, C> database; - std::set> loops; + public: + // We use this ordering of the edges in the adjacency matrix for + // exact compatibility with an older implementation. + struct IndirectCmp { + IndirectCmp(const std::vector &nodes) : node_cmp_(), nodes_(nodes) {} + bool operator()(int a, int b) const + { + log_assert(static_cast(a) < nodes_.size()); + log_assert(static_cast(b) < nodes_.size()); + return node_cmp_(nodes_[a], nodes_[b]); + } + const C node_cmp_; + const std::vector &nodes_; + }; + + bool analyze_loops; + std::map node_to_index; + std::vector> edges; std::vector sorted; + std::set> loops; - TopoSort() + TopoSort() : indirect_cmp(nodes) { analyze_loops = true; found_loops = false; } - void node(T n) + int node(T n) { - if (database.count(n) == 0) - database[n] = std::set(); + auto rv = node_to_index.emplace(n, static_cast(nodes.size())); + if (rv.second) { + nodes.push_back(n); + edges.push_back(std::set(indirect_cmp)); + } + return rv.first->second; } - void edge(T left, T right) + void edge(int l_index, int r_index) { edges[r_index].insert(l_index); } + + void edge(T left, T right) { edge(node(left), node(right)); } + + bool has_node(const T &node) { return node_to_index.find(node) != node_to_index.end(); } + + bool sort() { - node(left); - database[right].insert(left); + log_assert(GetSize(node_to_index) == GetSize(edges)); + log_assert(GetSize(nodes) == GetSize(edges)); + + loops.clear(); + sorted.clear(); + found_loops = false; + + std::vector marked_cells(edges.size(), false); + std::vector active_cells(edges.size(), false); + std::vector active_stack; + sorted.reserve(edges.size()); + + for (const auto &it : node_to_index) + sort_worker(it.second, marked_cells, active_cells, active_stack); + + log_assert(GetSize(sorted) == GetSize(nodes)); + + return !found_loops; } - void sort_worker(const T &n, std::set &marked_cells, std::set &active_cells, std::vector &active_stack) + // Build the more expensive representation of edges for + // a few passes that use it directly. + std::map, C> get_database() { - if (active_cells.count(n)) { + std::map, C> database; + for (size_t i = 0; i < nodes.size(); ++i) { + std::set converted_edge_set; + for (int other_node : edges[i]) { + converted_edge_set.insert(nodes[other_node]); + } + database.emplace(nodes[i], converted_edge_set); + } + return database; + } + + private: + bool found_loops; + std::vector nodes; + const IndirectCmp indirect_cmp; + + void sort_worker(const int root_index, std::vector &marked_cells, std::vector &active_cells, std::vector &active_stack) + { + if (active_cells[root_index]) { found_loops = true; if (analyze_loops) { std::set loop; - for (int i = GetSize(active_stack)-1; i >= 0; i--) { - loop.insert(active_stack[i]); - if (active_stack[i] == n) + for (int i = GetSize(active_stack) - 1; i >= 0; i--) { + const int index = active_stack[i]; + loop.insert(nodes[index]); + if (index == root_index) break; } loops.insert(loop); @@ -170,42 +232,24 @@ struct TopoSort return; } - if (marked_cells.count(n)) + if (marked_cells[root_index]) return; - if (!database.at(n).empty()) - { + if (!edges[root_index].empty()) { if (analyze_loops) - active_stack.push_back(n); - active_cells.insert(n); + active_stack.push_back(root_index); + active_cells[root_index] = true; - for (auto &left_n : database.at(n)) + for (int left_n : edges[root_index]) sort_worker(left_n, marked_cells, active_cells, active_stack); if (analyze_loops) active_stack.pop_back(); - active_cells.erase(n); + active_cells[root_index] = false; } - marked_cells.insert(n); - sorted.push_back(n); - } - - bool sort() - { - loops.clear(); - sorted.clear(); - found_loops = false; - - std::set marked_cells; - std::set active_cells; - std::vector active_stack; - - for (auto &it : database) - sort_worker(it.first, marked_cells, active_cells, active_stack); - - log_assert(GetSize(sorted) == GetSize(database)); - return !found_loops; + marked_cells[root_index] = true; + sorted.push_back(nodes[root_index]); } }; diff --git a/kernel/yosys.cc b/kernel/yosys.cc index dcaf364e929..57433d0d9c1 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -55,7 +55,7 @@ # include #endif -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__NetBSD__) # include #endif @@ -67,11 +67,14 @@ # define INIT_MODULE initlibyosys extern "C" void INIT_MODULE(); #endif +#include #endif #include #include +#include "libs/json11/json11.hpp" + YOSYS_NAMESPACE_BEGIN int autoidx = 1; @@ -81,6 +84,7 @@ CellTypes yosys_celltypes; #ifdef YOSYS_ENABLE_TCL Tcl_Interp *yosys_tcl_interp = NULL; +bool yosys_tcl_repl_active = false; #endif std::set yosys_input_files, yosys_output_files; @@ -134,27 +138,11 @@ void yosys_banner() { log("\n"); log(" /----------------------------------------------------------------------------\\\n"); - log(" | |\n"); log(" | yosys -- Yosys Open SYnthesis Suite |\n"); - log(" | |\n"); - log(" | Copyright (C) 2012 - 2020 Claire Wolf |\n"); - log(" | |\n"); - log(" | Permission to use, copy, modify, and/or distribute this software for any |\n"); - log(" | purpose with or without fee is hereby granted, provided that the above |\n"); - log(" | copyright notice and this permission notice appear in all copies. |\n"); - log(" | |\n"); - log(" | THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |\n"); - log(" | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |\n"); - log(" | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |\n"); - log(" | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |\n"); - log(" | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |\n"); - log(" | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |\n"); - log(" | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |\n"); - log(" | |\n"); + log(" | Copyright (C) 2012 - 2024 Claire Xenia Wolf |\n"); + log(" | Distributed under an ISC-like license, type \"license\" to see terms |\n"); log(" \\----------------------------------------------------------------------------/\n"); - log("\n"); log(" %s\n", yosys_version_str); - log("\n"); } int ceil_log2(int x) @@ -171,48 +159,6 @@ int ceil_log2(int x) #endif } -std::string stringf(const char *fmt, ...) -{ - std::string string; - va_list ap; - - va_start(ap, fmt); - string = vstringf(fmt, ap); - va_end(ap); - - return string; -} - -std::string vstringf(const char *fmt, va_list ap) -{ - std::string string; - char *str = NULL; - -#if defined(_WIN32 )|| defined(__CYGWIN__) - int sz = 64, rc; - while (1) { - va_list apc; - va_copy(apc, ap); - str = (char*)realloc(str, sz); - rc = vsnprintf(str, sz, fmt, apc); - va_end(apc); - if (rc >= 0 && rc < sz) - break; - sz *= 2; - } -#else - if (vasprintf(&str, fmt, ap) < 0) - str = NULL; -#endif - - if (str != NULL) { - string = str; - free(str); - } - - return string; -} - int readsome(std::istream &f, char *s, int n) { int rc = int(f.readsome(s, n)); @@ -375,35 +321,54 @@ int run_command(const std::string &command, std::functionwidth; -} - bool already_setup = false; void yosys_setup() @@ -540,6 +558,7 @@ void yosys_setup() PyImport_AppendInittab((char*)"libyosys", INIT_MODULE); Py_Initialize(); PyRun_SimpleString("import sys"); + signal(SIGINT, SIG_DFL); #endif Pass::init_register(); @@ -577,7 +596,9 @@ void yosys_shutdown() #ifdef YOSYS_ENABLE_TCL if (yosys_tcl_interp != NULL) { - Tcl_DeleteInterp(yosys_tcl_interp); + if (!Tcl_InterpDeleted(yosys_tcl_interp)) { + Tcl_DeleteInterp(yosys_tcl_interp); + } Tcl_Finalize(); yosys_tcl_interp = NULL; } @@ -616,6 +637,23 @@ RTLIL::IdString new_id(std::string file, int line, std::string func) return stringf("$auto$%s:%d:%s$%d", file.c_str(), line, func.c_str(), autoidx++); } +RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix) +{ +#ifdef _WIN32 + size_t pos = file.find_last_of("/\\"); +#else + size_t pos = file.find_last_of('/'); +#endif + if (pos != std::string::npos) + file = file.substr(pos+1); + + pos = func.find_last_of(':'); + if (pos != std::string::npos) + func = func.substr(pos+1); + + return stringf("$auto$%s:%d:%s$%s$%d", file.c_str(), line, func.c_str(), suffix.c_str(), autoidx++); +} + RTLIL::Design *yosys_get_design() { return yosys_design; @@ -677,6 +715,42 @@ void rewrite_filename(std::string &filename) } #ifdef YOSYS_ENABLE_TCL + +static Tcl_Obj *json_to_tcl(Tcl_Interp *interp, const json11::Json &json) +{ + if (json.is_null()) + return Tcl_NewStringObj("null", 4); + else if (json.is_string()) { + auto string = json.string_value(); + return Tcl_NewStringObj(string.data(), string.size()); + } else if (json.is_number()) { + double value = json.number_value(); + double round_val = std::nearbyint(value); + if (std::isfinite(round_val) && value == round_val && value >= LONG_MIN && value < -double(LONG_MIN)) + return Tcl_NewLongObj((long)round_val); + else + return Tcl_NewDoubleObj(value); + } else if (json.is_bool()) { + return Tcl_NewBooleanObj(json.bool_value()); + } else if (json.is_array()) { + auto list = json.array_items(); + Tcl_Obj *result = Tcl_NewListObj(list.size(), nullptr); + for (auto &item : list) + Tcl_ListObjAppendElement(interp, result, json_to_tcl(interp, item)); + return result; + } else if (json.is_object()) { + auto map = json.object_items(); + Tcl_Obj *result = Tcl_NewListObj(map.size() * 2, nullptr); + for (auto &item : map) { + Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(item.first.data(), item.first.size())); + Tcl_ListObjAppendElement(interp, result, json_to_tcl(interp, item.second)); + } + return result; + } else { + log_abort(); + } +} + static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *argv[]) { std::vector args; @@ -701,20 +775,73 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a return TCL_OK; } - if (args.size() == 1) { - Pass::call(yosys_get_design(), args[0]); - return TCL_OK; + yosys_get_design()->scratchpad_unset("result.json"); + yosys_get_design()->scratchpad_unset("result.string"); + + bool in_repl = yosys_tcl_repl_active; + bool restore_log_cmd_error_throw = log_cmd_error_throw; + + log_cmd_error_throw = true; + + try { + if (args.size() == 1) { + Pass::call(yosys_get_design(), args[0]); + } else { + Pass::call(yosys_get_design(), args); + } + } catch (log_cmd_error_exception) { + if (in_repl) { + auto design = yosys_get_design(); + while (design->selection_stack.size() > 1) + design->selection_stack.pop_back(); + log_reset_stack(); + } + Tcl_SetResult(interp, (char *)"Yosys command produced an error", TCL_STATIC); + + yosys_tcl_repl_active = in_repl; + log_cmd_error_throw = restore_log_cmd_error_throw; + return TCL_ERROR; + } catch (...) { + log_error("uncaught exception during Yosys command invoked from TCL\n"); + } + + yosys_tcl_repl_active = in_repl; + log_cmd_error_throw = restore_log_cmd_error_throw; + + auto &scratchpad = yosys_get_design()->scratchpad; + auto result = scratchpad.find("result.json"); + if (result != scratchpad.end()) { + std::string err; + auto json = json11::Json::parse(result->second, err); + if (err.empty()) { + Tcl_SetObjResult(interp, json_to_tcl(interp, json)); + } else + log_warning("Ignoring result.json scratchpad value due to parse error: %s\n", err.c_str()); + } else if ((result = scratchpad.find("result.string")) != scratchpad.end()) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(result->second.data(), result->second.size())); } - Pass::call(yosys_get_design(), args); return TCL_OK; } +int yosys_tcl_iterp_init(Tcl_Interp *interp) +{ + if (Tcl_Init(interp)!=TCL_OK) + log_warning("Tcl_Init() call failed - %s\n",Tcl_ErrnoMsg(Tcl_GetErrno())); + Tcl_CreateCommand(interp, "yosys", tcl_yosys_cmd, NULL, NULL); + return TCL_OK ; +} + +void yosys_tcl_activate_repl() +{ + yosys_tcl_repl_active = true; +} + extern Tcl_Interp *yosys_get_tcl_interp() { if (yosys_tcl_interp == NULL) { yosys_tcl_interp = Tcl_CreateInterp(); - Tcl_CreateCommand(yosys_tcl_interp, "yosys", tcl_yosys_cmd, NULL, NULL); + yosys_tcl_iterp_init(yosys_tcl_interp); } return yosys_tcl_interp; } @@ -737,6 +864,10 @@ struct TclPass : public Pass { log("If any arguments are specified, these arguments are provided to the script via\n"); log("the standard $argc and $argv variables.\n"); log("\n"); + log("Note, tcl will not recieve the output of any yosys command. If the output\n"); + log("of the tcl commands are needed, use the yosys command 'tee -s result.string'\n"); + log("to redirect yosys's output to the 'result.string' scratchpad value.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *) override { if (args.size() < 2) @@ -747,11 +878,13 @@ struct TclPass : public Pass { script_args.push_back(Tcl_NewStringObj((*it).c_str(), (*it).size())); Tcl_Interp *interp = yosys_get_tcl_interp(); + Tcl_Preserve(interp); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(script_args.size()), 0); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(script_args.size(), script_args.data()), 0); Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(args[1].c_str(), args[1].size()), 0); if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK) log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(interp)); + Tcl_Release(interp); } } TclPass; #endif @@ -768,10 +901,14 @@ std::string proc_self_dirname() buflen--; return std::string(path, buflen); } -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__NetBSD__) std::string proc_self_dirname() { +#ifdef __NetBSD__ + int mib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_PATHNAME}; +#else int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; +#endif size_t buflen; char *buffer; std::string path; @@ -797,7 +934,9 @@ std::string proc_self_dirname() path = (char *) realloc((void *) path, buflen); while (buflen > 0 && path[buflen-1] != '/') buflen--; - return std::string(path, buflen); + std::string str(path, buflen); + free(path); + return str; } #elif defined(_WIN32) std::string proc_self_dirname() @@ -828,6 +967,35 @@ std::string proc_self_dirname() { return "/"; } +#elif defined(__OpenBSD__) +char yosys_path[PATH_MAX]; +char *yosys_argv0; + +std::string proc_self_dirname(void) +{ + char buf[PATH_MAX + 1] = "", *path, *p; + // if case argv[0] contains a valid path, return it + if (strlen(yosys_path) > 0) { + p = strrchr(yosys_path, '/'); + snprintf(buf, sizeof buf, "%*s/", (int)(yosys_path - p), yosys_path); + return buf; + } + // if argv[0] does not, reconstruct the path out of $PATH + path = strdup(getenv("PATH")); + if (!path) + log_error("getenv(\"PATH\") failed: %s\n", strerror(errno)); + for (p = strtok(path, ":"); p; p = strtok(NULL, ":")) { + snprintf(buf, sizeof buf, "%s/%s", p, yosys_argv0); + if (access(buf, X_OK) == 0) { + *(strrchr(buf, '/') + 1) = '\0'; + free(path); + return buf; + } + } + free(path); + log_error("Can't determine yosys executable path\n."); + return NULL; +} #else #error "Don't know how to determine process executable base path!" #endif @@ -952,7 +1120,7 @@ static void handle_label(std::string &command, bool &from_to_active, const std:: } } -void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label, RTLIL::Design *design) +bool run_frontend(std::string filename, std::string command, RTLIL::Design *design, std::string *from_to_label) { if (design == nullptr) design = yosys_design; @@ -962,11 +1130,11 @@ void run_frontend(std::string filename, std::string command, std::string *backen if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".gz") == 0) filename_trim.erase(filename_trim.size()-3); if (filename_trim.size() > 2 && filename_trim.compare(filename_trim.size()-2, std::string::npos, ".v") == 0) - command = "verilog"; + command = " -vlog2k"; else if (filename_trim.size() > 2 && filename_trim.compare(filename_trim.size()-3, std::string::npos, ".sv") == 0) - command = "verilog -sv"; + command = " -sv"; else if (filename_trim.size() > 3 && filename_trim.compare(filename_trim.size()-4, std::string::npos, ".vhd") == 0) - command = "vhdl"; + command = " -vhdl"; else if (filename_trim.size() > 4 && filename_trim.compare(filename_trim.size()-5, std::string::npos, ".blif") == 0) command = "blif"; else if (filename_trim.size() > 5 && filename_trim.compare(filename_trim.size()-6, std::string::npos, ".eblif") == 0) @@ -1052,10 +1220,12 @@ void run_frontend(std::string filename, std::string command, std::string *backen if (filename != "-") fclose(f); - if (backend_command != NULL && *backend_command == "auto") - *backend_command = ""; + return true; + } - return; + if (command == "tcl") { + Pass::call(design, vector({command, filename})); + return true; } if (filename == "-") { @@ -1064,16 +1234,15 @@ void run_frontend(std::string filename, std::string command, std::string *backen log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str()); } - if (command == "tcl") - Pass::call(design, vector({command, filename})); - else + if (command[0] == ' ') { + auto argv = split_tokens("read" + command); + argv.push_back(filename); + Pass::call(design, argv); + } else Frontend::frontend_call(design, NULL, filename, command); - design->check(); -} -void run_frontend(std::string filename, std::string command, RTLIL::Design *design) -{ - run_frontend(filename, command, nullptr, nullptr, design); + design->check(); + return false; } void run_pass(std::string command, RTLIL::Design *design) @@ -1233,8 +1402,12 @@ void shell(RTLIL::Design *design) if ((command = fgets(command_buffer, 4096, stdin)) == NULL) break; #endif - if (command[strspn(command, " \t\r\n")] == 0) + if (command[strspn(command, " \t\r\n")] == 0) { +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) + free(command); +#endif continue; + } #if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) add_history(command); #endif @@ -1256,10 +1429,17 @@ void shell(RTLIL::Design *design) log_reset_stack(); } design->check(); +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) + if (command) + free(command); +#endif } if (command == NULL) printf("exit\n"); - +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) + else + free(command); +#endif recursion_counter--; log_cmd_error_throw = false; } @@ -1387,7 +1567,7 @@ struct ScriptCmdPass : public Pass { else if (args.size() == 2) run_frontend(args[1], "script", design); else if (args.size() == 3) - run_frontend(args[1], "script", NULL, &args[2], design); + run_frontend(args[1], "script", design, &args[2]); else extra_args(args, 2, design, false); } diff --git a/kernel/yosys.h b/kernel/yosys.h index ab6eb5f8c4c..0a4641d1819 100644 --- a/kernel/yosys.h +++ b/kernel/yosys.h @@ -1,7 +1,7 @@ /* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2012 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,7 +33,7 @@ // This header is very boring. It just defines some general things that // belong nowhere else and includes the interesting headers. // -// Find more information in the "CodingReadme" file. +// Find more information in the "guidelines/GettingStarted" file. #ifndef YOSYS_H @@ -66,6 +66,8 @@ #include #include #include +#include +#include #ifdef WITH_PYTHON #include @@ -82,6 +84,9 @@ # ifdef YOSYS_MXE_HACKS extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); extern Tcl_Interp *Tcl_CreateInterp(void); +extern void Tcl_Preserve(ClientData data); +extern void Tcl_Release(ClientData clientData); +extern int Tcl_InterpDeleted(Tcl_Interp *interp); extern void Tcl_DeleteInterp(Tcl_Interp *interp); extern int Tcl_Eval(Tcl_Interp *interp, const char *script); extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); @@ -93,6 +98,8 @@ extern Tcl_Obj *Tcl_NewIntObj(int intValue); extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]); extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags); # endif +# undef CONST +# undef INLINE #endif #ifdef _WIN32 @@ -119,8 +126,9 @@ extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *p # define fileno _fileno # endif -// mingw and msvc include `wingdi.h` which defines a TRANSPARENT macro -// that conflicts with X(TRANSPARENT) entry in kernel/constids.inc +// The following defines conflict with our identifiers: +# undef CONST +// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc # undef TRANSPARENT #endif @@ -144,9 +152,7 @@ extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *p # define YS_ATTRIBUTE(...) #endif -#if __cplusplus >= 201703L -# define YS_MAYBE_UNUSED [[maybe_unused]]; -#elif defined(__GNUC__) || defined(__clang__) +#if defined(__GNUC__) || defined(__clang__) # define YS_MAYBE_UNUSED __attribute__((__unused__)) #else # define YS_MAYBE_UNUSED @@ -219,6 +225,7 @@ namespace RTLIL { struct Wire; struct Cell; struct Memory; + struct Process; struct Module; struct Design; struct Monitor; @@ -242,6 +249,7 @@ namespace hashlib { template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; @@ -250,6 +258,7 @@ namespace hashlib { template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; + template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; template<> struct hash_ops : hash_obj_ops {}; @@ -265,8 +274,64 @@ inline void memhasher() { if (memhasher_active) memhasher_do(); } void yosys_banner(); int ceil_log2(int x) YS_ATTRIBUTE(const); + +inline std::string vstringf(const char *fmt, va_list ap) +{ + // For the common case of strings shorter than 128, save a heap + // allocation by using a stack allocated buffer. + const int kBufSize = 128; + char buf[kBufSize]; + buf[0] = '\0'; + va_list apc; + va_copy(apc, ap); + int n = vsnprintf(buf, kBufSize, fmt, apc); + va_end(apc); + if (n < kBufSize) + return std::string(buf); + + std::string string; + char *str = NULL; +#if defined(_WIN32 )|| defined(__CYGWIN__) + int sz = 2 * kBufSize, rc; + while (1) { + va_copy(apc, ap); + str = (char*)realloc(str, sz); + rc = vsnprintf(str, sz, fmt, apc); + va_end(apc); + if (rc >= 0 && rc < sz) + break; + sz *= 2; + } + if (str != NULL) { + string = str; + free(str); + } + return string; +#else + if (vasprintf(&str, fmt, ap) < 0) + str = NULL; + if (str != NULL) { + string = str; + free(str); + } + return string; +#endif +} + std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2)); -std::string vstringf(const char *fmt, va_list ap); + +inline std::string stringf(const char *fmt, ...) +{ + std::string string; + va_list ap; + + va_start(ap, fmt); + string = vstringf(fmt, ap); + va_end(ap); + + return string; +} + int readsome(std::istream &f, char *s, int n); std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false); std::vector split_tokens(const std::string &text, const char *sep = " \t\r\n"); @@ -274,15 +339,18 @@ bool patmatch(const char *pattern, const char *string); #if !defined(YOSYS_DISABLE_SPAWN) int run_command(const std::string &command, std::function process_line = std::function()); #endif -std::string make_temp_file(std::string template_str = "/tmp/yosys_XXXXXX"); -std::string make_temp_dir(std::string template_str = "/tmp/yosys_XXXXXX"); +std::string get_base_tmpdir(); +std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); +std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX"); bool check_file_exists(std::string filename, bool is_exec = false); +bool check_directory_exists(const std::string& dirname); bool is_absolute_path(std::string filename); void remove_directory(std::string dirname); +bool create_directory(const std::string& dirname); std::string escape_filename_spaces(const std::string& filename); template int GetSize(const T &obj) { return obj.size(); } -int GetSize(RTLIL::Wire *wire); +inline int GetSize(RTLIL::Wire *wire); extern int autoidx; extern int yosys_xtrace; @@ -318,9 +386,12 @@ Tcl_Interp *yosys_get_tcl_interp(); extern RTLIL::Design *yosys_design; RTLIL::IdString new_id(std::string file, int line, std::string func); +RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix); #define NEW_ID \ YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__) +#define NEW_ID_SUFFIX(suffix) \ + YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix) // Create a statically allocated IdString object, using for example ID::A or ID($add). // @@ -343,8 +414,7 @@ std::vector glob_filename(const std::string &filename_pattern); void rewrite_filename(std::string &filename); void run_pass(std::string command, RTLIL::Design *design = nullptr); -void run_frontend(std::string filename, std::string command, std::string *backend_command, std::string *from_to_label = nullptr, RTLIL::Design *design = nullptr); -void run_frontend(std::string filename, std::string command, RTLIL::Design *design = nullptr); +bool run_frontend(std::string filename, std::string command, RTLIL::Design *design = nullptr, std::string *from_to_label = nullptr); void run_backend(std::string filename, std::string command, RTLIL::Design *design = nullptr); void shell(RTLIL::Design *design); diff --git a/kernel/yw.cc b/kernel/yw.cc new file mode 100644 index 00000000000..ef043fb0d0f --- /dev/null +++ b/kernel/yw.cc @@ -0,0 +1,207 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yw.h" +#include "libs/json11/json11.hpp" + +USING_YOSYS_NAMESPACE + +// Use the same formatting as witness.py uses +static const char *pretty_name(IdString id) +{ + const char *c_str = id.c_str(); + const char *p = c_str; + + if (*p != '\\') + return c_str; + p++; + + if (*p == '[') { + p++; + while (*p >= '0' && *p <= '9') + p++; + if (p[0] != ']' || p[1] != 0) + return c_str; + return c_str + 1; + } + + if (!(*p >= 'a' && *p <= 'z') && !(*p >= 'A' && *p <= 'Z') && *p != '_') + return c_str; + p++; + while ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_') + p++; + + if (*p != 0) + return c_str; + return c_str + 1; +} + +std::string IdPath::str() const +{ + std::string result; + + for (auto &item : *this) { + const char *pretty = pretty_name(item); + if (pretty[0] == '[') { + result += pretty; + continue; + } + if (!result.empty()) + result += '.'; + result += pretty; + if (pretty[0] == '\\' || pretty[0] == '$') + result += ' '; + } + + return result; +} + +bool IdPath::get_address(int &addr) const +{ + if (empty()) + return false; + auto &last = back(); + if (!last.begins_with("\\[")) + return false; + if (last == "\\[0]") { + addr = 0; + return true; + } + char first = last.c_str()[2]; + if (first < '1' || first > '9') + return false; + char *endptr; + addr = std::strtol(last.c_str() + 2, &endptr, 10); + return endptr[0] == ']' && endptr[1] == 0; +} + +static std::vector get_path(const json11::Json &json) +{ + std::vector result; + for (auto &path_item : json.array_items()) { + auto const &path_item_str = path_item.string_value(); + if (path_item_str.empty()) + return {};; + result.push_back(path_item_str); + } + return result; +} + +ReadWitness::ReadWitness(const std::string &filename) : + filename(filename) +{ + std::ifstream f(filename.c_str()); + if (f.fail() || GetSize(filename) == 0) + log_error("Cannot open file `%s`\n", filename.c_str()); + std::stringstream buf; + buf << f.rdbuf(); + std::string err; + json11::Json json = json11::Json::parse(buf.str(), err); + if (!err.empty()) + log_error("Failed to parse `%s`: %s\n", filename.c_str(), err.c_str()); + + std::string format = json["format"].string_value(); + + if (format.empty()) + log_error("Failed to parse `%s`: Unknown format\n", filename.c_str()); + if (format != "Yosys Witness Trace") + log_error("Failed to parse `%s`: Unsupported format `%s`\n", filename.c_str(), format.c_str()); + + for (auto &clock_json : json["clocks"].array_items()) { + Clock clock; + clock.path = get_path(clock_json["path"]); + if (clock.path.empty()) + log_error("Failed to parse `%s`: Missing path for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); + auto edge_str = clock_json["edge"]; + if (edge_str.string_value() == "posedge") + clock.is_posedge = true; + else if (edge_str.string_value() == "negedge") + clock.is_negedge = true; + else + log_error("Failed to parse `%s`: Unknown edge type for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); + if (!clock_json["offset"].is_number()) + log_error("Failed to parse `%s`: Unknown offset for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); + clock.offset = clock_json["offset"].int_value(); + if (clock.offset < 0) + log_error("Failed to parse `%s`: Invalid offset for clock `%s`\n", filename.c_str(), clock_json.dump().c_str()); + clocks.push_back(clock); + } + + int bits_offset = 0; + for (auto &signal_json : json["signals"].array_items()) { + Signal signal; + signal.bits_offset = bits_offset; + signal.path = get_path(signal_json["path"]); + if (signal.path.empty()) + log_error("Failed to parse `%s`: Missing path for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + if (!signal_json["width"].is_number()) + log_error("Failed to parse `%s`: Unknown width for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + signal.width = signal_json["width"].int_value(); + if (signal.width < 0) + log_error("Failed to parse `%s`: Invalid width for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + bits_offset += signal.width; + if (!signal_json["offset"].is_number()) + log_error("Failed to parse `%s`: Unknown offset for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + signal.offset = signal_json["offset"].int_value(); + if (signal.offset < 0) + log_error("Failed to parse `%s`: Invalid offset for signal `%s`\n", filename.c_str(), signal_json.dump().c_str()); + signal.init_only = signal_json["init_only"].bool_value(); + signals.push_back(signal); + } + + for (auto &step_json : json["steps"].array_items()) { + Step step; + if (!step_json["bits"].is_string()) + log_error("Failed to parse `%s`: Expected string as bits value for step %d\n", filename.c_str(), GetSize(steps)); + step.bits = step_json["bits"].string_value(); + for (char c : step.bits) { + if (c != '0' && c != '1' && c != 'x' && c != '?') + log_error("Failed to parse `%s`: Invalid bit '%c' value for step %d\n", filename.c_str(), c, GetSize(steps)); + } + steps.push_back(step); + } +} + +RTLIL::Const ReadWitness::get_bits(int t, int bits_offset, int width) const +{ + log_assert(t >= 0 && t < GetSize(steps)); + + const std::string &bits = steps[t].bits; + + RTLIL::Const result(State::Sa, width); + result.bits.reserve(width); + + int read_begin = GetSize(bits) - 1 - bits_offset; + int read_end = max(-1, read_begin - width); + + for (int i = read_begin, j = 0; i > read_end; i--, j++) { + RTLIL::State bit = State::Sa; + switch (bits[i]) { + case '0': bit = State::S0; break; + case '1': bit = State::S1; break; + case 'x': bit = State::Sx; break; + case '?': bit = State::Sa; break; + default: + log_abort(); + } + result.bits[j] = bit; + } + + return result; +} diff --git a/kernel/yw.h b/kernel/yw.h new file mode 100644 index 00000000000..c2f5921b1d9 --- /dev/null +++ b/kernel/yw.h @@ -0,0 +1,182 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2022 Jannis Harder + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef YW_H +#define YW_H + +#include "kernel/yosys.h" +#include "kernel/mem.h" + +YOSYS_NAMESPACE_BEGIN + +struct IdPath : public std::vector +{ + template + IdPath(T&&... args) : std::vector(std::forward(args)...) { } + IdPath prefix() const { return {begin(), end() - !empty()}; } + std::string str() const; + + bool has_address() const { int tmp; return get_address(tmp); }; + bool get_address(int &addr) const; + + int hash() const { return hashlib::hash_ops>::hash(*this); } +}; + +struct WitnessHierarchyItem { + RTLIL::Module *module; + RTLIL::Wire *wire = nullptr; + RTLIL::Cell *cell = nullptr; + Mem *mem = nullptr; + + WitnessHierarchyItem(RTLIL::Module *module, RTLIL::Wire *wire) : module(module), wire(wire) {} + WitnessHierarchyItem(RTLIL::Module *module, RTLIL::Cell *cell) : module(module), cell(cell) {} + WitnessHierarchyItem(RTLIL::Module *module, Mem *mem) : module(module), mem(mem) {} +}; + +template +void witness_hierarchy(RTLIL::Module *module, D data, T callback); + +template static std::vector witness_path(T *obj) { + std::vector path; + if (obj->name.isPublic()) { + auto hdlname = obj->get_string_attribute(ID::hdlname); + for (auto token : split_tokens(hdlname)) + path.push_back("\\" + token); + } + if (path.empty()) + path.push_back(obj->name.str()); + return path; +} + +struct ReadWitness +{ + struct Clock { + IdPath path; + int offset; + bool is_posedge = false; + bool is_negedge = false; + }; + + struct Signal { + IdPath path; + int offset; + int width; + bool init_only; + + int bits_offset; + }; + + struct Step { + std::string bits; + }; + + std::string filename; + std::vector clocks; + std::vector signals; + std::vector steps; + + ReadWitness(const std::string &filename); + + RTLIL::Const get_bits(int t, int bits_offset, int width) const; +}; + +template +void witness_hierarchy_recursion(IdPath &path, int hdlname_mode, RTLIL::Module *module, D data, T &callback) +{ + auto const &const_path = path; + size_t path_size = path.size(); + for (auto wire : module->wires()) + { + auto hdlname = hdlname_mode < 0 ? std::vector() : wire->get_hdlname_attribute(); + for (auto item : hdlname) + path.push_back("\\" + item); + if (hdlname.size() == 1 && path.back() == wire->name) + hdlname.clear(); + if (!hdlname.empty()) + callback(const_path, WitnessHierarchyItem(module, wire), data); + path.resize(path_size); + if (hdlname.empty() || hdlname_mode <= 0) { + path.push_back(wire->name); + callback(const_path, WitnessHierarchyItem(module, wire), data); + path.pop_back(); + } + } + + for (auto cell : module->cells()) + { + Module *child = module->design->module(cell->type); + if (child == nullptr) + continue; + + auto hdlname = hdlname_mode < 0 ? std::vector() : cell->get_hdlname_attribute(); + for (auto item : hdlname) + path.push_back("\\" + item); + if (hdlname.size() == 1 && path.back() == cell->name) + hdlname.clear(); + if (!hdlname.empty()) { + D child_data = callback(const_path, WitnessHierarchyItem(module, cell), data); + witness_hierarchy_recursion(path, 1, child, child_data, callback); + } + path.resize(path_size); + if (hdlname.empty() || hdlname_mode <= 0) { + path.push_back(cell->name); + D child_data = callback(const_path, WitnessHierarchyItem(module, cell), data); + witness_hierarchy_recursion(path, hdlname.empty() ? hdlname_mode : -1, child, child_data, callback); + path.pop_back(); + } + } + + for (auto mem : Mem::get_all_memories(module)) { + std::vector hdlname; + + if (hdlname_mode >= 0 && mem.cell != nullptr) + hdlname = mem.cell->get_hdlname_attribute(); + for (auto item : hdlname) + path.push_back("\\" + item); + if (hdlname.size() == 1 && path.back() == mem.cell->name) + hdlname.clear(); + if (!hdlname.empty()) { + callback(const_path, WitnessHierarchyItem(module, &mem), data); + } + path.resize(path_size); + + if (hdlname.empty() || hdlname_mode <= 0) { + path.push_back(mem.memid); + callback(const_path, WitnessHierarchyItem(module, &mem), data); + path.pop_back(); + + if (mem.cell != nullptr && mem.cell->name != mem.memid) { + path.push_back(mem.cell->name); + callback(const_path, WitnessHierarchyItem(module, &mem), data); + path.pop_back(); + } + } + } +} + +template +void witness_hierarchy(RTLIL::Module *module, D data, T callback) +{ + IdPath path; + witness_hierarchy_recursion(path, 0, module, data, callback); +} + +YOSYS_NAMESPACE_END + +#endif diff --git a/libs/bigint/README b/libs/bigint/README index e1842381e72..c6e9bdba617 100644 --- a/libs/bigint/README +++ b/libs/bigint/README @@ -1,5 +1,5 @@ -Note by Clifford Wolf: +Note by Claire Wolf: This version of bigint was downloaded at 2012-08-29 from https://mattmccutchen.net/bigint/bigint-2010.04.30.tar.bz2 diff --git a/libs/bigint/run-testsuite b/libs/bigint/run-testsuite index ff737291641..8436ebda057 100755 --- a/libs/bigint/run-testsuite +++ b/libs/bigint/run-testsuite @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash bad= diff --git a/libs/dlfcn-win32/dlfcn.cc b/libs/dlfcn-win32/dlfcn.cc new file mode 100644 index 00000000000..03ed34dbaa4 --- /dev/null +++ b/libs/dlfcn-win32/dlfcn.cc @@ -0,0 +1,820 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * Copyright (c) 2015 Tiancheng "Timothy" Gu + * Copyright (c) 2019 Pali Rohár + * Copyright (c) 2020 Ralf Habacker + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif +#include +#include +#include + +/* Older versions do not have this type */ +#if _WIN32_WINNT < 0x0500 +typedef ULONG ULONG_PTR; +#endif + +/* Older SDK versions do not have these macros */ +#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS +#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4 +#endif +#ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT +#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2 +#endif + +#ifdef _MSC_VER +/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */ +#pragma intrinsic( _ReturnAddress ) +#else +/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */ +#ifndef _ReturnAddress +#define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) ) +#endif +#endif + +#ifdef DLFCN_WIN32_SHARED +#define DLFCN_WIN32_EXPORTS +#endif +#include "dlfcn.h" + +#if defined( _MSC_VER ) && _MSC_VER >= 1300 +/* https://docs.microsoft.com/en-us/cpp/cpp/noinline */ +#define DLFCN_NOINLINE __declspec( noinline ) +#elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) ) +/* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */ +#define DLFCN_NOINLINE __attribute__(( noinline )) +#else +#define DLFCN_NOINLINE +#endif + +/* Note: + * MSDN says these functions are not thread-safe. We make no efforts to have + * any kind of thread safety. + */ + +typedef struct local_object { + HMODULE hModule; + struct local_object *previous; + struct local_object *next; +} local_object; + +static local_object first_object; + +/* These functions implement a double linked list for the local objects. */ +static local_object *local_search( HMODULE hModule ) +{ + local_object *pobject; + + if( hModule == NULL ) + return NULL; + + for( pobject = &first_object; pobject; pobject = pobject->next ) + if( pobject->hModule == hModule ) + return pobject; + + return NULL; +} + +static BOOL local_add( HMODULE hModule ) +{ + local_object *pobject; + local_object *nobject; + + if( hModule == NULL ) + return TRUE; + + pobject = local_search( hModule ); + + /* Do not add object again if it's already on the list */ + if( pobject != NULL ) + return TRUE; + + for( pobject = &first_object; pobject->next; pobject = pobject->next ); + + nobject = (local_object *) malloc( sizeof( local_object ) ); + + if( !nobject ) + return FALSE; + + pobject->next = nobject; + nobject->next = NULL; + nobject->previous = pobject; + nobject->hModule = hModule; + + return TRUE; +} + +static void local_rem( HMODULE hModule ) +{ + local_object *pobject; + + if( hModule == NULL ) + return; + + pobject = local_search( hModule ); + + if( pobject == NULL ) + return; + + if( pobject->next ) + pobject->next->previous = pobject->previous; + if( pobject->previous ) + pobject->previous->next = pobject->next; + + free( pobject ); +} + +/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one + * static buffer. + * MSDN says the buffer cannot be larger than 64K bytes, so we set it to + * the limit. + */ +static char error_buffer[65535]; +static BOOL error_occurred; + +static void save_err_str( const char *str, DWORD dwMessageId ) +{ + DWORD ret; + size_t pos, len; + + len = strlen( str ); + if( len > sizeof( error_buffer ) - 5 ) + len = sizeof( error_buffer ) - 5; + + /* Format error message to: + * "": + */ + pos = 0; + error_buffer[pos++] = '"'; + memcpy( error_buffer + pos, str, len ); + pos += len; + error_buffer[pos++] = '"'; + error_buffer[pos++] = ':'; + error_buffer[pos++] = ' '; + + ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + error_buffer + pos, (DWORD) ( sizeof( error_buffer ) - pos ), NULL ); + pos += ret; + + /* When FormatMessageA() fails it returns zero and does not touch buffer + * so add trailing null byte */ + if( ret == 0 ) + error_buffer[pos] = '\0'; + + if( pos > 1 ) + { + /* POSIX says the string must not have trailing */ + if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' ) + error_buffer[pos-2] = '\0'; + } + + error_occurred = TRUE; +} + +static void save_err_ptr_str( const void *ptr, DWORD dwMessageId ) +{ + char ptr_buf[2 + 2 * sizeof( ptr ) + 1]; + char num; + size_t i; + + ptr_buf[0] = '0'; + ptr_buf[1] = 'x'; + + for( i = 0; i < 2 * sizeof( ptr ); i++ ) + { + num = (char) ( ( ( (ULONG_PTR) ptr ) >> ( 8 * sizeof( ptr ) - 4 * ( i + 1 ) ) ) & 0xF ); + ptr_buf[2 + i] = num + ( ( num < 0xA ) ? '0' : ( 'A' - 0xA ) ); + } + + ptr_buf[2 + 2 * sizeof( ptr )] = 0; + + save_err_str( ptr_buf, dwMessageId ); +} + +static UINT MySetErrorMode( UINT uMode ) +{ + static BOOL (WINAPI *SetThreadErrorModePtr)(DWORD, DWORD *) = NULL; + static BOOL failed = FALSE; + HMODULE kernel32; + DWORD oldMode; + + if( !failed && SetThreadErrorModePtr == NULL ) + { + kernel32 = GetModuleHandleA( "Kernel32.dll" ); + if( kernel32 != NULL ) + SetThreadErrorModePtr = (BOOL (WINAPI *)(DWORD, DWORD *)) (LPVOID) GetProcAddress( kernel32, "SetThreadErrorMode" ); + if( SetThreadErrorModePtr == NULL ) + failed = TRUE; + } + + if( !failed ) + { + if( !SetThreadErrorModePtr( uMode, &oldMode ) ) + return 0; + else + return oldMode; + } + else + { + return SetErrorMode( uMode ); + } +} + +static HMODULE MyGetModuleHandleFromAddress( const void *addr ) +{ + static BOOL (WINAPI *GetModuleHandleExAPtr)(DWORD, LPCSTR, HMODULE *) = NULL; + static BOOL failed = FALSE; + HMODULE kernel32; + HMODULE hModule; + MEMORY_BASIC_INFORMATION info; + SIZE_T sLen; + + if( !failed && GetModuleHandleExAPtr == NULL ) + { + kernel32 = GetModuleHandleA( "Kernel32.dll" ); + if( kernel32 != NULL ) + GetModuleHandleExAPtr = (BOOL (WINAPI *)(DWORD, LPCSTR, HMODULE *)) (LPVOID) GetProcAddress( kernel32, "GetModuleHandleExA" ); + if( GetModuleHandleExAPtr == NULL ) + failed = TRUE; + } + + if( !failed ) + { + /* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */ + if( !GetModuleHandleExAPtr( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const char *)addr, &hModule ) ) + return NULL; + } + else + { + /* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380 + * The HMODULE of a DLL is the same value as the module's base address. + */ + sLen = VirtualQuery( addr, &info, sizeof( info ) ); + if( sLen != sizeof( info ) ) + return NULL; + hModule = (HMODULE) info.AllocationBase; + } + + return hModule; +} + +/* Load Psapi.dll at runtime, this avoids linking caveat */ +static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded ) +{ + static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD) = NULL; + static BOOL failed = FALSE; + UINT uMode; + HMODULE psapi; + + if( failed ) + return FALSE; + + if( EnumProcessModulesPtr == NULL ) + { + /* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */ + psapi = GetModuleHandleA( "Kernel32.dll" ); + if( psapi != NULL ) + EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) (LPVOID) GetProcAddress( psapi, "K32EnumProcessModules" ); + + /* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */ + if( EnumProcessModulesPtr == NULL ) + { + /* Do not let Windows display the critical-error-handler message box */ + uMode = MySetErrorMode( SEM_FAILCRITICALERRORS ); + psapi = LoadLibraryA( "Psapi.dll" ); + if( psapi != NULL ) + { + EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) (LPVOID) GetProcAddress( psapi, "EnumProcessModules" ); + if( EnumProcessModulesPtr == NULL ) + FreeLibrary( psapi ); + } + MySetErrorMode( uMode ); + } + + if( EnumProcessModulesPtr == NULL ) + { + failed = TRUE; + return FALSE; + } + } + + return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded ); +} + +DLFCN_EXPORT +void *dlopen( const char *file, int mode ) +{ + HMODULE hModule; + UINT uMode; + + error_occurred = FALSE; + + /* Do not let Windows display the critical-error-handler message box */ + uMode = MySetErrorMode( SEM_FAILCRITICALERRORS ); + + if( file == NULL ) + { + /* POSIX says that if the value of file is NULL, a handle on a global + * symbol object must be provided. That object must be able to access + * all symbols from the original program file, and any objects loaded + * with the RTLD_GLOBAL flag. + * The return value from GetModuleHandle( ) allows us to retrieve + * symbols only from the original program file. EnumProcessModules() is + * used to access symbols from other libraries. For objects loaded + * with the RTLD_LOCAL flag, we create our own list later on. They are + * excluded from EnumProcessModules() iteration. + */ + hModule = GetModuleHandle( NULL ); + + if( !hModule ) + save_err_str( "(null)", GetLastError( ) ); + } + else + { + HANDLE hCurrentProc; + DWORD dwProcModsBefore, dwProcModsAfter; + char lpFileName[MAX_PATH]; + size_t i, len; + + len = strlen( file ); + + if( len >= sizeof( lpFileName ) ) + { + save_err_str( file, ERROR_FILENAME_EXCED_RANGE ); + hModule = NULL; + } + else + { + /* MSDN says backslashes *must* be used instead of forward slashes. */ + for( i = 0; i < len; i++ ) + { + if( file[i] == '/' ) + lpFileName[i] = '\\'; + else + lpFileName[i] = file[i]; + } + lpFileName[len] = '\0'; + + hCurrentProc = GetCurrentProcess( ); + + if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 ) + dwProcModsBefore = 0; + + /* POSIX says the search path is implementation-defined. + * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely + * to UNIX's search paths (start with system folders instead of current + * folder). + */ + hModule = LoadLibraryExA( lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); + + if( !hModule ) + { + save_err_str( lpFileName, GetLastError( ) ); + } + else + { + if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 ) + dwProcModsAfter = 0; + + /* If the object was loaded with RTLD_LOCAL, add it to list of local + * objects, so that its symbols cannot be retrieved even if the handle for + * the original program file is passed. POSIX says that if the same + * file is specified in multiple invocations, and any of them are + * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the + * symbols will remain global. If number of loaded modules was not + * changed after calling LoadLibraryEx(), it means that library was + * already loaded. + */ + if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter ) + { + if( !local_add( hModule ) ) + { + save_err_str( lpFileName, ERROR_NOT_ENOUGH_MEMORY ); + FreeLibrary( hModule ); + hModule = NULL; + } + } + else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter ) + { + local_rem( hModule ); + } + } + } + } + + /* Return to previous state of the error-mode bit flags. */ + MySetErrorMode( uMode ); + + return (void *) hModule; +} + +DLFCN_EXPORT +int dlclose( void *handle ) +{ + HMODULE hModule = (HMODULE) handle; + BOOL ret; + + error_occurred = FALSE; + + ret = FreeLibrary( hModule ); + + /* If the object was loaded with RTLD_LOCAL, remove it from list of local + * objects. + */ + if( ret ) + local_rem( hModule ); + else + save_err_ptr_str( handle, GetLastError( ) ); + + /* dlclose's return value in inverted in relation to FreeLibrary's. */ + ret = !ret; + + return (int) ret; +} + +DLFCN_NOINLINE /* Needed for _ReturnAddress() */ +DLFCN_EXPORT +void *dlsym( void *handle, const char *name ) +{ + FARPROC symbol; + HMODULE hCaller; + HMODULE hModule; + DWORD dwMessageId; + + error_occurred = FALSE; + + symbol = NULL; + hCaller = NULL; + hModule = GetModuleHandle( NULL ); + dwMessageId = 0; + + if( handle == RTLD_DEFAULT ) + { + /* The symbol lookup happens in the normal global scope; that is, + * a search for a symbol using this handle would find the same + * definition as a direct use of this symbol in the program code. + * So use same lookup procedure as when filename is NULL. + */ + handle = hModule; + } + else if( handle == RTLD_NEXT ) + { + /* Specifies the next object after this one that defines name. + * This one refers to the object containing the invocation of dlsym(). + * The next object is the one found upon the application of a load + * order symbol resolution algorithm. To get caller function of dlsym() + * use _ReturnAddress() intrinsic. To get HMODULE of caller function + * use MyGetModuleHandleFromAddress() which calls either standard + * GetModuleHandleExA() function or hack via VirtualQuery(). + */ + hCaller = MyGetModuleHandleFromAddress( _ReturnAddress( ) ); + + if( hCaller == NULL ) + { + dwMessageId = ERROR_INVALID_PARAMETER; + goto end; + } + } + + if( handle != RTLD_NEXT ) + { + symbol = GetProcAddress( (HMODULE) handle, name ); + + if( symbol != NULL ) + goto end; + } + + /* If the handle for the original program file is passed, also search + * in all globally loaded objects. + */ + + if( hModule == handle || handle == RTLD_NEXT ) + { + HANDLE hCurrentProc; + HMODULE *modules; + DWORD cbNeeded; + DWORD dwSize; + size_t i; + + hCurrentProc = GetCurrentProcess( ); + + /* GetModuleHandle( NULL ) only returns the current program file. So + * if we want to get ALL loaded module including those in linked DLLs, + * we have to use EnumProcessModules( ). + */ + if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 ) + { + modules = (HMODULE *)malloc( dwSize ); + if( modules ) + { + if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded ) + { + for( i = 0; i < dwSize / sizeof( HMODULE ); i++ ) + { + if( handle == RTLD_NEXT && hCaller ) + { + /* Next modules can be used for RTLD_NEXT */ + if( hCaller == modules[i] ) + hCaller = NULL; + continue; + } + if( local_search( modules[i] ) ) + continue; + symbol = GetProcAddress( modules[i], name ); + if( symbol != NULL ) + { + free( modules ); + goto end; + } + } + + } + free( modules ); + } + else + { + dwMessageId = ERROR_NOT_ENOUGH_MEMORY; + goto end; + } + } + } + +end: + if( symbol == NULL ) + { + if( !dwMessageId ) + dwMessageId = ERROR_PROC_NOT_FOUND; + save_err_str( name, dwMessageId ); + } + + return *(void **) (&symbol); +} + +DLFCN_EXPORT +char *dlerror( void ) +{ + /* If this is the second consecutive call to dlerror, return NULL */ + if( !error_occurred ) + return NULL; + + /* POSIX says that invoking dlerror( ) a second time, immediately following + * a prior invocation, shall result in NULL being returned. + */ + error_occurred = FALSE; + + return error_buffer; +} + +/* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2 + * for details */ + +/* Get specific image section */ +static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size ) +{ + IMAGE_DOS_HEADER *dosHeader; + IMAGE_NT_HEADERS *ntHeaders; + IMAGE_OPTIONAL_HEADER *optionalHeader; + + dosHeader = (IMAGE_DOS_HEADER *) module; + + if( dosHeader->e_magic != IMAGE_DOS_SIGNATURE ) + return FALSE; + + ntHeaders = (IMAGE_NT_HEADERS *) ( (BYTE *) dosHeader + dosHeader->e_lfanew ); + + if( ntHeaders->Signature != IMAGE_NT_SIGNATURE ) + return FALSE; + + optionalHeader = &ntHeaders->OptionalHeader; + + if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC ) + return FALSE; + + if( index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ) + return FALSE; + + if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 ) + return FALSE; + + if( size != NULL ) + *size = optionalHeader->DataDirectory[index].Size; + + *ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress ); + + return TRUE; +} + +/* Return symbol name for a given address from export table */ +static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, const void *addr, void **func_address ) +{ + DWORD i; + void *candidateAddr = NULL; + int candidateIndex = -1; + BYTE *base = (BYTE *) module; + DWORD *functionAddressesOffsets = (DWORD *) (base + ied->AddressOfFunctions); + DWORD *functionNamesOffsets = (DWORD *) (base + ied->AddressOfNames); + USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + ied->AddressOfNameOrdinals); + + for( i = 0; i < ied->NumberOfFunctions; i++ ) + { + if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) ) + continue; + + candidateAddr = (void *) ( base + functionAddressesOffsets[i] ); + candidateIndex = i; + } + + if( candidateIndex == -1 ) + return NULL; + + *func_address = candidateAddr; + + for( i = 0; i < ied->NumberOfNames; i++ ) + { + if( functionNameOrdinalsIndexes[i] == candidateIndex ) + return (const char *) ( base + functionNamesOffsets[i] ); + } + + return NULL; +} + +static BOOL is_valid_address( const void *addr ) +{ + MEMORY_BASIC_INFORMATION info; + SIZE_T result; + + if( addr == NULL ) + return FALSE; + + /* check valid pointer */ + result = VirtualQuery( addr, &info, sizeof( info ) ); + + if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS ) + return FALSE; + + return TRUE; +} + +/* Return state if address points to an import thunk + * + * An import thunk is setup with a 'jmp' instruction followed by an + * absolute address (32bit) or relative offset (64bit) pointing into + * the import address table (iat), which is partially maintained by + * the runtime linker. + */ +static BOOL is_import_thunk( const void *addr ) +{ + return *(short *) addr == 0x25ff ? TRUE : FALSE; +} + +/* Return adress from the import address table (iat), + * if the original address points to a thunk table entry. + */ +static void *get_address_from_import_address_table( void *iat, DWORD iat_size, const void *addr ) +{ + BYTE *thkp = (BYTE *) addr; + /* Get offset from thunk table (after instruction 0xff 0x25) + * 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00 + */ + ULONG offset = *(ULONG *)( thkp + 2 ); +#ifdef _WIN64 + /* On 64 bit the offset is relative + * 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery> + * And can be also negative (MSVC in WDK) + * 100002f20: ff 25 3a e1 ff ff jmpq *-0x1ec6(%rip) # 0x100001060 + * So cast to signed LONG type + */ + BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset ); +#else + /* On 32 bit the offset is absolute + * 4019b4: ff 25 90 71 40 00 jmp *0x40719 + */ + BYTE *ptr = (BYTE *) offset; +#endif + + if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size ) + return NULL; + + return *(void **) ptr; +} + +/* Holds module filename */ +static char module_filename[2*MAX_PATH]; + +static BOOL fill_info( const void *addr, Dl_info *info ) +{ + HMODULE hModule; + DWORD dwSize; + IMAGE_EXPORT_DIRECTORY *ied; + void *funcAddress = NULL; + + /* Get module of the specified address */ + hModule = MyGetModuleHandleFromAddress( addr ); + + if( hModule == NULL ) + return FALSE; + + dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) ); + + if( dwSize == 0 || dwSize == sizeof( module_filename ) ) + return FALSE; + + info->dli_fname = module_filename; + info->dli_fbase = (void *) hModule; + + /* Find function name and function address in module's export table */ + if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) ) + info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress ); + else + info->dli_sname = NULL; + + info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : (void *) addr; + + return TRUE; +} + +DLFCN_EXPORT +int dladdr( const void *addr, Dl_info *info ) +{ + if( info == NULL ) + return 0; + + if( !is_valid_address( addr ) ) + return 0; + + if( is_import_thunk( addr ) ) + { + void *iat; + DWORD iatSize; + HMODULE hModule; + + /* Get module of the import thunk address */ + hModule = MyGetModuleHandleFromAddress( addr ); + + if( hModule == NULL ) + return 0; + + if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) ) + { + /* Fallback for cases where the iat is not defined, + * for example i586-mingw32msvc-gcc */ + IMAGE_IMPORT_DESCRIPTOR *iid; + DWORD iidSize; + + if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) ) + return 0; + + if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 ) + return 0; + + iat = (void *)( (BYTE *) hModule + iid->FirstThunk ); + /* We assume that in this case iid and iat's are in linear order */ + iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid ); + } + + addr = get_address_from_import_address_table( iat, iatSize, addr ); + + if( !is_valid_address( addr ) ) + return 0; + } + + if( !fill_info( addr, info ) ) + return 0; + + return 1; +} + +#ifdef DLFCN_WIN32_SHARED +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) +{ + (void) hinstDLL; + (void) fdwReason; + (void) lpvReserved; + return TRUE; +} +#endif diff --git a/libs/dlfcn-win32/dlfcn.h b/libs/dlfcn-win32/dlfcn.h new file mode 100644 index 00000000000..bf5c7d4d19b --- /dev/null +++ b/libs/dlfcn-win32/dlfcn.h @@ -0,0 +1,94 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef DLFCN_H +#define DLFCN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(DLFCN_WIN32_SHARED) +#if defined(DLFCN_WIN32_EXPORTS) +# define DLFCN_EXPORT __declspec(dllexport) +#else +# define DLFCN_EXPORT __declspec(dllimport) +#endif +#else +# define DLFCN_EXPORT +#endif + +/* Relocations are performed when the object is loaded. */ +#define RTLD_NOW 0 + +/* Relocations are performed at an implementation-defined time. + * Windows API does not support lazy symbol resolving (when first reference + * to a given symbol occurs). So RTLD_LAZY implementation is same as RTLD_NOW. + */ +#define RTLD_LAZY RTLD_NOW + +/* All symbols are available for relocation processing of other modules. */ +#define RTLD_GLOBAL (1 << 1) + +/* All symbols are not made available for relocation processing by other modules. */ +#define RTLD_LOCAL (1 << 2) + +/* These two were added in The Open Group Base Specifications Issue 6. + * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant. + */ + +/* The symbol lookup happens in the normal global scope. */ +#define RTLD_DEFAULT ((void *)0) + +/* Specifies the next object after this one that defines name. */ +#define RTLD_NEXT ((void *)-1) + +/* Structure filled in by dladdr() */ +typedef struct dl_info +{ + const char *dli_fname; /* Filename of defining object (thread unsafe and reused on every call to dladdr) */ + void *dli_fbase; /* Load address of that object */ + const char *dli_sname; /* Name of nearest lower symbol */ + void *dli_saddr; /* Exact value of nearest symbol */ +} Dl_info; + +/* Open a symbol table handle. */ +DLFCN_EXPORT void *dlopen(const char *file, int mode); + +/* Close a symbol table handle. */ +DLFCN_EXPORT int dlclose(void *handle); + +/* Get the address of a symbol from a symbol table handle. */ +DLFCN_EXPORT void *dlsym(void *handle, const char *name); + +/* Get diagnostic information. */ +DLFCN_EXPORT char *dlerror(void); + +/* Translate address to symbolic information (no POSIX standard) */ +DLFCN_EXPORT int dladdr(const void *addr, Dl_info *info); + +#ifdef __cplusplus +} +#endif + +#endif /* DLFCN_H */ diff --git a/libs/ezsat/Makefile b/libs/ezsat/Makefile index b1f86416093..c41038dc938 100644 --- a/libs/ezsat/Makefile +++ b/libs/ezsat/Makefile @@ -1,9 +1,9 @@ CC = clang -CXX = clang +CXX = clang++ CXXFLAGS = -MD -Wall -Wextra -ggdb CXXFLAGS += -std=c++11 -O0 -LDLIBS = ../minisat/Options.cc ../minisat/SimpSolver.cc ../minisat/Solver.cc ../minisat/System.cc -lm -lstdc++ +LIBS = ../minisat/Options.cc ../minisat/SimpSolver.cc ../minisat/Solver.cc ../minisat/System.cc -lm -lstdc++ all: demo_vec demo_bit demo_cmp testbench puzzle3d @@ -27,4 +27,3 @@ clean: .PHONY: all test clean -include *.d - diff --git a/libs/ezsat/README b/libs/ezsat/README index c6745e6cf0f..db0a18a4e22 100644 --- a/libs/ezsat/README +++ b/libs/ezsat/README @@ -4,7 +4,7 @@ * The ezSAT C++11 library * * * * A simple frontend to SAT solvers with bindings to MiniSAT. * - * by Clifford Wolf * + * by Claire Xenia Wolf * * * ************************************************************************** diff --git a/libs/ezsat/demo_bit.cc b/libs/ezsat/demo_bit.cc index c7b11246cdf..b4b68970181 100644 --- a/libs/ezsat/demo_bit.cc +++ b/libs/ezsat/demo_bit.cc @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/ezsat/demo_cmp.cc b/libs/ezsat/demo_cmp.cc index 8d7ceb2b4b7..7b927c66402 100644 --- a/libs/ezsat/demo_cmp.cc +++ b/libs/ezsat/demo_cmp.cc @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/ezsat/demo_vec.cc b/libs/ezsat/demo_vec.cc index eb8d75997fb..a13430d5570 100644 --- a/libs/ezsat/demo_vec.cc +++ b/libs/ezsat/demo_vec.cc @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/ezsat/ezminisat.cc b/libs/ezsat/ezminisat.cc index ac4defac386..30df625cbb7 100644 --- a/libs/ezsat/ezminisat.cc +++ b/libs/ezsat/ezminisat.cc @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/ezsat/ezminisat.h b/libs/ezsat/ezminisat.h index 3a34c13c898..92a91d744a2 100644 --- a/libs/ezsat/ezminisat.h +++ b/libs/ezsat/ezminisat.h @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/ezsat/ezsat.cc b/libs/ezsat/ezsat.cc index 8c666ca1f97..3b089ccca9d 100644 --- a/libs/ezsat/ezsat.cc +++ b/libs/ezsat/ezsat.cc @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/ezsat/ezsat.h b/libs/ezsat/ezsat.h index 85b13685f3e..7f3bdf68dd0 100644 --- a/libs/ezsat/ezsat.h +++ b/libs/ezsat/ezsat.h @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/ezsat/puzzle3d.cc b/libs/ezsat/puzzle3d.cc index 59f840f9ec0..d39def6c6c8 100644 --- a/libs/ezsat/puzzle3d.cc +++ b/libs/ezsat/puzzle3d.cc @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/ezsat/testbench.cc b/libs/ezsat/testbench.cc index d6dc41fa944..f5a91f3fb68 100644 --- a/libs/ezsat/testbench.cc +++ b/libs/ezsat/testbench.cc @@ -1,7 +1,7 @@ /* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/fst/block_format.txt b/libs/fst/block_format.txt new file mode 100644 index 00000000000..e6fe1661b6d --- /dev/null +++ b/libs/fst/block_format.txt @@ -0,0 +1,130 @@ +See fstapi.h for the values for the FST_BL_XXX enums. + +=========================================================================== + +compressed wrapper (typically over whole file) + +uint8_t FST_BL_ZWRAPPER +uint64_t section length +uint64_t length of uncompressed data +[zlib compressed data] + +=========================================================================== + +header block + +uint8_t FST_BL_HDR +uint64_t section length +uint64_t start time +uint64_t end time +double endian test for "e" +uint64_t memory used by writer +uint64_t scope creation count +uint64_t var creation count +uint64_t max var idcode +uint64_t vc section count +int8_t timescale exponent +[128 bytes] version +[128 bytes] date + +=========================================================================== + +geometry block + +uint8_t FST_BL_GEOM +uint64_t section length +uint64_t length of uncompressed geometry data +uint64_t maxhandle +[compressed data] + +(length of compressed data is section length - 24) + +=========================================================================== + +hierarchy block + +uint8_t FST_BL_HIER +uint64_t section length +uint64_t length of uncompressed hier data +[zlib compressed data] + +or + +uint8_t FST_BL_HIER_LZ4 +uint64_t section length +uint64_t length of uncompressed hier data +[lz4 compressed data] + +uint8_t FST_BL_HIER_LZ4DUO +uint64_t section length +uint64_t length of uncompressed hier data +varint length of hier data compressed once with lz4 +[lz4 double compressed data] + + +=========================================================================== + +dumpon/off block + +uint8_t FST_BL_BLACKOUT +uint64_t section length +varint num blackouts (section below is repeated this # times) +[ +uint8_t on/off (nonzero = on) +varint delta time +] + +=========================================================================== + +1..n value change blocks: + +// header + +uint8_t FST_BL_VCDATA (or FST_BL_VCDATA_DYN_ALIAS) +uint64_t section length +uint64_t begin time of section +uint64_t end time of section +uint64_t amount of buffer memory required in reader for full vc traversal +varint maxvalpos (length of uncompressed data) +varint length of compressed data +varint maxhandle associated with this checkpoint data +[compressed data] + +--- + +// value changes + +varint maxhandle associated with the value change data +uint8_t pack type ('F' is fastlz, '4' is lz4, + others ['Z'/'!'] are zlib) + +varint chain 0 compressed data length (0 = uncompressed) +[compressed data] +... +varint chain n compressed data length (0 = uncompressed) +[compressed data] + +--- + +// index: chain pointer table (from 0..maxhandle-1) + +varint if &1 == 1, this is <<1 literal delta + if &1 == 0, this is <<1 RLE count of zeros + if == 0, next varint is handle of prev chain to use, + bit only if FST_BL_VCDATA_DYN_ALIAS or + later VCDATA format + +--- + +uint64_t index length (subtract from here to get index position) + +--- + +[compressed data for time section] +uint64_t uncompressed data length in bytes +uint64_t compressed data length in bytes +uint64_t number of time items + +// end of section + +=========================================================================== diff --git a/libs/fst/config.h b/libs/fst/config.h new file mode 100644 index 00000000000..a2f0fca82c4 --- /dev/null +++ b/libs/fst/config.h @@ -0,0 +1,30 @@ +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#define HAVE_FSEEKO 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#define HAVE_LIBPTHREAD 1 + +/* Define to 1 if you have the `realpath' function. */ +#define HAVE_REALPATH 1 + +#if defined(__MINGW32__) +#undef HAVE_ALLOCA_H +#undef HAVE_REALPATH +#endif +#if defined(_MSC_VER) +#undef HAVE_ALLOCA_H +#undef HAVE_REALPATH +#undef HAVE_LIBPTHREAD +#undef HAVE_FSEEKO +#endif +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#undef HAVE_ALLOCA_H +#endif + +# ifndef __STDC_FORMAT_MACROS +# define __STDC_FORMAT_MACROS 1 +# endif diff --git a/libs/fst/fastlz.cc b/libs/fst/fastlz.cc new file mode 100644 index 00000000000..68bda3346b3 --- /dev/null +++ b/libs/fst/fastlz.cc @@ -0,0 +1,528 @@ +/* + FastLZ - lightning-fast lossless compression library + + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#include "fastlz.h" + +#if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) + +/* + * Always check for bound when decompressing. + * Generally it is best to leave it defined. + */ +#define FASTLZ_SAFE + +/* + * Give hints to the compiler for branch prediction optimization. + */ +#if defined(__GNUC__) && (__GNUC__ > 2) +#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) +#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) +#else +#define FASTLZ_EXPECT_CONDITIONAL(c) (c) +#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c) +#endif + +/* + * Use inlined functions for supported systems. + */ +#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) +#define FASTLZ_INLINE inline +#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__) +#define FASTLZ_INLINE __inline +#else +#define FASTLZ_INLINE +#endif + +/* + * Prevent accessing more than 8-bit at once, except on x86 architectures. + */ +#if !defined(FASTLZ_STRICT_ALIGN) +#define FASTLZ_STRICT_ALIGN +#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__amd64) /* GNU C */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(_M_IX86) /* Intel, MSVC */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__386) +#undef FASTLZ_STRICT_ALIGN +#elif defined(_X86_) /* MinGW */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__I86__) /* Digital Mars */ +#undef FASTLZ_STRICT_ALIGN +#endif +#endif + +/* prototypes */ +int fastlz_compress(const void *input, int length, void *output); +int fastlz_compress_level(int level, const void *input, int length, void *output); +int fastlz_decompress(const void *input, int length, void *output, int maxout); + +#define MAX_COPY 32 +#define MAX_LEN 264 /* 256 + 8 */ +#define MAX_DISTANCE 8192 + +#if !defined(FASTLZ_STRICT_ALIGN) +#define FASTLZ_READU16(p) *((const flzuint16 *)(p)) +#else +#define FASTLZ_READU16(p) ((p)[0] | (p)[1] << 8) +#endif + +#define HASH_LOG 13 +#define HASH_SIZE (1 << HASH_LOG) +#define HASH_MASK (HASH_SIZE - 1) +#define HASH_FUNCTION(v, p) \ + { \ + v = FASTLZ_READU16(p); \ + v ^= FASTLZ_READU16(p + 1) ^ (v >> (16 - HASH_LOG)); \ + v &= HASH_MASK; \ + } + +#undef FASTLZ_LEVEL +#define FASTLZ_LEVEL 1 + +#undef FASTLZ_COMPRESSOR +#undef FASTLZ_DECOMPRESSOR +#define FASTLZ_COMPRESSOR fastlz1_compress +#define FASTLZ_DECOMPRESSOR fastlz1_decompress +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output); +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout); +#include "fastlz.cc" + +#undef FASTLZ_LEVEL +#define FASTLZ_LEVEL 2 + +#undef MAX_DISTANCE +#define MAX_DISTANCE 8191 +#define MAX_FARDISTANCE (65535 + MAX_DISTANCE - 1) + +#undef FASTLZ_COMPRESSOR +#undef FASTLZ_DECOMPRESSOR +#define FASTLZ_COMPRESSOR fastlz2_compress +#define FASTLZ_DECOMPRESSOR fastlz2_decompress +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output); +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout); +#include "fastlz.cc" + +int fastlz_compress(const void *input, int length, void *output) +{ + /* for short block, choose fastlz1 */ + if (length < 65536) + return fastlz1_compress(input, length, output); + + /* else... */ + return fastlz2_compress(input, length, output); +} + +int fastlz_decompress(const void *input, int length, void *output, int maxout) +{ + /* magic identifier for compression level */ + int level = ((*(const flzuint8 *)input) >> 5) + 1; + + if (level == 1) + return fastlz1_decompress(input, length, output, maxout); + if (level == 2) + return fastlz2_decompress(input, length, output, maxout); + + /* unknown level, trigger error */ + return 0; +} + +int fastlz_compress_level(int level, const void *input, int length, void *output) +{ + if (level == 1) + return fastlz1_compress(input, length, output); + if (level == 2) + return fastlz2_compress(input, length, output); + + return 0; +} + +#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ + +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void *input, int length, void *output) +{ + const flzuint8 *ip = (const flzuint8 *)input; + const flzuint8 *ip_bound = ip + length - 2; + const flzuint8 *ip_limit = ip + length - 12; + flzuint8 *op = (flzuint8 *)output; + + const flzuint8 *htab[HASH_SIZE]; + const flzuint8 **hslot; + flzuint32 hval; + + flzuint32 copy; + + /* sanity check */ + if (FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) { + if (length) { + /* create literal copy only */ + *op++ = length - 1; + ip_bound++; + while (ip <= ip_bound) + *op++ = *ip++; + return length + 1; + } else + return 0; + } + + /* initializes hash table */ + for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) + *hslot = ip; + + /* we start with literal copy */ + copy = 2; + *op++ = MAX_COPY - 1; + *op++ = *ip++; + *op++ = *ip++; + + /* main loop */ + while (FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) { + const flzuint8 *ref; + flzuint32 distance; + + /* minimum match length */ + flzuint32 len = 3; + + /* comparison starting-point */ + const flzuint8 *anchor = ip; + + /* check for a run */ +#if FASTLZ_LEVEL == 2 + if (ip[0] == ip[-1] && FASTLZ_READU16(ip - 1) == FASTLZ_READU16(ip + 1)) { + distance = 1; + /* ip += 3; */ /* scan-build, never used */ + ref = anchor - 1 + 3; + goto match; + } +#endif + + /* find potential match */ + HASH_FUNCTION(hval, ip); + hslot = htab + hval; + ref = htab[hval]; + + /* calculate distance to the match */ + distance = anchor - ref; + + /* update hash table */ + *hslot = anchor; + + /* is this a match? check the first 3 bytes */ + if (distance == 0 || +#if FASTLZ_LEVEL == 1 + (distance >= MAX_DISTANCE) || +#else + (distance >= MAX_FARDISTANCE) || +#endif + *ref++ != *ip++ || *ref++ != *ip++ || *ref++ != *ip++) + goto literal; + +#if FASTLZ_LEVEL == 2 + /* far, needs at least 5-byte match */ + if (distance >= MAX_DISTANCE) { + if (*ip++ != *ref++ || *ip++ != *ref++) + goto literal; + len += 2; + } + + match: +#endif + + /* last matched byte */ + ip = anchor + len; + + /* distance is biased */ + distance--; + + if (!distance) { + /* zero distance means a run */ + flzuint8 x = ip[-1]; + while (ip < ip_bound) + if (*ref++ != x) + break; + else + ip++; + } else + for (;;) { + /* safe because the outer check against ip limit */ + if (*ref++ != *ip++) + break; + if (*ref++ != *ip++) + break; + if (*ref++ != *ip++) + break; + if (*ref++ != *ip++) + break; + if (*ref++ != *ip++) + break; + if (*ref++ != *ip++) + break; + if (*ref++ != *ip++) + break; + if (*ref++ != *ip++) + break; + while (ip < ip_bound) + if (*ref++ != *ip++) + break; + break; + } + + /* if we have copied something, adjust the copy count */ + if (copy) + /* copy is biased, '0' means 1 byte copy */ + *(op - copy - 1) = copy - 1; + else + /* back, to overwrite the copy count */ + op--; + + /* reset literal counter */ + copy = 0; + + /* length is biased, '1' means a match of 3 bytes */ + ip -= 3; + len = ip - anchor; + + /* encode the match */ +#if FASTLZ_LEVEL == 2 + if (distance < MAX_DISTANCE) { + if (len < 7) { + *op++ = (len << 5) + (distance >> 8); + *op++ = (distance & 255); + } else { + *op++ = (7 << 5) + (distance >> 8); + for (len -= 7; len >= 255; len -= 255) + *op++ = 255; + *op++ = len; + *op++ = (distance & 255); + } + } else { + /* far away, but not yet in the another galaxy... */ + if (len < 7) { + distance -= MAX_DISTANCE; + *op++ = (len << 5) + 31; + *op++ = 255; + *op++ = distance >> 8; + *op++ = distance & 255; + } else { + distance -= MAX_DISTANCE; + *op++ = (7 << 5) + 31; + for (len -= 7; len >= 255; len -= 255) + *op++ = 255; + *op++ = len; + *op++ = 255; + *op++ = distance >> 8; + *op++ = distance & 255; + } + } +#else + + if (FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN - 2)) + while (len > MAX_LEN - 2) { + *op++ = (7 << 5) + (distance >> 8); + *op++ = MAX_LEN - 2 - 7 - 2; + *op++ = (distance & 255); + len -= MAX_LEN - 2; + } + + if (len < 7) { + *op++ = (len << 5) + (distance >> 8); + *op++ = (distance & 255); + } else { + *op++ = (7 << 5) + (distance >> 8); + *op++ = len - 7; + *op++ = (distance & 255); + } +#endif + + /* update the hash at match boundary */ + HASH_FUNCTION(hval, ip); + htab[hval] = ip++; + HASH_FUNCTION(hval, ip); + htab[hval] = ip++; + + /* assuming literal copy */ + *op++ = MAX_COPY - 1; + + continue; + + literal: + *op++ = *anchor++; + ip = anchor; + copy++; + if (FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) { + copy = 0; + *op++ = MAX_COPY - 1; + } + } + + /* left-over as literal copy */ + ip_bound++; + while (ip <= ip_bound) { + *op++ = *ip++; + copy++; + if (copy == MAX_COPY) { + copy = 0; + *op++ = MAX_COPY - 1; + } + } + + /* if we have copied something, adjust the copy length */ + if (copy) + *(op - copy - 1) = copy - 1; + else + op--; + +#if FASTLZ_LEVEL == 2 + /* marker for fastlz2 */ + *(flzuint8 *)output |= (1 << 5); +#endif + + return op - (flzuint8 *)output; +} + +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void *input, int length, void *output, int maxout) +{ + const flzuint8 *ip = (const flzuint8 *)input; + const flzuint8 *ip_limit = ip + length; + flzuint8 *op = (flzuint8 *)output; + flzuint8 *op_limit = op + maxout; + flzuint32 ctrl = (*ip++) & 31; + int loop = 1; + + do { + const flzuint8 *ref = op; + flzuint32 len = ctrl >> 5; + flzuint32 ofs = (ctrl & 31) << 8; + + if (ctrl >= 32) { +#if FASTLZ_LEVEL == 2 + flzuint8 code; +#endif + len--; + ref -= ofs; + if (len == 7 - 1) +#if FASTLZ_LEVEL == 1 + len += *ip++; + ref -= *ip++; +#else + do { + code = *ip++; + len += code; + } while (code == 255); + code = *ip++; + ref -= code; + + /* match from 16-bit distance */ + if (FASTLZ_UNEXPECT_CONDITIONAL(code == 255)) + if (FASTLZ_EXPECT_CONDITIONAL(ofs == (31 << 8))) { + ofs = (*ip++) << 8; + ofs += *ip++; + ref = op - ofs - MAX_DISTANCE; + } +#endif + +#ifdef FASTLZ_SAFE + if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) + return 0; + + if (FASTLZ_UNEXPECT_CONDITIONAL(ref - 1 < (flzuint8 *)output)) + return 0; +#endif + + if (FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) + ctrl = *ip++; + else + loop = 0; + + if (ref == op) { + /* optimize copy for a run */ + flzuint8 b = ref[-1]; + *op++ = b; + *op++ = b; + *op++ = b; + for (; len; --len) + *op++ = b; + } else { +#if !defined(FASTLZ_STRICT_ALIGN) + const flzuint16 *p; + flzuint16 *q; +#endif + /* copy from reference */ + ref--; + *op++ = *ref++; + *op++ = *ref++; + *op++ = *ref++; + +#if !defined(FASTLZ_STRICT_ALIGN) + /* copy a byte, so that now it's word aligned */ + if (len & 1) { + *op++ = *ref++; + len--; + } + + /* copy 16-bit at once */ + q = (flzuint16 *)op; + op += len; + p = (const flzuint16 *)ref; + for (len >>= 1; len > 4; len -= 4) { + *q++ = *p++; + *q++ = *p++; + *q++ = *p++; + *q++ = *p++; + } + for (; len; --len) + *q++ = *p++; +#else + for (; len; --len) + *op++ = *ref++; +#endif + } + } else { + ctrl++; +#ifdef FASTLZ_SAFE + if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) + return 0; + if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) + return 0; +#endif + + *op++ = *ip++; + for (--ctrl; ctrl; ctrl--) + *op++ = *ip++; + + loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit); + if (loop) + ctrl = *ip++; + } + } while (FASTLZ_EXPECT_CONDITIONAL(loop)); + + return op - (flzuint8 *)output; +} + +#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ diff --git a/libs/fst/fastlz.h b/libs/fst/fastlz.h new file mode 100644 index 00000000000..1ce44a32a8a --- /dev/null +++ b/libs/fst/fastlz.h @@ -0,0 +1,109 @@ +/* + FastLZ - lightning-fast lossless compression library + + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + SPDX-License-Identifier: MIT +*/ + +#ifndef FASTLZ_H +#define FASTLZ_H + +#include + +#define flzuint8 uint8_t +#define flzuint16 uint16_t +#define flzuint32 uint32_t + + +#define FASTLZ_VERSION 0x000100 + +#define FASTLZ_VERSION_MAJOR 0 +#define FASTLZ_VERSION_MINOR 0 +#define FASTLZ_VERSION_REVISION 0 + +#define FASTLZ_VERSION_STRING "0.1.0" + +#if defined (__cplusplus) +extern "C" { +#endif + +/** + Compress a block of data in the input buffer and returns the size of + compressed block. The size of input buffer is specified by length. The + minimum input buffer size is 16. + + The output buffer must be at least 5% larger than the input buffer + and can not be smaller than 66 bytes. + + If the input is not compressible, the return value might be larger than + length (input buffer size). + + The input buffer and the output buffer can not overlap. +*/ + +int fastlz_compress(const void* input, int length, void* output); + +/** + Decompress a block of compressed data and returns the size of the + decompressed block. If error occurs, e.g. the compressed data is + corrupted or the output buffer is not large enough, then 0 (zero) + will be returned instead. + + The input buffer and the output buffer can not overlap. + + Decompression is memory safe and guaranteed not to write the output buffer + more than what is specified in maxout. + */ + +int fastlz_decompress(const void* input, int length, void* output, int maxout); + +/** + Compress a block of data in the input buffer and returns the size of + compressed block. The size of input buffer is specified by length. The + minimum input buffer size is 16. + + The output buffer must be at least 5% larger than the input buffer + and can not be smaller than 66 bytes. + + If the input is not compressible, the return value might be larger than + length (input buffer size). + + The input buffer and the output buffer can not overlap. + + Compression level can be specified in parameter level. At the moment, + only level 1 and level 2 are supported. + Level 1 is the fastest compression and generally useful for short data. + Level 2 is slightly slower but it gives better compression ratio. + + Note that the compressed data, regardless of the level, can always be + decompressed using the function fastlz_decompress above. +*/ + +int fastlz_compress_level(int level, const void* input, int length, void* output); + +#if defined (__cplusplus) +} +#endif + +#endif /* FASTLZ_H */ diff --git a/libs/fst/fstapi.cc b/libs/fst/fstapi.cc new file mode 100644 index 00000000000..592b5cae6a7 --- /dev/null +++ b/libs/fst/fstapi.cc @@ -0,0 +1,6548 @@ +/* + * Copyright (c) 2009-2018 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + */ + +/* + * possible disables: + * + * FST_DYNAMIC_ALIAS_DISABLE : dynamic aliases are not processed + * FST_DYNAMIC_ALIAS2_DISABLE : new encoding for dynamic aliases is not generated + * FST_WRITEX_DISABLE : fast write I/O routines are disabled + * + * possible enables: + * + * FST_DEBUG : not for production use, only enable for development + * FST_REMOVE_DUPLICATE_VC : glitch removal (has writer performance impact) + * HAVE_LIBPTHREAD -> FST_WRITER_PARALLEL : enables inclusion of parallel writer code + * FST_DO_MISALIGNED_OPS (defined automatically for x86 and some others) : CPU architecture can handle misaligned + * loads/stores _WAVE_HAVE_JUDY : use Judy arrays instead of Jenkins (undefine if LGPL is not acceptable) + * + */ + +#ifndef FST_CONFIG_INCLUDE +#define FST_CONFIG_INCLUDE "config.h" +#endif +#include FST_CONFIG_INCLUDE + +#include "fstapi.h" +#include "fastlz.h" +#include "lz4.h" +#include + +#ifndef HAVE_LIBPTHREAD +#undef FST_WRITER_PARALLEL +#endif + +#ifdef FST_WRITER_PARALLEL +#include +#endif + +#ifdef __MINGW32__ +#include +#endif + +#ifdef HAVE_ALLOCA_H +#include +#elif defined(__GNUC__) +#ifndef __MINGW32__ +#ifndef alloca +#define alloca __builtin_alloca +#endif +#else +#include +#endif +#elif defined(_MSC_VER) +#include +#define alloca _alloca +#endif + +#ifndef PATH_MAX +#define PATH_MAX (4096) +#endif + +#if defined(_MSC_VER) +typedef int64_t fst_off_t; +#else +typedef off_t fst_off_t; +#endif + +/* note that Judy versus Jenkins requires more experimentation: they are */ +/* functionally equivalent though it appears Jenkins is slightly faster. */ +/* in addition, Jenkins is not bound by the LGPL. */ +#ifdef _WAVE_HAVE_JUDY +#include +#else +/* should be more than enough for fstWriterSetSourceStem() */ +#define FST_PATH_HASHMASK ((1UL << 16) - 1) +typedef const void *Pcvoid_t; +typedef void *Pvoid_t; +typedef void **PPvoid_t; +#define JudyHSIns(a, b, c, d) JenkinsIns((a), (b), (c), (hashmask)) +#define JudyHSFreeArray(a, b) JenkinsFree((a), (hashmask)) +void JenkinsFree(void *base_i, uint32_t hashmask); +void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint32_t hashmask); +#endif + +#ifndef FST_WRITEX_DISABLE +#define FST_WRITEX_MAX (64 * 1024) +#else +#define fstWritex(a, b, c) fstFwrite((b), (c), 1, fv) +#endif + +/* these defines have a large impact on writer speed when a model has a */ +/* huge number of symbols. as a default, use 128MB and increment when */ +/* every 1M signals are defined. */ +#define FST_BREAK_SIZE (1UL << 27) +#define FST_BREAK_ADD_SIZE (1UL << 22) +#define FST_BREAK_SIZE_MAX (1UL << 31) +#define FST_ACTIVATE_HUGE_BREAK (1000000) +#define FST_ACTIVATE_HUGE_INC (1000000) + +#define FST_WRITER_STR "fstWriter" +#define FST_ID_NAM_SIZ (512) +#define FST_ID_NAM_ATTR_SIZ (65536 + 4096) +#define FST_DOUBLE_ENDTEST (2.7182818284590452354) +#define FST_HDR_SIM_VERSION_SIZE (128) +#define FST_HDR_DATE_SIZE (119) +#define FST_HDR_FILETYPE_SIZE (1) +#define FST_HDR_TIMEZERO_SIZE (8) +#define FST_GZIO_LEN (32768) +#define FST_HDR_FOURPACK_DUO_SIZE (4 * 1024 * 1024) + +#if defined(__i386__) || defined(__x86_64__) || defined(_AIX) +#define FST_DO_MISALIGNED_OPS +#endif + +#if defined(__APPLE__) && defined(__MACH__) +#define FST_MACOSX +#include +#endif + +#ifdef __GNUC__ +/* Boolean expression more often true than false */ +#define FST_LIKELY(x) __builtin_expect(!!(x), 1) +/* Boolean expression more often false than true */ +#define FST_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define FST_LIKELY(x) (!!(x)) +#define FST_UNLIKELY(x) (!!(x)) +#endif + +#define FST_APIMESS "FSTAPI | " + +/***********************/ +/*** ***/ +/*** common function ***/ +/*** ***/ +/***********************/ + +#if defined(__MINGW32__) || defined(_MSC_VER) +#include +#ifndef HAVE_FSEEKO +#define ftello _ftelli64 +#define fseeko _fseeki64 +#endif +#endif + +/* + * the recoded "extra" values... + * note that FST_RCV_Q is currently unused and is for future expansion. + * its intended use is as another level of escape such that any arbitrary + * value can be stored as the value: { time_delta, 8 bits, FST_RCV_Q }. + * this is currently not implemented so that the branchless decode is: + * uint32_t shcnt = 2 << (vli & 1); tdelta = vli >> shcnt; + */ +#define FST_RCV_X (1 | (0 << 1)) +#define FST_RCV_Z (1 | (1 << 1)) +#define FST_RCV_H (1 | (2 << 1)) +#define FST_RCV_U (1 | (3 << 1)) +#define FST_RCV_W (1 | (4 << 1)) +#define FST_RCV_L (1 | (5 << 1)) +#define FST_RCV_D (1 | (6 << 1)) +#define FST_RCV_Q (1 | (7 << 1)) + +#define FST_RCV_STR "xzhuwl-?" +/* 01234567 */ + +/* + * prevent old file overwrite when currently being read + */ +static FILE *unlink_fopen(const char *nam, const char *mode) +{ + unlink(nam); + return (fopen(nam, mode)); +} + +/* + * system-specific temp file handling + */ +#ifdef __MINGW32__ + +static FILE *tmpfile_open(char **nam) +{ + char *fname = NULL; + TCHAR szTempFileName[MAX_PATH]; + TCHAR lpTempPathBuffer[MAX_PATH]; + DWORD dwRetVal = 0; + UINT uRetVal = 0; + FILE *fh = NULL; + + if (nam) /* cppcheck warning fix: nam is always defined, so this is not needed */ + { + dwRetVal = GetTempPath(MAX_PATH, lpTempPathBuffer); + if ((dwRetVal > MAX_PATH) || (dwRetVal == 0)) { + fprintf(stderr, FST_APIMESS "GetTempPath() failed in " __FILE__ " line %d, exiting.\n", __LINE__); + exit(255); + } else { + uRetVal = GetTempFileName(lpTempPathBuffer, TEXT("FSTW"), 0, szTempFileName); + if (uRetVal == 0) { + fprintf(stderr, FST_APIMESS "GetTempFileName() failed in " __FILE__ " line %d, exiting.\n", __LINE__); + exit(255); + } else { + fname = strdup(szTempFileName); + } + } + + if (fname) { + *nam = fname; + fh = unlink_fopen(fname, "w+b"); + } + } + + return (fh); +} + +#else + +static FILE *tmpfile_open(char **nam) +{ + FILE *f = tmpfile(); /* replace with mkstemp() + fopen(), etc if this is not good enough */ + if (nam) { + *nam = NULL; + } + return (f); +} + +#endif + +static void tmpfile_close(FILE **f, char **nam) +{ + if (f) { + if (*f) { + fclose(*f); + *f = NULL; + } + } + + if (nam) { + if (*nam) { + unlink(*nam); + free(*nam); + *nam = NULL; + } + } +} + +/*****************************************/ + +/* + * to remove warn_unused_result compile time messages + * (in the future there needs to be results checking) + */ +static size_t fstFread(void *buf, size_t siz, size_t cnt, FILE *fp) { return (fread(buf, siz, cnt, fp)); } + +static size_t fstFwrite(const void *buf, size_t siz, size_t cnt, FILE *fp) { return (fwrite(buf, siz, cnt, fp)); } + +static int fstFtruncate(int fd, fst_off_t length) { return (ftruncate(fd, length)); } + +/* + * realpath compatibility + */ +static char *fstRealpath(const char *path, char *resolved_path) +{ +#if defined __USE_BSD || defined __USE_XOPEN_EXTENDED || defined __CYGWIN__ || defined HAVE_REALPATH +#if (defined(__MACH__) && defined(__APPLE__)) + if (!resolved_path) { + resolved_path = (char *)malloc(PATH_MAX + 1); /* fixes bug on Leopard when resolved_path == NULL */ + } +#endif + + return (realpath(path, resolved_path)); + +#else +#ifdef __MINGW32__ + if (!resolved_path) { + resolved_path = (char *)malloc(PATH_MAX + 1); + } + return (_fullpath(resolved_path, path, PATH_MAX)); +#else + (void)path; + (void)resolved_path; + return (NULL); +#endif +#endif +} + +/* + * mmap compatibility + */ +#if defined __CYGWIN__ || defined __MINGW32__ || defined _MSC_VER +#include +#define fstMmap(__addr, __len, __prot, __flags, __fd, __off) fstMmap2((__len), (__fd), (__off)) +#define fstMunmap(__addr, __len) free(__addr) + +static void *fstMmap2(size_t __len, int __fd, fst_off_t __off) +{ + (void)__off; + + unsigned char *pnt = (unsigned char *)malloc(__len); + fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR); + size_t i; + + lseek(__fd, 0, SEEK_SET); + for (i = 0; i < __len; i += SSIZE_MAX) { + read(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i)); + } + lseek(__fd, cur_offs, SEEK_SET); + return (pnt); +} +#else +#include +#if defined(__SUNPRO_C) +#define FST_CADDR_T_CAST (caddr_t) +#else +#define FST_CADDR_T_CAST +#endif +#define fstMmap(__addr, __len, __prot, __flags, __fd, __off) \ + (void *)mmap(FST_CADDR_T_CAST(__addr), (__len), (__prot), (__flags), (__fd), (__off)) +#define fstMunmap(__addr, __len) \ + { \ + if (__addr) \ + munmap(FST_CADDR_T_CAST(__addr), (__len)); \ + } +#endif + +/* + * regular and variable-length integer access functions + */ +#ifdef FST_DO_MISALIGNED_OPS +#define fstGetUint32(x) (*(uint32_t *)(x)) +#else +static inline uint32_t fstGetUint32(unsigned char *mem) +{ + union { + uint8_t u8[sizeof(uint32_t)]; + uint32_t u32; + } u; + + for (size_t i=0; i < sizeof(u.u8); i++) + u.u8[i] = mem[i]; + + return u.u32; +} +#endif + +static int fstWriterUint64(FILE *handle, uint64_t v) +{ + unsigned char buf[8]; + int i; + + for (i = 7; i >= 0; i--) { + buf[i] = v & 0xff; + v >>= 8; + } + + fstFwrite(buf, 8, 1, handle); + return (8); +} + +static uint64_t fstReaderUint64(FILE *f) +{ + uint64_t val = 0; + unsigned char buf[sizeof(uint64_t)]; + unsigned int i; + + fstFread(buf, sizeof(uint64_t), 1, f); + for (i = 0; i < sizeof(uint64_t); i++) { + val <<= 8; + val |= buf[i]; + } + + return (val); +} + +static uint32_t fstGetVarint32(unsigned char *mem, int *skiplen) +{ + unsigned char *mem_orig = mem; + uint32_t rc = 0; + while (*mem & 0x80) { + mem++; + } + + *skiplen = mem - mem_orig + 1; + for (;;) { + rc <<= 7; + rc |= (uint32_t)(*mem & 0x7f); + if (mem == mem_orig) { + break; + } + mem--; + } + + return (rc); +} + +static uint32_t fstGetVarint32Length(unsigned char *mem) +{ + unsigned char *mem_orig = mem; + + while (*mem & 0x80) { + mem++; + } + + return (mem - mem_orig + 1); +} + +static uint32_t fstGetVarint32NoSkip(unsigned char *mem) +{ + unsigned char *mem_orig = mem; + uint32_t rc = 0; + while (*mem & 0x80) { + mem++; + } + + for (;;) { + rc <<= 7; + rc |= (uint32_t)(*mem & 0x7f); + if (mem == mem_orig) { + break; + } + mem--; + } + + return (rc); +} + +static unsigned char *fstCopyVarint32ToLeft(unsigned char *pnt, uint32_t v) +{ + unsigned char *spnt; + uint32_t nxt = v; + int cnt = 1; + int i; + + while ((nxt = nxt >> 7)) /* determine len to avoid temp buffer copying to cut down on load-hit-store */ + { + cnt++; + } + + pnt -= cnt; + spnt = pnt; + cnt--; + + for (i = 0; i < cnt; i++) /* now generate left to right as normal */ + { + nxt = v >> 7; + *(spnt++) = ((unsigned char)v) | 0x80; + v = nxt; + } + *spnt = (unsigned char)v; + + return (pnt); +} + +static unsigned char *fstCopyVarint64ToRight(unsigned char *pnt, uint64_t v) +{ + uint64_t nxt; + + while ((nxt = v >> 7)) { + *(pnt++) = ((unsigned char)v) | 0x80; + v = nxt; + } + *(pnt++) = (unsigned char)v; + + return (pnt); +} + +static uint64_t fstGetVarint64(unsigned char *mem, int *skiplen) +{ + unsigned char *mem_orig = mem; + uint64_t rc = 0; + while (*mem & 0x80) { + mem++; + } + + *skiplen = mem - mem_orig + 1; + for (;;) { + rc <<= 7; + rc |= (uint64_t)(*mem & 0x7f); + if (mem == mem_orig) { + break; + } + mem--; + } + + return (rc); +} + +static uint32_t fstReaderVarint32(FILE *f) +{ + unsigned char buf[5]; + unsigned char *mem = buf; + uint32_t rc = 0; + int ch; + + do { + ch = fgetc(f); + *(mem++) = ch; + } while (ch & 0x80); + mem--; + + for (;;) { + rc <<= 7; + rc |= (uint32_t)(*mem & 0x7f); + if (mem == buf) { + break; + } + mem--; + } + + return (rc); +} + +static uint32_t fstReaderVarint32WithSkip(FILE *f, uint32_t *skiplen) +{ + unsigned char buf[5]; + unsigned char *mem = buf; + uint32_t rc = 0; + int ch; + + do { + ch = fgetc(f); + *(mem++) = ch; + } while (ch & 0x80); + *skiplen = mem - buf; + mem--; + + for (;;) { + rc <<= 7; + rc |= (uint32_t)(*mem & 0x7f); + if (mem == buf) { + break; + } + mem--; + } + + return (rc); +} + +static uint64_t fstReaderVarint64(FILE *f) +{ + unsigned char buf[16]; + unsigned char *mem = buf; + uint64_t rc = 0; + int ch; + + do { + ch = fgetc(f); + *(mem++) = ch; + } while (ch & 0x80); + mem--; + + for (;;) { + rc <<= 7; + rc |= (uint64_t)(*mem & 0x7f); + if (mem == buf) { + break; + } + mem--; + } + + return (rc); +} + +static int fstWriterVarint(FILE *handle, uint64_t v) +{ + uint64_t nxt; + unsigned char buf[10]; /* ceil(64/7) = 10 */ + unsigned char *pnt = buf; + int len; + + while ((nxt = v >> 7)) { + *(pnt++) = ((unsigned char)v) | 0x80; + v = nxt; + } + *(pnt++) = (unsigned char)v; + + len = pnt - buf; + fstFwrite(buf, len, 1, handle); + return (len); +} + +/* signed integer read/write routines are currently unused */ +static int64_t fstGetSVarint64(unsigned char *mem, int *skiplen) +{ + unsigned char *mem_orig = mem; + int64_t rc = 0; + const int64_t one = 1; + const int siz = sizeof(int64_t) * 8; + int shift = 0; + unsigned char byt; + + do { + byt = *(mem++); + rc |= ((int64_t)(byt & 0x7f)) << shift; + shift += 7; + + } while (byt & 0x80); + + if ((shift < siz) && (byt & 0x40)) { + rc |= -(one << shift); /* sign extend */ + } + + *skiplen = mem - mem_orig; + + return (rc); +} + +#ifndef FST_DYNAMIC_ALIAS2_DISABLE +static int fstWriterSVarint(FILE *handle, int64_t v) +{ + unsigned char buf[15]; /* ceil(64/7) = 10 + sign byte padded way up */ + unsigned char byt; + unsigned char *pnt = buf; + int more = 1; + int len; + + do { + byt = v | 0x80; + v >>= 7; + + if (((!v) && (!(byt & 0x40))) || ((v == -1) && (byt & 0x40))) { + more = 0; + byt &= 0x7f; + } + + *(pnt++) = byt; + } while (more); + + len = pnt - buf; + fstFwrite(buf, len, 1, handle); + return (len); +} +#endif + +/***********************/ +/*** ***/ +/*** writer function ***/ +/*** ***/ +/***********************/ + +/* + * private structs + */ +struct fstBlackoutChain +{ + struct fstBlackoutChain *next; + uint64_t tim; + unsigned active : 1; +}; + +struct fstWriterContext +{ + FILE *handle; + FILE *hier_handle; + FILE *geom_handle; + FILE *valpos_handle; + FILE *curval_handle; + FILE *tchn_handle; + + unsigned char *vchg_mem; + + fst_off_t hier_file_len; + + uint32_t *valpos_mem; + unsigned char *curval_mem; + + unsigned char *outval_mem; /* for two-state / Verilator-style value changes */ + uint32_t outval_alloc_siz; + + char *filename; + + fstHandle maxhandle; + fstHandle numsigs; + uint32_t maxvalpos; + + unsigned vc_emitted : 1; + unsigned is_initial_time : 1; + unsigned fourpack : 1; + unsigned fastpack : 1; + + int64_t timezero; + fst_off_t section_header_truncpos; + uint32_t tchn_cnt, tchn_idx; + uint64_t curtime; + uint64_t firsttime; + uint32_t vchg_siz; + uint32_t vchg_alloc_siz; + + uint32_t secnum; + fst_off_t section_start; + + uint32_t numscopes; + double nan; /* nan value for uninitialized doubles */ + + struct fstBlackoutChain *blackout_head; + struct fstBlackoutChain *blackout_curr; + uint32_t num_blackouts; + + uint64_t dump_size_limit; + + unsigned char filetype; /* default is 0, FST_FT_VERILOG */ + + unsigned compress_hier : 1; + unsigned repack_on_close : 1; + unsigned skip_writing_section_hdr : 1; + unsigned size_limit_locked : 1; + unsigned section_header_only : 1; + unsigned flush_context_pending : 1; + unsigned parallel_enabled : 1; + unsigned parallel_was_enabled : 1; + + /* should really be semaphores, but are bytes to cut down on read-modify-write window size */ + unsigned char already_in_flush; /* in case control-c handlers interrupt */ + unsigned char already_in_close; /* in case control-c handlers interrupt */ + +#ifdef FST_WRITER_PARALLEL + pthread_mutex_t mutex; + pthread_t thread; + pthread_attr_t thread_attr; + struct fstWriterContext *xc_parent; +#endif + unsigned in_pthread : 1; + + size_t fst_orig_break_size; + size_t fst_orig_break_add_size; + + size_t fst_break_size; + size_t fst_break_add_size; + + size_t fst_huge_break_size; + + fstHandle next_huge_break; + + Pvoid_t path_array; + uint32_t path_array_count; + + unsigned fseek_failed : 1; + + char *geom_handle_nam; + char *valpos_handle_nam; + char *curval_handle_nam; + char *tchn_handle_nam; + + fstEnumHandle max_enumhandle; +}; + +static int fstWriterFseeko(struct fstWriterContext *xc, FILE *stream, fst_off_t offset, int whence) +{ + int rc = fseeko(stream, offset, whence); + + if (rc < 0) { + xc->fseek_failed = 1; +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "Seek to #%" PRId64 " (whence = %d) failed!\n", offset, whence); + perror("Why"); +#endif + } + + return (rc); +} + +static uint32_t fstWriterUint32WithVarint32(struct fstWriterContext *xc, uint32_t *u, uint32_t v, const void *dbuf, + uint32_t siz) +{ + unsigned char *buf = xc->vchg_mem + xc->vchg_siz; + unsigned char *pnt = buf; + uint32_t nxt; + uint32_t len; + +#ifdef FST_DO_MISALIGNED_OPS + (*(uint32_t *)(pnt)) = (*(uint32_t *)(u)); +#else + memcpy(pnt, u, sizeof(uint32_t)); +#endif + pnt += 4; + + while ((nxt = v >> 7)) { + *(pnt++) = ((unsigned char)v) | 0x80; + v = nxt; + } + *(pnt++) = (unsigned char)v; + memcpy(pnt, dbuf, siz); + + len = pnt - buf + siz; + return (len); +} + +static uint32_t fstWriterUint32WithVarint32AndLength(struct fstWriterContext *xc, uint32_t *u, uint32_t v, + const void *dbuf, uint32_t siz) +{ + unsigned char *buf = xc->vchg_mem + xc->vchg_siz; + unsigned char *pnt = buf; + uint32_t nxt; + uint32_t len; + +#ifdef FST_DO_MISALIGNED_OPS + (*(uint32_t *)(pnt)) = (*(uint32_t *)(u)); +#else + memcpy(pnt, u, sizeof(uint32_t)); +#endif + pnt += 4; + + while ((nxt = v >> 7)) { + *(pnt++) = ((unsigned char)v) | 0x80; + v = nxt; + } + *(pnt++) = (unsigned char)v; + + v = siz; + while ((nxt = v >> 7)) { + *(pnt++) = ((unsigned char)v) | 0x80; + v = nxt; + } + *(pnt++) = (unsigned char)v; + + memcpy(pnt, dbuf, siz); + + len = pnt - buf + siz; + return (len); +} + +/* + * header bytes, write here so defines are set up before anything else + * that needs to use them + */ +static void fstWriterEmitHdrBytes(struct fstWriterContext *xc) +{ + char vbuf[FST_HDR_SIM_VERSION_SIZE]; + char dbuf[FST_HDR_DATE_SIZE]; + double endtest = FST_DOUBLE_ENDTEST; + time_t walltime; + +#define FST_HDR_OFFS_TAG (0) + fputc(FST_BL_HDR, xc->handle); /* +0 tag */ + +#define FST_HDR_OFFS_SECLEN (FST_HDR_OFFS_TAG + 1) + fstWriterUint64(xc->handle, 329); /* +1 section length */ + +#define FST_HDR_OFFS_START_TIME (FST_HDR_OFFS_SECLEN + 8) + fstWriterUint64(xc->handle, 0); /* +9 start time */ + +#define FST_HDR_OFFS_END_TIME (FST_HDR_OFFS_START_TIME + 8) + fstWriterUint64(xc->handle, 0); /* +17 end time */ + +#define FST_HDR_OFFS_ENDIAN_TEST (FST_HDR_OFFS_END_TIME + 8) + fstFwrite(&endtest, 8, 1, xc->handle); /* +25 endian test for reals */ + +#define FST_HDR_OFFS_MEM_USED (FST_HDR_OFFS_ENDIAN_TEST + 8) + fstWriterUint64(xc->handle, xc->fst_break_size); /* +33 memory used by writer */ + +#define FST_HDR_OFFS_NUM_SCOPES (FST_HDR_OFFS_MEM_USED + 8) + fstWriterUint64(xc->handle, 0); /* +41 scope creation count */ + +#define FST_HDR_OFFS_NUM_VARS (FST_HDR_OFFS_NUM_SCOPES + 8) + fstWriterUint64(xc->handle, 0); /* +49 var creation count */ + +#define FST_HDR_OFFS_MAXHANDLE (FST_HDR_OFFS_NUM_VARS + 8) + fstWriterUint64(xc->handle, 0); /* +57 max var idcode */ + +#define FST_HDR_OFFS_SECTION_CNT (FST_HDR_OFFS_MAXHANDLE + 8) + fstWriterUint64(xc->handle, 0); /* +65 vc section count */ + +#define FST_HDR_OFFS_TIMESCALE (FST_HDR_OFFS_SECTION_CNT + 8) + fputc((-9) & 255, xc->handle); /* +73 timescale 1ns */ + +#define FST_HDR_OFFS_SIM_VERSION (FST_HDR_OFFS_TIMESCALE + 1) + memset(vbuf, 0, FST_HDR_SIM_VERSION_SIZE); + strcpy(vbuf, FST_WRITER_STR); + fstFwrite(vbuf, FST_HDR_SIM_VERSION_SIZE, 1, xc->handle); /* +74 version */ + +#define FST_HDR_OFFS_DATE (FST_HDR_OFFS_SIM_VERSION + FST_HDR_SIM_VERSION_SIZE) + memset(dbuf, 0, FST_HDR_DATE_SIZE); + time(&walltime); + strcpy(dbuf, asctime(localtime(&walltime))); + fstFwrite(dbuf, FST_HDR_DATE_SIZE, 1, xc->handle); /* +202 date */ + + /* date size is deliberately overspecified at 119 bytes (originally 128) in order to provide backfill for new args + */ + +#define FST_HDR_OFFS_FILETYPE (FST_HDR_OFFS_DATE + FST_HDR_DATE_SIZE) + fputc(xc->filetype, xc->handle); /* +321 filetype */ + +#define FST_HDR_OFFS_TIMEZERO (FST_HDR_OFFS_FILETYPE + FST_HDR_FILETYPE_SIZE) + fstWriterUint64(xc->handle, xc->timezero); /* +322 timezero */ + +#define FST_HDR_LENGTH (FST_HDR_OFFS_TIMEZERO + FST_HDR_TIMEZERO_SIZE) + /* +330 next section starts here */ + fflush(xc->handle); +} + +/* + * mmap functions + */ +static void fstWriterMmapSanity(void *pnt, const char *file, int line, const char *usage) +{ +#if !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(_MSC_VER) + if (pnt == MAP_FAILED) { + fprintf(stderr, "fstMmap() assigned to %s failed: errno: %d, file %s, line %d.\n", usage, errno, file, line); + perror("Why"); + pnt = NULL; + } +#endif +} + +static void fstWriterCreateMmaps(struct fstWriterContext *xc) +{ + fst_off_t curpos = ftello(xc->handle); + + fflush(xc->hier_handle); + + /* write out intermediate header */ + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_START_TIME, SEEK_SET); + fstWriterUint64(xc->handle, xc->firsttime); + fstWriterUint64(xc->handle, xc->curtime); + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_NUM_SCOPES, SEEK_SET); + fstWriterUint64(xc->handle, xc->numscopes); + fstWriterUint64(xc->handle, xc->numsigs); + fstWriterUint64(xc->handle, xc->maxhandle); + fstWriterUint64(xc->handle, xc->secnum); + fstWriterFseeko(xc, xc->handle, curpos, SEEK_SET); + fflush(xc->handle); + + /* do mappings */ + if (!xc->valpos_mem) { + fflush(xc->valpos_handle); + errno = 0; + if (xc->maxhandle) { + fstWriterMmapSanity(xc->valpos_mem = (uint32_t *)fstMmap(NULL, xc->maxhandle * 4 * sizeof(uint32_t), + PROT_READ | PROT_WRITE, MAP_SHARED, + fileno(xc->valpos_handle), 0), + __FILE__, __LINE__, "xc->valpos_mem"); + } + } + if (!xc->curval_mem) { + fflush(xc->curval_handle); + errno = 0; + if (xc->maxvalpos) { + fstWriterMmapSanity(xc->curval_mem = (unsigned char *)fstMmap(NULL, xc->maxvalpos, PROT_READ | PROT_WRITE, + MAP_SHARED, fileno(xc->curval_handle), 0), + __FILE__, __LINE__, "xc->curval_handle"); + } + } +} + +static void fstDestroyMmaps(struct fstWriterContext *xc, int is_closing) +{ +#if !defined __CYGWIN__ && !defined __MINGW32__ + (void)is_closing; +#endif + + fstMunmap(xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t)); + xc->valpos_mem = NULL; + +#if defined __CYGWIN__ || defined __MINGW32__ + if (xc->curval_mem) { + if (!is_closing) /* need to flush out for next emulated mmap() read */ + { + unsigned char *pnt = xc->curval_mem; + int __fd = fileno(xc->curval_handle); + fst_off_t cur_offs = lseek(__fd, 0, SEEK_CUR); + size_t i; + size_t __len = xc->maxvalpos; + + lseek(__fd, 0, SEEK_SET); + for (i = 0; i < __len; i += SSIZE_MAX) { + write(__fd, pnt + i, ((__len - i) >= SSIZE_MAX) ? SSIZE_MAX : (__len - i)); + } + lseek(__fd, cur_offs, SEEK_SET); + } + } +#endif + + fstMunmap(xc->curval_mem, xc->maxvalpos); + xc->curval_mem = NULL; +} + +/* + * set up large and small memory usages + * crossover point in model is FST_ACTIVATE_HUGE_BREAK number of signals + */ +static void fstDetermineBreakSize(struct fstWriterContext *xc) +{ +#if defined(__linux__) || defined(FST_MACOSX) + int was_set = 0; + +#ifdef __linux__ + FILE *f = fopen("/proc/meminfo", "rb"); + + if (f) { + char buf[257]; + char *s; + while (!feof(f)) { + buf[0] = 0; + s = fgets(buf, 256, f); + if (s && *s) { + if (!strncmp(s, "MemTotal:", 9)) { + size_t v = atol(s + 10); + v *= 1024; /* convert to bytes */ + v /= 8; /* chop down to 1/8 physical memory */ + if (v > FST_BREAK_SIZE) { + if (v > FST_BREAK_SIZE_MAX) { + v = FST_BREAK_SIZE_MAX; + } + + xc->fst_huge_break_size = v; + was_set = 1; + break; + } + } + } + } + + fclose(f); + } + + if (!was_set) { + xc->fst_huge_break_size = FST_BREAK_SIZE; + } +#else + int mib[2]; + int64_t v; + size_t length; + + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + length = sizeof(int64_t); + if (!sysctl(mib, 2, &v, &length, NULL, 0)) { + v /= 8; + + if (v > (int64_t)FST_BREAK_SIZE) { + if (v > (int64_t)FST_BREAK_SIZE_MAX) { + v = FST_BREAK_SIZE_MAX; + } + + xc->fst_huge_break_size = v; + was_set = 1; + } + } + + if (!was_set) { + xc->fst_huge_break_size = FST_BREAK_SIZE; + } +#endif +#else + xc->fst_huge_break_size = FST_BREAK_SIZE; +#endif + + xc->fst_break_size = xc->fst_orig_break_size = FST_BREAK_SIZE; + xc->fst_break_add_size = xc->fst_orig_break_add_size = FST_BREAK_ADD_SIZE; + xc->next_huge_break = FST_ACTIVATE_HUGE_BREAK; +} + +/* + * file creation and close + */ +void *fstWriterCreate(const char *nam, int use_compressed_hier) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)calloc(1, sizeof(struct fstWriterContext)); + + xc->compress_hier = use_compressed_hier; + fstDetermineBreakSize(xc); + + if ((!nam) || (!(xc->handle = unlink_fopen(nam, "w+b")))) { + free(xc); + xc = NULL; + } else { + int flen = strlen(nam); + char *hf = (char *)calloc(1, flen + 6); + + memcpy(hf, nam, flen); + strcpy(hf + flen, ".hier"); + xc->hier_handle = unlink_fopen(hf, "w+b"); + + xc->geom_handle = tmpfile_open(&xc->geom_handle_nam); /* .geom */ + xc->valpos_handle = tmpfile_open(&xc->valpos_handle_nam); /* .offs */ + xc->curval_handle = tmpfile_open(&xc->curval_handle_nam); /* .bits */ + xc->tchn_handle = tmpfile_open(&xc->tchn_handle_nam); /* .tchn */ + xc->vchg_alloc_siz = xc->fst_break_size + xc->fst_break_add_size; + xc->vchg_mem = (unsigned char *)malloc(xc->vchg_alloc_siz); + + if (xc->hier_handle && xc->geom_handle && xc->valpos_handle && xc->curval_handle && xc->vchg_mem && + xc->tchn_handle) { + xc->filename = strdup(nam); + xc->is_initial_time = 1; + + fstWriterEmitHdrBytes(xc); + xc->nan = strtod("NaN", NULL); +#ifdef FST_WRITER_PARALLEL + pthread_mutex_init(&xc->mutex, NULL); + pthread_attr_init(&xc->thread_attr); + pthread_attr_setdetachstate(&xc->thread_attr, PTHREAD_CREATE_DETACHED); +#endif + } else { + fclose(xc->handle); + if (xc->hier_handle) { + fclose(xc->hier_handle); + unlink(hf); + } + tmpfile_close(&xc->geom_handle, &xc->geom_handle_nam); + tmpfile_close(&xc->valpos_handle, &xc->valpos_handle_nam); + tmpfile_close(&xc->curval_handle, &xc->curval_handle_nam); + tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam); + free(xc->vchg_mem); + free(xc); + xc = NULL; + } + + free(hf); + } + + return (xc); +} + +/* + * generation and writing out of value change data sections + */ +static void fstWriterEmitSectionHeader(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + if (xc) { + unsigned long destlen; + unsigned char *dmem; + int rc; + + destlen = xc->maxvalpos; + dmem = (unsigned char *)malloc(compressBound(destlen)); + rc = compress2(dmem, &destlen, xc->curval_mem, xc->maxvalpos, + 4); /* was 9...which caused performance drag on traces with many signals */ + + fputc(FST_BL_SKIP, xc->handle); /* temporarily tag the section, use FST_BL_VCDATA on finalize */ + xc->section_start = ftello(xc->handle); +#ifdef FST_WRITER_PARALLEL + if (xc->xc_parent) + xc->xc_parent->section_start = xc->section_start; +#endif + xc->section_header_only = 1; /* indicates truncate might be needed */ + fstWriterUint64(xc->handle, 0); /* placeholder = section length */ + fstWriterUint64(xc->handle, xc->is_initial_time ? xc->firsttime : xc->curtime); /* begin time of section */ + fstWriterUint64(xc->handle, xc->curtime); /* end time of section (placeholder) */ + fstWriterUint64(xc->handle, + 0); /* placeholder = amount of buffer memory required in reader for full vc traversal */ + fstWriterVarint(xc->handle, xc->maxvalpos); /* maxvalpos = length of uncompressed data */ + + if ((rc == Z_OK) && (destlen < xc->maxvalpos)) { + fstWriterVarint(xc->handle, destlen); /* length of compressed data */ + } else { + fstWriterVarint(xc->handle, xc->maxvalpos); /* length of (unable to be) compressed data */ + } + fstWriterVarint(xc->handle, + xc->maxhandle); /* max handle associated with this data (in case of dynamic facility adds) */ + + if ((rc == Z_OK) && (destlen < xc->maxvalpos)) { + fstFwrite(dmem, destlen, 1, xc->handle); + } else /* comparison between compressed / decompressed len tells if compressed */ + { + fstFwrite(xc->curval_mem, xc->maxvalpos, 1, xc->handle); + } + + free(dmem); + } +} + +/* + * only to be called directly by fst code...otherwise must + * be synced up with time changes + */ +#ifdef FST_WRITER_PARALLEL +static void fstWriterFlushContextPrivate2(void *ctx) +#else +static void fstWriterFlushContextPrivate(void *ctx) +#endif +{ +#ifdef FST_DEBUG + int cnt = 0; +#endif + unsigned int i; + unsigned char *vchg_mem; + FILE *f; + fst_off_t fpos, indxpos, endpos; + uint32_t prevpos; + int zerocnt; + unsigned char *scratchpad; + unsigned char *scratchpnt; + unsigned char *tmem; + fst_off_t tlen; + fst_off_t unc_memreq = 0; /* for reader */ + unsigned char *packmem; + unsigned int packmemlen; + uint32_t *vm4ip; + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; +#ifdef FST_WRITER_PARALLEL + struct fstWriterContext *xc2 = xc->xc_parent; +#else + struct fstWriterContext *xc2 = xc; +#endif + +#ifndef FST_DYNAMIC_ALIAS_DISABLE + Pvoid_t PJHSArray = (Pvoid_t)NULL; +#ifndef _WAVE_HAVE_JUDY + uint32_t hashmask = xc->maxhandle; + hashmask |= hashmask >> 1; + hashmask |= hashmask >> 2; + hashmask |= hashmask >> 4; + hashmask |= hashmask >> 8; + hashmask |= hashmask >> 16; +#endif +#endif + + if ((xc->vchg_siz <= 1) || (xc->already_in_flush)) + return; + xc->already_in_flush = 1; /* should really do this with a semaphore */ + + xc->section_header_only = 0; + scratchpad = (unsigned char *)malloc(xc->vchg_siz); + + vchg_mem = xc->vchg_mem; + + f = xc->handle; + fstWriterVarint(f, xc->maxhandle); /* emit current number of handles */ + fputc(xc->fourpack ? '4' : (xc->fastpack ? 'F' : 'Z'), f); + fpos = 1; + + packmemlen = 1024; /* maintain a running "longest" allocation to */ + packmem = (unsigned char *)malloc(packmemlen); /* prevent continual malloc...free every loop iter */ + + for (i = 0; i < xc->maxhandle; i++) { + vm4ip = &(xc->valpos_mem[4 * i]); + + if (vm4ip[2]) { + uint32_t offs = vm4ip[2]; + uint32_t next_offs; + unsigned int wrlen; + + vm4ip[2] = fpos; + + scratchpnt = scratchpad + xc->vchg_siz; /* build this buffer backwards */ + if (vm4ip[1] <= 1) { + if (vm4ip[1] == 1) { + wrlen = fstGetVarint32Length(vchg_mem + offs + 4); /* used to advance and determine wrlen */ +#ifndef FST_REMOVE_DUPLICATE_VC + xc->curval_mem[vm4ip[0]] = vchg_mem[offs + 4 + wrlen]; /* checkpoint variable */ +#endif + while (offs) { + unsigned char val; + uint32_t time_delta, rcv; + next_offs = fstGetUint32(vchg_mem + offs); + offs += 4; + + time_delta = fstGetVarint32(vchg_mem + offs, (int *)&wrlen); + val = vchg_mem[offs + wrlen]; + offs = next_offs; + + switch (val) { + case '0': + case '1': + rcv = ((val & 1) << 1) | (time_delta << 2); + break; /* pack more delta bits in for 0/1 vchs */ + + case 'x': + case 'X': + rcv = FST_RCV_X | (time_delta << 4); + break; + case 'z': + case 'Z': + rcv = FST_RCV_Z | (time_delta << 4); + break; + case 'h': + case 'H': + rcv = FST_RCV_H | (time_delta << 4); + break; + case 'u': + case 'U': + rcv = FST_RCV_U | (time_delta << 4); + break; + case 'w': + case 'W': + rcv = FST_RCV_W | (time_delta << 4); + break; + case 'l': + case 'L': + rcv = FST_RCV_L | (time_delta << 4); + break; + default: + rcv = FST_RCV_D | (time_delta << 4); + break; + } + + scratchpnt = fstCopyVarint32ToLeft(scratchpnt, rcv); + } + } else { + /* variable length */ + /* fstGetUint32 (next_offs) + fstGetVarint32 (time_delta) + fstGetVarint32 (len) + payload */ + unsigned char *pnt; + uint32_t record_len; + uint32_t time_delta; + + while (offs) { + next_offs = fstGetUint32(vchg_mem + offs); + offs += 4; + pnt = vchg_mem + offs; + offs = next_offs; + time_delta = fstGetVarint32(pnt, (int *)&wrlen); + pnt += wrlen; + record_len = fstGetVarint32(pnt, (int *)&wrlen); + pnt += wrlen; + + scratchpnt -= record_len; + memcpy(scratchpnt, pnt, record_len); + + scratchpnt = fstCopyVarint32ToLeft(scratchpnt, record_len); + scratchpnt = fstCopyVarint32ToLeft( + scratchpnt, (time_delta << 1)); /* reserve | 1 case for future expansion */ + } + } + } else { + wrlen = fstGetVarint32Length(vchg_mem + offs + 4); /* used to advance and determine wrlen */ +#ifndef FST_REMOVE_DUPLICATE_VC + memcpy(xc->curval_mem + vm4ip[0], vchg_mem + offs + 4 + wrlen, vm4ip[1]); /* checkpoint variable */ +#endif + while (offs) { + unsigned int idx; + char is_binary = 1; + unsigned char *pnt; + uint32_t time_delta; + + next_offs = fstGetUint32(vchg_mem + offs); + offs += 4; + + time_delta = fstGetVarint32(vchg_mem + offs, (int *)&wrlen); + + pnt = vchg_mem + offs + wrlen; + offs = next_offs; + + for (idx = 0; idx < vm4ip[1]; idx++) { + if ((pnt[idx] == '0') || (pnt[idx] == '1')) { + continue; + } else { + is_binary = 0; + break; + } + } + + if (is_binary) { + unsigned char acc = 0; + /* new algorithm */ + idx = ((vm4ip[1] + 7) & ~7); + switch (vm4ip[1] & 7) { + case 0: + do { + acc = (pnt[idx + 7 - 8] & 1) << 0; /* fallthrough */ + case 7: + acc |= (pnt[idx + 6 - 8] & 1) << 1; /* fallthrough */ + case 6: + acc |= (pnt[idx + 5 - 8] & 1) << 2; /* fallthrough */ + case 5: + acc |= (pnt[idx + 4 - 8] & 1) << 3; /* fallthrough */ + case 4: + acc |= (pnt[idx + 3 - 8] & 1) << 4; /* fallthrough */ + case 3: + acc |= (pnt[idx + 2 - 8] & 1) << 5; /* fallthrough */ + case 2: + acc |= (pnt[idx + 1 - 8] & 1) << 6; /* fallthrough */ + case 1: + acc |= (pnt[idx + 0 - 8] & 1) << 7; + *(--scratchpnt) = acc; + idx -= 8; + } while (idx); + } + + scratchpnt = fstCopyVarint32ToLeft(scratchpnt, (time_delta << 1)); + } else { + scratchpnt -= vm4ip[1]; + memcpy(scratchpnt, pnt, vm4ip[1]); + + scratchpnt = fstCopyVarint32ToLeft(scratchpnt, (time_delta << 1) | 1); + } + } + } + + wrlen = scratchpad + xc->vchg_siz - scratchpnt; + unc_memreq += wrlen; + if (wrlen > 32) { + unsigned long destlen = wrlen; + unsigned char *dmem; + unsigned int rc; + + if (!xc->fastpack) { + if (wrlen <= packmemlen) { + dmem = packmem; + } else { + free(packmem); + dmem = packmem = (unsigned char *)malloc(compressBound(packmemlen = wrlen)); + } + + rc = compress2(dmem, &destlen, scratchpnt, wrlen, 4); + if (rc == Z_OK) { +#ifndef FST_DYNAMIC_ALIAS_DISABLE + PPvoid_t pv = JudyHSIns(&PJHSArray, dmem, destlen, NULL); + if (*pv) { + uint32_t pvi = (intptr_t)(*pv); + vm4ip[2] = -pvi; + } else { + *pv = (void *)(intptr_t)(i + 1); +#endif + fpos += fstWriterVarint(f, wrlen); + fpos += destlen; + fstFwrite(dmem, destlen, 1, f); +#ifndef FST_DYNAMIC_ALIAS_DISABLE + } +#endif + } else { +#ifndef FST_DYNAMIC_ALIAS_DISABLE + PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); + if (*pv) { + uint32_t pvi = (intptr_t)(*pv); + vm4ip[2] = -pvi; + } else { + *pv = (void *)(intptr_t)(i + 1); +#endif + fpos += fstWriterVarint(f, 0); + fpos += wrlen; + fstFwrite(scratchpnt, wrlen, 1, f); +#ifndef FST_DYNAMIC_ALIAS_DISABLE + } +#endif + } + } else { + /* this is extremely conservative: fastlz needs +5% for worst case, lz4 needs siz+(siz/255)+16 */ + if (((wrlen * 2) + 2) <= packmemlen) { + dmem = packmem; + } else { + free(packmem); + dmem = packmem = (unsigned char *)malloc(packmemlen = (wrlen * 2) + 2); + } + + rc = (xc->fourpack) ? LZ4_compress((char *)scratchpnt, (char *)dmem, wrlen) + : fastlz_compress(scratchpnt, wrlen, dmem); + if (rc < destlen) { +#ifndef FST_DYNAMIC_ALIAS_DISABLE + PPvoid_t pv = JudyHSIns(&PJHSArray, dmem, rc, NULL); + if (*pv) { + uint32_t pvi = (intptr_t)(*pv); + vm4ip[2] = -pvi; + } else { + *pv = (void *)(intptr_t)(i + 1); +#endif + fpos += fstWriterVarint(f, wrlen); + fpos += rc; + fstFwrite(dmem, rc, 1, f); +#ifndef FST_DYNAMIC_ALIAS_DISABLE + } +#endif + } else { +#ifndef FST_DYNAMIC_ALIAS_DISABLE + PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); + if (*pv) { + uint32_t pvi = (intptr_t)(*pv); + vm4ip[2] = -pvi; + } else { + *pv = (void *)(intptr_t)(i + 1); +#endif + fpos += fstWriterVarint(f, 0); + fpos += wrlen; + fstFwrite(scratchpnt, wrlen, 1, f); +#ifndef FST_DYNAMIC_ALIAS_DISABLE + } +#endif + } + } + } else { +#ifndef FST_DYNAMIC_ALIAS_DISABLE + PPvoid_t pv = JudyHSIns(&PJHSArray, scratchpnt, wrlen, NULL); + if (*pv) { + uint32_t pvi = (intptr_t)(*pv); + vm4ip[2] = -pvi; + } else { + *pv = (void *)(intptr_t)(i + 1); +#endif + fpos += fstWriterVarint(f, 0); + fpos += wrlen; + fstFwrite(scratchpnt, wrlen, 1, f); +#ifndef FST_DYNAMIC_ALIAS_DISABLE + } +#endif + } + + /* vm4ip[3] = 0; ...redundant with clearing below */ +#ifdef FST_DEBUG + cnt++; +#endif + } + } + +#ifndef FST_DYNAMIC_ALIAS_DISABLE + JudyHSFreeArray(&PJHSArray, NULL); +#endif + + free(packmem); + packmem = NULL; /* packmemlen = 0; */ /* scan-build */ + + prevpos = 0; + zerocnt = 0; + free(scratchpad); + scratchpad = NULL; + + indxpos = ftello(f); + xc->secnum++; + +#ifndef FST_DYNAMIC_ALIAS2_DISABLE + if (1) { + uint32_t prev_alias = 0; + + for (i = 0; i < xc->maxhandle; i++) { + vm4ip = &(xc->valpos_mem[4 * i]); + + if (vm4ip[2]) { + if (zerocnt) { + fpos += fstWriterVarint(f, (zerocnt << 1)); + zerocnt = 0; + } + + if (vm4ip[2] & 0x80000000) { + if (vm4ip[2] != prev_alias) { + fpos += fstWriterSVarint(f, (((int64_t)((int32_t)(prev_alias = vm4ip[2]))) << 1) | 1); + } else { + fpos += fstWriterSVarint(f, (0 << 1) | 1); + } + } else { + fpos += fstWriterSVarint(f, ((vm4ip[2] - prevpos) << 1) | 1); + prevpos = vm4ip[2]; + } + vm4ip[2] = 0; + vm4ip[3] = 0; /* clear out tchn idx */ + } else { + zerocnt++; + } + } + } else +#endif + { + for (i = 0; i < xc->maxhandle; i++) { + vm4ip = &(xc->valpos_mem[4 * i]); + + if (vm4ip[2]) { + if (zerocnt) { + fpos += fstWriterVarint(f, (zerocnt << 1)); + zerocnt = 0; + } + + if (vm4ip[2] & 0x80000000) { + fpos += fstWriterVarint(f, 0); /* signal, note that using a *signed* varint would be more efficient + than this byte escape! */ + fpos += fstWriterVarint(f, (-(int32_t)vm4ip[2])); + } else { + fpos += fstWriterVarint(f, ((vm4ip[2] - prevpos) << 1) | 1); + prevpos = vm4ip[2]; + } + vm4ip[2] = 0; + vm4ip[3] = 0; /* clear out tchn idx */ + } else { + zerocnt++; + } + } + } + + if (zerocnt) { + /* fpos += */ fstWriterVarint(f, (zerocnt << 1)); /* scan-build */ + } +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "value chains: %d\n", cnt); +#endif + + xc->vchg_mem[0] = '!'; + xc->vchg_siz = 1; + + endpos = ftello(xc->handle); + fstWriterUint64(xc->handle, endpos - indxpos); /* write delta index position at very end of block */ + + /*emit time changes for block */ + fflush(xc->tchn_handle); + tlen = ftello(xc->tchn_handle); + fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET); + + errno = 0; + fstWriterMmapSanity( + tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(xc->tchn_handle), 0), + __FILE__, __LINE__, "tmem"); + if (tmem) { + unsigned long destlen = tlen; + unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen)); + int rc = compress2(dmem, &destlen, tmem, tlen, 9); + + if ((rc == Z_OK) && (((fst_off_t)destlen) < tlen)) { + fstFwrite(dmem, destlen, 1, xc->handle); + } else /* comparison between compressed / decompressed len tells if compressed */ + { + fstFwrite(tmem, tlen, 1, xc->handle); + destlen = tlen; + } + free(dmem); + fstMunmap(tmem, tlen); + fstWriterUint64(xc->handle, tlen); /* uncompressed */ + fstWriterUint64(xc->handle, destlen); /* compressed */ + fstWriterUint64(xc->handle, xc->tchn_cnt); /* number of time items */ + } + + xc->tchn_cnt = xc->tchn_idx = 0; + fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET); + fstFtruncate(fileno(xc->tchn_handle), 0); + + /* write block trailer */ + endpos = ftello(xc->handle); + fstWriterFseeko(xc, xc->handle, xc->section_start, SEEK_SET); + fstWriterUint64(xc->handle, endpos - xc->section_start); /* write block length */ + fstWriterFseeko(xc, xc->handle, 8, SEEK_CUR); /* skip begin time */ + fstWriterUint64(xc->handle, xc->curtime); /* write end time for section */ + fstWriterUint64(xc->handle, unc_memreq); /* amount of buffer memory required in reader for full traversal */ + fflush(xc->handle); + + fstWriterFseeko(xc, xc->handle, xc->section_start - 1, SEEK_SET); /* write out FST_BL_VCDATA over FST_BL_SKIP */ + +#ifndef FST_DYNAMIC_ALIAS_DISABLE +#ifndef FST_DYNAMIC_ALIAS2_DISABLE + fputc(FST_BL_VCDATA_DYN_ALIAS2, xc->handle); +#else + fputc(FST_BL_VCDATA_DYN_ALIAS, xc->handle); +#endif +#else + fputc(FST_BL_VCDATA, xc->handle); +#endif + + fflush(xc->handle); + + fstWriterFseeko(xc, xc->handle, endpos, SEEK_SET); /* seek to end of file */ + + xc2->section_header_truncpos = endpos; /* cache in case of need to truncate */ + if (xc->dump_size_limit) { + if (endpos >= ((fst_off_t)xc->dump_size_limit)) { + xc2->skip_writing_section_hdr = 1; + xc2->size_limit_locked = 1; + xc2->is_initial_time = 1; /* to trick emit value and emit time change */ +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "<< dump file size limit reached, stopping dumping >>\n"); +#endif + } + } + + if (!xc2->skip_writing_section_hdr) { + fstWriterEmitSectionHeader(xc); /* emit next section header */ + } + fflush(xc->handle); + + xc->already_in_flush = 0; +} + +#ifdef FST_WRITER_PARALLEL +static void *fstWriterFlushContextPrivate1(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + struct fstWriterContext *xc_parent; + + pthread_mutex_lock(&(xc->xc_parent->mutex)); + fstWriterFlushContextPrivate2(xc); + +#ifdef FST_REMOVE_DUPLICATE_VC + free(xc->curval_mem); +#endif + free(xc->valpos_mem); + free(xc->vchg_mem); + tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam); + xc_parent = xc->xc_parent; + free(xc); + + xc_parent->in_pthread = 0; + pthread_mutex_unlock(&(xc_parent->mutex)); + + return (NULL); +} + +static void fstWriterFlushContextPrivate(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + if (xc->parallel_enabled) { + struct fstWriterContext *xc2 = (struct fstWriterContext *)malloc(sizeof(struct fstWriterContext)); + unsigned int i; + + pthread_mutex_lock(&xc->mutex); + pthread_mutex_unlock(&xc->mutex); + + xc->xc_parent = xc; + memcpy(xc2, xc, sizeof(struct fstWriterContext)); + + xc2->valpos_mem = (uint32_t *)malloc(xc->maxhandle * 4 * sizeof(uint32_t)); + memcpy(xc2->valpos_mem, xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t)); + + /* curval mem is updated in the thread */ +#ifdef FST_REMOVE_DUPLICATE_VC + xc2->curval_mem = (unsigned char *)malloc(xc->maxvalpos); + memcpy(xc2->curval_mem, xc->curval_mem, xc->maxvalpos); +#endif + + xc->vchg_mem = (unsigned char *)malloc(xc->vchg_alloc_siz); + xc->vchg_mem[0] = '!'; + xc->vchg_siz = 1; + + for (i = 0; i < xc->maxhandle; i++) { + uint32_t *vm4ip = &(xc->valpos_mem[4 * i]); + vm4ip[2] = 0; /* zero out offset val */ + vm4ip[3] = 0; /* zero out last time change val */ + } + + xc->tchn_cnt = xc->tchn_idx = 0; + xc->tchn_handle = tmpfile_open(&xc->tchn_handle_nam); /* child thread will deallocate file/name */ + fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET); + fstFtruncate(fileno(xc->tchn_handle), 0); + + xc->section_header_only = 0; + xc->secnum++; + + while (xc->in_pthread) { + pthread_mutex_lock(&xc->mutex); + pthread_mutex_unlock(&xc->mutex); + }; + + pthread_mutex_lock(&xc->mutex); + xc->in_pthread = 1; + pthread_mutex_unlock(&xc->mutex); + + pthread_create(&xc->thread, &xc->thread_attr, fstWriterFlushContextPrivate1, xc2); + } else { + if (xc->parallel_was_enabled) /* conservatively block */ + { + pthread_mutex_lock(&xc->mutex); + pthread_mutex_unlock(&xc->mutex); + } + + xc->xc_parent = xc; + fstWriterFlushContextPrivate2(xc); + } +} +#endif + +/* + * queues up a flush context operation + */ +void fstWriterFlushContext(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + if (xc->tchn_idx > 1) { + xc->flush_context_pending = 1; + } + } +} + +/* + * close out FST file + */ +void fstWriterClose(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + +#ifdef FST_WRITER_PARALLEL + if (xc) { + pthread_mutex_lock(&xc->mutex); + pthread_mutex_unlock(&xc->mutex); + } +#endif + + if (xc && !xc->already_in_close && !xc->already_in_flush) { + unsigned char *tmem = NULL; + fst_off_t fixup_offs, tlen, hlen; + + xc->already_in_close = 1; /* never need to zero this out as it is freed at bottom */ + + if (xc->section_header_only && xc->section_header_truncpos && (xc->vchg_siz <= 1) && (!xc->is_initial_time)) { + fstFtruncate(fileno(xc->handle), xc->section_header_truncpos); + fstWriterFseeko(xc, xc->handle, xc->section_header_truncpos, SEEK_SET); + xc->section_header_only = 0; + } else { + xc->skip_writing_section_hdr = 1; + if (!xc->size_limit_locked) { + if (FST_UNLIKELY(xc->is_initial_time)) /* simulation time never advanced so mock up the changes as time + zero ones */ + { + fstHandle dupe_idx; + + fstWriterEmitTimeChange(xc, 0); /* emit some time change just to have one */ + for (dupe_idx = 0; dupe_idx < xc->maxhandle; dupe_idx++) /* now clone the values */ + { + fstWriterEmitValueChange(xc, dupe_idx + 1, xc->curval_mem + xc->valpos_mem[4 * dupe_idx]); + } + } + fstWriterFlushContextPrivate(xc); +#ifdef FST_WRITER_PARALLEL + pthread_mutex_lock(&xc->mutex); + pthread_mutex_unlock(&xc->mutex); + + while (xc->in_pthread) { + pthread_mutex_lock(&xc->mutex); + pthread_mutex_unlock(&xc->mutex); + }; +#endif + } + } + fstDestroyMmaps(xc, 1); + if (xc->outval_mem) { + free(xc->outval_mem); + xc->outval_mem = NULL; + xc->outval_alloc_siz = 0; + } + + /* write out geom section */ + fflush(xc->geom_handle); + tlen = ftello(xc->geom_handle); + errno = 0; + if (tlen) { + fstWriterMmapSanity(tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ | PROT_WRITE, MAP_SHARED, + fileno(xc->geom_handle), 0), + __FILE__, __LINE__, "tmem"); + } + + if (tmem) { + unsigned long destlen = tlen; + unsigned char *dmem = (unsigned char *)malloc(compressBound(destlen)); + int rc = compress2(dmem, &destlen, tmem, tlen, 9); + + if ((rc != Z_OK) || (((fst_off_t)destlen) > tlen)) { + destlen = tlen; + } + + fixup_offs = ftello(xc->handle); + fputc(FST_BL_SKIP, xc->handle); /* temporary tag */ + fstWriterUint64(xc->handle, destlen + 24); /* section length */ + fstWriterUint64(xc->handle, tlen); /* uncompressed */ + /* compressed len is section length - 24 */ + fstWriterUint64(xc->handle, xc->maxhandle); /* maxhandle */ + fstFwrite((((fst_off_t)destlen) != tlen) ? dmem : tmem, destlen, 1, xc->handle); + fflush(xc->handle); + + fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET); + fputc(FST_BL_GEOM, xc->handle); /* actual tag */ + + fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */ + fflush(xc->handle); + + free(dmem); + fstMunmap(tmem, tlen); + } + + if (xc->num_blackouts) { + uint64_t cur_bl = 0; + fst_off_t bpos, eos; + uint32_t i; + + fixup_offs = ftello(xc->handle); + fputc(FST_BL_SKIP, xc->handle); /* temporary tag */ + bpos = fixup_offs + 1; + fstWriterUint64(xc->handle, 0); /* section length */ + fstWriterVarint(xc->handle, xc->num_blackouts); + + for (i = 0; i < xc->num_blackouts; i++) { + fputc(xc->blackout_head->active, xc->handle); + fstWriterVarint(xc->handle, xc->blackout_head->tim - cur_bl); + cur_bl = xc->blackout_head->tim; + xc->blackout_curr = xc->blackout_head->next; + free(xc->blackout_head); + xc->blackout_head = xc->blackout_curr; + } + + eos = ftello(xc->handle); + fstWriterFseeko(xc, xc->handle, bpos, SEEK_SET); + fstWriterUint64(xc->handle, eos - bpos); + fflush(xc->handle); + + fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET); + fputc(FST_BL_BLACKOUT, xc->handle); /* actual tag */ + + fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */ + fflush(xc->handle); + } + + if (xc->compress_hier) { + fst_off_t hl, eos; + gzFile zhandle; + int zfd; + int fourpack_duo = 0; +#ifndef __MINGW32__ + char *fnam = (char *)malloc(strlen(xc->filename) + 5 + 1); +#endif + + fixup_offs = ftello(xc->handle); + fputc(FST_BL_SKIP, xc->handle); /* temporary tag */ + hlen = ftello(xc->handle); + fstWriterUint64(xc->handle, 0); /* section length */ + fstWriterUint64(xc->handle, xc->hier_file_len); /* uncompressed length */ + + if (!xc->fourpack) { + unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN); + zfd = dup(fileno(xc->handle)); + fflush(xc->handle); + zhandle = gzdopen(zfd, "wb4"); + if (zhandle) { + fstWriterFseeko(xc, xc->hier_handle, 0, SEEK_SET); + for (hl = 0; hl < xc->hier_file_len; hl += FST_GZIO_LEN) { + unsigned len = + ((xc->hier_file_len - hl) > FST_GZIO_LEN) ? FST_GZIO_LEN : (xc->hier_file_len - hl); + fstFread(mem, len, 1, xc->hier_handle); + gzwrite(zhandle, mem, len); + } + gzclose(zhandle); + } else { + close(zfd); + } + free(mem); + } else { + int lz4_maxlen; + unsigned char *mem; + unsigned char *hmem = NULL; + int packed_len; + + fflush(xc->handle); + + lz4_maxlen = LZ4_compressBound(xc->hier_file_len); + mem = (unsigned char *)malloc(lz4_maxlen); + errno = 0; + if (xc->hier_file_len) { + fstWriterMmapSanity(hmem = (unsigned char *)fstMmap(NULL, xc->hier_file_len, PROT_READ | PROT_WRITE, + MAP_SHARED, fileno(xc->hier_handle), 0), + __FILE__, __LINE__, "hmem"); + } + packed_len = LZ4_compress((char *)hmem, (char *)mem, xc->hier_file_len); + fstMunmap(hmem, xc->hier_file_len); + + fourpack_duo = + (!xc->repack_on_close) && + (xc->hier_file_len > FST_HDR_FOURPACK_DUO_SIZE); /* double pack when hierarchy is large */ + + if (fourpack_duo) /* double packing with LZ4 is faster than gzip */ + { + unsigned char *mem_duo; + int lz4_maxlen_duo; + int packed_len_duo; + + lz4_maxlen_duo = LZ4_compressBound(packed_len); + mem_duo = (unsigned char *)malloc(lz4_maxlen_duo); + packed_len_duo = LZ4_compress((char *)mem, (char *)mem_duo, packed_len); + + fstWriterVarint(xc->handle, packed_len); /* 1st round compressed length */ + fstFwrite(mem_duo, packed_len_duo, 1, xc->handle); + free(mem_duo); + } else { + fstFwrite(mem, packed_len, 1, xc->handle); + } + + free(mem); + } + + fstWriterFseeko(xc, xc->handle, 0, SEEK_END); + eos = ftello(xc->handle); + fstWriterFseeko(xc, xc->handle, hlen, SEEK_SET); + fstWriterUint64(xc->handle, eos - hlen); + fflush(xc->handle); + + fstWriterFseeko(xc, xc->handle, fixup_offs, SEEK_SET); + fputc(xc->fourpack ? (fourpack_duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4) : FST_BL_HIER, + xc->handle); /* actual tag now also == compression type */ + + fstWriterFseeko(xc, xc->handle, 0, SEEK_END); /* move file pointer to end for any section adds */ + fflush(xc->handle); + +#ifndef __MINGW32__ + sprintf(fnam, "%s.hier", xc->filename); + unlink(fnam); + free(fnam); +#endif + } + + /* finalize out header */ + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_START_TIME, SEEK_SET); + fstWriterUint64(xc->handle, xc->firsttime); + fstWriterUint64(xc->handle, xc->curtime); + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_NUM_SCOPES, SEEK_SET); + fstWriterUint64(xc->handle, xc->numscopes); + fstWriterUint64(xc->handle, xc->numsigs); + fstWriterUint64(xc->handle, xc->maxhandle); + fstWriterUint64(xc->handle, xc->secnum); + fflush(xc->handle); + + tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam); + free(xc->vchg_mem); + xc->vchg_mem = NULL; + tmpfile_close(&xc->curval_handle, &xc->curval_handle_nam); + tmpfile_close(&xc->valpos_handle, &xc->valpos_handle_nam); + tmpfile_close(&xc->geom_handle, &xc->geom_handle_nam); + if (xc->hier_handle) { + fclose(xc->hier_handle); + xc->hier_handle = NULL; + } + if (xc->handle) { + if (xc->repack_on_close) { + FILE *fp; + fst_off_t offpnt, uclen; + int flen = strlen(xc->filename); + char *hf = (char *)calloc(1, flen + 5); + + strcpy(hf, xc->filename); + strcpy(hf + flen, ".pak"); + fp = fopen(hf, "wb"); + + if (fp) { + gzFile dsth; + int zfd; + char gz_membuf[FST_GZIO_LEN]; + + fstWriterFseeko(xc, xc->handle, 0, SEEK_END); + uclen = ftello(xc->handle); + + fputc(FST_BL_ZWRAPPER, fp); + fstWriterUint64(fp, 0); + fstWriterUint64(fp, uclen); + fflush(fp); + + fstWriterFseeko(xc, xc->handle, 0, SEEK_SET); + zfd = dup(fileno(fp)); + dsth = gzdopen(zfd, "wb4"); + if (dsth) { + for (offpnt = 0; offpnt < uclen; offpnt += FST_GZIO_LEN) { + size_t this_len = ((uclen - offpnt) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - offpnt); + fstFread(gz_membuf, this_len, 1, xc->handle); + gzwrite(dsth, gz_membuf, this_len); + } + gzclose(dsth); + } else { + close(zfd); + } + fstWriterFseeko(xc, fp, 0, SEEK_END); + offpnt = ftello(fp); + fstWriterFseeko(xc, fp, 1, SEEK_SET); + fstWriterUint64(fp, offpnt - 1); + fclose(fp); + fclose(xc->handle); + xc->handle = NULL; + + unlink(xc->filename); + rename(hf, xc->filename); + } else { + xc->repack_on_close = 0; + fclose(xc->handle); + xc->handle = NULL; + } + + free(hf); + } else { + fclose(xc->handle); + xc->handle = NULL; + } + } + +#ifdef __MINGW32__ + { + int flen = strlen(xc->filename); + char *hf = (char *)calloc(1, flen + 6); + strcpy(hf, xc->filename); + + if (xc->compress_hier) { + strcpy(hf + flen, ".hier"); + unlink(hf); /* no longer needed as a section now exists for this */ + } + + free(hf); + } +#endif + +#ifdef FST_WRITER_PARALLEL + pthread_mutex_destroy(&xc->mutex); + pthread_attr_destroy(&xc->thread_attr); +#endif + + if (xc->path_array) { +#ifndef _WAVE_HAVE_JUDY + const uint32_t hashmask = FST_PATH_HASHMASK; +#endif + JudyHSFreeArray(&(xc->path_array), NULL); + } + + free(xc->filename); + xc->filename = NULL; + free(xc); + } +} + +/* + * functions to set miscellaneous header/block information + */ +void fstWriterSetDate(void *ctx, const char *dat) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + char s[FST_HDR_DATE_SIZE]; + fst_off_t fpos = ftello(xc->handle); + int len = strlen(dat); + + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_DATE, SEEK_SET); + memset(s, 0, FST_HDR_DATE_SIZE); + memcpy(s, dat, (len < FST_HDR_DATE_SIZE) ? len : FST_HDR_DATE_SIZE); + fstFwrite(s, FST_HDR_DATE_SIZE, 1, xc->handle); + fflush(xc->handle); + fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); + } +} + +void fstWriterSetVersion(void *ctx, const char *vers) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc && vers) { + char s[FST_HDR_SIM_VERSION_SIZE]; + fst_off_t fpos = ftello(xc->handle); + int len = strlen(vers); + + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_SIM_VERSION, SEEK_SET); + memset(s, 0, FST_HDR_SIM_VERSION_SIZE); + memcpy(s, vers, (len < FST_HDR_SIM_VERSION_SIZE) ? len : FST_HDR_SIM_VERSION_SIZE); + fstFwrite(s, FST_HDR_SIM_VERSION_SIZE, 1, xc->handle); + fflush(xc->handle); + fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); + } +} + +void fstWriterSetFileType(void *ctx, enum fstFileType filetype) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + if (/*(filetype >= FST_FT_MIN) &&*/ (filetype <= FST_FT_MAX)) { + fst_off_t fpos = ftello(xc->handle); + + xc->filetype = filetype; + + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_FILETYPE, SEEK_SET); + fputc(xc->filetype, xc->handle); + fflush(xc->handle); + fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); + } + } +} + +static void fstWriterSetAttrDoubleArgGeneric(void *ctx, int typ, uint64_t arg1, uint64_t arg2) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + unsigned char buf[11]; /* ceil(64/7) = 10 + null term */ + unsigned char *pnt = fstCopyVarint64ToRight(buf, arg1); + if (arg1) { + *pnt = 0; /* this converts any *nonzero* arg1 when made a varint into a null-term string */ + } + + fstWriterSetAttrBegin(xc, FST_AT_MISC, typ, (char *)buf, arg2); + } +} + +static void fstWriterSetAttrGeneric(void *ctx, const char *comm, int typ, uint64_t arg) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc && comm) { + char *s = strdup(comm); + char *sf = s; + + while (*s) { + if ((*s == '\n') || (*s == '\r')) + *s = ' '; + s++; + } + + fstWriterSetAttrBegin(xc, FST_AT_MISC, typ, sf, arg); + free(sf); + } +} + +static void fstWriterSetSourceStem_2(void *ctx, const char *path, unsigned int line, unsigned int use_realpath, int typ) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + if (xc && path && path[0]) { + uint64_t sidx = 0; + int slen = strlen(path); +#ifndef _WAVE_HAVE_JUDY + const uint32_t hashmask = FST_PATH_HASHMASK; + const unsigned char *path2 = (const unsigned char *)path; + PPvoid_t pv; +#else + char *path2 = (char *)alloca(slen + 1); /* judy lacks const qualifier in its JudyHSIns definition */ + PPvoid_t pv; + strcpy(path2, path); +#endif + + pv = JudyHSIns(&(xc->path_array), path2, slen, NULL); + if (*pv) { + sidx = (intptr_t)(*pv); + } else { + char *rp = NULL; + + sidx = ++xc->path_array_count; + *pv = (void *)(intptr_t)(xc->path_array_count); + + if (use_realpath) { + rp = fstRealpath( +#ifndef _WAVE_HAVE_JUDY + (const char *) +#endif + path2, + NULL); + } + + fstWriterSetAttrGeneric(xc, + rp ? rp : +#ifndef _WAVE_HAVE_JUDY + (const char *) +#endif + path2, + FST_MT_PATHNAME, sidx); + + if (rp) { + free(rp); + } + } + + fstWriterSetAttrDoubleArgGeneric(xc, typ, sidx, line); + } +} + +void fstWriterSetSourceStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath) +{ + fstWriterSetSourceStem_2(ctx, path, line, use_realpath, FST_MT_SOURCESTEM); +} + +void fstWriterSetSourceInstantiationStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath) +{ + fstWriterSetSourceStem_2(ctx, path, line, use_realpath, FST_MT_SOURCEISTEM); +} + +void fstWriterSetComment(void *ctx, const char *comm) { fstWriterSetAttrGeneric(ctx, comm, FST_MT_COMMENT, 0); } + +void fstWriterSetValueList(void *ctx, const char *vl) { fstWriterSetAttrGeneric(ctx, vl, FST_MT_VALUELIST, 0); } + +void fstWriterSetEnvVar(void *ctx, const char *envvar) { fstWriterSetAttrGeneric(ctx, envvar, FST_MT_ENVVAR, 0); } + +void fstWriterSetTimescale(void *ctx, int ts) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + fst_off_t fpos = ftello(xc->handle); + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMESCALE, SEEK_SET); + fputc(ts & 255, xc->handle); + fflush(xc->handle); + fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); + } +} + +void fstWriterSetTimescaleFromString(void *ctx, const char *s) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc && s) { + int mat = 0; + int seconds_exp = -9; + int tv = atoi(s); + const char *pnt = s; + + while (*pnt) { + switch (*pnt) { + case 'm': + seconds_exp = -3; + mat = 1; + break; + case 'u': + seconds_exp = -6; + mat = 1; + break; + case 'n': + seconds_exp = -9; + mat = 1; + break; + case 'p': + seconds_exp = -12; + mat = 1; + break; + case 'f': + seconds_exp = -15; + mat = 1; + break; + case 'a': + seconds_exp = -18; + mat = 1; + break; + case 'z': + seconds_exp = -21; + mat = 1; + break; + case 's': + seconds_exp = 0; + mat = 1; + break; + default: + break; + } + + if (mat) + break; + pnt++; + } + + if (tv == 10) { + seconds_exp++; + } else if (tv == 100) { + seconds_exp += 2; + } + + fstWriterSetTimescale(ctx, seconds_exp); + } +} + +void fstWriterSetTimezero(void *ctx, int64_t tim) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + fst_off_t fpos = ftello(xc->handle); + fstWriterFseeko(xc, xc->handle, FST_HDR_OFFS_TIMEZERO, SEEK_SET); + fstWriterUint64(xc->handle, (xc->timezero = tim)); + fflush(xc->handle); + fstWriterFseeko(xc, xc->handle, fpos, SEEK_SET); + } +} + +void fstWriterSetPackType(void *ctx, enum fstWriterPackType typ) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + xc->fastpack = (typ != FST_WR_PT_ZLIB); + xc->fourpack = (typ == FST_WR_PT_LZ4); + } +} + +void fstWriterSetRepackOnClose(void *ctx, int enable) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + xc->repack_on_close = (enable != 0); + } +} + +void fstWriterSetParallelMode(void *ctx, int enable) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + xc->parallel_was_enabled |= xc->parallel_enabled; /* make sticky */ + xc->parallel_enabled = (enable != 0); +#ifndef FST_WRITER_PARALLEL + if (xc->parallel_enabled) { + fprintf(stderr, FST_APIMESS + "fstWriterSetParallelMode(), FST_WRITER_PARALLEL not enabled during compile, exiting.\n"); + exit(255); + } +#endif + } +} + +void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + xc->dump_size_limit = numbytes; + } +} + +int fstWriterGetDumpSizeLimitReached(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + return (xc->size_limit_locked != 0); + } + + return (0); +} + +int fstWriterGetFseekFailed(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc) { + return (xc->fseek_failed != 0); + } + + return (0); +} + +/* + * writer attr/scope/var creation: + * fstWriterCreateVar2() is used to dump VHDL or other languages, but the + * underlying variable needs to map to Verilog/SV via the proper fstVarType vt + */ +fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, + fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt, + enum fstSupplementalDataType sdt) +{ + fstWriterSetAttrGeneric(ctx, type ? type : "", FST_MT_SUPVAR, + (svt << FST_SDT_SVT_SHIFT_COUNT) | (sdt & FST_SDT_ABS_MAX)); + return (fstWriterCreateVar(ctx, vt, vd, len, nam, aliasHandle)); +} + +fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, + fstHandle aliasHandle) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + unsigned int i; + int nlen, is_real; + + if (xc && nam) { + if (xc->valpos_mem) { + fstDestroyMmaps(xc, 0); + } + + fputc(vt, xc->hier_handle); + fputc(vd, xc->hier_handle); + nlen = strlen(nam); + fstFwrite(nam, nlen, 1, xc->hier_handle); + fputc(0, xc->hier_handle); + xc->hier_file_len += (nlen + 3); + + if ((vt == FST_VT_VCD_REAL) || (vt == FST_VT_VCD_REAL_PARAMETER) || (vt == FST_VT_VCD_REALTIME) || + (vt == FST_VT_SV_SHORTREAL)) { + is_real = 1; + len = 8; /* recast number of bytes to that of what a double is */ + } else { + is_real = 0; + if (vt == FST_VT_GEN_STRING) { + len = 0; + } + } + + xc->hier_file_len += fstWriterVarint(xc->hier_handle, len); + + if (aliasHandle > xc->maxhandle) + aliasHandle = 0; + xc->hier_file_len += fstWriterVarint(xc->hier_handle, aliasHandle); + xc->numsigs++; + if (xc->numsigs == xc->next_huge_break) { + if (xc->fst_break_size < xc->fst_huge_break_size) { + xc->next_huge_break += FST_ACTIVATE_HUGE_INC; + xc->fst_break_size += xc->fst_orig_break_size; + xc->fst_break_add_size += xc->fst_orig_break_add_size; + + xc->vchg_alloc_siz = xc->fst_break_size + xc->fst_break_add_size; + if (xc->vchg_mem) { + xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz); + } + } + } + + if (!aliasHandle) { + uint32_t zero = 0; + + if (len) { + fstWriterVarint(xc->geom_handle, !is_real ? len : 0); /* geom section encodes reals as zero byte */ + } else { + fstWriterVarint(xc->geom_handle, 0xFFFFFFFF); /* geom section encodes zero len as 32b -1 */ + } + + fstFwrite(&xc->maxvalpos, sizeof(uint32_t), 1, xc->valpos_handle); + fstFwrite(&len, sizeof(uint32_t), 1, xc->valpos_handle); + fstFwrite(&zero, sizeof(uint32_t), 1, xc->valpos_handle); + fstFwrite(&zero, sizeof(uint32_t), 1, xc->valpos_handle); + + if (!is_real) { + for (i = 0; i < len; i++) { + fputc('x', xc->curval_handle); + } + } else { + fstFwrite(&xc->nan, 8, 1, xc->curval_handle); /* initialize doubles to NaN rather than x */ + } + + xc->maxvalpos += len; + xc->maxhandle++; + return (xc->maxhandle); + } else { + return (aliasHandle); + } + } + + return (0); +} + +void fstWriterSetScope(void *ctx, enum fstScopeType scopetype, const char *scopename, const char *scopecomp) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + if (xc) { + fputc(FST_ST_VCD_SCOPE, xc->hier_handle); + if (/*(scopetype < FST_ST_VCD_MODULE) ||*/ (scopetype > FST_ST_MAX)) { + scopetype = FST_ST_VCD_MODULE; + } + fputc(scopetype, xc->hier_handle); + fprintf(xc->hier_handle, "%s%c%s%c", scopename ? scopename : "", 0, scopecomp ? scopecomp : "", 0); + + if (scopename) { + xc->hier_file_len += strlen(scopename); + } + if (scopecomp) { + xc->hier_file_len += strlen(scopecomp); + } + + xc->hier_file_len += 4; /* FST_ST_VCD_SCOPE + scopetype + two string terminating zeros */ + xc->numscopes++; + } +} + +void fstWriterSetUpscope(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + if (xc) { + fputc(FST_ST_VCD_UPSCOPE, xc->hier_handle); + xc->hier_file_len++; + } +} + +void fstWriterSetAttrBegin(void *ctx, enum fstAttrType attrtype, int subtype, const char *attrname, uint64_t arg) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + if (xc) { + fputc(FST_ST_GEN_ATTRBEGIN, xc->hier_handle); + if (/*(attrtype < FST_AT_MISC) ||*/ (attrtype > FST_AT_MAX)) { + attrtype = FST_AT_MISC; + subtype = FST_MT_UNKNOWN; + } + fputc(attrtype, xc->hier_handle); + + switch (attrtype) { + case FST_AT_ARRAY: + if ((subtype < FST_AR_NONE) || (subtype > FST_AR_MAX)) + subtype = FST_AR_NONE; + break; + case FST_AT_ENUM: + if ((subtype < FST_EV_SV_INTEGER) || (subtype > FST_EV_MAX)) + subtype = FST_EV_SV_INTEGER; + break; + case FST_AT_PACK: + if ((subtype < FST_PT_NONE) || (subtype > FST_PT_MAX)) + subtype = FST_PT_NONE; + break; + + case FST_AT_MISC: + default: + break; + } + + fputc(subtype, xc->hier_handle); + fprintf(xc->hier_handle, "%s%c", attrname ? attrname : "", 0); + + if (attrname) { + xc->hier_file_len += strlen(attrname); + } + + xc->hier_file_len += 4; /* FST_ST_GEN_ATTRBEGIN + type + subtype + string terminating zero */ + xc->hier_file_len += fstWriterVarint(xc->hier_handle, arg); + } +} + +void fstWriterSetAttrEnd(void *ctx) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + if (xc) { + fputc(FST_ST_GEN_ATTREND, xc->hier_handle); + xc->hier_file_len++; + } +} + +fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits, + const char **literal_arr, const char **val_arr) +{ + fstEnumHandle handle = 0; + unsigned int *literal_lens = NULL; + unsigned int *val_lens = NULL; + int lit_len_tot = 0; + int val_len_tot = 0; + int name_len; + char elem_count_buf[16]; + int elem_count_len; + int total_len; + int pos = 0; + char *attr_str = NULL; + + if (ctx && name && literal_arr && val_arr && (elem_count != 0)) { + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + uint32_t i; + + name_len = strlen(name); + elem_count_len = sprintf(elem_count_buf, "%" PRIu32, elem_count); + + literal_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int)); + val_lens = (unsigned int *)calloc(elem_count, sizeof(unsigned int)); + + for (i = 0; i < elem_count; i++) { + literal_lens[i] = strlen(literal_arr[i]); + lit_len_tot += fstUtilityBinToEscConvertedLen((unsigned char *)literal_arr[i], literal_lens[i]); + + val_lens[i] = strlen(val_arr[i]); + val_len_tot += fstUtilityBinToEscConvertedLen((unsigned char *)val_arr[i], val_lens[i]); + + if (min_valbits > 0) { + if (val_lens[i] < min_valbits) { + val_len_tot += (min_valbits - val_lens[i]); /* additional converted len is same for '0' character */ + } + } + } + + total_len = name_len + 1 + elem_count_len + 1 + lit_len_tot + elem_count + val_len_tot + elem_count; + + attr_str = (char *)malloc(total_len); + pos = 0; + + memcpy(attr_str + pos, name, name_len); + pos += name_len; + attr_str[pos++] = ' '; + + memcpy(attr_str + pos, elem_count_buf, elem_count_len); + pos += elem_count_len; + attr_str[pos++] = ' '; + + for (i = 0; i < elem_count; i++) { + pos += fstUtilityBinToEsc((unsigned char *)attr_str + pos, (unsigned char *)literal_arr[i], + literal_lens[i]); + attr_str[pos++] = ' '; + } + + for (i = 0; i < elem_count; i++) { + if (min_valbits > 0) { + if (val_lens[i] < min_valbits) { + memset(attr_str + pos, '0', min_valbits - val_lens[i]); + pos += (min_valbits - val_lens[i]); + } + } + + pos += fstUtilityBinToEsc((unsigned char *)attr_str + pos, (unsigned char *)val_arr[i], val_lens[i]); + attr_str[pos++] = ' '; + } + + attr_str[pos - 1] = 0; + +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "fstWriterCreateEnumTable() total_len: %d, pos: %d\n", total_len, pos); + fprintf(stderr, FST_APIMESS "*%s*\n", attr_str); +#endif + + fstWriterSetAttrBegin(xc, FST_AT_MISC, FST_MT_ENUMTABLE, attr_str, handle = ++xc->max_enumhandle); + + free(attr_str); + free(val_lens); + free(literal_lens); + } + + return (handle); +} + +void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (xc && handle) { + fstWriterSetAttrBegin(xc, FST_AT_MISC, FST_MT_ENUMTABLE, NULL, handle); + } +} + +/* + * value and time change emission + */ +void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + const unsigned char *buf = (const unsigned char *)val; + uint32_t offs; + int len; + + if (FST_LIKELY((xc) && (handle <= xc->maxhandle))) { + uint32_t fpos; + uint32_t *vm4ip; + + if (FST_UNLIKELY(!xc->valpos_mem)) { + xc->vc_emitted = 1; + fstWriterCreateMmaps(xc); + } + + handle--; /* move starting at 1 index to starting at 0 */ + vm4ip = &(xc->valpos_mem[4 * handle]); + + len = vm4ip[1]; + if (FST_LIKELY(len)) /* len of zero = variable length, use fstWriterEmitVariableLengthValueChange */ + { + if (FST_LIKELY(!xc->is_initial_time)) { + fpos = xc->vchg_siz; + + if (FST_UNLIKELY((fpos + len + 10) > xc->vchg_alloc_siz)) { + xc->vchg_alloc_siz += + (xc->fst_break_add_size + + len); /* +len added in the case of extremely long vectors and small break add sizes */ + xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz); + if (FST_UNLIKELY(!xc->vchg_mem)) { + fprintf(stderr, FST_APIMESS "Could not realloc() in fstWriterEmitValueChange, exiting.\n"); + exit(255); + } + } +#ifdef FST_REMOVE_DUPLICATE_VC + offs = vm4ip[0]; + + if (len != 1) { + if ((vm4ip[3] == xc->tchn_idx) && (vm4ip[2])) { + unsigned char *old_value = xc->vchg_mem + vm4ip[2] + 4; /* the +4 skips old vm4ip[2] value */ + while (*(old_value++) & 0x80) { /* skips over varint encoded "xc->tchn_idx - vm4ip[3]" */ + } + memcpy(old_value, buf, len); /* overlay new value */ + + memcpy(xc->curval_mem + offs, buf, len); + return; + } else { + if (!memcmp(xc->curval_mem + offs, buf, len)) { + if (!xc->curtime) { + int i; + for (i = 0; i < len; i++) { + if (buf[i] != 'x') + break; + } + + if (i < len) + return; + } else { + return; + } + } + } + + memcpy(xc->curval_mem + offs, buf, len); + } else { + if ((vm4ip[3] == xc->tchn_idx) && (vm4ip[2])) { + unsigned char *old_value = xc->vchg_mem + vm4ip[2] + 4; /* the +4 skips old vm4ip[2] value */ + while (*(old_value++) & 0x80) { /* skips over varint encoded "xc->tchn_idx - vm4ip[3]" */ + } + *old_value = *buf; /* overlay new value */ + + *(xc->curval_mem + offs) = *buf; + return; + } else { + if ((*(xc->curval_mem + offs)) == (*buf)) { + if (!xc->curtime) { + if (*buf != 'x') + return; + } else { + return; + } + } + } + + *(xc->curval_mem + offs) = *buf; + } +#endif + xc->vchg_siz += fstWriterUint32WithVarint32(xc, &vm4ip[2], xc->tchn_idx - vm4ip[3], buf, + len); /* do one fwrite op only */ + vm4ip[3] = xc->tchn_idx; + vm4ip[2] = fpos; + } else { + offs = vm4ip[0]; + memcpy(xc->curval_mem + offs, buf, len); + } + } + } +} + +void fstWriterEmitValueChange32(void *ctx, fstHandle handle, uint32_t bits, uint32_t val) +{ + char buf[32]; + char *s = buf; + uint32_t i; + for (i = 0; i < bits; ++i) { + *s++ = '0' + ((val >> (bits - i - 1)) & 1); + } + fstWriterEmitValueChange(ctx, handle, buf); +} +void fstWriterEmitValueChange64(void *ctx, fstHandle handle, uint32_t bits, uint64_t val) +{ + char buf[64]; + char *s = buf; + uint32_t i; + for (i = 0; i < bits; ++i) { + *s++ = '0' + ((val >> (bits - i - 1)) & 1); + } + fstWriterEmitValueChange(ctx, handle, buf); +} +void fstWriterEmitValueChangeVec32(void *ctx, fstHandle handle, uint32_t bits, const uint32_t *val) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (FST_UNLIKELY(bits <= 32)) { + fstWriterEmitValueChange32(ctx, handle, bits, val[0]); + } else if (FST_LIKELY(xc)) { + int bq = bits / 32; + int br = bits & 31; + int i; + int w; + uint32_t v; + unsigned char *s; + if (FST_UNLIKELY(bits > xc->outval_alloc_siz)) { + xc->outval_alloc_siz = bits * 2 + 1; + xc->outval_mem = (unsigned char *)realloc(xc->outval_mem, xc->outval_alloc_siz); + if (FST_UNLIKELY(!xc->outval_mem)) { + fprintf(stderr, FST_APIMESS "Could not realloc() in fstWriterEmitValueChangeVec32, exiting.\n"); + exit(255); + } + } + s = xc->outval_mem; + { + w = bq; + v = val[w]; + for (i = 0; i < br; ++i) { + *s++ = '0' + ((v >> (br - i - 1)) & 1); + } + } + for (w = bq - 1; w >= 0; --w) { + v = val[w]; + for (i = (32 - 4); i >= 0; i -= 4) { + s[0] = '0' + ((v >> (i + 3)) & 1); + s[1] = '0' + ((v >> (i + 2)) & 1); + s[2] = '0' + ((v >> (i + 1)) & 1); + s[3] = '0' + ((v >> (i + 0)) & 1); + s += 4; + } + } + fstWriterEmitValueChange(ctx, handle, xc->outval_mem); + } +} +void fstWriterEmitValueChangeVec64(void *ctx, fstHandle handle, uint32_t bits, const uint64_t *val) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (FST_UNLIKELY(bits <= 64)) { + fstWriterEmitValueChange64(ctx, handle, bits, val[0]); + } else if (FST_LIKELY(xc)) { + int bq = bits / 64; + int br = bits & 63; + int i; + int w; + uint32_t v; + unsigned char *s; + if (FST_UNLIKELY(bits > xc->outval_alloc_siz)) { + xc->outval_alloc_siz = bits * 2 + 1; + xc->outval_mem = (unsigned char *)realloc(xc->outval_mem, xc->outval_alloc_siz); + if (FST_UNLIKELY(!xc->outval_mem)) { + fprintf(stderr, FST_APIMESS "Could not realloc() in fstWriterEmitValueChangeVec64, exiting.\n"); + exit(255); + } + } + s = xc->outval_mem; + { + w = bq; + v = val[w]; + for (i = 0; i < br; ++i) { + *s++ = '0' + ((v >> (br - i - 1)) & 1); + } + } + for (w = bq - 1; w >= 0; --w) { + v = val[w]; + for (i = (64 - 4); i >= 0; i -= 4) { + s[0] = '0' + ((v >> (i + 3)) & 1); + s[1] = '0' + ((v >> (i + 2)) & 1); + s[2] = '0' + ((v >> (i + 1)) & 1); + s[3] = '0' + ((v >> (i + 0)) & 1); + s += 4; + } + } + fstWriterEmitValueChange(ctx, handle, xc->outval_mem); + } +} + +void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + const unsigned char *buf = (const unsigned char *)val; + + if (FST_LIKELY((xc) && (handle <= xc->maxhandle))) { + uint32_t fpos; + uint32_t *vm4ip; + + if (FST_UNLIKELY(!xc->valpos_mem)) { + xc->vc_emitted = 1; + fstWriterCreateMmaps(xc); + } + + handle--; /* move starting at 1 index to starting at 0 */ + vm4ip = &(xc->valpos_mem[4 * handle]); + + /* there is no initial time dump for variable length value changes */ + if (FST_LIKELY(!vm4ip[1])) /* len of zero = variable length */ + { + fpos = xc->vchg_siz; + + if (FST_UNLIKELY((fpos + len + 10 + 5) > xc->vchg_alloc_siz)) { + xc->vchg_alloc_siz += + (xc->fst_break_add_size + len + + 5); /* +len added in the case of extremely long vectors and small break add sizes */ + xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz); + if (FST_UNLIKELY(!xc->vchg_mem)) { + fprintf(stderr, + FST_APIMESS "Could not realloc() in fstWriterEmitVariableLengthValueChange, exiting.\n"); + exit(255); + } + } + + xc->vchg_siz += fstWriterUint32WithVarint32AndLength(xc, &vm4ip[2], xc->tchn_idx - vm4ip[3], buf, + len); /* do one fwrite op only */ + vm4ip[3] = xc->tchn_idx; + vm4ip[2] = fpos; + } + } +} + +void fstWriterEmitTimeChange(void *ctx, uint64_t tim) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + unsigned int i; + int skip = 0; + if (xc) { + if (FST_UNLIKELY(xc->is_initial_time)) { + if (xc->size_limit_locked) /* this resets xc->is_initial_time to one */ + { + return; + } + + if (!xc->valpos_mem) { + fstWriterCreateMmaps(xc); + } + + skip = 1; + + xc->firsttime = (xc->vc_emitted) ? 0 : tim; + xc->curtime = 0; + xc->vchg_mem[0] = '!'; + xc->vchg_siz = 1; + fstWriterEmitSectionHeader(xc); + for (i = 0; i < xc->maxhandle; i++) { + xc->valpos_mem[4 * i + 2] = 0; /* zero out offset val */ + xc->valpos_mem[4 * i + 3] = 0; /* zero out last time change val */ + } + xc->is_initial_time = 0; + } else { + if ((xc->vchg_siz >= xc->fst_break_size) || (xc->flush_context_pending)) { + xc->flush_context_pending = 0; + fstWriterFlushContextPrivate(xc); + xc->tchn_cnt++; + fstWriterVarint(xc->tchn_handle, xc->curtime); + } + } + + if (!skip) { + xc->tchn_idx++; + } + fstWriterVarint(xc->tchn_handle, tim - xc->curtime); + xc->tchn_cnt++; + xc->curtime = tim; + } +} + +void fstWriterEmitDumpActive(void *ctx, int enable) +{ + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + + if (xc) { + struct fstBlackoutChain *b = (struct fstBlackoutChain *)calloc(1, sizeof(struct fstBlackoutChain)); + + b->tim = xc->curtime; + b->active = (enable != 0); + + xc->num_blackouts++; + if (xc->blackout_curr) { + xc->blackout_curr->next = b; + xc->blackout_curr = b; + } else { + xc->blackout_head = b; + xc->blackout_curr = b; + } + } +} + +/***********************/ +/*** ***/ +/*** reader function ***/ +/*** ***/ +/***********************/ + +/* + * private structs + */ +static const char *vartypes[] = {"event", "integer", "parameter", "real", "real_parameter", "reg", "supply0", + "supply1", "time", "tri", "triand", "trior", "trireg", "tri0", + "tri1", "wand", "wire", "wor", "port", "sparray", "realtime", + "string", "bit", "logic", "int", "shortint", "longint", "byte", + "enum", "shortreal"}; + +static const char *modtypes[] = {"module", + "task", + "function", + "begin", + "fork", + "generate", + "struct", + "union", + "class", + "interface", + "package", + "program", + "vhdl_architecture", + "vhdl_procedure", + "vhdl_function", + "vhdl_record", + "vhdl_process", + "vhdl_block", + "vhdl_for_generate", + "vhdl_if_generate", + "vhdl_generate", + "vhdl_package"}; + +static const char *attrtypes[] = {"misc", "array", "enum", "class"}; + +static const char *arraytypes[] = {"none", "unpacked", "packed", "sparse"}; + +static const char *enumvaluetypes[] = {"integer", + "bit", + "logic", + "int", + "shortint", + "longint", + "byte", + "unsigned_integer", + "unsigned_bit", + "unsigned_logic", + "unsigned_int", + "unsigned_shortint", + "unsigned_longint", + "unsigned_byte"}; + +static const char *packtypes[] = {"none", "unpacked", "packed", "tagged_packed"}; + +struct fstCurrHier +{ + struct fstCurrHier *prev; + void *user_info; + int len; +}; + +struct fstReaderContext +{ + /* common entries */ + + FILE *f, *fh; + + uint64_t start_time, end_time; + uint64_t mem_used_by_writer; + uint64_t scope_count; + uint64_t var_count; + fstHandle maxhandle; + uint64_t num_alias; + uint64_t vc_section_count; + + uint32_t *signal_lens; /* maxhandle sized */ + unsigned char *signal_typs; /* maxhandle sized */ + unsigned char *process_mask; /* maxhandle-based, bitwise sized */ + uint32_t longest_signal_value_len; /* longest len value encountered */ + unsigned char *temp_signal_value_buf; /* malloced for len in longest_signal_value_len */ + + signed char timescale; + unsigned char filetype; + + unsigned use_vcd_extensions : 1; + unsigned double_endian_match : 1; + unsigned native_doubles_for_cb : 1; + unsigned contains_geom_section : 1; + unsigned contains_hier_section : 1; /* valid for hier_pos */ + unsigned contains_hier_section_lz4duo : 1; /* valid for hier_pos (contains_hier_section_lz4 always also set) */ + unsigned contains_hier_section_lz4 : 1; /* valid for hier_pos */ + unsigned limit_range_valid : 1; /* valid for limit_range_start, limit_range_end */ + + char version[FST_HDR_SIM_VERSION_SIZE + 1]; + char date[FST_HDR_DATE_SIZE + 1]; + int64_t timezero; + + char *filename, *filename_unpacked; + fst_off_t hier_pos; + + uint32_t num_blackouts; + uint64_t *blackout_times; + unsigned char *blackout_activity; + + uint64_t limit_range_start, limit_range_end; + + /* entries specific to read value at time functions */ + + unsigned rvat_data_valid : 1; + uint64_t *rvat_time_table; + uint64_t rvat_beg_tim, rvat_end_tim; + unsigned char *rvat_frame_data; + uint64_t rvat_frame_maxhandle; + fst_off_t *rvat_chain_table; + uint32_t *rvat_chain_table_lengths; + uint64_t rvat_vc_maxhandle; + fst_off_t rvat_vc_start; + uint32_t *rvat_sig_offs; + int rvat_packtype; + + uint32_t rvat_chain_len; + unsigned char *rvat_chain_mem; + fstHandle rvat_chain_facidx; + + uint32_t rvat_chain_pos_tidx; + uint32_t rvat_chain_pos_idx; + uint64_t rvat_chain_pos_time; + unsigned rvat_chain_pos_valid : 1; + + /* entries specific to hierarchy traversal */ + + struct fstHier hier; + struct fstCurrHier *curr_hier; + fstHandle current_handle; + char *curr_flat_hier_nam; + int flat_hier_alloc_len; + unsigned do_rewind : 1; + char str_scope_nam[FST_ID_NAM_SIZ + 1]; + char str_scope_comp[FST_ID_NAM_SIZ + 1]; + + unsigned fseek_failed : 1; + + /* self-buffered I/O for writes */ + +#ifndef FST_WRITEX_DISABLE + int writex_pos; + int writex_fd; + unsigned char writex_buf[FST_WRITEX_MAX]; +#endif + + char *f_nam; + char *fh_nam; +}; + +int fstReaderFseeko(struct fstReaderContext *xc, FILE *stream, fst_off_t offset, int whence) +{ + int rc = fseeko(stream, offset, whence); + + if (rc < 0) { + xc->fseek_failed = 1; +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "Seek to #%" PRId64 " (whence = %d) failed!\n", offset, whence); + perror("Why"); +#endif + } + + return (rc); +} + +#ifndef FST_WRITEX_DISABLE +static void fstWritex(struct fstReaderContext *xc, void *v, int len) +{ + unsigned char *s = (unsigned char *)v; + + if (len) { + if (len < FST_WRITEX_MAX) { + if (xc->writex_pos + len >= FST_WRITEX_MAX) { + fstWritex(xc, NULL, 0); + } + + memcpy(xc->writex_buf + xc->writex_pos, s, len); + xc->writex_pos += len; + } else { + fstWritex(xc, NULL, 0); + if (write(xc->writex_fd, s, len)) { + }; + } + } else { + if (xc->writex_pos) { + if (write(xc->writex_fd, xc->writex_buf, xc->writex_pos)) { + }; + xc->writex_pos = 0; + } + } +} +#endif + +/* + * scope -> flat name handling + */ +static void fstReaderDeallocateScopeData(struct fstReaderContext *xc) +{ + struct fstCurrHier *chp; + + free(xc->curr_flat_hier_nam); + xc->curr_flat_hier_nam = NULL; + while (xc->curr_hier) { + chp = xc->curr_hier->prev; + free(xc->curr_hier); + xc->curr_hier = chp; + } +} + +const char *fstReaderGetCurrentFlatScope(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + if (xc) { + return (xc->curr_flat_hier_nam ? xc->curr_flat_hier_nam : ""); + } else { + return (NULL); + } +} + +void *fstReaderGetCurrentScopeUserInfo(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + if (xc) { + return (xc->curr_hier ? xc->curr_hier->user_info : NULL); + } else { + return (NULL); + } +} + +const char *fstReaderPopScope(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + if (xc && xc->curr_hier) { + struct fstCurrHier *ch = xc->curr_hier; + if (xc->curr_hier->prev) { + xc->curr_flat_hier_nam[xc->curr_hier->prev->len] = 0; + } else { + *xc->curr_flat_hier_nam = 0; + } + xc->curr_hier = xc->curr_hier->prev; + free(ch); + return (xc->curr_flat_hier_nam ? xc->curr_flat_hier_nam : ""); + } + + return (NULL); +} + +void fstReaderResetScope(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + while (fstReaderPopScope(xc)) + ; /* remove any already-built scoping info */ + } +} + +const char *fstReaderPushScope(void *ctx, const char *nam, void *user_info) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + if (xc) { + struct fstCurrHier *ch = (struct fstCurrHier *)malloc(sizeof(struct fstCurrHier)); + int chl = xc->curr_hier ? xc->curr_hier->len : 0; + int len = chl + 1 + strlen(nam); + if (len >= xc->flat_hier_alloc_len) { + xc->curr_flat_hier_nam = + xc->curr_flat_hier_nam ? (char *)realloc(xc->curr_flat_hier_nam, len + 1) : (char *)malloc(len + 1); + } + + if (chl) { + xc->curr_flat_hier_nam[chl] = '.'; + strcpy(xc->curr_flat_hier_nam + chl + 1, nam); + } else { + strcpy(xc->curr_flat_hier_nam, nam); + len--; + } + + ch->len = len; + ch->prev = xc->curr_hier; + ch->user_info = user_info; + xc->curr_hier = ch; + return (xc->curr_flat_hier_nam); + } + + return (NULL); +} + +int fstReaderGetCurrentScopeLen(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc && xc->curr_hier) { + return (xc->curr_hier->len); + } + + return (0); +} + +int fstReaderGetFseekFailed(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + if (xc) { + return (xc->fseek_failed != 0); + } + + return (0); +} + +/* + * iter mask manipulation util functions + */ +int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + facidx--; + if (facidx < xc->maxhandle) { + int process_idx = facidx / 8; + int process_bit = facidx & 7; + + return ((xc->process_mask[process_idx] & (1 << process_bit)) != 0); + } + } + return (0); +} + +void fstReaderSetFacProcessMask(void *ctx, fstHandle facidx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + facidx--; + if (facidx < xc->maxhandle) { + int idx = facidx / 8; + int bitpos = facidx & 7; + + xc->process_mask[idx] |= (1 << bitpos); + } + } +} + +void fstReaderClrFacProcessMask(void *ctx, fstHandle facidx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + facidx--; + if (facidx < xc->maxhandle) { + int idx = facidx / 8; + int bitpos = facidx & 7; + + xc->process_mask[idx] &= (~(1 << bitpos)); + } + } +} + +void fstReaderSetFacProcessMaskAll(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + memset(xc->process_mask, 0xff, (xc->maxhandle + 7) / 8); + } +} + +void fstReaderClrFacProcessMaskAll(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + memset(xc->process_mask, 0x00, (xc->maxhandle + 7) / 8); + } +} + +/* + * various utility read/write functions + */ +signed char fstReaderGetTimescale(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->timescale : 0); +} + +uint64_t fstReaderGetStartTime(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->start_time : 0); +} + +uint64_t fstReaderGetEndTime(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->end_time : 0); +} + +uint64_t fstReaderGetMemoryUsedByWriter(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->mem_used_by_writer : 0); +} + +uint64_t fstReaderGetScopeCount(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->scope_count : 0); +} + +uint64_t fstReaderGetVarCount(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->var_count : 0); +} + +fstHandle fstReaderGetMaxHandle(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->maxhandle : 0); +} + +uint64_t fstReaderGetAliasCount(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->num_alias : 0); +} + +uint64_t fstReaderGetValueChangeSectionCount(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->vc_section_count : 0); +} + +int fstReaderGetDoubleEndianMatchState(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->double_endian_match : 0); +} + +const char *fstReaderGetVersionString(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->version : NULL); +} + +const char *fstReaderGetDateString(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->date : NULL); +} + +int fstReaderGetFileType(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? (int)xc->filetype : (int)FST_FT_VERILOG); +} + +int64_t fstReaderGetTimezero(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->timezero : 0); +} + +uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + return (xc ? xc->num_blackouts : 0); +} + +uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc && (idx < xc->num_blackouts) && (xc->blackout_times)) { + return (xc->blackout_times[idx]); + } else { + return (0); + } +} + +unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc && (idx < xc->num_blackouts) && (xc->blackout_activity)) { + return (xc->blackout_activity[idx]); + } else { + return (0); + } +} + +void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + xc->limit_range_valid = 1; + xc->limit_range_start = start_time; + xc->limit_range_end = end_time; + } +} + +void fstReaderSetUnlimitedTimeRange(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + xc->limit_range_valid = 0; + } +} + +void fstReaderSetVcdExtensions(void *ctx, int enable) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + xc->use_vcd_extensions = (enable != 0); + } +} + +void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + if (xc) { + xc->native_doubles_for_cb = (enable != 0); + } +} + +/* + * hierarchy processing + */ +static void fstVcdID(char *buf, unsigned int value) +{ + char *pnt = buf; + + /* zero is illegal for a value...it is assumed they start at one */ + while (value) { + value--; + *(pnt++) = (char)('!' + value % 94); + value = value / 94; + } + + *pnt = 0; +} + +static int fstVcdIDForFwrite(char *buf, unsigned int value) +{ + char *pnt = buf; + + /* zero is illegal for a value...it is assumed they start at one */ + while (value) { + value--; + *(pnt++) = (char)('!' + value % 94); + value = value / 94; + } + + return (pnt - buf); +} + +static int fstReaderRecreateHierFile(struct fstReaderContext *xc) +{ + int pass_status = 1; + + if (!xc->fh) { + fst_off_t offs_cache = ftello(xc->f); + char *fnam = (char *)malloc(strlen(xc->filename) + 6 + 16 + 32 + 1); + unsigned char *mem = (unsigned char *)malloc(FST_GZIO_LEN); + fst_off_t hl, uclen; + fst_off_t clen = 0; + gzFile zhandle = NULL; + int zfd; + int htyp = FST_BL_SKIP; + + /* can't handle both set at once should never happen in a real file */ + if (!xc->contains_hier_section_lz4 && xc->contains_hier_section) { + htyp = FST_BL_HIER; + } else if (xc->contains_hier_section_lz4 && !xc->contains_hier_section) { + htyp = xc->contains_hier_section_lz4duo ? FST_BL_HIER_LZ4DUO : FST_BL_HIER_LZ4; + } + + sprintf(fnam, "%s.hier_%d_%p", xc->filename, getpid(), (void *)xc); + fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET); + uclen = fstReaderUint64(xc->f); +#ifndef __MINGW32__ + fflush(xc->f); +#endif + if (htyp == FST_BL_HIER) { + fstReaderFseeko(xc, xc->f, xc->hier_pos, SEEK_SET); + uclen = fstReaderUint64(xc->f); +#ifndef __MINGW32__ + fflush(xc->f); +#endif + zfd = dup(fileno(xc->f)); + lseek(zfd, ftell(xc->f), SEEK_SET); + zhandle = gzdopen(zfd, "rb"); + if (!zhandle) { + close(zfd); + free(mem); + free(fnam); + return (0); + } + } else if ((htyp == FST_BL_HIER_LZ4) || (htyp == FST_BL_HIER_LZ4DUO)) { + fstReaderFseeko(xc, xc->f, xc->hier_pos - 8, SEEK_SET); /* get section len */ + clen = fstReaderUint64(xc->f) - 16; + uclen = fstReaderUint64(xc->f); +#ifndef __MINGW32__ + fflush(xc->f); +#endif + } + +#ifndef __MINGW32__ + xc->fh = fopen(fnam, "w+b"); + if (!xc->fh) +#endif + { + xc->fh = tmpfile_open(&xc->fh_nam); + free(fnam); + fnam = NULL; + if (!xc->fh) { + tmpfile_close(&xc->fh, &xc->fh_nam); + free(mem); + return (0); + } + } + +#ifndef __MINGW32__ + if (fnam) + unlink(fnam); +#endif + + if (htyp == FST_BL_HIER) { + for (hl = 0; hl < uclen; hl += FST_GZIO_LEN) { + size_t len = ((uclen - hl) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - hl); + size_t gzreadlen = gzread(zhandle, mem, len); /* rc should equal len... */ + size_t fwlen; + + if (gzreadlen != len) { + pass_status = 0; + break; + } + + fwlen = fstFwrite(mem, len, 1, xc->fh); + if (fwlen != 1) { + pass_status = 0; + break; + } + } + gzclose(zhandle); + } else if (htyp == FST_BL_HIER_LZ4DUO) { + unsigned char *lz4_cmem = (unsigned char *)malloc(clen); + unsigned char *lz4_ucmem = (unsigned char *)malloc(uclen); + unsigned char *lz4_ucmem2; + uint64_t uclen2; + int skiplen2 = 0; + + fstFread(lz4_cmem, clen, 1, xc->f); + + uclen2 = fstGetVarint64(lz4_cmem, &skiplen2); + lz4_ucmem2 = (unsigned char *)malloc(uclen2); + pass_status = + (uclen2 == (uint64_t)LZ4_decompress_safe_partial((char *)lz4_cmem + skiplen2, (char *)lz4_ucmem2, + clen - skiplen2, uclen2, uclen2)); + if (pass_status) { + pass_status = (uclen == LZ4_decompress_safe_partial((char *)lz4_ucmem2, (char *)lz4_ucmem, uclen2, + uclen, uclen)); + + if (fstFwrite(lz4_ucmem, uclen, 1, xc->fh) != 1) { + pass_status = 0; + } + } + + free(lz4_ucmem2); + free(lz4_ucmem); + free(lz4_cmem); + } else if (htyp == FST_BL_HIER_LZ4) { + unsigned char *lz4_cmem = (unsigned char *)malloc(clen); + unsigned char *lz4_ucmem = (unsigned char *)malloc(uclen); + + fstFread(lz4_cmem, clen, 1, xc->f); + pass_status = + (uclen == LZ4_decompress_safe_partial((char *)lz4_cmem, (char *)lz4_ucmem, clen, uclen, uclen)); + + if (fstFwrite(lz4_ucmem, uclen, 1, xc->fh) != 1) { + pass_status = 0; + } + + free(lz4_ucmem); + free(lz4_cmem); + } else /* FST_BL_SKIP */ + { + pass_status = 0; + if (xc->fh) { + fclose(xc->fh); + xc->fh = NULL; /* needed in case .hier file is missing and there are no hier sections */ + } + } + + free(mem); + free(fnam); + + fstReaderFseeko(xc, xc->f, offs_cache, SEEK_SET); + } + + return (pass_status); +} + +int fstReaderIterateHierRewind(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + int pass_status = 0; + + if (xc) { + pass_status = 1; + if (!xc->fh) { + pass_status = fstReaderRecreateHierFile(xc); + } + + xc->do_rewind = 1; + } + + return (pass_status); +} + +struct fstHier *fstReaderIterateHier(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + int isfeof; + fstHandle alias; + char *pnt; + int ch; + + if (!xc) + return (NULL); + + if (!xc->fh) { + if (!fstReaderRecreateHierFile(xc)) { + return (NULL); + } + } + + if (xc->do_rewind) { + xc->do_rewind = 0; + xc->current_handle = 0; + fstReaderFseeko(xc, xc->fh, 0, SEEK_SET); + clearerr(xc->fh); + } + + if (!(isfeof = feof(xc->fh))) { + int tag = fgetc(xc->fh); + switch (tag) { + case FST_ST_VCD_SCOPE: + xc->hier.htyp = FST_HT_SCOPE; + xc->hier.u.scope.typ = fgetc(xc->fh); + xc->hier.u.scope.name = pnt = xc->str_scope_nam; + while ((ch = fgetc(xc->fh))) { + *(pnt++) = ch; + }; /* scopename */ + *pnt = 0; + xc->hier.u.scope.name_length = pnt - xc->hier.u.scope.name; + + xc->hier.u.scope.component = pnt = xc->str_scope_comp; + while ((ch = fgetc(xc->fh))) { + *(pnt++) = ch; + }; /* scopecomp */ + *pnt = 0; + xc->hier.u.scope.component_length = pnt - xc->hier.u.scope.component; + break; + + case FST_ST_VCD_UPSCOPE: + xc->hier.htyp = FST_HT_UPSCOPE; + break; + + case FST_ST_GEN_ATTRBEGIN: + xc->hier.htyp = FST_HT_ATTRBEGIN; + xc->hier.u.attr.typ = fgetc(xc->fh); + xc->hier.u.attr.subtype = fgetc(xc->fh); + xc->hier.u.attr.name = pnt = xc->str_scope_nam; + while ((ch = fgetc(xc->fh))) { + *(pnt++) = ch; + }; /* scopename */ + *pnt = 0; + xc->hier.u.attr.name_length = pnt - xc->hier.u.scope.name; + + xc->hier.u.attr.arg = fstReaderVarint64(xc->fh); + + if (xc->hier.u.attr.typ == FST_AT_MISC) { + if ((xc->hier.u.attr.subtype == FST_MT_SOURCESTEM) || (xc->hier.u.attr.subtype == FST_MT_SOURCEISTEM)) { + int sidx_skiplen_dummy = 0; + xc->hier.u.attr.arg_from_name = + fstGetVarint64((unsigned char *)xc->str_scope_nam, &sidx_skiplen_dummy); + } + } + break; + + case FST_ST_GEN_ATTREND: + xc->hier.htyp = FST_HT_ATTREND; + break; + + case FST_VT_VCD_EVENT: + case FST_VT_VCD_INTEGER: + case FST_VT_VCD_PARAMETER: + case FST_VT_VCD_REAL: + case FST_VT_VCD_REAL_PARAMETER: + case FST_VT_VCD_REG: + case FST_VT_VCD_SUPPLY0: + case FST_VT_VCD_SUPPLY1: + case FST_VT_VCD_TIME: + case FST_VT_VCD_TRI: + case FST_VT_VCD_TRIAND: + case FST_VT_VCD_TRIOR: + case FST_VT_VCD_TRIREG: + case FST_VT_VCD_TRI0: + case FST_VT_VCD_TRI1: + case FST_VT_VCD_WAND: + case FST_VT_VCD_WIRE: + case FST_VT_VCD_WOR: + case FST_VT_VCD_PORT: + case FST_VT_VCD_SPARRAY: + case FST_VT_VCD_REALTIME: + case FST_VT_GEN_STRING: + case FST_VT_SV_BIT: + case FST_VT_SV_LOGIC: + case FST_VT_SV_INT: + case FST_VT_SV_SHORTINT: + case FST_VT_SV_LONGINT: + case FST_VT_SV_BYTE: + case FST_VT_SV_ENUM: + case FST_VT_SV_SHORTREAL: + xc->hier.htyp = FST_HT_VAR; + xc->hier.u.var.svt_workspace = FST_SVT_NONE; + xc->hier.u.var.sdt_workspace = FST_SDT_NONE; + xc->hier.u.var.sxt_workspace = 0; + xc->hier.u.var.typ = tag; + xc->hier.u.var.direction = fgetc(xc->fh); + xc->hier.u.var.name = pnt = xc->str_scope_nam; + while ((ch = fgetc(xc->fh))) { + *(pnt++) = ch; + }; /* varname */ + *pnt = 0; + xc->hier.u.var.name_length = pnt - xc->hier.u.var.name; + xc->hier.u.var.length = fstReaderVarint32(xc->fh); + if (tag == FST_VT_VCD_PORT) { + xc->hier.u.var.length -= 2; /* removal of delimiting spaces */ + xc->hier.u.var.length /= 3; /* port -> signal size adjust */ + } + + alias = fstReaderVarint32(xc->fh); + + if (!alias) { + xc->current_handle++; + xc->hier.u.var.handle = xc->current_handle; + xc->hier.u.var.is_alias = 0; + } else { + xc->hier.u.var.handle = alias; + xc->hier.u.var.is_alias = 1; + } + + break; + + default: + isfeof = 1; + break; + } + } + + return (!isfeof ? &xc->hier : NULL); +} + +int fstReaderProcessHier(void *ctx, FILE *fv) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + char *str; + char *pnt; + int ch, scopetype; + int vartype; + uint32_t len, alias; + /* uint32_t maxvalpos=0; */ + unsigned int num_signal_dyn = 65536; + int attrtype, subtype; + uint64_t attrarg; + fstHandle maxhandle_scanbuild; + + if (!xc) + return (0); + + xc->longest_signal_value_len = 32; /* arbitrarily set at 32...this is much longer than an expanded double */ + + if (!xc->fh) { + if (!fstReaderRecreateHierFile(xc)) { + return (0); + } + } + + str = (char *)malloc(FST_ID_NAM_ATTR_SIZ + 1); + + if (fv) { + char time_dimension[2] = {0, 0}; + int time_scale = 1; + + fprintf(fv, "$date\n\t%s\n$end\n", xc->date); + fprintf(fv, "$version\n\t%s\n$end\n", xc->version); + if (xc->timezero) + fprintf(fv, "$timezero\n\t%" PRId64 "\n$end\n", xc->timezero); + + switch (xc->timescale) { + case 2: + time_scale = 100; + time_dimension[0] = 0; + break; + case 1: + time_scale = 10; /* fallthrough */ + case 0: + time_dimension[0] = 0; + break; + + case -1: + time_scale = 100; + time_dimension[0] = 'm'; + break; + case -2: + time_scale = 10; /* fallthrough */ + case -3: + time_dimension[0] = 'm'; + break; + + case -4: + time_scale = 100; + time_dimension[0] = 'u'; + break; + case -5: + time_scale = 10; /* fallthrough */ + case -6: + time_dimension[0] = 'u'; + break; + + case -10: + time_scale = 100; + time_dimension[0] = 'p'; + break; + case -11: + time_scale = 10; /* fallthrough */ + case -12: + time_dimension[0] = 'p'; + break; + + case -13: + time_scale = 100; + time_dimension[0] = 'f'; + break; + case -14: + time_scale = 10; /* fallthrough */ + case -15: + time_dimension[0] = 'f'; + break; + + case -16: + time_scale = 100; + time_dimension[0] = 'a'; + break; + case -17: + time_scale = 10; /* fallthrough */ + case -18: + time_dimension[0] = 'a'; + break; + + case -19: + time_scale = 100; + time_dimension[0] = 'z'; + break; + case -20: + time_scale = 10; /* fallthrough */ + case -21: + time_dimension[0] = 'z'; + break; + + case -7: + time_scale = 100; + time_dimension[0] = 'n'; + break; + case -8: + time_scale = 10; /* fallthrough */ + case -9: + default: + time_dimension[0] = 'n'; + break; + } + + if (fv) + fprintf(fv, "$timescale\n\t%d%ss\n$end\n", time_scale, time_dimension); + } + + xc->maxhandle = 0; + xc->num_alias = 0; + + free(xc->signal_lens); + xc->signal_lens = (uint32_t *)malloc(num_signal_dyn * sizeof(uint32_t)); + + free(xc->signal_typs); + xc->signal_typs = (unsigned char *)malloc(num_signal_dyn * sizeof(unsigned char)); + + fstReaderFseeko(xc, xc->fh, 0, SEEK_SET); + while (!feof(xc->fh)) { + int tag = fgetc(xc->fh); + switch (tag) { + case FST_ST_VCD_SCOPE: + scopetype = fgetc(xc->fh); + if ((scopetype < FST_ST_MIN) || (scopetype > FST_ST_MAX)) + scopetype = FST_ST_VCD_MODULE; + pnt = str; + while ((ch = fgetc(xc->fh))) { + *(pnt++) = ch; + }; /* scopename */ + *pnt = 0; + while (fgetc(xc->fh)) { + }; /* scopecomp */ + + if (fv) + fprintf(fv, "$scope %s %s $end\n", modtypes[scopetype], str); + break; + + case FST_ST_VCD_UPSCOPE: + if (fv) + fprintf(fv, "$upscope $end\n"); + break; + + case FST_ST_GEN_ATTRBEGIN: + attrtype = fgetc(xc->fh); + subtype = fgetc(xc->fh); + pnt = str; + while ((ch = fgetc(xc->fh))) { + *(pnt++) = ch; + }; /* attrname */ + *pnt = 0; + + if (!str[0]) { + strcpy(str, "\"\""); + } + + attrarg = fstReaderVarint64(xc->fh); + + if (fv && xc->use_vcd_extensions) { + switch (attrtype) { + case FST_AT_ARRAY: + if ((subtype < FST_AR_NONE) || (subtype > FST_AR_MAX)) + subtype = FST_AR_NONE; + fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], arraytypes[subtype], str, + attrarg); + break; + case FST_AT_ENUM: + if ((subtype < FST_EV_SV_INTEGER) || (subtype > FST_EV_MAX)) + subtype = FST_EV_SV_INTEGER; + fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], enumvaluetypes[subtype], + str, attrarg); + break; + case FST_AT_PACK: + if ((subtype < FST_PT_NONE) || (subtype > FST_PT_MAX)) + subtype = FST_PT_NONE; + fprintf(fv, "$attrbegin %s %s %s %" PRId64 " $end\n", attrtypes[attrtype], packtypes[subtype], str, + attrarg); + break; + case FST_AT_MISC: + default: + attrtype = FST_AT_MISC; + if (subtype == FST_MT_COMMENT) { + fprintf(fv, "$comment\n\t%s\n$end\n", str); + } else { + if ((subtype == FST_MT_SOURCESTEM) || (subtype == FST_MT_SOURCEISTEM)) { + int sidx_skiplen_dummy = 0; + uint64_t sidx = fstGetVarint64((unsigned char *)str, &sidx_skiplen_dummy); + + fprintf(fv, "$attrbegin %s %02x %" PRId64 " %" PRId64 " $end\n", attrtypes[attrtype], + subtype, sidx, attrarg); + } else { + fprintf(fv, "$attrbegin %s %02x %s %" PRId64 " $end\n", attrtypes[attrtype], subtype, str, + attrarg); + } + } + break; + } + } + break; + + case FST_ST_GEN_ATTREND: + if (fv && xc->use_vcd_extensions) + fprintf(fv, "$attrend $end\n"); + break; + + case FST_VT_VCD_EVENT: + case FST_VT_VCD_INTEGER: + case FST_VT_VCD_PARAMETER: + case FST_VT_VCD_REAL: + case FST_VT_VCD_REAL_PARAMETER: + case FST_VT_VCD_REG: + case FST_VT_VCD_SUPPLY0: + case FST_VT_VCD_SUPPLY1: + case FST_VT_VCD_TIME: + case FST_VT_VCD_TRI: + case FST_VT_VCD_TRIAND: + case FST_VT_VCD_TRIOR: + case FST_VT_VCD_TRIREG: + case FST_VT_VCD_TRI0: + case FST_VT_VCD_TRI1: + case FST_VT_VCD_WAND: + case FST_VT_VCD_WIRE: + case FST_VT_VCD_WOR: + case FST_VT_VCD_PORT: + case FST_VT_VCD_SPARRAY: + case FST_VT_VCD_REALTIME: + case FST_VT_GEN_STRING: + case FST_VT_SV_BIT: + case FST_VT_SV_LOGIC: + case FST_VT_SV_INT: + case FST_VT_SV_SHORTINT: + case FST_VT_SV_LONGINT: + case FST_VT_SV_BYTE: + case FST_VT_SV_ENUM: + case FST_VT_SV_SHORTREAL: + vartype = tag; + /* vardir = */ fgetc(xc->fh); /* unused in VCD reader, but need to advance read pointer */ + pnt = str; + while ((ch = fgetc(xc->fh))) { + *(pnt++) = ch; + }; /* varname */ + *pnt = 0; + len = fstReaderVarint32(xc->fh); + alias = fstReaderVarint32(xc->fh); + + if (!alias) { + if (xc->maxhandle == num_signal_dyn) { + num_signal_dyn *= 2; + xc->signal_lens = (uint32_t *)realloc(xc->signal_lens, num_signal_dyn * sizeof(uint32_t)); + xc->signal_typs = (unsigned char *)realloc(xc->signal_typs, num_signal_dyn * sizeof(unsigned char)); + } + xc->signal_lens[xc->maxhandle] = len; + xc->signal_typs[xc->maxhandle] = vartype; + + /* maxvalpos+=len; */ + if (len > xc->longest_signal_value_len) { + xc->longest_signal_value_len = len; + } + + if ((vartype == FST_VT_VCD_REAL) || (vartype == FST_VT_VCD_REAL_PARAMETER) || + (vartype == FST_VT_VCD_REALTIME) || (vartype == FST_VT_SV_SHORTREAL)) { + len = (vartype != FST_VT_SV_SHORTREAL) ? 64 : 32; + xc->signal_typs[xc->maxhandle] = FST_VT_VCD_REAL; + } + if (fv) { + char vcdid_buf[16]; + uint32_t modlen = (vartype != FST_VT_VCD_PORT) ? len : ((len - 2) / 3); + fstVcdID(vcdid_buf, xc->maxhandle + 1); + fprintf(fv, "$var %s %" PRIu32 " %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str); + } + xc->maxhandle++; + } else { + if ((vartype == FST_VT_VCD_REAL) || (vartype == FST_VT_VCD_REAL_PARAMETER) || + (vartype == FST_VT_VCD_REALTIME) || (vartype == FST_VT_SV_SHORTREAL)) { + len = (vartype != FST_VT_SV_SHORTREAL) ? 64 : 32; + xc->signal_typs[xc->maxhandle] = FST_VT_VCD_REAL; + } + if (fv) { + char vcdid_buf[16]; + uint32_t modlen = (vartype != FST_VT_VCD_PORT) ? len : ((len - 2) / 3); + fstVcdID(vcdid_buf, alias); + fprintf(fv, "$var %s %" PRIu32 " %s %s $end\n", vartypes[vartype], modlen, vcdid_buf, str); + } + xc->num_alias++; + } + + break; + + default: + break; + } + } + if (fv) + fprintf(fv, "$enddefinitions $end\n"); + + maxhandle_scanbuild = xc->maxhandle ? xc->maxhandle + : 1; /*scan-build warning suppression, in reality we have at least one signal */ + + xc->signal_lens = (uint32_t *)realloc(xc->signal_lens, maxhandle_scanbuild * sizeof(uint32_t)); + xc->signal_typs = (unsigned char *)realloc(xc->signal_typs, maxhandle_scanbuild * sizeof(unsigned char)); + + free(xc->process_mask); + xc->process_mask = (unsigned char *)calloc(1, (maxhandle_scanbuild + 7) / 8); + + free(xc->temp_signal_value_buf); + xc->temp_signal_value_buf = (unsigned char *)malloc(xc->longest_signal_value_len + 1); + + xc->var_count = xc->maxhandle + xc->num_alias; + + free(str); + return (1); +} + +/* + * reader file open/close functions + */ +int fstReaderInit(struct fstReaderContext *xc) +{ + fst_off_t blkpos = 0; + fst_off_t endfile; + uint64_t seclen; + int sectype; + uint64_t vc_section_count_actual = 0; + int hdr_incomplete = 0; + int hdr_seen = 0; + int gzread_pass_status = 1; + + sectype = fgetc(xc->f); + if (sectype == FST_BL_ZWRAPPER) { + FILE *fcomp; + fst_off_t offpnt, uclen; + char gz_membuf[FST_GZIO_LEN]; + gzFile zhandle; + int zfd; + int flen = strlen(xc->filename); + char *hf; + + seclen = fstReaderUint64(xc->f); + uclen = fstReaderUint64(xc->f); + + if (!seclen) + return (0); /* not finished compressing, this is a failed read */ + + hf = (char *)calloc(1, flen + 16 + 32 + 1); + + sprintf(hf, "%s.upk_%d_%p", xc->filename, getpid(), (void *)xc); + fcomp = fopen(hf, "w+b"); + if (!fcomp) { + fcomp = tmpfile_open(&xc->f_nam); + free(hf); + hf = NULL; + if (!fcomp) { + tmpfile_close(&fcomp, &xc->f_nam); + return (0); + } + } + +#if defined(FST_MACOSX) + setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */ +#endif + +#ifdef __MINGW32__ + setvbuf(fcomp, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */ + xc->filename_unpacked = hf; +#else + if (hf) { + unlink(hf); + free(hf); + } +#endif + + fstReaderFseeko(xc, xc->f, 1 + 8 + 8, SEEK_SET); +#ifndef __MINGW32__ + fflush(xc->f); +#endif + + zfd = dup(fileno(xc->f)); + lseek(zfd, ftell(xc->f), SEEK_SET); + zhandle = gzdopen(zfd, "rb"); + if (zhandle) { + for (offpnt = 0; offpnt < uclen; offpnt += FST_GZIO_LEN) { + size_t this_len = ((uclen - offpnt) > FST_GZIO_LEN) ? FST_GZIO_LEN : (uclen - offpnt); + size_t gzreadlen = gzread(zhandle, gz_membuf, this_len); + size_t fwlen; + + if (gzreadlen != this_len) { + gzread_pass_status = 0; + break; + } + fwlen = fstFwrite(gz_membuf, this_len, 1, fcomp); + if (fwlen != 1) { + gzread_pass_status = 0; + break; + } + } + gzclose(zhandle); + } else { + close(zfd); + } + fflush(fcomp); + fclose(xc->f); + xc->f = fcomp; + } + + if (gzread_pass_status) { + fstReaderFseeko(xc, xc->f, 0, SEEK_END); + endfile = ftello(xc->f); + + while (blkpos < endfile) { + fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET); + + sectype = fgetc(xc->f); + seclen = fstReaderUint64(xc->f); + + if (sectype == EOF) { + break; + } + + if ((hdr_incomplete) && (!seclen)) { + break; + } + + if (!hdr_seen && (sectype != FST_BL_HDR)) { + break; + } + + blkpos++; + if (sectype == FST_BL_HDR) { + if (!hdr_seen) { + int ch; + double dcheck; + + xc->start_time = fstReaderUint64(xc->f); + xc->end_time = fstReaderUint64(xc->f); + + hdr_incomplete = (xc->start_time == 0) && (xc->end_time == 0); + + fstFread(&dcheck, 8, 1, xc->f); + xc->double_endian_match = (dcheck == (double)FST_DOUBLE_ENDTEST); + if (!xc->double_endian_match) { + union + { + unsigned char rvs_buf[8]; + double d; + } vu; + + unsigned char *dcheck_alias = (unsigned char *)&dcheck; + int rvs_idx; + + for (rvs_idx = 0; rvs_idx < 8; rvs_idx++) { + vu.rvs_buf[rvs_idx] = dcheck_alias[7 - rvs_idx]; + } + if (vu.d != FST_DOUBLE_ENDTEST) { + break; /* either corrupt file or wrong architecture (offset +33 also functions as matchword) + */ + } + } + + hdr_seen = 1; + + xc->mem_used_by_writer = fstReaderUint64(xc->f); + xc->scope_count = fstReaderUint64(xc->f); + xc->var_count = fstReaderUint64(xc->f); + xc->maxhandle = fstReaderUint64(xc->f); + xc->num_alias = xc->var_count - xc->maxhandle; + xc->vc_section_count = fstReaderUint64(xc->f); + ch = fgetc(xc->f); + xc->timescale = (signed char)ch; + fstFread(xc->version, FST_HDR_SIM_VERSION_SIZE, 1, xc->f); + xc->version[FST_HDR_SIM_VERSION_SIZE] = 0; + fstFread(xc->date, FST_HDR_DATE_SIZE, 1, xc->f); + xc->date[FST_HDR_DATE_SIZE] = 0; + ch = fgetc(xc->f); + xc->filetype = (unsigned char)ch; + xc->timezero = fstReaderUint64(xc->f); + } + } else if ((sectype == FST_BL_VCDATA) || (sectype == FST_BL_VCDATA_DYN_ALIAS) || + (sectype == FST_BL_VCDATA_DYN_ALIAS2)) { + if (hdr_incomplete) { + uint64_t bt = fstReaderUint64(xc->f); + xc->end_time = fstReaderUint64(xc->f); + + if (!vc_section_count_actual) { + xc->start_time = bt; + } + } + + vc_section_count_actual++; + } else if (sectype == FST_BL_GEOM) { + if (!hdr_incomplete) { + uint64_t clen = seclen - 24; + uint64_t uclen = fstReaderUint64(xc->f); + unsigned char *ucdata = (unsigned char *)malloc(uclen); + unsigned char *pnt = ucdata; + unsigned int i; + + xc->contains_geom_section = 1; + xc->maxhandle = fstReaderUint64(xc->f); + xc->longest_signal_value_len = + 32; /* arbitrarily set at 32...this is much longer than an expanded double */ + + free(xc->process_mask); + xc->process_mask = (unsigned char *)calloc(1, (xc->maxhandle + 7) / 8); + + if (clen != uclen) { + unsigned char *cdata = (unsigned char *)malloc(clen); + unsigned long destlen = uclen; + unsigned long sourcelen = clen; + int rc; + + fstFread(cdata, clen, 1, xc->f); + rc = uncompress(ucdata, &destlen, cdata, sourcelen); + + if (rc != Z_OK) { + fprintf(stderr, FST_APIMESS "fstReaderInit(), geom uncompress rc = %d, exiting.\n", rc); + exit(255); + } + + free(cdata); + } else { + fstFread(ucdata, uclen, 1, xc->f); + } + + free(xc->signal_lens); + xc->signal_lens = (uint32_t *)malloc(sizeof(uint32_t) * xc->maxhandle); + free(xc->signal_typs); + xc->signal_typs = (unsigned char *)malloc(sizeof(unsigned char) * xc->maxhandle); + + for (i = 0; i < xc->maxhandle; i++) { + int skiplen; + uint64_t val = fstGetVarint32(pnt, &skiplen); + + pnt += skiplen; + + if (val) { + xc->signal_lens[i] = (val != 0xFFFFFFFF) ? val : 0; + xc->signal_typs[i] = FST_VT_VCD_WIRE; + if (xc->signal_lens[i] > xc->longest_signal_value_len) { + xc->longest_signal_value_len = xc->signal_lens[i]; + } + } else { + xc->signal_lens[i] = 8; /* backpatch in real */ + xc->signal_typs[i] = FST_VT_VCD_REAL; + /* xc->longest_signal_value_len handled above by overly large init size */ + } + } + + free(xc->temp_signal_value_buf); + xc->temp_signal_value_buf = (unsigned char *)malloc(xc->longest_signal_value_len + 1); + + free(ucdata); + } + } else if (sectype == FST_BL_HIER) { + xc->contains_hier_section = 1; + xc->hier_pos = ftello(xc->f); + } else if (sectype == FST_BL_HIER_LZ4DUO) { + xc->contains_hier_section_lz4 = 1; + xc->contains_hier_section_lz4duo = 1; + xc->hier_pos = ftello(xc->f); + } else if (sectype == FST_BL_HIER_LZ4) { + xc->contains_hier_section_lz4 = 1; + xc->hier_pos = ftello(xc->f); + } else if (sectype == FST_BL_BLACKOUT) { + uint32_t i; + uint64_t cur_bl = 0; + uint64_t delta; + + xc->num_blackouts = fstReaderVarint32(xc->f); + free(xc->blackout_times); + xc->blackout_times = (uint64_t *)calloc(xc->num_blackouts, sizeof(uint64_t)); + free(xc->blackout_activity); + xc->blackout_activity = (unsigned char *)calloc(xc->num_blackouts, sizeof(unsigned char)); + + for (i = 0; i < xc->num_blackouts; i++) { + xc->blackout_activity[i] = fgetc(xc->f) != 0; + delta = fstReaderVarint64(xc->f); + cur_bl += delta; + xc->blackout_times[i] = cur_bl; + } + } + + blkpos += seclen; + if (!hdr_seen) + break; + } + + if (hdr_seen) { + if (xc->vc_section_count != vc_section_count_actual) { + xc->vc_section_count = vc_section_count_actual; + } + + if (!xc->contains_geom_section) { + fstReaderProcessHier(xc, NULL); /* recreate signal_lens/signal_typs info */ + } + } + } + + return (hdr_seen); +} + +void *fstReaderOpenForUtilitiesOnly(void) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)calloc(1, sizeof(struct fstReaderContext)); + + return (xc); +} + +void *fstReaderOpen(const char *nam) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)calloc(1, sizeof(struct fstReaderContext)); + + if ((!nam) || (!(xc->f = fopen(nam, "rb")))) { + free(xc); + xc = NULL; + } else { + int flen = strlen(nam); + char *hf = (char *)calloc(1, flen + 6); + int rc; + +#if defined(__MINGW32__) || defined(FST_MACOSX) + setvbuf(xc->f, (char *)NULL, _IONBF, 0); /* keeps gzip from acting weird in tandem with fopen */ +#endif + + memcpy(hf, nam, flen); + strcpy(hf + flen, ".hier"); + xc->fh = fopen(hf, "rb"); + + free(hf); + xc->filename = strdup(nam); + rc = fstReaderInit(xc); + + if ((rc) && (xc->vc_section_count) && (xc->maxhandle) && + ((xc->fh) || (xc->contains_hier_section || (xc->contains_hier_section_lz4)))) { + /* more init */ + xc->do_rewind = 1; + } else { + fstReaderClose(xc); + xc = NULL; + } + } + + return (xc); +} + +static void fstReaderDeallocateRvatData(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + if (xc) { + free(xc->rvat_chain_mem); + xc->rvat_chain_mem = NULL; + free(xc->rvat_frame_data); + xc->rvat_frame_data = NULL; + free(xc->rvat_time_table); + xc->rvat_time_table = NULL; + free(xc->rvat_chain_table); + xc->rvat_chain_table = NULL; + free(xc->rvat_chain_table_lengths); + xc->rvat_chain_table_lengths = NULL; + + xc->rvat_data_valid = 0; + } +} + +void fstReaderClose(void *ctx) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + if (xc) { + fstReaderDeallocateScopeData(xc); + fstReaderDeallocateRvatData(xc); + free(xc->rvat_sig_offs); + xc->rvat_sig_offs = NULL; + + free(xc->process_mask); + xc->process_mask = NULL; + free(xc->blackout_times); + xc->blackout_times = NULL; + free(xc->blackout_activity); + xc->blackout_activity = NULL; + free(xc->temp_signal_value_buf); + xc->temp_signal_value_buf = NULL; + free(xc->signal_typs); + xc->signal_typs = NULL; + free(xc->signal_lens); + xc->signal_lens = NULL; + free(xc->filename); + xc->filename = NULL; + + if (xc->fh) { + tmpfile_close(&xc->fh, &xc->fh_nam); + } + + if (xc->f) { + tmpfile_close(&xc->f, &xc->f_nam); + if (xc->filename_unpacked) { + unlink(xc->filename_unpacked); + free(xc->filename_unpacked); + } + } + + free(xc); + } +} + +/* + * read processing + */ + +/* normal read which re-interleaves the value change data */ +int fstReaderIterBlocks(void *ctx, + void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, + const unsigned char *value), + void *user_callback_data_pointer, FILE *fv) +{ + return (fstReaderIterBlocks2(ctx, value_change_callback, NULL, user_callback_data_pointer, fv)); +} + +int fstReaderIterBlocks2(void *ctx, + void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, + fstHandle facidx, const unsigned char *value), + void (*value_change_callback_varlen)(void *user_callback_data_pointer, uint64_t time, + fstHandle facidx, const unsigned char *value, + uint32_t len), + void *user_callback_data_pointer, FILE *fv) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + + uint64_t previous_time = UINT64_MAX; + uint64_t *time_table = NULL; + uint64_t tsec_nitems; + unsigned int secnum = 0; + int blocks_skipped = 0; + fst_off_t blkpos = 0; + uint64_t seclen, beg_tim; + uint64_t end_tim; + uint64_t frame_uclen, frame_clen, frame_maxhandle, vc_maxhandle; + fst_off_t vc_start; + fst_off_t indx_pntr, indx_pos; + fst_off_t *chain_table = NULL; + uint32_t *chain_table_lengths = NULL; + unsigned char *chain_cmem; + unsigned char *pnt; + long chain_clen; + fstHandle idx, pidx = 0, i; + uint64_t pval; + uint64_t vc_maxhandle_largest = 0; + uint64_t tsec_uclen = 0, tsec_clen = 0; + int sectype; + uint64_t mem_required_for_traversal; + unsigned char *mem_for_traversal = NULL; + uint32_t traversal_mem_offs; + uint32_t *scatterptr, *headptr, *length_remaining; + uint32_t cur_blackout = 0; + int packtype; + unsigned char *mc_mem = NULL; + uint32_t mc_mem_len; /* corresponds to largest value encountered in chain_table_lengths[i] */ + int dumpvars_state = 0; + + if (!xc) + return (0); + + scatterptr = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t)); + headptr = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t)); + length_remaining = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t)); + + if (fv) { +#ifndef FST_WRITEX_DISABLE + fflush(fv); + setvbuf(fv, (char *)NULL, _IONBF, + 0); /* even buffered IO is slow so disable it and use our own routines that don't need seeking */ + xc->writex_fd = fileno(fv); +#endif + } + + for (;;) { + uint32_t *tc_head = NULL; + traversal_mem_offs = 0; + + fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET); + + sectype = fgetc(xc->f); + seclen = fstReaderUint64(xc->f); + + if ((sectype == EOF) || (sectype == FST_BL_SKIP)) { +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "<< EOF >>\n"); +#endif + break; + } + + blkpos++; + if ((sectype != FST_BL_VCDATA) && (sectype != FST_BL_VCDATA_DYN_ALIAS) && + (sectype != FST_BL_VCDATA_DYN_ALIAS2)) { + blkpos += seclen; + continue; + } + + if (!seclen) + break; + + beg_tim = fstReaderUint64(xc->f); + end_tim = fstReaderUint64(xc->f); + + if (xc->limit_range_valid) { + if (end_tim < xc->limit_range_start) { + blocks_skipped++; + blkpos += seclen; + continue; + } + + if (beg_tim > + xc->limit_range_end) /* likely the compare in for(i=0;if); + mem_for_traversal = + (unsigned char *)malloc(mem_required_for_traversal + 66); /* add in potential fastlz overhead */ +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "sec: %u seclen: %d begtim: %d endtim: %d\n", secnum, (int)seclen, (int)beg_tim, + (int)end_tim); + fprintf(stderr, FST_APIMESS "mem_required_for_traversal: %d\n", (int)mem_required_for_traversal); +#endif + /* process time block */ + { + unsigned char *ucdata; + unsigned char *cdata; + unsigned long destlen /* = tsec_uclen */; /* scan-build */ + unsigned long sourcelen /*= tsec_clen */; /* scan-build */ + int rc; + unsigned char *tpnt; + uint64_t tpval; + unsigned int ti; + + if (fstReaderFseeko(xc, xc->f, blkpos + seclen - 24, SEEK_SET) != 0) + break; + tsec_uclen = fstReaderUint64(xc->f); + tsec_clen = fstReaderUint64(xc->f); + tsec_nitems = fstReaderUint64(xc->f); +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "time section unc: %d, com: %d (%d items)\n", (int)tsec_uclen, (int)tsec_clen, + (int)tsec_nitems); +#endif + if (tsec_clen > seclen) + break; /* corrupted tsec_clen: by definition it can't be larger than size of section */ + ucdata = (unsigned char *)malloc(tsec_uclen); + if (!ucdata) + break; /* malloc fail as tsec_uclen out of range from corrupted file */ + destlen = tsec_uclen; + sourcelen = tsec_clen; + + fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR); + + if (tsec_uclen != tsec_clen) { + cdata = (unsigned char *)malloc(tsec_clen); + fstFread(cdata, tsec_clen, 1, xc->f); + + rc = uncompress(ucdata, &destlen, cdata, sourcelen); + + if (rc != Z_OK) { + fprintf(stderr, FST_APIMESS "fstReaderIterBlocks2(), tsec uncompress rc = %d, exiting.\n", rc); + exit(255); + } + + free(cdata); + } else { + fstFread(ucdata, tsec_uclen, 1, xc->f); + } + + free(time_table); + time_table = (uint64_t *)calloc(tsec_nitems, sizeof(uint64_t)); + tpnt = ucdata; + tpval = 0; + for (ti = 0; ti < tsec_nitems; ti++) { + int skiplen; + uint64_t val = fstGetVarint64(tpnt, &skiplen); + tpval = time_table[ti] = tpval + val; + tpnt += skiplen; + } + + tc_head = (uint32_t *)calloc(tsec_nitems /* scan-build */ ? tsec_nitems : 1, sizeof(uint32_t)); + free(ucdata); + } + + fstReaderFseeko(xc, xc->f, blkpos + 32, SEEK_SET); + + frame_uclen = fstReaderVarint64(xc->f); + frame_clen = fstReaderVarint64(xc->f); + frame_maxhandle = fstReaderVarint64(xc->f); + + if (secnum == 0) { + if ((beg_tim != time_table[0]) || (blocks_skipped)) { + unsigned char *mu = (unsigned char *)malloc(frame_uclen); + uint32_t sig_offs = 0; + + if (fv) { + char wx_buf[32]; + int wx_len; + + if (beg_tim) { + if (dumpvars_state == 1) { + wx_len = sprintf(wx_buf, "$end\n"); + fstWritex(xc, wx_buf, wx_len); + dumpvars_state = 2; + } + wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", beg_tim); + fstWritex(xc, wx_buf, wx_len); + if (!dumpvars_state) { + wx_len = sprintf(wx_buf, "$dumpvars\n"); + fstWritex(xc, wx_buf, wx_len); + dumpvars_state = 1; + } + } + if ((xc->num_blackouts) && (cur_blackout != xc->num_blackouts)) { + if (beg_tim == xc->blackout_times[cur_blackout]) { + wx_len = sprintf(wx_buf, "$dump%s $end\n", + (xc->blackout_activity[cur_blackout++]) ? "on" : "off"); + fstWritex(xc, wx_buf, wx_len); + } + } + } + + if (frame_uclen == frame_clen) { + fstFread(mu, frame_uclen, 1, xc->f); + } else { + unsigned char *mc = (unsigned char *)malloc(frame_clen); + int rc; + + unsigned long destlen = frame_uclen; + unsigned long sourcelen = frame_clen; + + fstFread(mc, sourcelen, 1, xc->f); + rc = uncompress(mu, &destlen, mc, sourcelen); + if (rc != Z_OK) { + fprintf(stderr, FST_APIMESS "fstReaderIterBlocks2(), frame uncompress rc: %d, exiting.\n", rc); + exit(255); + } + free(mc); + } + + for (idx = 0; idx < frame_maxhandle; idx++) { + int process_idx = idx / 8; + int process_bit = idx & 7; + + if (xc->process_mask[process_idx] & (1 << process_bit)) { + if (xc->signal_lens[idx] <= 1) { + if (xc->signal_lens[idx] == 1) { + unsigned char val = mu[sig_offs]; + if (value_change_callback) { + xc->temp_signal_value_buf[0] = val; + xc->temp_signal_value_buf[1] = 0; + value_change_callback(user_callback_data_pointer, beg_tim, idx + 1, + xc->temp_signal_value_buf); + } else { + if (fv) { + char vcd_id[16]; + + int vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1); + vcd_id[0] = val; /* collapse 3 writes into one I/O call */ + vcd_id[vcdid_len + 1] = '\n'; + fstWritex(xc, vcd_id, vcdid_len + 2); + } + } + } else { + /* variable-length ("0" length) records have no initial state */ + } + } else { + if (xc->signal_typs[idx] != FST_VT_VCD_REAL) { + if (value_change_callback) { + memcpy(xc->temp_signal_value_buf, mu + sig_offs, xc->signal_lens[idx]); + xc->temp_signal_value_buf[xc->signal_lens[idx]] = 0; + value_change_callback(user_callback_data_pointer, beg_tim, idx + 1, + xc->temp_signal_value_buf); + } else { + if (fv) { + char vcd_id[16]; + int vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1); + + vcd_id[0] = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p'; + fstWritex(xc, vcd_id, 1); + fstWritex(xc, mu + sig_offs, xc->signal_lens[idx]); + + vcd_id[0] = ' '; /* collapse 3 writes into one I/O call */ + vcd_id[vcdid_len + 1] = '\n'; + fstWritex(xc, vcd_id, vcdid_len + 2); + } + } + } else { + double d; + unsigned char *clone_d; + unsigned char *srcdata = mu + sig_offs; + + if (value_change_callback) { + if (xc->native_doubles_for_cb) { + if (xc->double_endian_match) { + clone_d = srcdata; + } else { + int j; + + clone_d = (unsigned char *)&d; + for (j = 0; j < 8; j++) { + clone_d[j] = srcdata[7 - j]; + } + } + value_change_callback(user_callback_data_pointer, beg_tim, idx + 1, clone_d); + } else { + clone_d = (unsigned char *)&d; + if (xc->double_endian_match) { + memcpy(clone_d, srcdata, 8); + } else { + int j; + + for (j = 0; j < 8; j++) { + clone_d[j] = srcdata[7 - j]; + } + } + sprintf((char *)xc->temp_signal_value_buf, "%.16g", d); + value_change_callback(user_callback_data_pointer, beg_tim, idx + 1, + xc->temp_signal_value_buf); + } + } else { + if (fv) { + char vcdid_buf[16]; + char wx_buf[64]; + int wx_len; + + clone_d = (unsigned char *)&d; + if (xc->double_endian_match) { + memcpy(clone_d, srcdata, 8); + } else { + int j; + + for (j = 0; j < 8; j++) { + clone_d[j] = srcdata[7 - j]; + } + } + + fstVcdID(vcdid_buf, idx + 1); + wx_len = sprintf(wx_buf, "r%.16g %s\n", d, vcdid_buf); + fstWritex(xc, wx_buf, wx_len); + } + } + } + } + } + + sig_offs += xc->signal_lens[idx]; + } + + free(mu); + fstReaderFseeko(xc, xc->f, -((fst_off_t)frame_clen), SEEK_CUR); + } + } + + fstReaderFseeko(xc, xc->f, (fst_off_t)frame_clen, SEEK_CUR); /* skip past compressed data */ + + vc_maxhandle = fstReaderVarint64(xc->f); + vc_start = ftello(xc->f); /* points to '!' character */ + packtype = fgetc(xc->f); + +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "frame_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", (int)frame_uclen, + (int)frame_clen, (int)frame_maxhandle); + fprintf(stderr, FST_APIMESS "vc_maxhandle: %d, packtype: %c\n", (int)vc_maxhandle, packtype); +#endif + + indx_pntr = blkpos + seclen - 24 - tsec_clen - 8; + fstReaderFseeko(xc, xc->f, indx_pntr, SEEK_SET); + chain_clen = fstReaderUint64(xc->f); + indx_pos = indx_pntr - chain_clen; +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "indx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen); +#endif + chain_cmem = (unsigned char *)malloc(chain_clen); + if (!chain_cmem) + goto block_err; + fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET); + fstFread(chain_cmem, chain_clen, 1, xc->f); + + if (vc_maxhandle > vc_maxhandle_largest) { + free(chain_table); + free(chain_table_lengths); + + vc_maxhandle_largest = vc_maxhandle; + chain_table = (fst_off_t *)calloc((vc_maxhandle + 1), sizeof(fst_off_t)); + chain_table_lengths = (uint32_t *)calloc((vc_maxhandle + 1), sizeof(uint32_t)); + } + + if (!chain_table || !chain_table_lengths) + goto block_err; + + pnt = chain_cmem; + idx = 0; + pval = 0; + + if (sectype == FST_BL_VCDATA_DYN_ALIAS2) { + uint32_t prev_alias = 0; + + do { + int skiplen; + + if (*pnt & 0x01) { + int64_t shval = fstGetSVarint64(pnt, &skiplen) >> 1; + if (shval > 0) { + pval = chain_table[idx] = pval + shval; + if (idx) { + chain_table_lengths[pidx] = pval - chain_table[pidx]; + } + pidx = idx++; + } else if (shval < 0) { + chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ + chain_table_lengths[idx] = prev_alias = + shval; /* because during this loop iter would give stale data! */ + idx++; + } else { + chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ + chain_table_lengths[idx] = + prev_alias; /* because during this loop iter would give stale data! */ + idx++; + } + } else { + uint64_t val = fstGetVarint32(pnt, &skiplen); + + fstHandle loopcnt = val >> 1; + for (i = 0; i < loopcnt; i++) { + chain_table[idx++] = 0; + } + } + + pnt += skiplen; + } while (pnt != (chain_cmem + chain_clen)); + } else { + do { + int skiplen; + uint64_t val = fstGetVarint32(pnt, &skiplen); + + if (!val) { + pnt += skiplen; + val = fstGetVarint32(pnt, &skiplen); + chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ + chain_table_lengths[idx] = -val; /* because during this loop iter would give stale data! */ + idx++; + } else if (val & 1) { + pval = chain_table[idx] = pval + (val >> 1); + if (idx) { + chain_table_lengths[pidx] = pval - chain_table[pidx]; + } + pidx = idx++; + } else { + fstHandle loopcnt = val >> 1; + for (i = 0; i < loopcnt; i++) { + chain_table[idx++] = 0; + } + } + + pnt += skiplen; + } while (pnt != (chain_cmem + chain_clen)); + } + + chain_table[idx] = indx_pos - vc_start; + chain_table_lengths[pidx] = chain_table[idx] - chain_table[pidx]; + + for (i = 0; i < idx; i++) { + int32_t v32 = chain_table_lengths[i]; + if ((v32 < 0) && (!chain_table[i])) { + v32 = -v32; + v32--; + if (((uint32_t)v32) < i) /* sanity check */ + { + chain_table[i] = chain_table[v32]; + chain_table_lengths[i] = chain_table_lengths[v32]; + } + } + } + +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "decompressed chain idx len: %" PRIu32 "\n", idx); +#endif + + mc_mem_len = 16384; + mc_mem = (unsigned char *)malloc(mc_mem_len); /* buffer for compressed reads */ + + /* check compressed VC data */ + if (idx > xc->maxhandle) + idx = xc->maxhandle; + for (i = 0; i < idx; i++) { + if (chain_table[i]) { + int process_idx = i / 8; + int process_bit = i & 7; + + if (xc->process_mask[process_idx] & (1 << process_bit)) { + int rc = Z_OK; + uint32_t val; + uint32_t skiplen; + uint32_t tdelta; + + fstReaderFseeko(xc, xc->f, vc_start + chain_table[i], SEEK_SET); + val = fstReaderVarint32WithSkip(xc->f, &skiplen); + if (val) { + unsigned char *mu = mem_for_traversal + traversal_mem_offs; /* uncomp: dst */ + unsigned char *mc; /* comp: src */ + unsigned long destlen = val; + unsigned long sourcelen = chain_table_lengths[i]; + + if (mc_mem_len < chain_table_lengths[i]) { + free(mc_mem); + mc_mem = (unsigned char *)malloc(mc_mem_len = chain_table_lengths[i]); + } + mc = mc_mem; + + fstFread(mc, chain_table_lengths[i], 1, xc->f); + + switch (packtype) { + case '4': + rc = (destlen == (unsigned long)LZ4_decompress_safe_partial((char *)mc, (char *)mu, + sourcelen, destlen, destlen)) + ? Z_OK + : Z_DATA_ERROR; + break; + case 'F': + fastlz_decompress(mc, sourcelen, mu, destlen); /* rc appears unreliable */ + break; + default: + rc = uncompress(mu, &destlen, mc, sourcelen); + break; + } + + /* data to process is for(j=0;jf); + /* data to process is for(j=0;jsignal_lens[i] == 1) { + uint32_t vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[i]); + uint32_t shcnt = 2 << (vli & 1); + tdelta = vli >> shcnt; + } else { + uint32_t vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[i]); + tdelta = vli >> 1; + } + + scatterptr[i] = tc_head[tdelta]; + tc_head[tdelta] = i + 1; + } + } + } + + free(mc_mem); /* there is no usage below for this, no real need to clear out mc_mem or mc_mem_len */ + + for (i = 0; i < tsec_nitems; i++) { + uint32_t tdelta; + int skiplen, skiplen2; + uint32_t vli; + + if (fv) { + char wx_buf[32]; + int wx_len; + + if (time_table[i] != previous_time) { + if (xc->limit_range_valid) { + if (time_table[i] > xc->limit_range_end) { + break; + } + } + + if (dumpvars_state == 1) { + wx_len = sprintf(wx_buf, "$end\n"); + fstWritex(xc, wx_buf, wx_len); + dumpvars_state = 2; + } + wx_len = sprintf(wx_buf, "#%" PRIu64 "\n", time_table[i]); + fstWritex(xc, wx_buf, wx_len); + if (!dumpvars_state) { + wx_len = sprintf(wx_buf, "$dumpvars\n"); + fstWritex(xc, wx_buf, wx_len); + dumpvars_state = 1; + } + + if ((xc->num_blackouts) && (cur_blackout != xc->num_blackouts)) { + if (time_table[i] == xc->blackout_times[cur_blackout]) { + wx_len = sprintf(wx_buf, "$dump%s $end\n", + (xc->blackout_activity[cur_blackout++]) ? "on" : "off"); + fstWritex(xc, wx_buf, wx_len); + } + } + previous_time = time_table[i]; + } + } else { + if (time_table[i] != previous_time) { + if (xc->limit_range_valid) { + if (time_table[i] > xc->limit_range_end) { + break; + } + } + previous_time = time_table[i]; + } + } + + while (tc_head[i]) { + idx = tc_head[i] - 1; + vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen); + + if (xc->signal_lens[idx] <= 1) { + if (xc->signal_lens[idx] == 1) { + unsigned char val; + if (!(vli & 1)) { + /* tdelta = vli >> 2; */ /* scan-build */ + val = ((vli >> 1) & 1) | '0'; + } else { + /* tdelta = vli >> 4; */ /* scan-build */ + val = FST_RCV_STR[((vli >> 1) & 7)]; + } + + if (value_change_callback) { + xc->temp_signal_value_buf[0] = val; + xc->temp_signal_value_buf[1] = 0; + value_change_callback(user_callback_data_pointer, time_table[i], idx + 1, + xc->temp_signal_value_buf); + } else { + if (fv) { + char vcd_id[16]; + int vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1); + + vcd_id[0] = val; + vcd_id[vcdid_len + 1] = '\n'; + fstWritex(xc, vcd_id, vcdid_len + 2); + } + } + headptr[idx] += skiplen; + length_remaining[idx] -= skiplen; + + tc_head[i] = scatterptr[idx]; + scatterptr[idx] = 0; + + if (length_remaining[idx]) { + int shamt; + vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]); + shamt = 2 << (vli & 1); + tdelta = vli >> shamt; + + scatterptr[idx] = tc_head[i + tdelta]; + tc_head[i + tdelta] = idx + 1; + } + } else { + unsigned char *vdata; + uint32_t len; + + vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen); + len = fstGetVarint32(mem_for_traversal + headptr[idx] + skiplen, &skiplen2); + /* tdelta = vli >> 1; */ /* scan-build */ + skiplen += skiplen2; + vdata = mem_for_traversal + headptr[idx] + skiplen; + + if (!(vli & 1)) { + if (value_change_callback_varlen) { + value_change_callback_varlen(user_callback_data_pointer, time_table[i], idx + 1, vdata, + len); + } else { + if (fv) { + char vcd_id[16]; + int vcdid_len; + + vcd_id[0] = 's'; + fstWritex(xc, vcd_id, 1); + + vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1); + { + unsigned char *vesc = (unsigned char *)malloc(len * 4 + 1); + int vlen = fstUtilityBinToEsc(vesc, vdata, len); + fstWritex(xc, vesc, vlen); + free(vesc); + } + + vcd_id[0] = ' '; + vcd_id[vcdid_len + 1] = '\n'; + fstWritex(xc, vcd_id, vcdid_len + 2); + } + } + } + + skiplen += len; + headptr[idx] += skiplen; + length_remaining[idx] -= skiplen; + + tc_head[i] = scatterptr[idx]; + scatterptr[idx] = 0; + + if (length_remaining[idx]) { + vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]); + tdelta = vli >> 1; + + scatterptr[idx] = tc_head[i + tdelta]; + tc_head[i + tdelta] = idx + 1; + } + } + } else { + uint32_t len = xc->signal_lens[idx]; + unsigned char *vdata; + + vli = fstGetVarint32(mem_for_traversal + headptr[idx], &skiplen); + /* tdelta = vli >> 1; */ /* scan-build */ + vdata = mem_for_traversal + headptr[idx] + skiplen; + + if (xc->signal_typs[idx] != FST_VT_VCD_REAL) { + if (!(vli & 1)) { + int byte = 0; + int bit; + unsigned int j; + + for (j = 0; j < len; j++) { + unsigned char ch; + byte = j / 8; + bit = 7 - (j & 7); + ch = ((vdata[byte] >> bit) & 1) | '0'; + xc->temp_signal_value_buf[j] = ch; + } + xc->temp_signal_value_buf[j] = 0; + + if (value_change_callback) { + value_change_callback(user_callback_data_pointer, time_table[i], idx + 1, + xc->temp_signal_value_buf); + } else { + if (fv) { + unsigned char ch_bp = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p'; + + fstWritex(xc, &ch_bp, 1); + fstWritex(xc, xc->temp_signal_value_buf, len); + } + } + + len = byte + 1; + } else { + if (value_change_callback) { + memcpy(xc->temp_signal_value_buf, vdata, len); + xc->temp_signal_value_buf[len] = 0; + value_change_callback(user_callback_data_pointer, time_table[i], idx + 1, + xc->temp_signal_value_buf); + } else { + if (fv) { + unsigned char ch_bp = (xc->signal_typs[idx] != FST_VT_VCD_PORT) ? 'b' : 'p'; + + fstWritex(xc, &ch_bp, 1); + fstWritex(xc, vdata, len); + } + } + } + } else { + double d; + unsigned char *clone_d /*= (unsigned char *)&d */; /* scan-build */ + unsigned char buf[8]; + unsigned char *srcdata; + + if (!(vli & 1)) /* very rare case, but possible */ + { + int bit; + int j; + + for (j = 0; j < 8; j++) { + unsigned char ch; + bit = 7 - (j & 7); + ch = ((vdata[0] >> bit) & 1) | '0'; + buf[j] = ch; + } + + len = 1; + srcdata = buf; + } else { + srcdata = vdata; + } + + if (value_change_callback) { + if (xc->native_doubles_for_cb) { + if (xc->double_endian_match) { + clone_d = srcdata; + } else { + int j; + + clone_d = (unsigned char *)&d; + for (j = 0; j < 8; j++) { + clone_d[j] = srcdata[7 - j]; + } + } + value_change_callback(user_callback_data_pointer, time_table[i], idx + 1, clone_d); + } else { + clone_d = (unsigned char *)&d; + if (xc->double_endian_match) { + memcpy(clone_d, srcdata, 8); + } else { + int j; + + for (j = 0; j < 8; j++) { + clone_d[j] = srcdata[7 - j]; + } + } + sprintf((char *)xc->temp_signal_value_buf, "%.16g", d); + value_change_callback(user_callback_data_pointer, time_table[i], idx + 1, + xc->temp_signal_value_buf); + } + } else { + if (fv) { + char wx_buf[32]; + int wx_len; + + clone_d = (unsigned char *)&d; + if (xc->double_endian_match) { + memcpy(clone_d, srcdata, 8); + } else { + int j; + + for (j = 0; j < 8; j++) { + clone_d[j] = srcdata[7 - j]; + } + } + + wx_len = sprintf(wx_buf, "r%.16g", d); + fstWritex(xc, wx_buf, wx_len); + } + } + } + + if (fv) { + char vcd_id[16]; + int vcdid_len = fstVcdIDForFwrite(vcd_id + 1, idx + 1); + vcd_id[0] = ' '; + vcd_id[vcdid_len + 1] = '\n'; + fstWritex(xc, vcd_id, vcdid_len + 2); + } + + skiplen += len; + headptr[idx] += skiplen; + length_remaining[idx] -= skiplen; + + tc_head[i] = scatterptr[idx]; + scatterptr[idx] = 0; + + if (length_remaining[idx]) { + vli = fstGetVarint32NoSkip(mem_for_traversal + headptr[idx]); + tdelta = vli >> 1; + + scatterptr[idx] = tc_head[i + tdelta]; + tc_head[i + tdelta] = idx + 1; + } + } + } + } + + block_err: + free(tc_head); + free(chain_cmem); + free(mem_for_traversal); + mem_for_traversal = NULL; + + secnum++; + if (secnum == xc->vc_section_count) + break; /* in case file is growing, keep with original block count */ + blkpos += seclen; + } + + if (mem_for_traversal) + free(mem_for_traversal); /* scan-build */ + free(length_remaining); + free(headptr); + free(scatterptr); + + if (chain_table) + free(chain_table); + if (chain_table_lengths) + free(chain_table_lengths); + + free(time_table); + +#ifndef FST_WRITEX_DISABLE + if (fv) { + fstWritex(xc, NULL, 0); + } +#endif + + return (1); +} + +/* rvat functions */ + +static char *fstExtractRvatDataFromFrame(struct fstReaderContext *xc, fstHandle facidx, char *buf) +{ + if (facidx >= xc->rvat_frame_maxhandle) { + return (NULL); + } + + if (xc->signal_lens[facidx] == 1) { + buf[0] = (char)xc->rvat_frame_data[xc->rvat_sig_offs[facidx]]; + buf[1] = 0; + } else { + if (xc->signal_typs[facidx] != FST_VT_VCD_REAL) { + memcpy(buf, xc->rvat_frame_data + xc->rvat_sig_offs[facidx], xc->signal_lens[facidx]); + buf[xc->signal_lens[facidx]] = 0; + } else { + double d; + unsigned char *clone_d = (unsigned char *)&d; + unsigned char *srcdata = xc->rvat_frame_data + xc->rvat_sig_offs[facidx]; + + if (xc->double_endian_match) { + memcpy(clone_d, srcdata, 8); + } else { + int j; + + for (j = 0; j < 8; j++) { + clone_d[j] = srcdata[7 - j]; + } + } + + sprintf((char *)buf, "%.16g", d); + } + } + + return (buf); +} + +char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf) +{ + struct fstReaderContext *xc = (struct fstReaderContext *)ctx; + fst_off_t blkpos = 0, prev_blkpos; + uint64_t beg_tim, end_tim, beg_tim2, end_tim2; + int sectype; + unsigned int secnum = 0; + uint64_t seclen; + uint64_t tsec_uclen = 0, tsec_clen = 0; + uint64_t tsec_nitems; + uint64_t frame_uclen, frame_clen; +#ifdef FST_DEBUG + uint64_t mem_required_for_traversal; +#endif + fst_off_t indx_pntr, indx_pos; + long chain_clen; + unsigned char *chain_cmem; + unsigned char *pnt; + fstHandle idx, pidx = 0, i; + uint64_t pval; + + if ((!xc) || (!facidx) || (facidx > xc->maxhandle) || (!buf) || (!xc->signal_lens[facidx - 1])) { + return (NULL); + } + + if (!xc->rvat_sig_offs) { + uint32_t cur_offs = 0; + + xc->rvat_sig_offs = (uint32_t *)calloc(xc->maxhandle, sizeof(uint32_t)); + for (i = 0; i < xc->maxhandle; i++) { + xc->rvat_sig_offs[i] = cur_offs; + cur_offs += xc->signal_lens[i]; + } + } + + if (xc->rvat_data_valid) { + if ((xc->rvat_beg_tim <= tim) && (tim <= xc->rvat_end_tim)) { + goto process_value; + } + + fstReaderDeallocateRvatData(xc); + } + + xc->rvat_chain_pos_valid = 0; + + for (;;) { + fstReaderFseeko(xc, xc->f, (prev_blkpos = blkpos), SEEK_SET); + + sectype = fgetc(xc->f); + seclen = fstReaderUint64(xc->f); + + if ((sectype == EOF) || (sectype == FST_BL_SKIP) || (!seclen)) { + return (NULL); /* if this loop exits on break, it's successful */ + } + + blkpos++; + if ((sectype != FST_BL_VCDATA) && (sectype != FST_BL_VCDATA_DYN_ALIAS) && + (sectype != FST_BL_VCDATA_DYN_ALIAS2)) { + blkpos += seclen; + continue; + } + + beg_tim = fstReaderUint64(xc->f); + end_tim = fstReaderUint64(xc->f); + + if ((beg_tim <= tim) && (tim <= end_tim)) { + if ((tim == end_tim) && (tim != xc->end_time)) { + fst_off_t cached_pos = ftello(xc->f); + fstReaderFseeko(xc, xc->f, blkpos, SEEK_SET); + + sectype = fgetc(xc->f); + seclen = fstReaderUint64(xc->f); + + beg_tim2 = fstReaderUint64(xc->f); + end_tim2 = fstReaderUint64(xc->f); + + if (((sectype != FST_BL_VCDATA) && (sectype != FST_BL_VCDATA_DYN_ALIAS) && + (sectype != FST_BL_VCDATA_DYN_ALIAS2)) || + (!seclen) || (beg_tim2 != tim)) { + blkpos = prev_blkpos; + break; + } + beg_tim = beg_tim2; + end_tim = end_tim2; + fstReaderFseeko(xc, xc->f, cached_pos, SEEK_SET); + } + break; + } + + blkpos += seclen; + secnum++; + } + + xc->rvat_beg_tim = beg_tim; + xc->rvat_end_tim = end_tim; + +#ifdef FST_DEBUG + mem_required_for_traversal = +#endif + fstReaderUint64(xc->f); + +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "rvat sec: %u seclen: %d begtim: %d endtim: %d\n", secnum, (int)seclen, (int)beg_tim, + (int)end_tim); + fprintf(stderr, FST_APIMESS "mem_required_for_traversal: %d\n", (int)mem_required_for_traversal); +#endif + + /* process time block */ + { + unsigned char *ucdata; + unsigned char *cdata; + unsigned long destlen /* = tsec_uclen */; /* scan-build */ + unsigned long sourcelen /* = tsec_clen */; /* scan-build */ + int rc; + unsigned char *tpnt; + uint64_t tpval; + unsigned int ti; + + fstReaderFseeko(xc, xc->f, blkpos + seclen - 24, SEEK_SET); + tsec_uclen = fstReaderUint64(xc->f); + tsec_clen = fstReaderUint64(xc->f); + tsec_nitems = fstReaderUint64(xc->f); +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "time section unc: %d, com: %d (%d items)\n", (int)tsec_uclen, (int)tsec_clen, + (int)tsec_nitems); +#endif + ucdata = (unsigned char *)malloc(tsec_uclen); + destlen = tsec_uclen; + sourcelen = tsec_clen; + + fstReaderFseeko(xc, xc->f, -24 - ((fst_off_t)tsec_clen), SEEK_CUR); + if (tsec_uclen != tsec_clen) { + cdata = (unsigned char *)malloc(tsec_clen); + fstFread(cdata, tsec_clen, 1, xc->f); + + rc = uncompress(ucdata, &destlen, cdata, sourcelen); + + if (rc != Z_OK) { + fprintf(stderr, FST_APIMESS "fstReaderGetValueFromHandleAtTime(), tsec uncompress rc = %d, exiting.\n", + rc); + exit(255); + } + + free(cdata); + } else { + fstFread(ucdata, tsec_uclen, 1, xc->f); + } + + xc->rvat_time_table = (uint64_t *)calloc(tsec_nitems, sizeof(uint64_t)); + tpnt = ucdata; + tpval = 0; + for (ti = 0; ti < tsec_nitems; ti++) { + int skiplen; + uint64_t val = fstGetVarint64(tpnt, &skiplen); + tpval = xc->rvat_time_table[ti] = tpval + val; + tpnt += skiplen; + } + + free(ucdata); + } + + fstReaderFseeko(xc, xc->f, blkpos + 32, SEEK_SET); + + frame_uclen = fstReaderVarint64(xc->f); + frame_clen = fstReaderVarint64(xc->f); + xc->rvat_frame_maxhandle = fstReaderVarint64(xc->f); + xc->rvat_frame_data = (unsigned char *)malloc(frame_uclen); + + if (frame_uclen == frame_clen) { + fstFread(xc->rvat_frame_data, frame_uclen, 1, xc->f); + } else { + unsigned char *mc = (unsigned char *)malloc(frame_clen); + int rc; + + unsigned long destlen = frame_uclen; + unsigned long sourcelen = frame_clen; + + fstFread(mc, sourcelen, 1, xc->f); + rc = uncompress(xc->rvat_frame_data, &destlen, mc, sourcelen); + if (rc != Z_OK) { + fprintf(stderr, FST_APIMESS "fstReaderGetValueFromHandleAtTime(), frame decompress rc: %d, exiting.\n", rc); + exit(255); + } + free(mc); + } + + xc->rvat_vc_maxhandle = fstReaderVarint64(xc->f); + xc->rvat_vc_start = ftello(xc->f); /* points to '!' character */ + xc->rvat_packtype = fgetc(xc->f); + +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "frame_uclen: %d, frame_clen: %d, frame_maxhandle: %d\n", (int)frame_uclen, + (int)frame_clen, (int)xc->rvat_frame_maxhandle); + fprintf(stderr, FST_APIMESS "vc_maxhandle: %d\n", (int)xc->rvat_vc_maxhandle); +#endif + + indx_pntr = blkpos + seclen - 24 - tsec_clen - 8; + fstReaderFseeko(xc, xc->f, indx_pntr, SEEK_SET); + chain_clen = fstReaderUint64(xc->f); + indx_pos = indx_pntr - chain_clen; +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "indx_pos: %d (%d bytes)\n", (int)indx_pos, (int)chain_clen); +#endif + chain_cmem = (unsigned char *)malloc(chain_clen); + fstReaderFseeko(xc, xc->f, indx_pos, SEEK_SET); + fstFread(chain_cmem, chain_clen, 1, xc->f); + + xc->rvat_chain_table = (fst_off_t *)calloc((xc->rvat_vc_maxhandle + 1), sizeof(fst_off_t)); + xc->rvat_chain_table_lengths = (uint32_t *)calloc((xc->rvat_vc_maxhandle + 1), sizeof(uint32_t)); + + pnt = chain_cmem; + idx = 0; + pval = 0; + + if (sectype == FST_BL_VCDATA_DYN_ALIAS2) { + uint32_t prev_alias = 0; + + do { + int skiplen; + + if (*pnt & 0x01) { + int64_t shval = fstGetSVarint64(pnt, &skiplen) >> 1; + if (shval > 0) { + pval = xc->rvat_chain_table[idx] = pval + shval; + if (idx) { + xc->rvat_chain_table_lengths[pidx] = pval - xc->rvat_chain_table[pidx]; + } + pidx = idx++; + } else if (shval < 0) { + xc->rvat_chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ + xc->rvat_chain_table_lengths[idx] = prev_alias = + shval; /* because during this loop iter would give stale data! */ + idx++; + } else { + xc->rvat_chain_table[idx] = 0; /* need to explicitly zero as calloc above might not run */ + xc->rvat_chain_table_lengths[idx] = + prev_alias; /* because during this loop iter would give stale data! */ + idx++; + } + } else { + uint64_t val = fstGetVarint32(pnt, &skiplen); + + fstHandle loopcnt = val >> 1; + for (i = 0; i < loopcnt; i++) { + xc->rvat_chain_table[idx++] = 0; + } + } + + pnt += skiplen; + } while (pnt != (chain_cmem + chain_clen)); + } else { + do { + int skiplen; + uint64_t val = fstGetVarint32(pnt, &skiplen); + + if (!val) { + pnt += skiplen; + val = fstGetVarint32(pnt, &skiplen); + xc->rvat_chain_table[idx] = 0; + xc->rvat_chain_table_lengths[idx] = -val; + idx++; + } else if (val & 1) { + pval = xc->rvat_chain_table[idx] = pval + (val >> 1); + if (idx) { + xc->rvat_chain_table_lengths[pidx] = pval - xc->rvat_chain_table[pidx]; + } + pidx = idx++; + } else { + fstHandle loopcnt = val >> 1; + for (i = 0; i < loopcnt; i++) { + xc->rvat_chain_table[idx++] = 0; + } + } + + pnt += skiplen; + } while (pnt != (chain_cmem + chain_clen)); + } + + free(chain_cmem); + xc->rvat_chain_table[idx] = indx_pos - xc->rvat_vc_start; + xc->rvat_chain_table_lengths[pidx] = xc->rvat_chain_table[idx] - xc->rvat_chain_table[pidx]; + + for (i = 0; i < idx; i++) { + int32_t v32 = xc->rvat_chain_table_lengths[i]; + if ((v32 < 0) && (!xc->rvat_chain_table[i])) { + v32 = -v32; + v32--; + if (((uint32_t)v32) < i) /* sanity check */ + { + xc->rvat_chain_table[i] = xc->rvat_chain_table[v32]; + xc->rvat_chain_table_lengths[i] = xc->rvat_chain_table_lengths[v32]; + } + } + } + +#ifdef FST_DEBUG + fprintf(stderr, FST_APIMESS "decompressed chain idx len: %" PRIu32 "\n", idx); +#endif + + xc->rvat_data_valid = 1; + +/* all data at this point is loaded or resident in fst cache, process and return appropriate value */ +process_value: + if (facidx > xc->rvat_vc_maxhandle) { + return (NULL); + } + + facidx--; /* scale down for array which starts at zero */ + + if (((tim == xc->rvat_beg_tim) && (!xc->rvat_chain_table[facidx])) || (!xc->rvat_chain_table[facidx])) { + return (fstExtractRvatDataFromFrame(xc, facidx, buf)); + } + + if (facidx != xc->rvat_chain_facidx) { + if (xc->rvat_chain_mem) { + free(xc->rvat_chain_mem); + xc->rvat_chain_mem = NULL; + + xc->rvat_chain_pos_valid = 0; + } + } + + if (!xc->rvat_chain_mem) { + uint32_t skiplen; + fstReaderFseeko(xc, xc->f, xc->rvat_vc_start + xc->rvat_chain_table[facidx], SEEK_SET); + xc->rvat_chain_len = fstReaderVarint32WithSkip(xc->f, &skiplen); + if (xc->rvat_chain_len) { + unsigned char *mu = (unsigned char *)malloc(xc->rvat_chain_len); + unsigned char *mc = (unsigned char *)malloc(xc->rvat_chain_table_lengths[facidx]); + unsigned long destlen = xc->rvat_chain_len; + unsigned long sourcelen = xc->rvat_chain_table_lengths[facidx]; + int rc = Z_OK; + + fstFread(mc, xc->rvat_chain_table_lengths[facidx], 1, xc->f); + + switch (xc->rvat_packtype) { + case '4': + rc = (destlen == + (unsigned long)LZ4_decompress_safe_partial((char *)mc, (char *)mu, sourcelen, destlen, destlen)) + ? Z_OK + : Z_DATA_ERROR; + break; + case 'F': + fastlz_decompress(mc, sourcelen, mu, destlen); /* rc appears unreliable */ + break; + default: + rc = uncompress(mu, &destlen, mc, sourcelen); + break; + } + + free(mc); + + if (rc != Z_OK) { + fprintf(stderr, + FST_APIMESS "fstReaderGetValueFromHandleAtTime(), rvat decompress clen: %d (rc=%d), exiting.\n", + (int)xc->rvat_chain_len, rc); + exit(255); + } + + /* data to process is for(j=0;jrvat_chain_mem = mu; + } else { + int destlen = xc->rvat_chain_table_lengths[facidx] - skiplen; + unsigned char *mu = (unsigned char *)malloc(xc->rvat_chain_len = destlen); + fstFread(mu, destlen, 1, xc->f); + /* data to process is for(j=0;jrvat_chain_mem = mu; + } + + xc->rvat_chain_facidx = facidx; + } + + /* process value chain here */ + + { + uint32_t tidx = 0, ptidx = 0; + uint32_t tdelta; + int skiplen; + unsigned int iprev = xc->rvat_chain_len; + uint32_t pvli = 0; + int pskip = 0; + + if ((xc->rvat_chain_pos_valid) && (tim >= xc->rvat_chain_pos_time)) { + i = xc->rvat_chain_pos_idx; + tidx = xc->rvat_chain_pos_tidx; + } else { + i = 0; + tidx = 0; + xc->rvat_chain_pos_time = xc->rvat_beg_tim; + } + + if (xc->signal_lens[facidx] == 1) { + while (i < xc->rvat_chain_len) { + uint32_t vli = fstGetVarint32(xc->rvat_chain_mem + i, &skiplen); + uint32_t shcnt = 2 << (vli & 1); + tdelta = vli >> shcnt; + + if (xc->rvat_time_table[tidx + tdelta] <= tim) { + iprev = i; + pvli = vli; + ptidx = tidx; + /* pskip = skiplen; */ /* scan-build */ + + tidx += tdelta; + i += skiplen; + } else { + break; + } + } + if (iprev != xc->rvat_chain_len) { + xc->rvat_chain_pos_tidx = ptidx; + xc->rvat_chain_pos_idx = iprev; + xc->rvat_chain_pos_time = tim; + xc->rvat_chain_pos_valid = 1; + + if (!(pvli & 1)) { + buf[0] = ((pvli >> 1) & 1) | '0'; + } else { + buf[0] = FST_RCV_STR[((pvli >> 1) & 7)]; + } + buf[1] = 0; + return (buf); + } else { + return (fstExtractRvatDataFromFrame(xc, facidx, buf)); + } + } else { + while (i < xc->rvat_chain_len) { + uint32_t vli = fstGetVarint32(xc->rvat_chain_mem + i, &skiplen); + tdelta = vli >> 1; + + if (xc->rvat_time_table[tidx + tdelta] <= tim) { + iprev = i; + pvli = vli; + ptidx = tidx; + pskip = skiplen; + + tidx += tdelta; + i += skiplen; + + if (!(pvli & 1)) { + i += ((xc->signal_lens[facidx] + 7) / 8); + } else { + i += xc->signal_lens[facidx]; + } + } else { + break; + } + } + + if (iprev != xc->rvat_chain_len) { + unsigned char *vdata = xc->rvat_chain_mem + iprev + pskip; + + xc->rvat_chain_pos_tidx = ptidx; + xc->rvat_chain_pos_idx = iprev; + xc->rvat_chain_pos_time = tim; + xc->rvat_chain_pos_valid = 1; + + if (xc->signal_typs[facidx] != FST_VT_VCD_REAL) { + if (!(pvli & 1)) { + int byte = 0; + int bit; + unsigned int j; + + for (j = 0; j < xc->signal_lens[facidx]; j++) { + unsigned char ch; + byte = j / 8; + bit = 7 - (j & 7); + ch = ((vdata[byte] >> bit) & 1) | '0'; + buf[j] = ch; + } + buf[j] = 0; + + return (buf); + } else { + memcpy(buf, vdata, xc->signal_lens[facidx]); + buf[xc->signal_lens[facidx]] = 0; + return (buf); + } + } else { + double d; + unsigned char *clone_d = (unsigned char *)&d; + unsigned char bufd[8]; + unsigned char *srcdata; + + if (!(pvli & 1)) /* very rare case, but possible */ + { + int bit; + int j; + + for (j = 0; j < 8; j++) { + unsigned char ch; + bit = 7 - (j & 7); + ch = ((vdata[0] >> bit) & 1) | '0'; + bufd[j] = ch; + } + + srcdata = bufd; + } else { + srcdata = vdata; + } + + if (xc->double_endian_match) { + memcpy(clone_d, srcdata, 8); + } else { + int j; + + for (j = 0; j < 8; j++) { + clone_d[j] = srcdata[7 - j]; + } + } + + sprintf(buf, "r%.16g", d); + return (buf); + } + } else { + return (fstExtractRvatDataFromFrame(xc, facidx, buf)); + } + } + } + + /* return(NULL); */ +} + +/**********************************************************************/ +#ifndef _WAVE_HAVE_JUDY + +/***********************/ +/*** ***/ +/*** jenkins hash ***/ +/*** ***/ +/***********************/ + +/* +-------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. +For every delta with one or two bits set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, +* If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. +* If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) +mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. +-------------------------------------------------------------------- +*/ +#define mix(a, b, c) \ + { \ + a -= b; \ + a -= c; \ + a ^= (c >> 13); \ + b -= c; \ + b -= a; \ + b ^= (a << 8); \ + c -= a; \ + c -= b; \ + c ^= (b >> 13); \ + a -= b; \ + a -= c; \ + a ^= (c >> 12); \ + b -= c; \ + b -= a; \ + b ^= (a << 16); \ + c -= a; \ + c -= b; \ + c ^= (b >> 5); \ + a -= b; \ + a -= c; \ + a ^= (c >> 3); \ + b -= c; \ + b -= a; \ + b ^= (a << 10); \ + c -= a; \ + c -= b; \ + c ^= (b >> 15); \ + } + +/* +-------------------------------------------------------------------- +j_hash() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Every 1-bit and 2-bit delta achieves avalanche. +About 6*len+35 instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i= 12) { + a += (k[0] + ((uint32_t)k[1] << 8) + ((uint32_t)k[2] << 16) + ((uint32_t)k[3] << 24)); + b += (k[4] + ((uint32_t)k[5] << 8) + ((uint32_t)k[6] << 16) + ((uint32_t)k[7] << 24)); + c += (k[8] + ((uint32_t)k[9] << 8) + ((uint32_t)k[10] << 16) + ((uint32_t)k[11] << 24)); + mix(a, b, c); + k += 12; + len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch (len) /* all the case statements fall through */ + { + case 11: + c += ((uint32_t)k[10] << 24); /* fallthrough */ + case 10: + c += ((uint32_t)k[9] << 16); /* fallthrough */ + case 9: + c += ((uint32_t)k[8] << 8); /* fallthrough */ + /* the first byte of c is reserved for the length */ + case 8: + b += ((uint32_t)k[7] << 24); /* fallthrough */ + case 7: + b += ((uint32_t)k[6] << 16); /* fallthrough */ + case 6: + b += ((uint32_t)k[5] << 8); /* fallthrough */ + case 5: + b += k[4]; /* fallthrough */ + case 4: + a += ((uint32_t)k[3] << 24); /* fallthrough */ + case 3: + a += ((uint32_t)k[2] << 16); /* fallthrough */ + case 2: + a += ((uint32_t)k[1] << 8); /* fallthrough */ + case 1: + a += k[0]; + /* case 0: nothing left to add */ + } + mix(a, b, c); + /*-------------------------------------------- report the result */ + return (c); +} + +/********************************************************************/ + +/***************************/ +/*** ***/ +/*** judy HS emulation ***/ +/*** ***/ +/***************************/ + +struct collchain_t +{ + struct collchain_t *next; + void *payload; + uint32_t fullhash, length; + unsigned char mem[1]; +}; + +void **JenkinsIns(void *base_i, const unsigned char *mem, uint32_t length, uint32_t hashmask) +{ + struct collchain_t ***base = (struct collchain_t ***)base_i; + uint32_t hf, h; + struct collchain_t **ar; + struct collchain_t *chain, *pchain; + + if (!*base) { + *base = (struct collchain_t **)calloc(1, (hashmask + 1) * sizeof(void *)); + } + ar = *base; + + h = (hf = j_hash(mem, length, length)) & hashmask; + pchain = chain = ar[h]; + while (chain) { + if ((chain->fullhash == hf) && (chain->length == length) && !memcmp(chain->mem, mem, length)) { + if (pchain != chain) /* move hit to front */ + { + pchain->next = chain->next; + chain->next = ar[h]; + ar[h] = chain; + } + return (&(chain->payload)); + } + + pchain = chain; + chain = chain->next; + } + + chain = (struct collchain_t *)calloc(1, sizeof(struct collchain_t) + length - 1); + memcpy(chain->mem, mem, length); + chain->fullhash = hf; + chain->length = length; + chain->next = ar[h]; + ar[h] = chain; + return (&(chain->payload)); +} + +void JenkinsFree(void *base_i, uint32_t hashmask) +{ + struct collchain_t ***base = (struct collchain_t ***)base_i; + uint32_t h; + struct collchain_t **ar; + struct collchain_t *chain, *chain_next; + + if (base && *base) { + ar = *base; + for (h = 0; h <= hashmask; h++) { + chain = ar[h]; + while (chain) { + chain_next = chain->next; + free(chain); + chain = chain_next; + } + } + + free(*base); + *base = NULL; + } +} + +#endif + +/**********************************************************************/ + +/************************/ +/*** ***/ +/*** utility function ***/ +/*** ***/ +/************************/ + +int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len) +{ + const unsigned char *src = s; + int dlen = 0; + int i; + + for (i = 0; i < len; i++) { + switch (src[i]) { + case '\a': /* fallthrough */ + case '\b': /* fallthrough */ + case '\f': /* fallthrough */ + case '\n': /* fallthrough */ + case '\r': /* fallthrough */ + case '\t': /* fallthrough */ + case '\v': /* fallthrough */ + case '\'': /* fallthrough */ + case '\"': /* fallthrough */ + case '\\': /* fallthrough */ + case '\?': + dlen += 2; + break; + default: + if ((src[i] > ' ') && (src[i] <= '~')) /* no white spaces in output */ + { + dlen++; + } else { + dlen += 4; + } + break; + } + } + + return (dlen); +} + +int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len) +{ + const unsigned char *src = s; + unsigned char *dst = d; + unsigned char val; + int i; + + for (i = 0; i < len; i++) { + switch (src[i]) { + case '\a': + *(dst++) = '\\'; + *(dst++) = 'a'; + break; + case '\b': + *(dst++) = '\\'; + *(dst++) = 'b'; + break; + case '\f': + *(dst++) = '\\'; + *(dst++) = 'f'; + break; + case '\n': + *(dst++) = '\\'; + *(dst++) = 'n'; + break; + case '\r': + *(dst++) = '\\'; + *(dst++) = 'r'; + break; + case '\t': + *(dst++) = '\\'; + *(dst++) = 't'; + break; + case '\v': + *(dst++) = '\\'; + *(dst++) = 'v'; + break; + case '\'': + *(dst++) = '\\'; + *(dst++) = '\''; + break; + case '\"': + *(dst++) = '\\'; + *(dst++) = '\"'; + break; + case '\\': + *(dst++) = '\\'; + *(dst++) = '\\'; + break; + case '\?': + *(dst++) = '\\'; + *(dst++) = '\?'; + break; + default: + if ((src[i] > ' ') && (src[i] <= '~')) /* no white spaces in output */ + { + *(dst++) = src[i]; + } else { + val = src[i]; + *(dst++) = '\\'; + *(dst++) = (val / 64) + '0'; + val = val & 63; + *(dst++) = (val / 8) + '0'; + val = val & 7; + *(dst++) = (val) + '0'; + } + break; + } + } + + return (dst - d); +} + +/* + * this overwrites the original string if the destination pointer is NULL + */ +int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len) +{ + unsigned char *src = s; + unsigned char *dst = (!d) ? s : (s = d); + unsigned char val[3]; + int i; + + for (i = 0; i < len; i++) { + if (src[i] != '\\') { + *(dst++) = src[i]; + } else { + switch (src[++i]) { + case 'a': + *(dst++) = '\a'; + break; + case 'b': + *(dst++) = '\b'; + break; + case 'f': + *(dst++) = '\f'; + break; + case 'n': + *(dst++) = '\n'; + break; + case 'r': + *(dst++) = '\r'; + break; + case 't': + *(dst++) = '\t'; + break; + case 'v': + *(dst++) = '\v'; + break; + case '\'': + *(dst++) = '\''; + break; + case '\"': + *(dst++) = '\"'; + break; + case '\\': + *(dst++) = '\\'; + break; + case '\?': + *(dst++) = '\?'; + break; + + case 'x': + val[0] = toupper(src[++i]); + val[1] = toupper(src[++i]); + val[0] = ((val[0] >= 'A') && (val[0] <= 'F')) ? (val[0] - 'A' + 10) : (val[0] - '0'); + val[1] = ((val[1] >= 'A') && (val[1] <= 'F')) ? (val[1] - 'A' + 10) : (val[1] - '0'); + *(dst++) = val[0] * 16 + val[1]; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val[0] = src[i] - '0'; + val[1] = src[++i] - '0'; + val[2] = src[++i] - '0'; + *(dst++) = val[0] * 64 + val[1] * 8 + val[2]; + break; + + default: + *(dst++) = src[i]; + break; + } + } + } + + return (dst - s); +} + +struct fstETab *fstUtilityExtractEnumTableFromString(const char *s) +{ + struct fstETab *et = NULL; + int num_spaces = 0; + int i; + int newlen; + + if (s) { + const char *csp = strchr(s, ' '); + int cnt = atoi(csp + 1); + + for (;;) { + csp = strchr(csp + 1, ' '); + if (csp) { + num_spaces++; + } else { + break; + } + } + + if (num_spaces == (2 * cnt)) { + char *sp, *sp2; + + et = (struct fstETab *)calloc(1, sizeof(struct fstETab)); + et->elem_count = cnt; + et->name = strdup(s); + et->literal_arr = (char **)calloc(cnt, sizeof(char *)); + et->val_arr = (char **)calloc(cnt, sizeof(char *)); + + sp = strchr(et->name, ' '); + *sp = 0; + + sp = strchr(sp + 1, ' '); + + for (i = 0; i < cnt; i++) { + sp2 = strchr(sp + 1, ' '); + *(char *)sp2 = 0; + et->literal_arr[i] = sp + 1; + sp = sp2; + + newlen = fstUtilityEscToBin(NULL, (unsigned char *)et->literal_arr[i], strlen(et->literal_arr[i])); + et->literal_arr[i][newlen] = 0; + } + + for (i = 0; i < cnt; i++) { + sp2 = strchr(sp + 1, ' '); + if (sp2) { + *sp2 = 0; + } + et->val_arr[i] = sp + 1; + sp = sp2; + + newlen = fstUtilityEscToBin(NULL, (unsigned char *)et->val_arr[i], strlen(et->val_arr[i])); + et->val_arr[i][newlen] = 0; + } + } + } + + return (et); +} + +void fstUtilityFreeEnumTable(struct fstETab *etab) +{ + if (etab) { + free(etab->literal_arr); + free(etab->val_arr); + free(etab->name); + free(etab); + } +} diff --git a/libs/fst/fstapi.h b/libs/fst/fstapi.h new file mode 100644 index 00000000000..a5e0971a16b --- /dev/null +++ b/libs/fst/fstapi.h @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2009-2018 Tony Bybell. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + */ + +#ifndef FST_API_H +#define FST_API_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#if defined(_MSC_VER) +#include "libs/zlib/zlib.h" +#include + +#include + +#define ftruncate _chsize_s +#define unlink _unlink +#define fileno _fileno +#define lseek _lseeki64 + +#ifdef _WIN64 +#define ssize_t __int64 +#define SSIZE_MAX 9223372036854775807i64 +#else +#define ssize_t long +#define SSIZE_MAX 2147483647L +#endif + +#include "stdint.h" +#else +#include +#include +#endif +#include + +#define FST_RDLOAD "FSTLOAD | " + +typedef uint32_t fstHandle; +typedef uint32_t fstEnumHandle; + +enum fstWriterPackType +{ + FST_WR_PT_ZLIB = 0, + FST_WR_PT_FASTLZ = 1, + FST_WR_PT_LZ4 = 2 +}; + +enum fstFileType +{ + FST_FT_MIN = 0, + + FST_FT_VERILOG = 0, + FST_FT_VHDL = 1, + FST_FT_VERILOG_VHDL = 2, + + FST_FT_MAX = 2 +}; + +enum fstBlockType +{ + FST_BL_HDR = 0, + FST_BL_VCDATA = 1, + FST_BL_BLACKOUT = 2, + FST_BL_GEOM = 3, + FST_BL_HIER = 4, + FST_BL_VCDATA_DYN_ALIAS = 5, + FST_BL_HIER_LZ4 = 6, + FST_BL_HIER_LZ4DUO = 7, + FST_BL_VCDATA_DYN_ALIAS2 = 8, + + FST_BL_ZWRAPPER = 254, /* indicates that whole trace is gz wrapped */ + FST_BL_SKIP = 255 /* used while block is being written */ +}; + +enum fstScopeType +{ + FST_ST_MIN = 0, + + FST_ST_VCD_MODULE = 0, + FST_ST_VCD_TASK = 1, + FST_ST_VCD_FUNCTION = 2, + FST_ST_VCD_BEGIN = 3, + FST_ST_VCD_FORK = 4, + FST_ST_VCD_GENERATE = 5, + FST_ST_VCD_STRUCT = 6, + FST_ST_VCD_UNION = 7, + FST_ST_VCD_CLASS = 8, + FST_ST_VCD_INTERFACE = 9, + FST_ST_VCD_PACKAGE = 10, + FST_ST_VCD_PROGRAM = 11, + + FST_ST_VHDL_ARCHITECTURE = 12, + FST_ST_VHDL_PROCEDURE = 13, + FST_ST_VHDL_FUNCTION = 14, + FST_ST_VHDL_RECORD = 15, + FST_ST_VHDL_PROCESS = 16, + FST_ST_VHDL_BLOCK = 17, + FST_ST_VHDL_FOR_GENERATE = 18, + FST_ST_VHDL_IF_GENERATE = 19, + FST_ST_VHDL_GENERATE = 20, + FST_ST_VHDL_PACKAGE = 21, + + FST_ST_MAX = 21, + + FST_ST_GEN_ATTRBEGIN = 252, + FST_ST_GEN_ATTREND = 253, + + FST_ST_VCD_SCOPE = 254, + FST_ST_VCD_UPSCOPE = 255 +}; + +enum fstVarType +{ + FST_VT_MIN = 0, /* start of vartypes */ + + FST_VT_VCD_EVENT = 0, + FST_VT_VCD_INTEGER = 1, + FST_VT_VCD_PARAMETER = 2, + FST_VT_VCD_REAL = 3, + FST_VT_VCD_REAL_PARAMETER = 4, + FST_VT_VCD_REG = 5, + FST_VT_VCD_SUPPLY0 = 6, + FST_VT_VCD_SUPPLY1 = 7, + FST_VT_VCD_TIME = 8, + FST_VT_VCD_TRI = 9, + FST_VT_VCD_TRIAND = 10, + FST_VT_VCD_TRIOR = 11, + FST_VT_VCD_TRIREG = 12, + FST_VT_VCD_TRI0 = 13, + FST_VT_VCD_TRI1 = 14, + FST_VT_VCD_WAND = 15, + FST_VT_VCD_WIRE = 16, + FST_VT_VCD_WOR = 17, + FST_VT_VCD_PORT = 18, + FST_VT_VCD_SPARRAY = 19, /* used to define the rownum (index) port for a sparse array */ + FST_VT_VCD_REALTIME = 20, + + FST_VT_GEN_STRING = + 21, /* generic string type (max len is defined dynamically via fstWriterEmitVariableLengthValueChange) */ + + FST_VT_SV_BIT = 22, + FST_VT_SV_LOGIC = 23, + FST_VT_SV_INT = 24, /* declare as size = 32 */ + FST_VT_SV_SHORTINT = 25, /* declare as size = 16 */ + FST_VT_SV_LONGINT = 26, /* declare as size = 64 */ + FST_VT_SV_BYTE = 27, /* declare as size = 8 */ + FST_VT_SV_ENUM = 28, /* declare as appropriate type range */ + FST_VT_SV_SHORTREAL = + 29, /* declare and emit same as FST_VT_VCD_REAL (needs to be emitted as double, not a float) */ + + FST_VT_MAX = 29 /* end of vartypes */ +}; + +enum fstVarDir +{ + FST_VD_MIN = 0, + + FST_VD_IMPLICIT = 0, + FST_VD_INPUT = 1, + FST_VD_OUTPUT = 2, + FST_VD_INOUT = 3, + FST_VD_BUFFER = 4, + FST_VD_LINKAGE = 5, + + FST_VD_MAX = 5 +}; + +enum fstHierType +{ + FST_HT_MIN = 0, + + FST_HT_SCOPE = 0, + FST_HT_UPSCOPE = 1, + FST_HT_VAR = 2, + FST_HT_ATTRBEGIN = 3, + FST_HT_ATTREND = 4, + + /* FST_HT_TREEBEGIN and FST_HT_TREEEND are not yet used by FST but are currently used when fstHier bridges other + formats */ + FST_HT_TREEBEGIN = 5, + FST_HT_TREEEND = 6, + + FST_HT_MAX = 6 +}; + +enum fstAttrType +{ + FST_AT_MIN = 0, + + FST_AT_MISC = 0, /* self-contained: does not need matching FST_HT_ATTREND */ + FST_AT_ARRAY = 1, + FST_AT_ENUM = 2, + FST_AT_PACK = 3, + + FST_AT_MAX = 3 +}; + +enum fstMiscType +{ + FST_MT_MIN = 0, + + FST_MT_COMMENT = 0, /* use fstWriterSetComment() to emit */ + FST_MT_ENVVAR = 1, /* use fstWriterSetEnvVar() to emit */ + FST_MT_SUPVAR = 2, /* use fstWriterCreateVar2() to emit */ + FST_MT_PATHNAME = 3, /* reserved for fstWriterSetSourceStem() string -> number management */ + FST_MT_SOURCESTEM = 4, /* use fstWriterSetSourceStem() to emit */ + FST_MT_SOURCEISTEM = 5, /* use fstWriterSetSourceInstantiationStem() to emit */ + FST_MT_VALUELIST = 6, /* use fstWriterSetValueList() to emit, followed by fstWriterCreateVar*() */ + FST_MT_ENUMTABLE = 7, /* use fstWriterCreateEnumTable() and fstWriterEmitEnumTableRef() to emit */ + FST_MT_UNKNOWN = 8, + + FST_MT_MAX = 8 +}; + +enum fstArrayType +{ + FST_AR_MIN = 0, + + FST_AR_NONE = 0, + FST_AR_UNPACKED = 1, + FST_AR_PACKED = 2, + FST_AR_SPARSE = 3, + + FST_AR_MAX = 3 +}; + +enum fstEnumValueType +{ + FST_EV_SV_INTEGER = 0, + FST_EV_SV_BIT = 1, + FST_EV_SV_LOGIC = 2, + FST_EV_SV_INT = 3, + FST_EV_SV_SHORTINT = 4, + FST_EV_SV_LONGINT = 5, + FST_EV_SV_BYTE = 6, + FST_EV_SV_UNSIGNED_INTEGER = 7, + FST_EV_SV_UNSIGNED_BIT = 8, + FST_EV_SV_UNSIGNED_LOGIC = 9, + FST_EV_SV_UNSIGNED_INT = 10, + FST_EV_SV_UNSIGNED_SHORTINT = 11, + FST_EV_SV_UNSIGNED_LONGINT = 12, + FST_EV_SV_UNSIGNED_BYTE = 13, + + FST_EV_REG = 14, + FST_EV_TIME = 15, + + FST_EV_MAX = 15 +}; + +enum fstPackType +{ + FST_PT_NONE = 0, + FST_PT_UNPACKED = 1, + FST_PT_PACKED = 2, + FST_PT_TAGGED_PACKED = 3, + + FST_PT_MAX = 3 +}; + +enum fstSupplementalVarType +{ + FST_SVT_MIN = 0, + + FST_SVT_NONE = 0, + + FST_SVT_VHDL_SIGNAL = 1, + FST_SVT_VHDL_VARIABLE = 2, + FST_SVT_VHDL_CONSTANT = 3, + FST_SVT_VHDL_FILE = 4, + FST_SVT_VHDL_MEMORY = 5, + + FST_SVT_MAX = 5 +}; + +enum fstSupplementalDataType +{ + FST_SDT_MIN = 0, + + FST_SDT_NONE = 0, + + FST_SDT_VHDL_BOOLEAN = 1, + FST_SDT_VHDL_BIT = 2, + FST_SDT_VHDL_BIT_VECTOR = 3, + FST_SDT_VHDL_STD_ULOGIC = 4, + FST_SDT_VHDL_STD_ULOGIC_VECTOR = 5, + FST_SDT_VHDL_STD_LOGIC = 6, + FST_SDT_VHDL_STD_LOGIC_VECTOR = 7, + FST_SDT_VHDL_UNSIGNED = 8, + FST_SDT_VHDL_SIGNED = 9, + FST_SDT_VHDL_INTEGER = 10, + FST_SDT_VHDL_REAL = 11, + FST_SDT_VHDL_NATURAL = 12, + FST_SDT_VHDL_POSITIVE = 13, + FST_SDT_VHDL_TIME = 14, + FST_SDT_VHDL_CHARACTER = 15, + FST_SDT_VHDL_STRING = 16, + + FST_SDT_MAX = 16, + + FST_SDT_SVT_SHIFT_COUNT = + 10, /* FST_SVT_* is ORed in by fstWriterCreateVar2() to the left after shifting FST_SDT_SVT_SHIFT_COUNT */ + FST_SDT_ABS_MAX = ((1 << (FST_SDT_SVT_SHIFT_COUNT)) - 1) +}; + +struct fstHier +{ + unsigned char htyp; + + union + { + /* if htyp == FST_HT_SCOPE */ + struct fstHierScope + { + unsigned char typ; /* FST_ST_MIN ... FST_ST_MAX */ + const char *name; + const char *component; + uint32_t name_length; /* strlen(u.scope.name) */ + uint32_t component_length; /* strlen(u.scope.component) */ + } scope; + + /* if htyp == FST_HT_VAR */ + struct fstHierVar + { + unsigned char typ; /* FST_VT_MIN ... FST_VT_MAX */ + unsigned char direction; /* FST_VD_MIN ... FST_VD_MAX */ + unsigned char svt_workspace; /* zeroed out by FST reader, for client code use */ + unsigned char sdt_workspace; /* zeroed out by FST reader, for client code use */ + unsigned int sxt_workspace; /* zeroed out by FST reader, for client code use */ + const char *name; + uint32_t length; + fstHandle handle; + uint32_t name_length; /* strlen(u.var.name) */ + unsigned is_alias : 1; + } var; + + /* if htyp == FST_HT_ATTRBEGIN */ + struct fstHierAttr + { + unsigned char typ; /* FST_AT_MIN ... FST_AT_MAX */ + unsigned char subtype; /* from fstMiscType, fstArrayType, fstEnumValueType, fstPackType */ + const char *name; + uint64_t arg; /* number of array elements, struct members, or some other payload (possibly ignored) */ + uint64_t arg_from_name; /* for when name is overloaded as a variable-length integer (FST_AT_MISC + + FST_MT_SOURCESTEM) */ + uint32_t name_length; /* strlen(u.attr.name) */ + } attr; + } u; +}; + +struct fstETab +{ + char *name; + uint32_t elem_count; + char **literal_arr; + char **val_arr; +}; + +/* + * writer functions + */ +void fstWriterClose(void *ctx); +void *fstWriterCreate(const char *nam, int use_compressed_hier); +fstEnumHandle fstWriterCreateEnumTable(void *ctx, const char *name, uint32_t elem_count, unsigned int min_valbits, + const char **literal_arr, const char **val_arr); +/* used for Verilog/SV */ +fstHandle fstWriterCreateVar(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, + fstHandle aliasHandle); +/* future expansion for VHDL and other languages. The variable type, data type, etc map onto + the current Verilog/SV one. The "type" string is optional for a more verbose or custom description */ +fstHandle fstWriterCreateVar2(void *ctx, enum fstVarType vt, enum fstVarDir vd, uint32_t len, const char *nam, + fstHandle aliasHandle, const char *type, enum fstSupplementalVarType svt, + enum fstSupplementalDataType sdt); +void fstWriterEmitDumpActive(void *ctx, int enable); +void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle); +void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val); +void fstWriterEmitValueChange32(void *ctx, fstHandle handle, uint32_t bits, uint32_t val); +void fstWriterEmitValueChange64(void *ctx, fstHandle handle, uint32_t bits, uint64_t val); +void fstWriterEmitValueChangeVec32(void *ctx, fstHandle handle, uint32_t bits, const uint32_t *val); +void fstWriterEmitValueChangeVec64(void *ctx, fstHandle handle, uint32_t bits, const uint64_t *val); +void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len); +void fstWriterEmitTimeChange(void *ctx, uint64_t tim); +void fstWriterFlushContext(void *ctx); +int fstWriterGetDumpSizeLimitReached(void *ctx); +int fstWriterGetFseekFailed(void *ctx); +void fstWriterSetAttrBegin(void *ctx, enum fstAttrType attrtype, int subtype, const char *attrname, uint64_t arg); +void fstWriterSetAttrEnd(void *ctx); +void fstWriterSetComment(void *ctx, const char *comm); +void fstWriterSetDate(void *ctx, const char *dat); +void fstWriterSetDumpSizeLimit(void *ctx, uint64_t numbytes); +void fstWriterSetEnvVar(void *ctx, const char *envvar); +void fstWriterSetFileType(void *ctx, enum fstFileType filetype); +void fstWriterSetPackType(void *ctx, enum fstWriterPackType typ); +void fstWriterSetParallelMode(void *ctx, int enable); +void fstWriterSetRepackOnClose(void *ctx, int enable); /* type = 0 (none), 1 (libz) */ +void fstWriterSetScope(void *ctx, enum fstScopeType scopetype, const char *scopename, const char *scopecomp); +void fstWriterSetSourceInstantiationStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath); +void fstWriterSetSourceStem(void *ctx, const char *path, unsigned int line, unsigned int use_realpath); +void fstWriterSetTimescale(void *ctx, int ts); +void fstWriterSetTimescaleFromString(void *ctx, const char *s); +void fstWriterSetTimezero(void *ctx, int64_t tim); +void fstWriterSetUpscope(void *ctx); +void fstWriterSetValueList(void *ctx, const char *vl); +void fstWriterSetVersion(void *ctx, const char *vers); + +/* + * reader functions + */ +void fstReaderClose(void *ctx); +void fstReaderClrFacProcessMask(void *ctx, fstHandle facidx); +void fstReaderClrFacProcessMaskAll(void *ctx); +uint64_t fstReaderGetAliasCount(void *ctx); +const char *fstReaderGetCurrentFlatScope(void *ctx); +void *fstReaderGetCurrentScopeUserInfo(void *ctx); +int fstReaderGetCurrentScopeLen(void *ctx); +const char *fstReaderGetDateString(void *ctx); +int fstReaderGetDoubleEndianMatchState(void *ctx); +uint64_t fstReaderGetDumpActivityChangeTime(void *ctx, uint32_t idx); +unsigned char fstReaderGetDumpActivityChangeValue(void *ctx, uint32_t idx); +uint64_t fstReaderGetEndTime(void *ctx); +int fstReaderGetFacProcessMask(void *ctx, fstHandle facidx); +int fstReaderGetFileType(void *ctx); +int fstReaderGetFseekFailed(void *ctx); +fstHandle fstReaderGetMaxHandle(void *ctx); +uint64_t fstReaderGetMemoryUsedByWriter(void *ctx); +uint32_t fstReaderGetNumberDumpActivityChanges(void *ctx); +uint64_t fstReaderGetScopeCount(void *ctx); +uint64_t fstReaderGetStartTime(void *ctx); +signed char fstReaderGetTimescale(void *ctx); +int64_t fstReaderGetTimezero(void *ctx); +uint64_t fstReaderGetValueChangeSectionCount(void *ctx); +char *fstReaderGetValueFromHandleAtTime(void *ctx, uint64_t tim, fstHandle facidx, char *buf); +uint64_t fstReaderGetVarCount(void *ctx); +const char *fstReaderGetVersionString(void *ctx); +struct fstHier *fstReaderIterateHier(void *ctx); +int fstReaderIterateHierRewind(void *ctx); +int fstReaderIterBlocks(void *ctx, + void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, fstHandle facidx, + const unsigned char *value), + void *user_callback_data_pointer, FILE *vcdhandle); +int fstReaderIterBlocks2(void *ctx, + void (*value_change_callback)(void *user_callback_data_pointer, uint64_t time, + fstHandle facidx, const unsigned char *value), + void (*value_change_callback_varlen)(void *user_callback_data_pointer, uint64_t time, + fstHandle facidx, const unsigned char *value, + uint32_t len), + void *user_callback_data_pointer, FILE *vcdhandle); +void fstReaderIterBlocksSetNativeDoublesOnCallback(void *ctx, int enable); +void *fstReaderOpen(const char *nam); +void *fstReaderOpenForUtilitiesOnly(void); +const char *fstReaderPopScope(void *ctx); +int fstReaderProcessHier(void *ctx, FILE *vcdhandle); +const char *fstReaderPushScope(void *ctx, const char *nam, void *user_info); +void fstReaderResetScope(void *ctx); +void fstReaderSetFacProcessMask(void *ctx, fstHandle facidx); +void fstReaderSetFacProcessMaskAll(void *ctx); +void fstReaderSetLimitTimeRange(void *ctx, uint64_t start_time, uint64_t end_time); +void fstReaderSetUnlimitedTimeRange(void *ctx); +void fstReaderSetVcdExtensions(void *ctx, int enable); + +/* + * utility functions + */ +int fstUtilityBinToEscConvertedLen(const unsigned char *s, int len); /* used for mallocs for fstUtilityBinToEsc() */ +int fstUtilityBinToEsc(unsigned char *d, const unsigned char *s, int len); +int fstUtilityEscToBin(unsigned char *d, unsigned char *s, int len); +struct fstETab *fstUtilityExtractEnumTableFromString(const char *s); +void fstUtilityFreeEnumTable(struct fstETab *etab); /* must use to free fstETab properly */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/fst/lz4.cc b/libs/fst/lz4.cc new file mode 100644 index 00000000000..7e94f2492f4 --- /dev/null +++ b/libs/fst/lz4.cc @@ -0,0 +1,1615 @@ +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2015, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + SPDX-License-Identifier: BSD-2-Clause + + You can contact the author at : + - LZ4 source repository : https://github.com/Cyan4973/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + +/************************************** + * Tuning parameters + **************************************/ +/* + * HEAPMODE : + * Select how default compression functions will allocate memory for their hash table, + * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#define HEAPMODE 0 + +/* + * ACCELERATION_DEFAULT : + * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 + */ +#define ACCELERATION_DEFAULT 1 + +/************************************** + * CPU Feature Detection + **************************************/ +/* + * LZ4_FORCE_SW_BITCOUNT + * Define this parameter if your target system or compiler does not support hardware bit count + */ +#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */ +#define LZ4_FORCE_SW_BITCOUNT +#endif + +/************************************** + * Includes + **************************************/ +#include "lz4.h" + +/************************************** + * Compiler Options + **************************************/ +#ifdef _MSC_VER /* Visual Studio */ +#define FORCE_INLINE static __forceinline +#include +#pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ +#else +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ +#if defined(__GNUC__) || defined(__clang__) +#define FORCE_INLINE static inline __attribute__((always_inline)) +#else +#define FORCE_INLINE static inline +#endif +#else +#define FORCE_INLINE static +#endif /* __STDC_VERSION__ */ +#endif /* _MSC_VER */ + +/* LZ4_GCC_VERSION is defined into lz4.h */ +#if (LZ4_GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__) +#define expect(expr, value) (__builtin_expect((expr), (value))) +#else +#define expect(expr, value) (expr) +#endif + +#define likely(expr) expect((expr) != 0, 1) +#define unlikely(expr) expect((expr) != 0, 0) + +/************************************** + * Memory routines + **************************************/ +#include /* malloc, calloc, free */ +#define ALLOCATOR(n, s) calloc(n, s) +#define FREEMEM free +#include /* memset, memcpy */ +#define MEM_INIT memset + +/************************************** + * Basic Types + **************************************/ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */ +#include +typedef uint8_t BYTE; +typedef uint16_t U16; +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; +#else +typedef unsigned char BYTE; +typedef unsigned short U16; +typedef unsigned int U32; +typedef signed int S32; +typedef unsigned long long U64; +#endif + +/************************************** + * Reading and writing into memory + **************************************/ +#define STEPSIZE sizeof(size_t) + +static unsigned LZ4_64bits(void) { return sizeof(void *) == 8; } + +static unsigned LZ4_isLittleEndian(void) +{ + const union + { + U32 i; + BYTE c[4]; + } one = {1}; /* don't use static : performance detrimental */ + return one.c[0]; +} + +static U16 LZ4_read16(const void *memPtr) +{ + U16 val16; + memcpy(&val16, memPtr, 2); + return val16; +} + +static U16 LZ4_readLE16(const void *memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read16(memPtr); + } else { + const BYTE *p = (const BYTE *)memPtr; + return (U16)((U16)p[0] + (p[1] << 8)); + } +} + +static void LZ4_writeLE16(void *memPtr, U16 value) +{ + if (LZ4_isLittleEndian()) { + memcpy(memPtr, &value, 2); + } else { + BYTE *p = (BYTE *)memPtr; + p[0] = (BYTE)value; + p[1] = (BYTE)(value >> 8); + } +} + +static U32 LZ4_read32(const void *memPtr) +{ + U32 val32; + memcpy(&val32, memPtr, 4); + return val32; +} + +static U64 LZ4_read64(const void *memPtr) +{ + U64 val64; + memcpy(&val64, memPtr, 8); + return val64; +} + +static size_t LZ4_read_ARCH(const void *p) +{ + if (LZ4_64bits()) + return (size_t)LZ4_read64(p); + else + return (size_t)LZ4_read32(p); +} + +static void LZ4_copy4(void *dstPtr, const void *srcPtr) { memcpy(dstPtr, srcPtr, 4); } + +static void LZ4_copy8(void *dstPtr, const void *srcPtr) { memcpy(dstPtr, srcPtr, 8); } + +/* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */ +static void LZ4_wildCopy(void *dstPtr, const void *srcPtr, void *dstEnd) +{ + BYTE *d = (BYTE *)dstPtr; + const BYTE *s = (const BYTE *)srcPtr; + BYTE *e = (BYTE *)dstEnd; + do { + LZ4_copy8(d, s); + d += 8; + s += 8; + } while (d < e); +} + +/************************************** + * Common Constants + **************************************/ +#define MINMATCH 4 + +#define COPYLENGTH 8 +#define LASTLITERALS 5 +#define MFLIMIT (COPYLENGTH + MINMATCH) +static const int LZ4_minLength = (MFLIMIT + 1); + +#define KB *(1 << 10) +#define MB *(1 << 20) +#define GB *(1U << 30) + +#define MAXD_LOG 16 +#ifdef MAX_DISTANCE +#undef MAX_DISTANCE +#endif +#define MAX_DISTANCE ((1 << MAXD_LOG) - 1) + +#define ML_BITS 4 +#define ML_MASK ((1U << ML_BITS) - 1) +#define RUN_BITS (8 - ML_BITS) +#define RUN_MASK ((1U << RUN_BITS) - 1) + +/************************************** + * Common Utils + **************************************/ +#define LZ4_STATIC_ASSERT(c) \ + { \ + enum \ + { \ + LZ4_static_assert = 1 / (int)(!!(c)) \ + }; \ + } /* use only *after* variable declarations */ + +/************************************** + * Common functions + **************************************/ +static unsigned LZ4_NbCommonBytes(size_t val) +{ + if (LZ4_isLittleEndian()) { + if (LZ4_64bits()) { +#if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanForward64(&r, (U64)val); + return (int)(r >> 3); +#elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_ctzll((U64)val) >> 3); +#else + static const int DeBruijnBytePos[64] = {0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, + 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, + 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7}; + return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; +#endif + } else /* 32 bits */ + { +#if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward(&r, (U32)val); + return (int)(r >> 3); +#elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_ctz((U32)val) >> 3); +#else + static const int DeBruijnBytePos[32] = {0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, + 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1}; + return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; +#endif + } + } else /* Big Endian CPU */ + { + if (LZ4_64bits()) { +#if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanReverse64(&r, val); + return (unsigned)(r >> 3); +#elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clzll((U64)val) >> 3); +#else + unsigned r; + if (!(val >> 32)) { + r = 4; + } else { + r = 0; + val >>= 32; + } + if (!(val >> 16)) { + r += 2; + val >>= 8; + } else { + val >>= 24; + } + r += (!val); + return r; +#endif + } else /* 32 bits */ + { +#if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanReverse(&r, (unsigned long)val); + return (unsigned)(r >> 3); +#elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clz((U32)val) >> 3); +#else + unsigned r; + if (!(val >> 16)) { + r = 2; + val >>= 8; + } else { + r = 0; + val >>= 24; + } + r += (!val); + return r; +#endif + } + } +} + +static unsigned LZ4_count(const BYTE *pIn, const BYTE *pMatch, const BYTE *pInLimit) +{ + const BYTE *const pStart = pIn; + + while (likely(pIn < pInLimit - (STEPSIZE - 1))) { + size_t diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { + pIn += STEPSIZE; + pMatch += STEPSIZE; + continue; + } + pIn += LZ4_NbCommonBytes(diff); + return (unsigned)(pIn - pStart); + } + + if (LZ4_64bits()) + if ((pIn < (pInLimit - 3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { + pIn += 4; + pMatch += 4; + } + if ((pIn < (pInLimit - 1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { + pIn += 2; + pMatch += 2; + } + if ((pIn < pInLimit) && (*pMatch == *pIn)) + pIn++; + return (unsigned)(pIn - pStart); +} + +#ifndef LZ4_COMMONDEFS_ONLY +/************************************** + * Local Constants + **************************************/ +#define LZ4_HASHLOG (LZ4_MEMORY_USAGE - 2) +#define HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) +#define HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ + +static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT - 1)); +static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */ + +/************************************** + * Local Structures and types + **************************************/ +typedef struct +{ + U32 hashTable[HASH_SIZE_U32]; + U32 currentOffset; + U32 initCheck; + const BYTE *dictionary; + BYTE *bufferStart; /* obsolete, used for slideInputBuffer */ + U32 dictSize; +} LZ4_stream_t_internal; + +typedef enum +{ + notLimited = 0, + limitedOutput = 1 +} limitedOutput_directive; +typedef enum +{ + byPtr, + byU32, + byU16 +} tableType_t; + +typedef enum +{ + noDict = 0, + withPrefix64k, + usingExtDict +} dict_directive; +typedef enum +{ + noDictIssue = 0, + dictSmall +} dictIssue_directive; + +typedef enum +{ + endOnOutputSize = 0, + endOnInputSize = 1 +} endCondition_directive; +typedef enum +{ + full = 0, + partial = 1 +} earlyEnd_directive; + +/************************************** + * Local Utils + **************************************/ +int LZ4_versionNumber(void) { return LZ4_VERSION_NUMBER; } +int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } +int LZ4_sizeofState() { return LZ4_STREAMSIZE; } + +/******************************** + * Compression functions + ********************************/ + +static U32 LZ4_hashSequence(U32 sequence, tableType_t const tableType) +{ + if (tableType == byU16) + return (((sequence)*2654435761U) >> ((MINMATCH * 8) - (LZ4_HASHLOG + 1))); + else + return (((sequence)*2654435761U) >> ((MINMATCH * 8) - LZ4_HASHLOG)); +} + +static const U64 prime5bytes = 889523592379ULL; +static U32 LZ4_hashSequence64(size_t sequence, tableType_t const tableType) +{ + const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG + 1 : LZ4_HASHLOG; + const U32 hashMask = (1 << hashLog) - 1; + return ((sequence * prime5bytes) >> (40 - hashLog)) & hashMask; +} + +static U32 LZ4_hashSequenceT(size_t sequence, tableType_t const tableType) +{ + if (LZ4_64bits()) + return LZ4_hashSequence64(sequence, tableType); + return LZ4_hashSequence((U32)sequence, tableType); +} + +static U32 LZ4_hashPosition(const void *p, tableType_t tableType) +{ + return LZ4_hashSequenceT(LZ4_read_ARCH(p), tableType); +} + +static void LZ4_putPositionOnHash(const BYTE *p, U32 h, void *tableBase, tableType_t const tableType, + const BYTE *srcBase) +{ + switch (tableType) { + case byPtr: { + const BYTE **hashTable = (const BYTE **)tableBase; + hashTable[h] = p; + return; + } + case byU32: { + U32 *hashTable = (U32 *)tableBase; + hashTable[h] = (U32)(p - srcBase); + return; + } + case byU16: { + U16 *hashTable = (U16 *)tableBase; + hashTable[h] = (U16)(p - srcBase); + return; + } + } +} + +static void LZ4_putPosition(const BYTE *p, void *tableBase, tableType_t tableType, const BYTE *srcBase) +{ + U32 h = LZ4_hashPosition(p, tableType); + LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); +} + +static const BYTE *LZ4_getPositionOnHash(U32 h, void *tableBase, tableType_t tableType, const BYTE *srcBase) +{ + if (tableType == byPtr) { + const BYTE **hashTable = (const BYTE **)tableBase; + return hashTable[h]; + } + if (tableType == byU32) { + U32 *hashTable = (U32 *)tableBase; + return hashTable[h] + srcBase; + } + { + U16 *hashTable = (U16 *)tableBase; + return hashTable[h] + srcBase; + } /* default, to ensure a return */ +} + +static const BYTE *LZ4_getPosition(const BYTE *p, void *tableBase, tableType_t tableType, const BYTE *srcBase) +{ + U32 h = LZ4_hashPosition(p, tableType); + return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); +} + +FORCE_INLINE int LZ4_compress_generic(void *const ctx, const char *const source, char *const dest, const int inputSize, + const int maxOutputSize, const limitedOutput_directive outputLimited, + const tableType_t tableType, const dict_directive dict, + const dictIssue_directive dictIssue, const U32 acceleration) +{ + LZ4_stream_t_internal *const dictPtr = (LZ4_stream_t_internal *)ctx; + + const BYTE *ip = (const BYTE *)source; + const BYTE *base; + const BYTE *lowLimit; + const BYTE *const lowRefLimit = ip - dictPtr->dictSize; + const BYTE *const dictionary = dictPtr->dictionary; + const BYTE *const dictEnd = dictionary + dictPtr->dictSize; + const size_t dictDelta = dictEnd - (const BYTE *)source; + const BYTE *anchor = (const BYTE *)source; + const BYTE *const iend = ip + inputSize; + const BYTE *const mflimit = iend - MFLIMIT; + const BYTE *const matchlimit = iend - LASTLITERALS; + + BYTE *op = (BYTE *)dest; + BYTE *const olimit = op + maxOutputSize; + + U32 forwardH; + size_t refDelta = 0; + + /* Init conditions */ + if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) + return 0; /* Unsupported input size, too large (or negative) */ + switch (dict) { + case noDict: + default: + base = (const BYTE *)source; + lowLimit = (const BYTE *)source; + break; + case withPrefix64k: + base = (const BYTE *)source - dictPtr->currentOffset; + lowLimit = (const BYTE *)source - dictPtr->dictSize; + break; + case usingExtDict: + base = (const BYTE *)source - dictPtr->currentOffset; + lowLimit = (const BYTE *)source; + break; + } + if ((tableType == byU16) && (inputSize >= LZ4_64Klimit)) + return 0; /* Size too large (not within 64K limit) */ + if (inputSize < LZ4_minLength) + goto _last_literals; /* Input too small, no compression (all literals) */ + + /* First Byte */ + LZ4_putPosition(ip, ctx, tableType, base); + ip++; + forwardH = LZ4_hashPosition(ip, tableType); + + /* Main Loop */ + for (;;) { + const BYTE *match; + BYTE *token; + { + const BYTE *forwardIp = ip; + unsigned step = 1; + unsigned searchMatchNb = acceleration << LZ4_skipTrigger; + + /* Find a match */ + do { + U32 h = forwardH; + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimit)) + goto _last_literals; + + match = LZ4_getPositionOnHash(h, ctx, tableType, base); + if (dict == usingExtDict) { + if (match < (const BYTE *)source) { + refDelta = dictDelta; + lowLimit = dictionary; + } else { + refDelta = 0; + lowLimit = (const BYTE *)source; + } + } + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, ctx, tableType, base); + + } while (((dictIssue == dictSmall) ? (match < lowRefLimit) : 0) || + ((tableType == byU16) ? 0 : (match + MAX_DISTANCE < ip)) || + (LZ4_read32(match + refDelta) != LZ4_read32(ip))); + } + + /* Catch up */ + while ((ip > anchor) && (match + refDelta > lowLimit) && (unlikely(ip[-1] == match[refDelta - 1]))) { + ip--; + match--; + } + + { + /* Encode Literal length */ + unsigned litLength = (unsigned)(ip - anchor); + token = op++; + if ((outputLimited) && (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength / 255) > olimit))) + return 0; /* Check output limit */ + if (litLength >= RUN_MASK) { + int len = (int)litLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) + *op++ = 255; + *op++ = (BYTE)len; + } else + *token = (BYTE)(litLength << ML_BITS); + + /* Copy Literals */ + LZ4_wildCopy(op, anchor, op + litLength); + op += litLength; + } + + _next_match: + /* Encode Offset */ + LZ4_writeLE16(op, (U16)(ip - match)); + op += 2; + + /* Encode MatchLength */ + { + unsigned matchLength; + + if ((dict == usingExtDict) && (lowLimit == dictionary)) { + const BYTE *limit; + match += refDelta; + limit = ip + (dictEnd - match); + if (limit > matchlimit) + limit = matchlimit; + matchLength = LZ4_count(ip + MINMATCH, match + MINMATCH, limit); + ip += MINMATCH + matchLength; + if (ip == limit) { + unsigned more = LZ4_count(ip, (const BYTE *)source, matchlimit); + matchLength += more; + ip += more; + } + } else { + matchLength = LZ4_count(ip + MINMATCH, match + MINMATCH, matchlimit); + ip += MINMATCH + matchLength; + } + + if ((outputLimited) && (unlikely(op + (1 + LASTLITERALS) + (matchLength >> 8) > olimit))) + return 0; /* Check output limit */ + if (matchLength >= ML_MASK) { + *token += ML_MASK; + matchLength -= ML_MASK; + for (; matchLength >= 510; matchLength -= 510) { + *op++ = 255; + *op++ = 255; + } + if (matchLength >= 255) { + matchLength -= 255; + *op++ = 255; + } + *op++ = (BYTE)matchLength; + } else + *token += (BYTE)(matchLength); + } + + anchor = ip; + + /* Test end of chunk */ + if (ip > mflimit) + break; + + /* Fill table */ + LZ4_putPosition(ip - 2, ctx, tableType, base); + + /* Test next position */ + match = LZ4_getPosition(ip, ctx, tableType, base); + if (dict == usingExtDict) { + if (match < (const BYTE *)source) { + refDelta = dictDelta; + lowLimit = dictionary; + } else { + refDelta = 0; + lowLimit = (const BYTE *)source; + } + } + LZ4_putPosition(ip, ctx, tableType, base); + if (((dictIssue == dictSmall) ? (match >= lowRefLimit) : 1) && (match + MAX_DISTANCE >= ip) && + (LZ4_read32(match + refDelta) == LZ4_read32(ip))) { + token = op++; + *token = 0; + goto _next_match; + } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + } + +_last_literals: + /* Encode Last Literals */ + { + const size_t lastRun = (size_t)(iend - anchor); + if ((outputLimited) && + ((op - (BYTE *)dest) + lastRun + 1 + ((lastRun + 255 - RUN_MASK) / 255) > (U32)maxOutputSize)) + return 0; /* Check output limit */ + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for (; accumulator >= 255; accumulator -= 255) + *op++ = 255; + *op++ = (BYTE)accumulator; + } else { + *op++ = (BYTE)(lastRun << ML_BITS); + } + memcpy(op, anchor, lastRun); + op += lastRun; + } + + /* End */ + return (int)(((char *)op) - dest); +} + +int LZ4_compress_fast_extState(void *state, const char *source, char *dest, int inputSize, int maxOutputSize, + int acceleration) +{ + LZ4_resetStream((LZ4_stream_t *)state); + if (acceleration < 1) + acceleration = ACCELERATION_DEFAULT; + + if (maxOutputSize >= LZ4_compressBound(inputSize)) { + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, + acceleration); + else + return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, + noDict, noDictIssue, acceleration); + } else { + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, + noDictIssue, acceleration); + else + return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, + LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); + } +} + +int LZ4_compress_fast(const char *source, char *dest, int inputSize, int maxOutputSize, int acceleration) +{ +#if (HEAPMODE) + void *ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ +#else + LZ4_stream_t ctx; + void *ctxPtr = &ctx; +#endif + + int result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); + +#if (HEAPMODE) + FREEMEM(ctxPtr); +#endif + return result; +} + +int LZ4_compress_default(const char *source, char *dest, int inputSize, int maxOutputSize) +{ + return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1); +} + +/* hidden debug function */ +/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ +int LZ4_compress_fast_force(const char *source, char *dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t ctx; + + LZ4_resetStream(&ctx); + + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, + noDictIssue, acceleration); + else + return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, + LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration); +} + +/******************************** + * destSize variant + ********************************/ + +static int LZ4_compress_destSize_generic(void *const ctx, const char *const src, char *const dst, int *const srcSizePtr, + const int targetDstSize, const tableType_t tableType) +{ + const BYTE *ip = (const BYTE *)src; + const BYTE *base = (const BYTE *)src; + const BYTE *lowLimit = (const BYTE *)src; + const BYTE *anchor = ip; + const BYTE *const iend = ip + *srcSizePtr; + const BYTE *const mflimit = iend - MFLIMIT; + const BYTE *const matchlimit = iend - LASTLITERALS; + + BYTE *op = (BYTE *)dst; + BYTE *const oend = op + targetDstSize; + BYTE *const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */; + BYTE *const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */); + BYTE *const oMaxSeq = oMaxLit - 1 /* token */; + + U32 forwardH; + + /* Init conditions */ + if (targetDstSize < 1) + return 0; /* Impossible to store anything */ + if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) + return 0; /* Unsupported input size, too large (or negative) */ + if ((tableType == byU16) && (*srcSizePtr >= LZ4_64Klimit)) + return 0; /* Size too large (not within 64K limit) */ + if (*srcSizePtr < LZ4_minLength) + goto _last_literals; /* Input too small, no compression (all literals) */ + + /* First Byte */ + *srcSizePtr = 0; + LZ4_putPosition(ip, ctx, tableType, base); + ip++; + forwardH = LZ4_hashPosition(ip, tableType); + + /* Main Loop */ + for (;;) { + const BYTE *match; + BYTE *token; + { + const BYTE *forwardIp = ip; + unsigned step = 1; + unsigned searchMatchNb = 1 << LZ4_skipTrigger; + + /* Find a match */ + do { + U32 h = forwardH; + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimit)) + goto _last_literals; + + match = LZ4_getPositionOnHash(h, ctx, tableType, base); + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, ctx, tableType, base); + + } while (((tableType == byU16) ? 0 : (match + MAX_DISTANCE < ip)) || (LZ4_read32(match) != LZ4_read32(ip))); + } + + /* Catch up */ + while ((ip > anchor) && (match > lowLimit) && (unlikely(ip[-1] == match[-1]))) { + ip--; + match--; + } + + { + /* Encode Literal length */ + unsigned litLength = (unsigned)(ip - anchor); + token = op++; + if (op + ((litLength + 240) / 255) + litLength > oMaxLit) { + /* Not enough space for a last match */ + op--; + goto _last_literals; + } + if (litLength >= RUN_MASK) { + unsigned len = litLength - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for (; len >= 255; len -= 255) + *op++ = 255; + *op++ = (BYTE)len; + } else + *token = (BYTE)(litLength << ML_BITS); + + /* Copy Literals */ + LZ4_wildCopy(op, anchor, op + litLength); + op += litLength; + } + + _next_match: + /* Encode Offset */ + LZ4_writeLE16(op, (U16)(ip - match)); + op += 2; + + /* Encode MatchLength */ + { + size_t matchLength; + + matchLength = LZ4_count(ip + MINMATCH, match + MINMATCH, matchlimit); + + if (op + ((matchLength + 240) / 255) > oMaxMatch) { + /* Match description too long : reduce it */ + matchLength = (15 - 1) + (oMaxMatch - op) * 255; + } + /*printf("offset %5i, matchLength%5i \n", (int)(ip-match), matchLength + MINMATCH);*/ + ip += MINMATCH + matchLength; + + if (matchLength >= ML_MASK) { + *token += ML_MASK; + matchLength -= ML_MASK; + while (matchLength >= 255) { + matchLength -= 255; + *op++ = 255; + } + *op++ = (BYTE)matchLength; + } else + *token += (BYTE)(matchLength); + } + + anchor = ip; + + /* Test end of block */ + if (ip > mflimit) + break; + if (op > oMaxSeq) + break; + + /* Fill table */ + LZ4_putPosition(ip - 2, ctx, tableType, base); + + /* Test next position */ + match = LZ4_getPosition(ip, ctx, tableType, base); + LZ4_putPosition(ip, ctx, tableType, base); + if ((match + MAX_DISTANCE >= ip) && (LZ4_read32(match) == LZ4_read32(ip))) { + token = op++; + *token = 0; + goto _next_match; + } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + } + +_last_literals: + /* Encode Last Literals */ + { + size_t lastRunSize = (size_t)(iend - anchor); + if (op + 1 /* token */ + ((lastRunSize + 240) / 255) /* litLength */ + lastRunSize /* literals */ > oend) { + /* adapt lastRunSize to fill 'dst' */ + lastRunSize = (oend - op) - 1; + lastRunSize -= (lastRunSize + 240) / 255; + } + ip = anchor + lastRunSize; + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for (; accumulator >= 255; accumulator -= 255) + *op++ = 255; + *op++ = (BYTE)accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + *srcSizePtr = (int)(((const char *)ip) - src); + return (int)(((char *)op) - dst); +} + +static int LZ4_compress_destSize_extState(void *state, const char *src, char *dst, int *srcSizePtr, int targetDstSize) +{ + LZ4_resetStream((LZ4_stream_t *)state); + + if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) /* compression success is guaranteed */ + { + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); + } else { + if (*srcSizePtr < LZ4_64Klimit) + return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16); + else + return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, + LZ4_64bits() ? byU32 : byPtr); + } +} + +int LZ4_compress_destSize(const char *src, char *dst, int *srcSizePtr, int targetDstSize) +{ +#if (HEAPMODE) + void *ctx = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ +#else + LZ4_stream_t ctxBody; + void *ctx = &ctxBody; +#endif + + int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); + +#if (HEAPMODE) + FREEMEM(ctx); +#endif + return result; +} + +/******************************** + * Streaming functions + ********************************/ + +LZ4_stream_t *LZ4_createStream(void) +{ + LZ4_stream_t *lz4s = (LZ4_stream_t *)ALLOCATOR(8, LZ4_STREAMSIZE_U64); + LZ4_STATIC_ASSERT( + LZ4_STREAMSIZE >= + sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ + LZ4_resetStream(lz4s); + return lz4s; +} + +void LZ4_resetStream(LZ4_stream_t *LZ4_stream) { MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); } + +int LZ4_freeStream(LZ4_stream_t *LZ4_stream) +{ + FREEMEM(LZ4_stream); + return (0); +} + +#define HASH_UNIT sizeof(size_t) +int LZ4_loadDict(LZ4_stream_t *LZ4_dict, const char *dictionary, int dictSize) +{ + LZ4_stream_t_internal *dict = (LZ4_stream_t_internal *)LZ4_dict; + const BYTE *p = (const BYTE *)dictionary; + const BYTE *const dictEnd = p + dictSize; + const BYTE *base; + + if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */ + LZ4_resetStream(LZ4_dict); + + if (dictSize < (int)HASH_UNIT) { + dict->dictionary = NULL; + dict->dictSize = 0; + return 0; + } + + if ((dictEnd - p) > 64 KB) + p = dictEnd - 64 KB; + dict->currentOffset += 64 KB; + base = p - dict->currentOffset; + dict->dictionary = p; + dict->dictSize = (U32)(dictEnd - p); + dict->currentOffset += dict->dictSize; + + while (p <= dictEnd - HASH_UNIT) { + LZ4_putPosition(p, dict->hashTable, byU32, base); + p += 3; + } + + return dict->dictSize; +} + +static void LZ4_renormDictT(LZ4_stream_t_internal *LZ4_dict, const BYTE *src) +{ + if ((LZ4_dict->currentOffset > 0x80000000) || + ((size_t)LZ4_dict->currentOffset > (size_t)src)) /* address space overflow */ + { + /* rescale hash table */ + U32 delta = LZ4_dict->currentOffset - 64 KB; + const BYTE *dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; + int i; + for (i = 0; i < HASH_SIZE_U32; i++) { + if (LZ4_dict->hashTable[i] < delta) + LZ4_dict->hashTable[i] = 0; + else + LZ4_dict->hashTable[i] -= delta; + } + LZ4_dict->currentOffset = 64 KB; + if (LZ4_dict->dictSize > 64 KB) + LZ4_dict->dictSize = 64 KB; + LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; + } +} + +int LZ4_compress_fast_continue(LZ4_stream_t *LZ4_stream, const char *source, char *dest, int inputSize, + int maxOutputSize, int acceleration) +{ + LZ4_stream_t_internal *streamPtr = (LZ4_stream_t_internal *)LZ4_stream; + const BYTE *const dictEnd = streamPtr->dictionary + streamPtr->dictSize; + + const BYTE *smallest = (const BYTE *)source; + if (streamPtr->initCheck) + return 0; /* Uninitialized structure detected */ + if ((streamPtr->dictSize > 0) && (smallest > dictEnd)) + smallest = dictEnd; + LZ4_renormDictT(streamPtr, smallest); + if (acceleration < 1) + acceleration = ACCELERATION_DEFAULT; + + /* Check overlapping input/dictionary space */ + { + const BYTE *sourceEnd = (const BYTE *)source + inputSize; + if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) { + streamPtr->dictSize = (U32)(dictEnd - sourceEnd); + if (streamPtr->dictSize > 64 KB) + streamPtr->dictSize = 64 KB; + if (streamPtr->dictSize < 4) + streamPtr->dictSize = 0; + streamPtr->dictionary = dictEnd - streamPtr->dictSize; + } + } + + /* prefix mode : source data follows dictionary */ + if (dictEnd == (const BYTE *)source) { + int result; + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, + withPrefix64k, dictSmall, acceleration); + else + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, + withPrefix64k, noDictIssue, acceleration); + streamPtr->dictSize += (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + return result; + } + + /* external dictionary mode */ + { + int result; + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, + usingExtDict, dictSmall, acceleration); + else + result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, + usingExtDict, noDictIssue, acceleration); + streamPtr->dictionary = (const BYTE *)source; + streamPtr->dictSize = (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + return result; + } +} + +/* Hidden debug function, to force external dictionary mode */ +int LZ4_compress_forceExtDict(LZ4_stream_t *LZ4_dict, const char *source, char *dest, int inputSize) +{ + LZ4_stream_t_internal *streamPtr = (LZ4_stream_t_internal *)LZ4_dict; + int result; + const BYTE *const dictEnd = streamPtr->dictionary + streamPtr->dictSize; + + const BYTE *smallest = dictEnd; + if (smallest > (const BYTE *)source) + smallest = (const BYTE *)source; + LZ4_renormDictT((LZ4_stream_t_internal *)LZ4_dict, smallest); + + result = + LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); + + streamPtr->dictionary = (const BYTE *)source; + streamPtr->dictSize = (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + + return result; +} + +int LZ4_saveDict(LZ4_stream_t *LZ4_dict, char *safeBuffer, int dictSize) +{ + LZ4_stream_t_internal *dict = (LZ4_stream_t_internal *)LZ4_dict; + const BYTE *previousDictEnd = dict->dictionary + dict->dictSize; + + if ((U32)dictSize > 64 KB) + dictSize = 64 KB; /* useless to define a dictionary > 64 KB */ + if ((U32)dictSize > dict->dictSize) + dictSize = dict->dictSize; + + memmove(safeBuffer, previousDictEnd - dictSize, dictSize); + + dict->dictionary = (const BYTE *)safeBuffer; + dict->dictSize = (U32)dictSize; + + return dictSize; +} + +/******************************* + * Decompression functions + *******************************/ +/* + * This generic decompression function cover all use cases. + * It shall be instantiated several times, using different sets of directives + * Note that it is essential this generic function is really inlined, + * in order to remove useless branches during compilation optimization. + */ +FORCE_INLINE int +LZ4_decompress_generic(const char *const source, char *const dest, int inputSize, + int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */ + + int endOnInput, /* endOnOutputSize, endOnInputSize */ + int partialDecoding, /* full, partial */ + int targetOutputSize, /* only used if partialDecoding==partial */ + int dict, /* noDict, withPrefix64k, usingExtDict */ + const BYTE *const lowPrefix, /* == dest if dict == noDict */ + const BYTE *const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note : = 0 if noDict */ +) +{ + /* Local Variables */ + const BYTE *ip = (const BYTE *)source; + const BYTE *const iend = ip + inputSize; + + BYTE *op = (BYTE *)dest; + BYTE *const oend = op + outputSize; + BYTE *cpy; + BYTE *oexit = op + targetOutputSize; + const BYTE *const lowLimit = lowPrefix - dictSize; + + const BYTE *const dictEnd = (const BYTE *)dictStart + dictSize; + const size_t dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4}; + const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3}; + + const int safeDecode = (endOnInput == endOnInputSize); + const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); + + /* Special cases */ + if ((partialDecoding) && (oexit > oend - MFLIMIT)) + oexit = oend - MFLIMIT; /* targetOutputSize too high => decode everything */ + if ((endOnInput) && (unlikely(outputSize == 0))) + return ((inputSize == 1) && (*ip == 0)) ? 0 : -1; /* Empty output buffer */ + if ((!endOnInput) && (unlikely(outputSize == 0))) + return (*ip == 0 ? 1 : -1); + + /* Main Loop */ + while (1) { + unsigned token; + size_t length; + const BYTE *match; + + /* get literal length */ + token = *ip++; + if ((length = (token >> ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while (likely((endOnInput) ? ip < iend - RUN_MASK : 1) && (s == 255)); + if ((safeDecode) && unlikely((size_t)(op + length) < (size_t)(op))) + goto _output_error; /* overflow detection */ + if ((safeDecode) && unlikely((size_t)(ip + length) < (size_t)(ip))) + goto _output_error; /* overflow detection */ + } + + /* copy literals */ + cpy = op + length; + if (((endOnInput) && + ((cpy > (partialDecoding ? oexit : oend - MFLIMIT)) || (ip + length > iend - (2 + 1 + LASTLITERALS)))) || + ((!endOnInput) && (cpy > oend - COPYLENGTH))) { + if (partialDecoding) { + if (cpy > oend) + goto _output_error; /* Error : write attempt beyond end of output buffer */ + if ((endOnInput) && (ip + length > iend)) + goto _output_error; /* Error : read attempt beyond end of input buffer */ + } else { + if ((!endOnInput) && (cpy != oend)) + goto _output_error; /* Error : block decoding must stop exactly there */ + if ((endOnInput) && ((ip + length != iend) || (cpy > oend))) + goto _output_error; /* Error : input must be consumed */ + } + memcpy(op, ip, length); + ip += length; + op += length; + break; /* Necessarily EOF, due to parsing restrictions */ + } + LZ4_wildCopy(op, ip, cpy); + ip += length; + op = cpy; + + /* get offset */ + match = cpy - LZ4_readLE16(ip); + ip += 2; + if ((checkOffset) && (unlikely(match < lowLimit))) + goto _output_error; /* Error : offset outside destination buffer */ + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) { + unsigned s; + do { + if ((endOnInput) && (ip > iend - LASTLITERALS)) + goto _output_error; + s = *ip++; + length += s; + } while (s == 255); + if ((safeDecode) && unlikely((size_t)(op + length) < (size_t)op)) + goto _output_error; /* overflow detection */ + } + length += MINMATCH; + + /* check external dictionary */ + if ((dict == usingExtDict) && (match < lowPrefix)) { + if (unlikely(op + length > oend - LASTLITERALS)) + goto _output_error; /* doesn't respect parsing restriction */ + + if (length <= (size_t)(lowPrefix - match)) { + /* match can be copied as a single segment from external dictionary */ + match = dictEnd - (lowPrefix - match); + memmove(op, match, length); + op += length; + } else { + /* match encompass external dictionary and current segment */ + size_t copySize = (size_t)(lowPrefix - match); + memcpy(op, dictEnd - copySize, copySize); + op += copySize; + copySize = length - copySize; + if (copySize > (size_t)(op - lowPrefix)) /* overlap within current segment */ + { + BYTE *const endOfMatch = op + copySize; + const BYTE *copyFrom = lowPrefix; + while (op < endOfMatch) + *op++ = *copyFrom++; + } else { + memcpy(op, lowPrefix, copySize); + op += copySize; + } + } + continue; + } + + /* copy repeated sequence */ + cpy = op + length; + if (unlikely((op - match) < 8)) { + const size_t dec64 = dec64table[op - match]; + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += dec32table[op - match]; + LZ4_copy4(op + 4, match); + op += 8; + match -= dec64; + } else { + LZ4_copy8(op, match); + op += 8; + match += 8; + } + + if (unlikely(cpy > oend - 12)) { + if (cpy > oend - LASTLITERALS) + goto _output_error; /* Error : last LASTLITERALS bytes must be literals */ + if (op < oend - 8) { + LZ4_wildCopy(op, match, oend - 8); + match += (oend - 8) - op; + op = oend - 8; + } + while (op < cpy) + *op++ = *match++; + } else + LZ4_wildCopy(op, match, cpy); + op = cpy; /* correction */ + } + + /* end of decoding */ + if (endOnInput) + return (int)(((char *)op) - dest); /* Nb of output bytes decoded */ + else + return (int)(((const char *)ip) - source); /* Nb of input bytes read */ + + /* Overflow error detected */ +_output_error: + return (int)(-(((const char *)ip) - source)) - 1; +} + +int LZ4_decompress_safe(const char *source, char *dest, int compressedSize, int maxDecompressedSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, + (BYTE *)dest, NULL, 0); +} + +int LZ4_decompress_safe_partial(const char *source, char *dest, int compressedSize, int targetOutputSize, + int maxDecompressedSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, + targetOutputSize, noDict, (BYTE *)dest, NULL, 0); +} + +int LZ4_decompress_fast(const char *source, char *dest, int originalSize) +{ + return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, + (BYTE *)(dest - 64 KB), NULL, 64 KB); +} + +/* streaming decompression functions */ + +typedef struct +{ + const BYTE *externalDict; + size_t extDictSize; + const BYTE *prefixEnd; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + +/* + * If you prefer dynamic allocation methods, + * LZ4_createStreamDecode() + * provides a pointer (void*) towards an initialized LZ4_streamDecode_t structure. + */ +LZ4_streamDecode_t *LZ4_createStreamDecode(void) +{ + LZ4_streamDecode_t *lz4s = (LZ4_streamDecode_t *)ALLOCATOR(1, sizeof(LZ4_streamDecode_t)); + return lz4s; +} + +int LZ4_freeStreamDecode(LZ4_streamDecode_t *LZ4_stream) +{ + FREEMEM(LZ4_stream); + return 0; +} + +/* + * LZ4_setStreamDecode + * Use this function to instruct where to find the dictionary + * This function is not necessary if previous data is still available where it was decoded. + * Loading a size of 0 is allowed (same effect as no dictionary). + * Return : 1 if OK, 0 if error + */ +int LZ4_setStreamDecode(LZ4_streamDecode_t *LZ4_streamDecode, const char *dictionary, int dictSize) +{ + LZ4_streamDecode_t_internal *lz4sd = (LZ4_streamDecode_t_internal *)LZ4_streamDecode; + lz4sd->prefixSize = (size_t)dictSize; + lz4sd->prefixEnd = (const BYTE *)dictionary + dictSize; + lz4sd->externalDict = NULL; + lz4sd->extDictSize = 0; + return 1; +} + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks must still be available at the memory position where they were decoded. + If it's not possible, save the relevant part of decoded data into a safe buffer, + and indicate where it stands using LZ4_setStreamDecode() +*/ +int LZ4_decompress_safe_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest, + int compressedSize, int maxOutputSize) +{ + LZ4_streamDecode_t_internal *lz4sd = (LZ4_streamDecode_t_internal *)LZ4_streamDecode; + int result; + + if (lz4sd->prefixEnd == (BYTE *)dest) { + result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, + usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, + lz4sd->extDictSize); + if (result <= 0) + return result; + lz4sd->prefixSize += result; + lz4sd->prefixEnd += result; + } else { + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, + usingExtDict, (BYTE *)dest, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) + return result; + lz4sd->prefixSize = result; + lz4sd->prefixEnd = (BYTE *)dest + result; + } + + return result; +} + +int LZ4_decompress_fast_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest, int originalSize) +{ + LZ4_streamDecode_t_internal *lz4sd = (LZ4_streamDecode_t_internal *)LZ4_streamDecode; + int result; + + if (lz4sd->prefixEnd == (BYTE *)dest) { + result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict, + lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) + return result; + lz4sd->prefixSize += originalSize; + lz4sd->prefixEnd += originalSize; + } else { + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = (BYTE *)dest - lz4sd->extDictSize; + result = LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, usingExtDict, + (BYTE *)dest, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) + return result; + lz4sd->prefixSize = originalSize; + lz4sd->prefixEnd = (BYTE *)dest + originalSize; + } + + return result; +} + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as "_continue" ones, + the dictionary must be explicitly provided within parameters +*/ + +FORCE_INLINE int LZ4_decompress_usingDict_generic(const char *source, char *dest, int compressedSize, int maxOutputSize, + int safe, const char *dictStart, int dictSize) +{ + if (dictSize == 0) + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE *)dest, + NULL, 0); + if (dictStart + dictSize == dest) { + if (dictSize >= (int)(64 KB - 1)) + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, + (BYTE *)dest - 64 KB, NULL, 0); + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, + (BYTE *)dest - dictSize, NULL, 0); + } + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, + (BYTE *)dest, (const BYTE *)dictStart, dictSize); +} + +int LZ4_decompress_safe_usingDict(const char *source, char *dest, int compressedSize, int maxOutputSize, + const char *dictStart, int dictSize) +{ + return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize); +} + +int LZ4_decompress_fast_usingDict(const char *source, char *dest, int originalSize, const char *dictStart, int dictSize) +{ + return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize); +} + +/* debug function */ +int LZ4_decompress_safe_forceExtDict(const char *source, char *dest, int compressedSize, int maxOutputSize, + const char *dictStart, int dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, + (BYTE *)dest, (const BYTE *)dictStart, dictSize); +} + +/*************************************************** + * Obsolete Functions + ***************************************************/ +/* obsolete compression functions */ +int LZ4_compress_limitedOutput(const char *source, char *dest, int inputSize, int maxOutputSize) +{ + return LZ4_compress_default(source, dest, inputSize, maxOutputSize); +} +int LZ4_compress(const char *source, char *dest, int inputSize) +{ + return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); +} +int LZ4_compress_limitedOutput_withState(void *state, const char *src, char *dst, int srcSize, int dstSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); +} +int LZ4_compress_withState(void *state, const char *src, char *dst, int srcSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); +} +int LZ4_compress_limitedOutput_continue(LZ4_stream_t *LZ4_stream, const char *src, char *dst, int srcSize, + int maxDstSize) +{ + return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); +} +int LZ4_compress_continue(LZ4_stream_t *LZ4_stream, const char *source, char *dest, int inputSize) +{ + return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); +} + +/* +These function names are deprecated and should no longer be used. +They are only provided here for compatibility with older user programs. +- LZ4_uncompress is totally equivalent to LZ4_decompress_fast +- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe +*/ +int LZ4_uncompress(const char *source, char *dest, int outputSize) +{ + return LZ4_decompress_fast(source, dest, outputSize); +} +int LZ4_uncompress_unknownOutputSize(const char *source, char *dest, int isize, int maxOutputSize) +{ + return LZ4_decompress_safe(source, dest, isize, maxOutputSize); +} + +/* Obsolete Streaming functions */ + +int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } + +static void LZ4_init(LZ4_stream_t_internal *lz4ds, BYTE *base) +{ + MEM_INIT(lz4ds, 0, LZ4_STREAMSIZE); + lz4ds->bufferStart = base; +} + +int LZ4_resetStreamState(void *state, char *inputBuffer) +{ + if ((((size_t)state) & 3) != 0) + return 1; /* Error : pointer is not aligned on 4-bytes boundary */ + LZ4_init((LZ4_stream_t_internal *)state, (BYTE *)inputBuffer); + return 0; +} + +void *LZ4_create(char *inputBuffer) +{ + void *lz4ds = ALLOCATOR(8, LZ4_STREAMSIZE_U64); + LZ4_init((LZ4_stream_t_internal *)lz4ds, (BYTE *)inputBuffer); + return lz4ds; +} + +char *LZ4_slideInputBuffer(void *LZ4_Data) +{ + LZ4_stream_t_internal *ctx = (LZ4_stream_t_internal *)LZ4_Data; + int dictSize = LZ4_saveDict((LZ4_stream_t *)LZ4_Data, (char *)ctx->bufferStart, 64 KB); + return (char *)(ctx->bufferStart + dictSize); +} + +/* Obsolete streaming decompression functions */ + +int LZ4_decompress_safe_withPrefix64k(const char *source, char *dest, int compressedSize, int maxOutputSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, + (BYTE *)dest - 64 KB, NULL, 64 KB); +} + +int LZ4_decompress_fast_withPrefix64k(const char *source, char *dest, int originalSize) +{ + return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, + (BYTE *)dest - 64 KB, NULL, 64 KB); +} + +#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/libs/fst/lz4.h b/libs/fst/lz4.h new file mode 100644 index 00000000000..929cf02cac7 --- /dev/null +++ b/libs/fst/lz4.h @@ -0,0 +1,367 @@ +/* + LZ4 - Fast LZ compression algorithm + Header File + Copyright (C) 2011-2015, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + SPDX-License-Identifier: BSD-2-Clause + + You can contact the author at : + - LZ4 source repository : https://github.com/Cyan4973/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +#pragma once + +#if defined(__cplusplus) +extern "C" { +#endif + +/* + * lz4.h provides block compression functions, and gives full buffer control to programmer. + * If you need to generate inter-operable compressed data (respecting LZ4 frame specification), + * and can let the library handle its own memory, please use lz4frame.h instead. + */ + +/************************************** + * Version + **************************************/ +#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ +#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */ +#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR * 100 * 100 + LZ4_VERSION_MINOR * 100 + LZ4_VERSION_RELEASE) +int LZ4_versionNumber(void); + +/************************************** + * Tuning parameter + **************************************/ +/* + * LZ4_MEMORY_USAGE : + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) + * Increasing memory usage improves compression ratio + * Reduced memory usage can improve speed, due to cache effect + * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache + */ +#define LZ4_MEMORY_USAGE 14 + +/************************************** + * Simple Functions + **************************************/ + +int LZ4_compress_default(const char *source, char *dest, int sourceSize, int maxDestSize); +int LZ4_decompress_safe(const char *source, char *dest, int compressedSize, int maxDecompressedSize); + +/* +LZ4_compress_default() : + Compresses 'sourceSize' bytes from buffer 'source' + into already allocated 'dest' buffer of size 'maxDestSize'. + Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize). + It also runs faster, so it's a recommended setting. + If the function cannot compress 'source' into a more limited 'dest' budget, + compression stops *immediately*, and the function result is zero. + As a consequence, 'dest' content is not valid. + This function never writes outside 'dest' buffer, nor read outside 'source' buffer. + sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE + maxDestSize : full or partial size of buffer 'dest' (which must be already allocated) + return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize) + or 0 if compression fails + +LZ4_decompress_safe() : + compressedSize : is the precise full size of the compressed block. + maxDecompressedSize : is the size of destination buffer, which must be already allocated. + return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize) + If destination buffer is not large enough, decoding will stop and output an error code (<0). + If the source stream is detected malformed, the function will stop decoding and return a negative result. + This function is protected against buffer overflow exploits, including malicious data packets. + It never writes outside output buffer, nor reads outside input buffer. +*/ + +/************************************** + * Advanced Functions + **************************************/ +#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize) / 255) + 16) + +/* +LZ4_compressBound() : + Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) + This function is primarily useful for memory allocation purposes (destination buffer size). + Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). + Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize) + inputSize : max supported value is LZ4_MAX_INPUT_SIZE + return : maximum output size in a "worst case" scenario + or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) +*/ +int LZ4_compressBound(int inputSize); + +/* +LZ4_compress_fast() : + Same as LZ4_compress_default(), but allows to select an "acceleration" factor. + The larger the acceleration value, the faster the algorithm, but also the lesser the compression. + It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. + An acceleration value of "1" is the same as regular LZ4_compress_default() + Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1. +*/ +int LZ4_compress_fast(const char *source, char *dest, int sourceSize, int maxDestSize, int acceleration); + +/* +LZ4_compress_fast_extState() : + Same compression function, just using an externally allocated memory space to store compression state. + Use LZ4_sizeofState() to know how much memory must be allocated, + and allocate it on 8-bytes boundaries (using malloc() typically). + Then, provide it as 'void* state' to compression function. +*/ +int LZ4_sizeofState(void); +int LZ4_compress_fast_extState(void *state, const char *source, char *dest, int inputSize, int maxDestSize, + int acceleration); + +/* +LZ4_compress_destSize() : + Reverse the logic, by compressing as much data as possible from 'source' buffer + into already allocated buffer 'dest' of size 'targetDestSize'. + This function either compresses the entire 'source' content into 'dest' if it's large enough, + or fill 'dest' buffer completely with as much data as possible from 'source'. + *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'. + New value is necessarily <= old value. + return : Nb bytes written into 'dest' (necessarily <= targetDestSize) + or 0 if compression fails +*/ +int LZ4_compress_destSize(const char *source, char *dest, int *sourceSizePtr, int targetDestSize); + +/* +LZ4_decompress_fast() : + originalSize : is the original and therefore uncompressed size + return : the number of bytes read from the source buffer (in other words, the compressed size) + If the source stream is detected malformed, the function will stop decoding and return a negative result. + Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes. + note : This function fully respect memory boundaries for properly formed compressed data. + It is a bit faster than LZ4_decompress_safe(). + However, it does not provide any protection against intentionally modified data stream (malicious input). + Use this function in trusted environment only (data to decode comes from a trusted source). +*/ +int LZ4_decompress_fast(const char *source, char *dest, int originalSize); + +/* +LZ4_decompress_safe_partial() : + This function decompress a compressed block of size 'compressedSize' at position 'source' + into destination buffer 'dest' of size 'maxDecompressedSize'. + The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached, + reducing decompression time. + return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize) + Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller. + Always control how many bytes were decoded. + If the source stream is detected malformed, the function will stop decoding and return a negative result. + This function never writes outside of output buffer, and never reads outside of input buffer. It is +therefore protected against malicious data packets +*/ +int LZ4_decompress_safe_partial(const char *source, char *dest, int compressedSize, int targetOutputSize, + int maxDecompressedSize); + +/*********************************************** + * Streaming Compression Functions + ***********************************************/ +#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE - 3)) + 4) +#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long)) +/* + * LZ4_stream_t + * information structure to track an LZ4 stream. + * important : init this structure content before first use ! + * note : only allocated directly the structure if you are statically linking LZ4 + * If you are using liblz4 as a DLL, please use below construction methods instead. + */ +typedef struct +{ + long long table[LZ4_STREAMSIZE_U64]; +} LZ4_stream_t; + +/* + * LZ4_resetStream + * Use this function to init an allocated LZ4_stream_t structure + */ +void LZ4_resetStream(LZ4_stream_t *streamPtr); + +/* + * LZ4_createStream will allocate and initialize an LZ4_stream_t structure + * LZ4_freeStream releases its memory. + * In the context of a DLL (liblz4), please use these methods rather than the static struct. + * They are more future proof, in case of a change of LZ4_stream_t size. + */ +LZ4_stream_t *LZ4_createStream(void); +int LZ4_freeStream(LZ4_stream_t *streamPtr); + +/* + * LZ4_loadDict + * Use this function to load a static dictionary into LZ4_stream. + * Any previous data will be forgotten, only 'dictionary' will remain in memory. + * Loading a size of 0 is allowed. + * Return : dictionary size, in bytes (necessarily <= 64 KB) + */ +int LZ4_loadDict(LZ4_stream_t *streamPtr, const char *dictionary, int dictSize); + +/* + * LZ4_compress_fast_continue + * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression + * ratio. Important : Previous data blocks are assumed to still be present and unmodified ! 'dst' buffer must be already + * allocated. If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. If + * not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero. + */ +int LZ4_compress_fast_continue(LZ4_stream_t *streamPtr, const char *src, char *dst, int srcSize, int maxDstSize, + int acceleration); + +/* + * LZ4_saveDict + * If previously compressed data block is not guaranteed to remain available at its memory location + * save it into a safer place (char* safeBuffer) + * Note : you don't need to call LZ4_loadDict() afterwards, + * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue() + * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error + */ +int LZ4_saveDict(LZ4_stream_t *streamPtr, char *safeBuffer, int dictSize); + +/************************************************ + * Streaming Decompression Functions + ************************************************/ + +#define LZ4_STREAMDECODESIZE_U64 4 +#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) +typedef struct +{ + unsigned long long table[LZ4_STREAMDECODESIZE_U64]; +} LZ4_streamDecode_t; +/* + * LZ4_streamDecode_t + * information structure to track an LZ4 stream. + * init this structure content using LZ4_setStreamDecode or memset() before first use ! + * + * In the context of a DLL (liblz4) please prefer usage of construction methods below. + * They are more future proof, in case of a change of LZ4_streamDecode_t size in the future. + * LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure + * LZ4_freeStreamDecode releases its memory. + */ +LZ4_streamDecode_t *LZ4_createStreamDecode(void); +int LZ4_freeStreamDecode(LZ4_streamDecode_t *LZ4_stream); + +/* + * LZ4_setStreamDecode + * Use this function to instruct where to find the dictionary. + * Setting a size of 0 is allowed (same effect as reset). + * Return : 1 if OK, 0 if error + */ +int LZ4_setStreamDecode(LZ4_streamDecode_t *LZ4_streamDecode, const char *dictionary, int dictSize); + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB) + In the case of a ring buffers, decoding buffer must be either : + - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions) + In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB). + - Larger than encoding buffer, by a minimum of maxBlockSize more bytes. + maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block. + In which case, encoding and decoding buffers do not need to be synchronized, + and encoding ring buffer can have any size, including small ones ( < 64 KB). + - _At least_ 64 KB + 8 bytes + maxBlockSize. + In which case, encoding and decoding buffers do not need to be synchronized, + and encoding ring buffer can have any size, including larger than decoding buffer. + Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer, + and indicate where it is saved using LZ4_setStreamDecode() +*/ +int LZ4_decompress_safe_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest, + int compressedSize, int maxDecompressedSize); +int LZ4_decompress_fast_continue(LZ4_streamDecode_t *LZ4_streamDecode, const char *source, char *dest, + int originalSize); + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as + a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue() + They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure. +*/ +int LZ4_decompress_safe_usingDict(const char *source, char *dest, int compressedSize, int maxDecompressedSize, + const char *dictStart, int dictSize); +int LZ4_decompress_fast_usingDict(const char *source, char *dest, int originalSize, const char *dictStart, + int dictSize); + +/************************************** + * Obsolete Functions + **************************************/ +/* Deprecate Warnings */ +/* Should these warnings messages be a problem, + it is generally possible to disable them, + with -Wno-deprecated-declarations for gcc + or _CRT_SECURE_NO_WARNINGS in Visual for example. + You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */ +#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK +#define LZ4_DEPRECATE_WARNING_DEFBLOCK +#define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#if (LZ4_GCC_VERSION >= 405) || defined(__clang__) +#define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +#elif (LZ4_GCC_VERSION >= 301) +#define LZ4_DEPRECATED(message) __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +#else +#pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") +#define LZ4_DEPRECATED(message) +#endif +#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */ + +/* Obsolete compression functions */ +/* These functions are planned to start generate warnings by r131 approximately */ +int LZ4_compress(const char *source, char *dest, int sourceSize); +int LZ4_compress_limitedOutput(const char *source, char *dest, int sourceSize, int maxOutputSize); +int LZ4_compress_withState(void *state, const char *source, char *dest, int inputSize); +int LZ4_compress_limitedOutput_withState(void *state, const char *source, char *dest, int inputSize, int maxOutputSize); +int LZ4_compress_continue(LZ4_stream_t *LZ4_streamPtr, const char *source, char *dest, int inputSize); +int LZ4_compress_limitedOutput_continue(LZ4_stream_t *LZ4_streamPtr, const char *source, char *dest, int inputSize, + int maxOutputSize); + +/* Obsolete decompression functions */ +/* These function names are completely deprecated and must no longer be used. + They are only provided here for compatibility with older programs. + - LZ4_uncompress is the same as LZ4_decompress_fast + - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe + These function prototypes are now disabled; uncomment them only if you really need them. + It is highly recommended to stop using these prototypes and migrate to maintained ones */ +/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */ +/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */ + +/* Obsolete streaming functions; use new streaming interface whenever possible */ +LZ4_DEPRECATED("use LZ4_createStream() instead") void *LZ4_create(char *inputBuffer); +LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void); +LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void *state, char *inputBuffer); +LZ4_DEPRECATED("use LZ4_saveDict() instead") char *LZ4_slideInputBuffer(void *state); + +/* Obsolete streaming decoding functions */ +LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") +int LZ4_decompress_safe_withPrefix64k(const char *src, char *dst, int compressedSize, int maxDstSize); +LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") +int LZ4_decompress_fast_withPrefix64k(const char *src, char *dst, int originalSize); + +#if defined(__cplusplus) +} +#endif diff --git a/libs/json11/json11.cpp b/libs/json11/json11.cpp index 549463d712a..189e63881ac 100644 --- a/libs/json11/json11.cpp +++ b/libs/json11/json11.cpp @@ -151,7 +151,7 @@ class Value : public JsonValue { // Constructors explicit Value(const T &value) : m_value(value) {} - explicit Value(T &&value) : m_value(move(value)) {} + explicit Value(T &&value) : m_value(std::move(value)) {} // Get type tag Json::Type type() const override { @@ -198,7 +198,7 @@ class JsonString final : public Value { const string &string_value() const override { return m_value; } public: explicit JsonString(const string &value) : Value(value) {} - explicit JsonString(string &&value) : Value(move(value)) {} + explicit JsonString(string &&value) : Value(std::move(value)) {} }; class JsonArray final : public Value { @@ -206,7 +206,7 @@ class JsonArray final : public Value { const Json & operator[](size_t i) const override; public: explicit JsonArray(const Json::array &value) : Value(value) {} - explicit JsonArray(Json::array &&value) : Value(move(value)) {} + explicit JsonArray(Json::array &&value) : Value(std::move(value)) {} }; class JsonObject final : public Value { @@ -214,7 +214,7 @@ class JsonObject final : public Value { const Json & operator[](const string &key) const override; public: explicit JsonObject(const Json::object &value) : Value(value) {} - explicit JsonObject(Json::object &&value) : Value(move(value)) {} + explicit JsonObject(Json::object &&value) : Value(std::move(value)) {} }; class JsonNull final : public Value { @@ -256,12 +256,12 @@ Json::Json(double value) : m_ptr(make_shared(value)) { Json::Json(int value) : m_ptr(make_shared(value)) {} Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} Json::Json(const string &value) : m_ptr(make_shared(value)) {} -Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} +Json::Json(string &&value) : m_ptr(make_shared(std::move(value))) {} Json::Json(const char * value) : m_ptr(make_shared(value)) {} Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} -Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} +Json::Json(Json::array &&values) : m_ptr(make_shared(std::move(values))) {} Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} -Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} +Json::Json(Json::object &&values) : m_ptr(make_shared(std::move(values))) {} /* * * * * * * * * * * * * * * * * * * * * Accessors @@ -359,7 +359,7 @@ struct JsonParser final { * Mark this parse as failed. */ Json fail(string &&msg) { - return fail(move(msg), Json()); + return fail(std::move(msg), Json()); } template diff --git a/libs/subcircuit/Makefile b/libs/subcircuit/Makefile index f81085b5bc3..3d93ad0a2e4 100644 --- a/libs/subcircuit/Makefile +++ b/libs/subcircuit/Makefile @@ -5,9 +5,9 @@ CONFIG := clang-debug # CONFIG := release CC = clang -CXX = clang +CXX = clang++ CXXFLAGS = -MD -Wall -Wextra -ggdb -LDLIBS = -lstdc++ +LIBS = -lstdc++ ifeq ($(CONFIG),clang-debug) CXXFLAGS += -std=c++11 -O0 @@ -15,19 +15,19 @@ endif ifeq ($(CONFIG),gcc-debug) CC = gcc -CXX = gcc +CXX = g++ CXXFLAGS += -std=gnu++0x -O0 endif ifeq ($(CONFIG),profile) CC = gcc -CXX = gcc +CXX = g++ CXXFLAGS += -std=gnu++0x -Os -DNDEBUG endif ifeq ($(CONFIG),release) CC = gcc -CXX = gcc +CXX = g++ CXXFLAGS += -std=gnu++0x -march=native -O3 -DNDEBUG endif @@ -50,4 +50,3 @@ clean: .PHONY: all test clean -include *.d - diff --git a/libs/subcircuit/README b/libs/subcircuit/README index ecaa987db0a..de85cdfea7e 100644 --- a/libs/subcircuit/README +++ b/libs/subcircuit/README @@ -4,7 +4,7 @@ * The SubCircuit C++11 library * * * * An implementation of a modified Ullmann Subgraph Isomorphism Algorithm * - * for coarse grain logic networks. by Clifford Wolf * + * for coarse grain logic networks. by Claire Xenia Wolf * * * ************************************************************************** diff --git a/libs/subcircuit/subcircuit.cc b/libs/subcircuit/subcircuit.cc index 4068dc09aa6..f38da3fcc57 100644 --- a/libs/subcircuit/subcircuit.cc +++ b/libs/subcircuit/subcircuit.cc @@ -2,7 +2,7 @@ * SubCircuit -- An implementation of the Ullmann Subgraph Isomorphism * algorithm for coarse grain logic networks * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/libs/subcircuit/subcircuit.h b/libs/subcircuit/subcircuit.h index 8368efab138..f2a28ecd26d 100644 --- a/libs/subcircuit/subcircuit.h +++ b/libs/subcircuit/subcircuit.h @@ -2,7 +2,7 @@ * SubCircuit -- An implementation of the Ullmann Subgraph Isomorphism * algorithm for coarse grain logic networks * - * Copyright (C) 2013 Clifford Wolf + * Copyright (C) 2013 Claire Xenia Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/manual/.gitignore b/manual/.gitignore deleted file mode 100644 index 110f65b1900..00000000000 --- a/manual/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -*.aux -*.bbl -*.blg -*.idx -*.log -*.out -*.pdf -*.toc -*.snm -*.nav -*.vrb -*.ok diff --git a/manual/APPNOTE_010_Verilog_to_BLIF.tex b/manual/APPNOTE_010_Verilog_to_BLIF.tex deleted file mode 100644 index 0ecdf619422..00000000000 --- a/manual/APPNOTE_010_Verilog_to_BLIF.tex +++ /dev/null @@ -1,466 +0,0 @@ - -% IEEEtran howto: -% http://ftp.univie.ac.at/packages/tex/macros/latex/contrib/IEEEtran/IEEEtran_HOWTO.pdf -\documentclass[9pt,technote,a4paper]{IEEEtran} - -\usepackage[T1]{fontenc} % required for luximono! -\usepackage[scaled=0.8]{luximono} % typewriter font with bold face - -% To install the luximono font files: -% getnonfreefonts-sys --all or -% getnonfreefonts-sys luximono -% -% when there are trouble you might need to: -% - Create /etc/texmf/updmap.d/99local-luximono.cfg -% containing the single line: Map ul9.map -% - Run update-updmap followed by mktexlsr and updmap-sys -% -% This commands must be executed as root with a root environment -% (i.e. run "sudo su" and then execute the commands in the root -% shell, don't just prefix the commands with "sudo"). - -\usepackage[unicode,bookmarks=false]{hyperref} -\usepackage[english]{babel} -\usepackage[utf8]{inputenc} -\usepackage{amssymb} -\usepackage{amsmath} -\usepackage{amsfonts} -\usepackage{units} -\usepackage{nicefrac} -\usepackage{eurosym} -\usepackage{graphicx} -\usepackage{verbatim} -\usepackage{algpseudocode} -\usepackage{scalefnt} -\usepackage{xspace} -\usepackage{color} -\usepackage{colortbl} -\usepackage{multirow} -\usepackage{hhline} -\usepackage{listings} -\usepackage{float} - -\usepackage{tikz} -\usetikzlibrary{calc} -\usetikzlibrary{arrows} -\usetikzlibrary{scopes} -\usetikzlibrary{through} -\usetikzlibrary{shapes.geometric} - -\lstset{basicstyle=\ttfamily,frame=trBL,xleftmargin=2em,xrightmargin=1em,numbers=left} - -\begin{document} - -\title{Yosys Application Note 010: \\ Converting Verilog to BLIF} -\author{Clifford Wolf \\ November 2013} -\maketitle - -\begin{abstract} -Verilog-2005 is a powerful Hardware Description Language (HDL) that can be used -to easily create complex designs from small HDL code. It is the preferred -method of design entry for many designers\footnote{The other half prefers VHDL, -a very different but -- of course -- equally powerful language.}. - -The Berkeley Logic Interchange Format (BLIF) \cite{blif} is a simple file format for -exchanging sequential logic between programs. It is easy to generate and -easy to parse and is therefore the preferred method of design entry for -many authors of logic synthesis tools. - -Yosys \cite{yosys} is a feature-rich -Open-Source Verilog synthesis tool that can be used to bridge the gap between -the two file formats. It implements most of Verilog-2005 and thus can be used -to import modern behavioral Verilog designs into BLIF-based design flows -without dependencies on proprietary synthesis tools. - -The scope of Yosys goes of course far beyond Verilog logic synthesis. But -it is a useful and important feature and this Application Note will focus -on this aspect of Yosys. -\end{abstract} - -\section{Installation} - -Yosys written in C++ (using features from C++11) and is tested on modern Linux. -It should compile fine on most UNIX systems with a C++11 compiler. The README -file contains useful information on building Yosys and its prerequisites. - -Yosys is a large and feature-rich program with a couple of dependencies. It is, -however, possible to deactivate some of the dependencies in the Makefile, -resulting in features in Yosys becoming unavailable. When problems with building -Yosys are encountered, a user who is only interested in the features of Yosys -that are discussed in this Application Note may deactivate {\tt TCL}, {\tt Qt} -and {\tt MiniSAT} support in the {\tt Makefile} and may opt against building -{\tt yosys-abc}. - -\bigskip - -This Application Note is based on GIT Rev. {\tt e216e0e} from 2013-11-23 of -Yosys \cite{yosys}. The Verilog sources used for the examples are taken from -yosys-bigsim \cite{bigsim}, a collection of real-world designs used for -regression testing Yosys. - -\section{Getting Started} - -We start our tour with the Navr\'e processor from yosys-bigsim. The Navr\'e -processor \cite{navre} is an Open Source AVR clone. It is a single module ({\tt -softusb\_navre}) in a single design file ({\tt softusb\_navre.v}). It also is -using only features that map nicely to the BLIF format, for example it only -uses synchronous resets. - -Converting {\tt softusb\_navre.v} to {\tt softusb\_navre.blif} could not be -easier: - -\begin{figure}[H] -\begin{lstlisting}[language=sh] -yosys -o softusb_navre.blif -S softusb_navre.v -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{Calling Yosys without script file} -\end{figure} - -Behind the scenes Yosys is controlled by synthesis scripts that execute -commands that operate on Yosys' internal state. For example, the {\tt -o -softusb\_navre.blif} option just adds the command {\tt write\_blif -softusb\_navre.blif} to the end of the script. Likewise a file on the -command line -- {\tt softusb\_navre.v} in this case -- adds the command -{\tt read\_verilog softusb\_navre.v} to the beginning of the -synthesis script. In both cases the file type is detected from the -file extension. - -Finally the option {\tt -S} instantiates a built-in default synthesis script. -Instead of using {\tt -S} one could also specify the synthesis commands -for the script on the command line using the {\tt -p} option, either using -individual options for each command or by passing one big command string -with a semicolon-separated list of commands. But in most cases it is more -convenient to use an actual script file. - -\section{Using a Synthesis Script} - -With a script file we have better control over Yosys. The following script -file replicates what the command from the last section did: - -\begin{figure}[H] -\begin{lstlisting}[language=sh] -read_verilog softusb_navre.v -hierarchy -proc; opt; memory; opt; techmap; opt -write_blif softusb_navre.blif -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{\tt softusb\_navre.ys} -\end{figure} - -The first and last line obviously read the Verilog file and write the BLIF -file. - -\medskip - -The 2nd line checks the design hierarchy and instantiates parametrized -versions of the modules in the design, if necessary. In the case of this -simple design this is a no-op. However, as a general rule a synthesis script -should always contain this command as first command after reading the input -files. - -\medskip - -The 3rd line does most of the actual work: - -\begin{itemize} -\item The command {\tt opt} is the Yosys' built-in optimizer. It can perform -some simple optimizations such as const-folding and removing unconnected parts -of the design. It is common practice to call opt after each major step in the -synthesis procedure. In cases where too much optimization is not appreciated -(for example when analyzing a design), it is recommended to call {\tt clean} -instead of {\tt opt}. -\item The command {\tt proc} converts {\it processes} (Yosys' internal -representation of Verilog {\tt always}- and {\tt initial}-blocks) to circuits -of multiplexers and storage elements (various types of flip-flops). -\item The command {\tt memory} converts Yosys' internal representations of -arrays and array accesses to multi-port block memories, and then maps this -block memories to address decoders and flip-flops, unless the option {\tt -nomap} -is used, in which case the multi-port block memories stay in the design -and can then be mapped to architecture-specific memory primitives using -other commands. -\item The command {\tt techmap} turns a high-level circuit with coarse grain -cells such as wide adders and multipliers to a fine-grain circuit of simple -logic primitives and single-bit storage elements. The command does that by -substituting the complex cells by circuits of simpler cells. It is possible -to provide a custom set of rules for this process in the form of a Verilog -source file, as we will see in the next section. -\end{itemize} - -Now Yosys can be run with the filename of the synthesis script as argument: - -\begin{figure}[H] -\begin{lstlisting}[language=sh] -yosys softusb_navre.ys -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{Calling Yosys with script file} -\end{figure} - -\medskip - -Now that we are using a synthesis script we can easily modify how Yosys -synthesizes the design. The first thing we should customize is the -call to the {\tt hierarchy} command: - -Whenever it is known that there are no implicit blackboxes in the design, i.e. -modules that are referenced but are not defined, the {\tt hierarchy} command -should be called with the {\tt -check} option. This will then cause synthesis -to fail when implicit blackboxes are found in the design. - -The 2nd thing we can improve regarding the {\tt hierarchy} command is that we -can tell it the name of the top level module of the design hierarchy. It will -then automatically remove all modules that are not referenced from this top -level module. - -\medskip - -For many designs it is also desired to optimize the encodings for the finite -state machines (FSMs) in the design. The {\tt fsm} command finds FSMs, extracts -them, performs some basic optimizations and then generate a circuit from -the extracted and optimized description. It would also be possible to tell -the {\tt fsm} command to leave the FSMs in their extracted form, so they can be -further processed using custom commands. But in this case we don't want that. - -\medskip - -So now we have the final synthesis script for generating a BLIF file -for the Navr\'e CPU: - -\begin{figure}[H] -\begin{lstlisting}[language=sh] -read_verilog softusb_navre.v -hierarchy -check -top softusb_navre -proc; opt; memory; opt; fsm; opt; techmap; opt -write_blif softusb_navre.blif -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{{\tt softusb\_navre.ys} (improved)} -\end{figure} - -\section{Advanced Example: The Amber23 ARMv2a CPU} - -Our 2nd example is the Amber23 \cite{amber} -ARMv2a CPU. Once again we base our example on the Verilog code that is included -in yosys-bigsim \cite{bigsim}. - -\begin{figure}[b!] -\begin{lstlisting}[language=sh] -read_verilog a23_alu.v -read_verilog a23_barrel_shift_fpga.v -read_verilog a23_barrel_shift.v -read_verilog a23_cache.v -read_verilog a23_coprocessor.v -read_verilog a23_core.v -read_verilog a23_decode.v -read_verilog a23_execute.v -read_verilog a23_fetch.v -read_verilog a23_multiply.v -read_verilog a23_ram_register_bank.v -read_verilog a23_register_bank.v -read_verilog a23_wishbone.v -read_verilog generic_sram_byte_en.v -read_verilog generic_sram_line_en.v -hierarchy -check -top a23_core -add -global_input globrst 1 -proc -global_arst globrst -techmap -map adff2dff.v -opt; memory; opt; fsm; opt; techmap -write_blif amber23.blif -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{\tt amber23.ys} -\label{aber23.ys} -\end{figure} - -The problem with this core is that it contains no dedicated reset logic. -Instead the coding techniques shown in Listing~\ref{glob_arst} are used to -define reset values for the global asynchronous reset in an FPGA -implementation. This design can not be expressed in BLIF as it is. Instead we -need to use a synthesis script that transforms this form to synchronous resets that -can be expressed in BLIF. - -(Note that there is no problem if this coding techniques are used to model -ROM, where the register is initialized using this syntax but is never updated -otherwise.) - -\medskip - -Listing~\ref{aber23.ys} shows the synthesis script for the Amber23 core. In -line 17 the {\tt add} command is used to add a 1-bit wide global input signal -with the name {\tt globrst}. That means that an input with that name is added -to each module in the design hierarchy and then all module instantiations are -altered so that this new signal is connected throughout the whole design -hierarchy. - -\begin{figure}[t!] -\begin{lstlisting}[language=Verilog] -reg [7:0] a = 13, b; -initial b = 37; -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{Implicit coding of global asynchronous resets} -\label{glob_arst} -\end{figure} - -\begin{figure}[b!] -\begin{lstlisting}[language=Verilog] -(* techmap_celltype = "$adff" *) -module adff2dff (CLK, ARST, D, Q); - -parameter WIDTH = 1; -parameter CLK_POLARITY = 1; -parameter ARST_POLARITY = 1; -parameter ARST_VALUE = 0; - -input CLK, ARST; -input [WIDTH-1:0] D; -output reg [WIDTH-1:0] Q; - -wire [1023:0] _TECHMAP_DO_ = "proc"; - -wire _TECHMAP_FAIL_ = - !CLK_POLARITY || !ARST_POLARITY; - -always @(posedge CLK) - if (ARST) - Q <= ARST_VALUE; - else - Q <= D; - -endmodule -\end{lstlisting} -\renewcommand{\figurename}{Listing} -\caption{\tt adff2dff.v} -\label{adff2dff.v} -\end{figure} - -In line 18 the {\tt proc} command is called. But in this script the signal name -{\tt globrst} is passed to the command as a global reset signal for resetting -the registers to their assigned initial values. - -Finally in line 19 the {\tt techmap} command is used to replace all instances -of flip-flops with asynchronous resets with flip-flops with synchronous resets. -The map file used for this is shown in Listing~\ref{adff2dff.v}. Note how the -{\tt techmap\_celltype} attribute is used in line 1 to tell the techmap command -which cells to replace in the design, how the {\tt \_TECHMAP\_FAIL\_} wire in -lines 15 and 16 (which evaluates to a constant value) determines if the -parameter set is compatible with this replacement circuit, and how the {\tt -\_TECHMAP\_DO\_} wire in line 13 provides a mini synthesis-script to be used to -process this cell. - -\begin{figure*} -\begin{lstlisting}[language=C] -#include -#include - -#define BITMAP_SIZE 64 -#define OUTPORT 0x10000000 - -static uint32_t bitmap[BITMAP_SIZE/32]; - -static void bitmap_set(uint32_t idx) { bitmap[idx/32] |= 1 << (idx % 32); } -static bool bitmap_get(uint32_t idx) { return (bitmap[idx/32] & (1 << (idx % 32))) != 0; } -static void output(uint32_t val) { *((volatile uint32_t*)OUTPORT) = val; } - -int main() { - uint32_t i, j, k; - output(2); - for (i = 0; i < BITMAP_SIZE; i++) { - if (bitmap_get(i)) continue; - output(3+2*i); - for (j = 2*(3+2*i);; j += 3+2*i) { - if (j%2 == 0) continue; - k = (j-3)/2; - if (k >= BITMAP_SIZE) break; - bitmap_set(k); - } - } - output(0); - return 0; -} -\end{lstlisting} -\renewcommand{\figurename}{Listing} -\caption{Test program for the Amber23 CPU (Sieve of Eratosthenes). Compiled using -GCC 4.6.3 for ARM with {\tt -Os -marm -march=armv2a -mno-thumb-interwork --ffreestanding}, linked with {\tt -{}-fix-v4bx} set and booted with a custom -setup routine written in ARM assembler.} -\label{sieve} -\end{figure*} - -\section{Verification of the Amber23 CPU} - -The BLIF file for the Amber23 core, generated using Listings~\ref{aber23.ys} -and \ref{adff2dff.v} and the version of the Amber23 RTL source that is bundled -with yosys-bigsim, was verified using the test-bench from yosys-bigsim. -It successfully executed the program shown in Listing~\ref{sieve} in the -test-bench. - -For simulation the BLIF file was converted back to Verilog using ABC -\cite{ABC}. So this test includes the successful transformation of the BLIF -file into ABC's internal format as well. - -The only thing left to write about the simulation itself is that it probably -was one of the most energy inefficient and time consuming ways of successfully -calculating the first 31 primes the author has ever conducted. - -\section{Limitations} - -At the time of this writing Yosys does not support multi-dimensional memories, -does not support writing to individual bits of array elements, does not -support initialization of arrays with {\tt \$readmemb} and {\tt \$readmemh}, -and has only limited support for tristate logic, to name just a few -limitations. - -That being said, Yosys can synthesize an overwhelming majority of real-world -Verilog RTL code. The remaining cases can usually be modified to be compatible -with Yosys quite easily. - -The various designs in yosys-bigsim are a good place to look for examples -of what is within the capabilities of Yosys. - -\section{Conclusion} - -Yosys is a feature-rich Verilog-2005 synthesis tool. It has many uses, but -one is to provide an easy gateway from high-level Verilog code to low-level -logic circuits. - -The command line option {\tt -S} can be used to quickly synthesize Verilog -code to BLIF files without a hassle. - -With custom synthesis scripts it becomes possible to easily perform high-level -optimizations, such as re-encoding FSMs. In some extreme cases, such as the -Amber23 ARMv2 CPU, the more advanced Yosys features can be used to change a -design to fit a certain need without actually touching the RTL code. - -\begin{thebibliography}{9} - -\bibitem{yosys} -Clifford Wolf. The Yosys Open SYnthesis Suite. \\ -\url{http://www.clifford.at/yosys/} - -\bibitem{bigsim} -yosys-bigsim, a collection of real-world Verilog designs for regression testing purposes. \\ -\url{https://github.com/cliffordwolf/yosys-bigsim} - -\bibitem{navre} -Sebastien Bourdeauducq. Navr\'e AVR clone (8-bit RISC). \\ -\url{http://opencores.org/project,navre} - -\bibitem{amber} -Conor Santifort. Amber ARM-compatible core. \\ -\url{http://opencores.org/project,amber} - -\bibitem{ABC} -Berkeley Logic Synthesis and Verification Group. ABC: A System for Sequential Synthesis and Verification. \\ -\url{http://www.eecs.berkeley.edu/~alanmi/abc/} - -\bibitem{blif} -Berkeley Logic Interchange Format (BLIF) \\ -\url{http://vlsi.colorado.edu/~vis/blif.ps} - -\end{thebibliography} - - -\end{document} diff --git a/manual/APPNOTE_011_Design_Investigation.tex b/manual/APPNOTE_011_Design_Investigation.tex deleted file mode 100644 index 9780c78336c..00000000000 --- a/manual/APPNOTE_011_Design_Investigation.tex +++ /dev/null @@ -1,1070 +0,0 @@ - -% IEEEtran howto: -% http://ftp.univie.ac.at/packages/tex/macros/latex/contrib/IEEEtran/IEEEtran_HOWTO.pdf -\documentclass[9pt,technote,a4paper]{IEEEtran} - -\usepackage[T1]{fontenc} % required for luximono! -\usepackage[scaled=0.8]{luximono} % typewriter font with bold face - -% To install the luximono font files: -% getnonfreefonts-sys --all or -% getnonfreefonts-sys luximono -% -% when there are trouble you might need to: -% - Create /etc/texmf/updmap.d/99local-luximono.cfg -% containing the single line: Map ul9.map -% - Run update-updmap followed by mktexlsr and updmap-sys -% -% This commands must be executed as root with a root environment -% (i.e. run "sudo su" and then execute the commands in the root -% shell, don't just prefix the commands with "sudo"). - -\usepackage[unicode,bookmarks=false]{hyperref} -\usepackage[english]{babel} -\usepackage[utf8]{inputenc} -\usepackage{amssymb} -\usepackage{amsmath} -\usepackage{amsfonts} -\usepackage{units} -\usepackage{nicefrac} -\usepackage{eurosym} -\usepackage{graphicx} -\usepackage{verbatim} -\usepackage{algpseudocode} -\usepackage{scalefnt} -\usepackage{xspace} -\usepackage{color} -\usepackage{colortbl} -\usepackage{multirow} -\usepackage{hhline} -\usepackage{listings} -\usepackage{float} - -\usepackage{tikz} -\usetikzlibrary{calc} -\usetikzlibrary{arrows} -\usetikzlibrary{scopes} -\usetikzlibrary{through} -\usetikzlibrary{shapes.geometric} - -\def\FIXME{{\color{red}\bf FIXME}} - -\lstset{basicstyle=\ttfamily,frame=trBL,xleftmargin=0.7cm,xrightmargin=0.2cm,numbers=left} - -\begin{document} - -\title{Yosys Application Note 011: \\ Interactive Design Investigation} -\author{Clifford Wolf \\ Original Version December 2013} -\maketitle - -\begin{abstract} -Yosys \cite{yosys} can be a great environment for building custom synthesis -flows. It can also be an excellent tool for teaching and learning Verilog based -RTL synthesis. In both applications it is of great importance to be able to -analyze the designs it produces easily. - -This Yosys application note covers the generation of circuit diagrams with the -Yosys {\tt show} command, the selection of interesting parts of the circuit -using the {\tt select} command, and briefly discusses advanced investigation -commands for evaluating circuits and solving SAT problems. -\end{abstract} - -\section{Installation and Prerequisites} - -This Application Note is based on the Yosys \cite{yosys} GIT Rev. {\tt 2b90ba1} from -2013-12-08. The {\tt README} file covers how to install Yosys. The -{\tt show} command requires a working installation of GraphViz \cite{graphviz} -and \cite{xdot} for generating the actual circuit diagrams. - -\section{Overview} - -This application note is structured as follows: - -Sec.~\ref{intro_show} introduces the {\tt show} command and explains the -symbols used in the circuit diagrams generated by it. - -Sec.~\ref{navigate} introduces additional commands used to navigate in the -design, select portions of the design, and print additional information on -the elements in the design that are not contained in the circuit diagrams. - -Sec.~\ref{poke} introduces commands to evaluate the design and solve SAT -problems within the design. - -Sec.~\ref{conclusion} concludes the document and summarizes the key points. - -\section{Introduction to the {\tt show} command} -\label{intro_show} - -\begin{figure}[b] -\begin{lstlisting} -$ cat example.ys -read_verilog example.v -show -pause -proc -show -pause -opt -show -pause - -$ cat example.v -module example(input clk, a, b, c, - output reg [1:0] y); - always @(posedge clk) - if (c) - y <= c ? a + b : 2'd0; -endmodule -\end{lstlisting} -\caption{Yosys script with {\tt show} commands and example design} -\label{example_src} -\end{figure} - -\begin{figure}[b!] -\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/example_00.pdf} -\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/example_01.pdf} -\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/example_02.pdf} -\caption{Output of the three {\tt show} commands from Fig.~\ref{example_src}} -\label{example_out} -\end{figure} - -The {\tt show} command generates a circuit diagram for the design in its -current state. Various options can be used to change the appearance of the -circuit diagram, set the name and format for the output file, and so forth. -When called without any special options, it saves the circuit diagram in -a temporary file and launches {\tt xdot} to display the diagram. -Subsequent calls to {\tt show} re-use the {\tt xdot} instance -(if still running). - -\subsection{A simple circuit} - -Fig.~\ref{example_src} shows a simple synthesis script and a Verilog file that -demonstrate the usage of {\tt show} in a simple setting. Note that {\tt show} -is called with the {\tt -pause} option, that halts execution of the Yosys -script until the user presses the Enter key. The {\tt show -pause} command -also allows the user to enter an interactive shell to further investigate the -circuit before continuing synthesis. - -So this script, when executed, will show the design after each of the three -synthesis commands. The generated circuit diagrams are shown in Fig.~\ref{example_out}. - -The first diagram (from top to bottom) shows the design directly after being -read by the Verilog front-end. Input and output ports are displayed as -octagonal shapes. Cells are displayed as rectangles with inputs on the left -and outputs on the right side. The cell labels are two lines long: The first line -contains a unique identifier for the cell and the second line contains the cell -type. Internal cell types are prefixed with a dollar sign. The Yosys manual -contains a chapter on the internal cell library used in Yosys. - -Constants are shown as ellipses with the constant value as label. The syntax -{\tt '} is used for for constants that are not 32-bit wide -and/or contain bits that are not 0 or 1 (i.e. {\tt x} or {\tt z}). Ordinary -32-bit constants are written using decimal numbers. - -Single-bit signals are shown as thin arrows pointing from the driver to the -load. Signals that are multiple bits wide are shown as think arrows. - -Finally {\it processes\/} are shown in boxes with round corners. Processes -are Yosys' internal representation of the decision-trees and synchronization -events modelled in a Verilog {\tt always}-block. The label reads {\tt PROC} -followed by a unique identifier in the first line and contains the source code -location of the original {\tt always}-block in the 2nd line. Note how the -multiplexer from the {\tt ?:}-expression is represented as a {\tt \$mux} cell -but the multiplexer from the {\tt if}-statement is yet still hidden within the -process. - -\medskip - -The {\tt proc} command transforms the process from the first diagram into a -multiplexer and a d-type flip-flip, which brings us to the 2nd diagram. - -The Rhombus shape to the right is a dangling wire. (Wire nodes are only shown -if they are dangling or have ``public'' names, for example names assigned from -the Verilog input.) Also note that the design now contains two instances of a -{\tt BUF}-node. This are artefacts left behind by the {\tt proc}-command. It is -quite usual to see such artefacts after calling commands that perform changes -in the design, as most commands only care about doing the transformation in the -least complicated way, not about cleaning up after them. The next call to {\tt -clean} (or {\tt opt}, which includes {\tt clean} as one of its operations) will -clean up this artefacts. This operation is so common in Yosys scripts that it -can simply be abbreviated with the {\tt ;;} token, which doubles as -separator for commands. Unless one wants to specifically analyze this artefacts -left behind some operations, it is therefore recommended to always call {\tt clean} -before calling {\tt show}. - -\medskip - -In this script we directly call {\tt opt} as next step, which finally leads us to -the 3rd diagram in Fig.~\ref{example_out}. Here we see that the {\tt opt} command -not only has removed the artifacts left behind by {\tt proc}, but also determined -correctly that it can remove the first {\tt \$mux} cell without changing the behavior -of the circuit. - -\begin{figure}[b!] -\includegraphics[width=\linewidth,trim=0 2cm 0 0]{APPNOTE_011_Design_Investigation/splice.pdf} -\caption{Output of {\tt yosys -p 'proc; opt; show' splice.v}} -\label{splice_dia} -\end{figure} - -\begin{figure}[b!] -\lstinputlisting{APPNOTE_011_Design_Investigation/splice.v} -\caption{\tt splice.v} -\label{splice_src} -\end{figure} - -\begin{figure}[t!] -\includegraphics[height=\linewidth]{APPNOTE_011_Design_Investigation/cmos_00.pdf} -\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/cmos_01.pdf} -\caption{Effects of {\tt splitnets} command and of providing a cell library. (The -circuit is a half-adder built from simple CMOS gates.)} -\label{splitnets_libfile} -\end{figure} - -\subsection{Break-out boxes for signal vectors} - -As has been indicated by the last example, Yosys is can manage signal vectors (aka. -multi-bit wires or buses) as native objects. This provides great advantages -when analyzing circuits that operate on wide integers. But it also introduces -some additional complexity when the individual bits of of a signal vector -are accessed. The example show in Fig.~\ref{splice_dia} and \ref{splice_src} -demonstrates how such circuits are visualized by the {\tt show} command. - -The key elements in understanding this circuit diagram are of course the boxes -with round corners and rows labeled {\tt : -- :}. -Each of this boxes has one signal per row on one side and a common signal for all rows on the -other side. The {\tt :} tuples specify which bits of the signals are broken out -and connected. So the top row of the box connecting the signals {\tt a} and {\tt x} indicates -that the bit 0 (i.e. the range 0:0) from signal {\tt a} is connected to bit 1 (i.e. the range -1:1) of signal {\tt x}. - -Lines connecting such boxes together and lines connecting such boxes to cell -ports have a slightly different look to emphasise that they are not actual signal -wires but a necessity of the graphical representation. This distinction seems -like a technicality, until one wants to debug a problem related to the way -Yosys internally represents signal vectors, for example when writing custom -Yosys commands. - -\subsection{Gate level netlists} - -Finally Fig.~\ref{splitnets_libfile} shows two common pitfalls when working -with designs mapped to a cell library. The top figure has two problems: First -Yosys did not have access to the cell library when this diagram was generated, -resulting in all cell ports defaulting to being inputs. This is why all ports -are drawn on the left side the cells are awkwardly arranged in a large column. -Secondly the two-bit vector {\tt y} requires breakout-boxes for its individual -bits, resulting in an unnecessary complex diagram. - -For the 2nd diagram Yosys has been given a description of the cell library as -Verilog file containing blackbox modules. There are two ways to load cell -descriptions into Yosys: First the Verilog file for the cell library can be -passed directly to the {\tt show} command using the {\tt -lib } -option. Secondly it is possible to load cell libraries into the design with -the {\tt read\_verilog -lib } command. The 2nd method has the great -advantage that the library only needs to be loaded once and can then be used -in all subsequent calls to the {\tt show} command. - -In addition to that, the 2nd diagram was generated after {\tt splitnet -ports} -was run on the design. This command splits all signal vectors into individual -signal bits, which is often desirable when looking at gate-level circuits. The -{\tt -ports} option is required to also split module ports. Per default the -command only operates on interior signals. - -\subsection{Miscellaneous notes} - -Per default the {\tt show} command outputs a temporary {\tt dot} file and launches -{\tt xdot} to display it. The options {\tt -format}, {\tt -viewer} -and {\tt -prefix} can be used to change format, viewer and filename prefix. -Note that the {\tt pdf} and {\tt ps} format are the only formats that support -plotting multiple modules in one run. - -In densely connected circuits it is sometimes hard to keep track of the -individual signal wires. For this cases it can be useful to call {\tt show} -with the {\tt -colors } argument, which randomly assigns colors to the -nets. The integer (> 0) is used as seed value for the random color -assignments. Sometimes it is necessary it try some values to find an assignment -of colors that looks good. - -The command {\tt help show} prints a complete listing of all options supported -by the {\tt show} command. - -\section{Navigating the design} -\label{navigate} - -Plotting circuit diagrams for entire modules in the design brings us only helps -in simple cases. For complex modules the generated circuit diagrams are just stupidly big -and are no help at all. In such cases one first has to select the relevant -portions of the circuit. - -In addition to {\it what\/} to display one also needs to carefully decide -{\it when\/} to display it, with respect to the synthesis flow. In general -it is a good idea to troubleshoot a circuit in the earliest state in which -a problem can be reproduced. So if, for example, the internal state before calling -the {\tt techmap} command already fails to verify, it is better to troubleshoot -the coarse-grain version of the circuit before {\tt techmap} than the gate-level -circuit after {\tt techmap}. - -\medskip - -Note: It is generally recommended to verify the internal state of a design by -writing it to a Verilog file using {\tt write\_verilog -noexpr} and using the -simulation models from {\tt simlib.v} and {\tt simcells.v} from the Yosys data -directory (as printed by {\tt yosys-config -{}-datdir}). - -\subsection{Interactive Navigation} - -\begin{figure} -\begin{lstlisting} -yosys> ls - -1 modules: - example - -yosys> cd example - -yosys [example]> ls - -7 wires: - $0\y[1:0] - $add$example.v:5$2_Y - a - b - c - clk - y - -3 cells: - $add$example.v:5$2 - $procdff$7 - $procmux$5 -\end{lstlisting} -\caption{Demonstration of {\tt ls} and {\tt cd} using {\tt example.v} from Fig.~\ref{example_src}} -\label{lscd} -\end{figure} - -\begin{figure}[b] -\begin{lstlisting} - attribute \src "example.v:5" - cell $add $add$example.v:5$2 - parameter \A_SIGNED 0 - parameter \A_WIDTH 1 - parameter \B_SIGNED 0 - parameter \B_WIDTH 1 - parameter \Y_WIDTH 2 - connect \A \a - connect \B \b - connect \Y $add$example.v:5$2_Y - end -\end{lstlisting} -\caption{Output of {\tt dump \$2} using the design from Fig.~\ref{example_src} and Fig.~\ref{example_out}} -\label{dump2} -\end{figure} - -Once the right state within the synthesis flow for debugging the circuit has -been identified, it is recommended to simply add the {\tt shell} command -to the matching place in the synthesis script. This command will stop the -synthesis at the specified moment and go to shell mode, where the user can -interactively enter commands. - -For most cases, the shell will start with the whole design selected (i.e. when -the synthesis script does not already narrow the selection). The command {\tt -ls} can now be used to create a list of all modules. The command {\tt cd} can -be used to switch to one of the modules (type {\tt cd ..} to switch back). Now -the {\tt ls} command lists the objects within that module. Fig.~\ref{lscd} -demonstrates this using the design from Fig.~\ref{example_src}. - -There is a thing to note in Fig.~\ref{lscd}: We can see that the cell names -from Fig.~\ref{example_out} are just abbreviations of the actual cell names, -namely the part after the last dollar-sign. Most auto-generated names (the ones -starting with a dollar sign) are rather long and contains some additional -information on the origin of the named object. But in most cases those names -can simply be abbreviated using the last part. - -Usually all interactive work is done with one module selected using the {\tt cd} -command. But it is also possible to work from the design-context ({\tt cd ..}). In -this case all object names must be prefixed with {\tt /}. For -example {\tt a*/b*} would refer to all objects whose names start with {\tt b} from -all modules whose names start with {\tt a}. - -The {\tt dump} command can be used to print all information about an object. -For example {\tt dump \$2} will print Fig.~\ref{dump2}. This can for example -be useful to determine the names of nets connected to cells, as the net-names -are usually suppressed in the circuit diagram if they are auto-generated. - -For the remainder of this document we will assume that the commands are run from -module-context and not design-context. - -\subsection{Working with selections} - -\begin{figure}[t] -\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/example_03.pdf} -\caption{Output of {\tt show} after {\tt select \$2} or {\tt select t:\$add} -(see also Fig.~\ref{example_out})} -\label{seladd} -\end{figure} - -When a module is selected using the {\tt cd} command, all commands (with a few -exceptions, such as the {\tt read\_*} and {\tt write\_*} commands) operate -only on the selected module. This can also be useful for synthesis scripts -where different synthesis strategies should be applied to different modules -in the design. - -But for most interactive work we want to further narrow the set of selected -objects. This can be done using the {\tt select} command. - -For example, if the command {\tt select \$2} is executed, a subsequent {\tt show} -command will yield the diagram shown in Fig.~\ref{seladd}. Note that the nets are -now displayed in ellipses. This indicates that they are not selected, but only -shown because the diagram contains a cell that is connected to the net. This -of course makes no difference for the circuit that is shown, but it can be a useful -information when manipulating selections. - -Objects can not only be selected by their name but also by other properties. -For example {\tt select t:\$add} will select all cells of type {\tt \$add}. In -this case this is also yields the diagram shown in Fig.~\ref{seladd}. - -\begin{figure}[b] -\lstinputlisting{APPNOTE_011_Design_Investigation/foobaraddsub.v} -\caption{Test module for operations on selections} -\label{foobaraddsub} -\end{figure} - -The output of {\tt help select} contains a complete syntax reference for -matching different properties. - -Many commands can operate on explicit selections. For example the command {\tt -dump t:\$add} will print information on all {\tt \$add} cells in the active -module. Whenever a command has {\tt [selection]} as last argument in its usage -help, this means that it will use the engine behind the {\tt select} command -to evaluate additional arguments and use the resulting selection instead of -the selection created by the last {\tt select} command. - -Normally the {\tt select} command overwrites a previous selection. The -commands {\tt select -add} and {\tt select -del} can be used to add -or remove objects from the current selection. - -The command {\tt select -clear} can be used to reset the selection to the -default, which is a complete selection of everything in the current module. - -\subsection{Operations on selections} - -\begin{figure}[t] -\lstinputlisting{APPNOTE_011_Design_Investigation/sumprod.v} -\caption{Another test module for operations on selections} -\label{sumprod} -\end{figure} - -\begin{figure}[b] -\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/sumprod_00.pdf} -\caption{Output of {\tt show a:sumstuff} on Fig.~\ref{sumprod}} -\label{sumprod_00} -\end{figure} - -The {\tt select} command is actually much more powerful than it might seem on -the first glimpse. When it is called with multiple arguments, each argument is -evaluated and pushed separately on a stack. After all arguments have been -processed it simply creates the union of all elements on the stack. So the -following command will select all {\tt \$add} cells and all objects with -the {\tt foo} attribute set: - -\begin{verbatim} -select t:$add a:foo -\end{verbatim} - -(Try this with the design shown in Fig.~\ref{foobaraddsub}. Use the {\tt -select -list} command to list the current selection.) - -In many cases simply adding more and more stuff to the selection is an -ineffective way of selecting the interesting part of the design. Special -arguments can be used to combine the elements on the stack. -For example the {\tt \%i} arguments pops the last two elements from -the stack, intersects them, and pushes the result back on the stack. So the -following command will select all {\$add} cells that have the {\tt foo} -attribute set: - -\begin{verbatim} -select t:$add a:foo %i -\end{verbatim} - -The listing in Fig.~\ref{sumprod} uses the Yosys non-standard {\tt \{* ... *\}} -syntax to set the attribute {\tt sumstuff} on all cells generated by the first -assign statement. (This works on arbitrary large blocks of Verilog code an -can be used to mark portions of code for analysis.) - -Selecting {\tt a:sumstuff} in this module will yield the circuit diagram shown -in Fig.~\ref{sumprod_00}. As only the cells themselves are selected, but not -the temporary wire {\tt \$1\_Y}, the two adders are shown as two disjunct -parts. This can be very useful for global signals like clock and reset signals: just -unselect them using a command such as {\tt select -del clk rst} and each cell -using them will get its own net label. - -In this case however we would like to see the cells connected properly. This -can be achieved using the {\tt \%x} action, that broadens the selection, i.e. -for each selected wire it selects all cells connected to the wire and vice -versa. So {\tt show a:sumstuff \%x} yields the diagram shown in Fig.~\ref{sumprod_01}. - -\begin{figure}[t] -\includegraphics[width=\linewidth]{APPNOTE_011_Design_Investigation/sumprod_01.pdf} -\caption{Output of {\tt show a:sumstuff \%x} on Fig.~\ref{sumprod}} -\label{sumprod_01} -\end{figure} - -\subsection{Selecting logic cones} - -Fig.~\ref{sumprod_01} shows what is called the {\it input cone\/} of {\tt sum}, i.e. -all cells and signals that are used to generate the signal {\tt sum}. The {\tt \%ci} -action can be used to select the input cones of all object in the top selection -in the stack maintained by the {\tt select} command. - -As the {\tt \%x} action, this commands broadens the selection by one ``step''. But -this time the operation only works against the direction of data flow. That means, -wires only select cells via output ports and cells only select wires via input ports. - -Fig.~\ref{select_prod} show the sequence of diagrams generated by the following -commands: - -\begin{verbatim} -show prod -show prod %ci -show prod %ci %ci -show prod %ci %ci %ci -\end{verbatim} - -When selecting many levels of logic, repeating {\tt \%ci} over and over again -can be a bit dull. So there is a shortcut for that: the number of iterations -can be appended to the action. So for example the action {\tt \%ci3} is -identical to performing the {\tt \%ci} action three times. - -The action {\tt \%ci*} performs the {\tt \%ci} action over and over again until -it has no effect anymore. - -\begin{figure}[t] -\hfill \includegraphics[width=4cm,trim=0 1cm 0 1cm]{APPNOTE_011_Design_Investigation/sumprod_02.pdf} \\ -\includegraphics[width=\linewidth,trim=0 0cm 0 1cm]{APPNOTE_011_Design_Investigation/sumprod_03.pdf} \\ -\includegraphics[width=\linewidth,trim=0 0cm 0 1cm]{APPNOTE_011_Design_Investigation/sumprod_04.pdf} \\ -\includegraphics[width=\linewidth,trim=0 2cm 0 1cm]{APPNOTE_011_Design_Investigation/sumprod_05.pdf} \\ -\caption{Objects selected by {\tt select prod \%ci...}} -\label{select_prod} -\end{figure} - -\medskip - -In most cases there are certain cell types and/or ports that should not be considered for the {\tt \%ci} -action, or we only want to follow certain cell types and/or ports. This can be achieved using additional -patterns that can be appended to the {\tt \%ci} action. - -Lets consider the design from Fig.~\ref{memdemo_src}. It serves no purpose other than being a non-trivial -circuit for demonstrating some of the advanced Yosys features. We synthesize the circuit using {\tt proc; -opt; memory; opt} and change to the {\tt memdemo} module with {\tt cd memdemo}. If we type {\tt show} -now we see the diagram shown in Fig.~\ref{memdemo_00}. - -\begin{figure}[b!] -\lstinputlisting{APPNOTE_011_Design_Investigation/memdemo.v} -\caption{Demo circuit for demonstrating some advanced Yosys features} -\label{memdemo_src} -\end{figure} - -\begin{figure*}[t] -\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{APPNOTE_011_Design_Investigation/memdemo_00.pdf} \\ -\caption{Complete circuit diagram for the design shown in Fig.~\ref{memdemo_src}} -\label{memdemo_00} -\end{figure*} - -But maybe we are only interested in the tree of multiplexers that select the -output value. In order to get there, we would start by just showing the output signal -and its immediate predecessors: - -\begin{verbatim} -show y %ci2 -\end{verbatim} - -From this we would learn that {\tt y} is driven by a {\tt \$dff cell}, that -{\tt y} is connected to the output port {\tt Q}, that the {\tt clk} signal goes -into the {\tt CLK} input port of the cell, and that the data comes from a -auto-generated wire into the input {\tt D} of the flip-flop cell. - -As we are not interested in the clock signal we add an additional pattern to the {\tt \%ci} -action, that tells it to only follow ports {\tt Q} and {\tt D} of {\tt \$dff} cells: - -\begin{verbatim} -show y %ci2:+$dff[Q,D] -\end{verbatim} - -To add a pattern we add a colon followed by the pattern to the {\tt \%ci} -action. The pattern it self starts with {\tt -} or {\tt +}, indicating if it is -an include or exclude pattern, followed by an optional comma separated list -of cell types, followed by an optional comma separated list of port names in -square brackets. - -Since we know that the only cell considered in this case is a {\tt \$dff} cell, -we could as well only specify the port names: - -\begin{verbatim} -show y %ci2:+[Q,D] -\end{verbatim} - -Or we could decide to tell the {\tt \%ci} action to not follow the {\tt CLK} input: - -\begin{verbatim} -show y %ci2:-[CLK] -\end{verbatim} - -\begin{figure}[b] -\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{APPNOTE_011_Design_Investigation/memdemo_01.pdf} \\ -\caption{Output of {\tt show y \%ci2:+\$dff[Q,D] \%ci*:-\$mux[S]:-\$dff}} -\label{memdemo_01} -\end{figure} - -Next we would investigate the next logic level by adding another {\tt \%ci2} to -the command: - -\begin{verbatim} -show y %ci2:-[CLK] %ci2 -\end{verbatim} - -From this we would learn that the next cell is a {\tt \$mux} cell and we would add additional -pattern to narrow the selection on the path we are interested. In the end we would end up -with a command such as - -\begin{verbatim} -show y %ci2:+$dff[Q,D] %ci*:-$mux[S]:-$dff -\end{verbatim} - -in which the first {\tt \%ci} jumps over the initial d-type flip-flop and the -2nd action selects the entire input cone without going over multiplexer select -inputs and flip-flop cells. The diagram produces by this command is shown in -Fig.~\ref{memdemo_01}. - -\medskip - -Similar to {\tt \%ci} exists an action {\tt \%co} to select output cones that -accepts the same syntax for pattern and repetition. The {\tt \%x} action mentioned -previously also accepts this advanced syntax. - -This actions for traversing the circuit graph, combined with the actions for -boolean operations such as intersection ({\tt \%i}) and difference ({\tt \%d}) -are powerful tools for extracting the relevant portions of the circuit under -investigation. - -See {\tt help select} for a complete list of actions available in selections. - -\subsection{Storing and recalling selections} - -The current selection can be stored in memory with the command {\tt select -set -}. It can later be recalled using {\tt select @}. In fact, the {\tt -@} expression pushes the stored selection on the stack maintained by the -{\tt select} command. So for example - -\begin{verbatim} -select @foo @bar %i -\end{verbatim} - -will select the intersection between the stored selections {\tt foo} and {\tt bar}. - -\medskip - -In larger investigation efforts it is highly recommended to maintain a script that -sets up relevant selections, so they can easily be recalled, for example when -Yosys needs to be re-run after a design or source code change. - -The {\tt history} command can be used to list all recent interactive commands. -This feature can be useful for creating such a script from the commands used in -an interactive session. - -\section{Advanced investigation techniques} -\label{poke} - -When working with very large modules, it is often not enough to just select the -interesting part of the module. Instead it can be useful to extract the -interesting part of the circuit into a separate module. This can for example be -useful if one wants to run a series of synthesis commands on the critical part -of the module and wants to carefully read all the debug output created by the -commands in order to spot a problem. This kind of troubleshooting is much easier -if the circuit under investigation is encapsulated in a separate module. - -Fig.~\ref{submod} shows how the {\tt submod} command can be used to split the -circuit from Fig.~\ref{memdemo_src} and \ref{memdemo_00} into its components. -The {\tt -name} option is used to specify the name of the new module and -also the name of the new cell in the current module. - -\begin{figure}[t] -\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{APPNOTE_011_Design_Investigation/submod_00.pdf} \\ \centerline{\tt memdemo} \vskip1em\par -\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{APPNOTE_011_Design_Investigation/submod_01.pdf} \\ \centerline{\tt scramble} \vskip1em\par -\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{APPNOTE_011_Design_Investigation/submod_02.pdf} \\ \centerline{\tt outstage} \vskip1em\par -\includegraphics[width=\linewidth,trim=0 1.3cm 0 0cm]{APPNOTE_011_Design_Investigation/submod_03.pdf} \\ \centerline{\tt selstage} \vskip1em\par -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize] -select -set outstage y %ci2:+$dff[Q,D] %ci*:-$mux[S]:-$dff -select -set selstage y %ci2:+$dff[Q,D] %ci*:-$dff @outstage %d -select -set scramble mem* %ci2 %ci*:-$dff mem* %d @selstage %d -submod -name scramble @scramble -submod -name outstage @outstage -submod -name selstage @selstage -\end{lstlisting} -\caption{The circuit from Fig.~\ref{memdemo_src} and \ref{memdemo_00} broken up using {\tt submod}} -\label{submod} -\end{figure} - -\subsection{Evaluation of combinatorial circuits} - -The {\tt eval} command can be used to evaluate combinatorial circuits. -For example (see Fig.~\ref{submod} for the circuit diagram of {\tt selstage}): - -{\scriptsize -\begin{verbatim} - yosys [selstage]> eval -set s2,s1 4'b1001 -set d 4'hc -show n2 -show n1 - - 9. Executing EVAL pass (evaluate the circuit given an input). - Full command line: eval -set s2,s1 4'b1001 -set d 4'hc -show n2 -show n1 - Eval result: \n2 = 2'10. - Eval result: \n1 = 2'10. -\end{verbatim} -\par} - -So the {\tt -set} option is used to set input values and the {\tt -show} option -is used to specify the nets to evaluate. If no {\tt -show} option is specified, -all selected output ports are used per default. - -If a necessary input value is not given, an error is produced. The option -{\tt -set-undef} can be used to instead set all unspecified input nets to -undef ({\tt x}). - -The {\tt -table} option can be used to create a truth table. For example: - -{\scriptsize -\begin{verbatim} - yosys [selstage]> eval -set-undef -set d[3:1] 0 -table s1,d[0] - - 10. Executing EVAL pass (evaluate the circuit given an input). - Full command line: eval -set-undef -set d[3:1] 0 -table s1,d[0] - - \s1 \d [0] | \n1 \n2 - ---- ------ | ---- ---- - 2'00 1'0 | 2'00 2'00 - 2'00 1'1 | 2'xx 2'00 - 2'01 1'0 | 2'00 2'00 - 2'01 1'1 | 2'xx 2'01 - 2'10 1'0 | 2'00 2'00 - 2'10 1'1 | 2'xx 2'10 - 2'11 1'0 | 2'00 2'00 - 2'11 1'1 | 2'xx 2'11 - - Assumed undef (x) value for the following signals: \s2 -\end{verbatim} -} - -Note that the {\tt eval} command (as well as the {\tt sat} command discussed in -the next sections) does only operate on flattened modules. It can not analyze -signals that are passed through design hierarchy levels. So the {\tt flatten} -command must be used on modules that instantiate other modules before this -commands can be applied. - -\subsection{Solving combinatorial SAT problems} - -\begin{figure}[b] -\lstinputlisting{APPNOTE_011_Design_Investigation/primetest.v} -\caption{A simple miter circuit for testing if a number is prime. But it has a -problem (see main text and Fig.~\ref{primesat}).} -\label{primetest} -\end{figure} - -\begin{figure*}[!t] -\begin{lstlisting}[basicstyle=\ttfamily\small] -yosys [primetest]> sat -prove ok 1 -set p 31 - -8. Executing SAT pass (solving SAT problems in the circuit). -Full command line: sat -prove ok 1 -set p 31 - -Setting up SAT problem: -Import set-constraint: \p = 16'0000000000011111 -Final constraint equation: \p = 16'0000000000011111 -Imported 6 cells to SAT database. -Import proof-constraint: \ok = 1'1 -Final proof equation: \ok = 1'1 - -Solving problem with 2790 variables and 8241 clauses.. -SAT proof finished - model found: FAIL! - - ______ ___ ___ _ _ _ _ - (_____ \ / __) / __) (_) | | | | - _____) )___ ___ ___ _| |__ _| |__ _____ _| | _____ __| | | - | ____/ ___) _ \ / _ (_ __) (_ __|____ | | || ___ |/ _ |_| - | | | | | |_| | |_| || | | | / ___ | | || ____( (_| |_ - |_| |_| \___/ \___/ |_| |_| \_____|_|\_)_____)\____|_| - - - Signal Name Dec Hex Bin - -------------------- ---------- ---------- --------------------- - \a 15029 3ab5 0011101010110101 - \b 4099 1003 0001000000000011 - \ok 0 0 0 - \p 31 1f 0000000000011111 - -yosys [primetest]> sat -prove ok 1 -set p 31 -set a[15:8],b[15:8] 0 - -9. Executing SAT pass (solving SAT problems in the circuit). -Full command line: sat -prove ok 1 -set p 31 -set a[15:8],b[15:8] 0 - -Setting up SAT problem: -Import set-constraint: \p = 16'0000000000011111 -Import set-constraint: { \a [15:8] \b [15:8] } = 16'0000000000000000 -Final constraint equation: { \a [15:8] \b [15:8] \p } = { 16'0000000000000000 16'0000000000011111 } -Imported 6 cells to SAT database. -Import proof-constraint: \ok = 1'1 -Final proof equation: \ok = 1'1 - -Solving problem with 2790 variables and 8257 clauses.. -SAT proof finished - no model found: SUCCESS! - - /$$$$$$ /$$$$$$$$ /$$$$$$$ - /$$__ $$ | $$_____/ | $$__ $$ - | $$ \ $$ | $$ | $$ \ $$ - | $$ | $$ | $$$$$ | $$ | $$ - | $$ | $$ | $$__/ | $$ | $$ - | $$/$$ $$ | $$ | $$ | $$ - | $$$$$$/ /$$| $$$$$$$$ /$$| $$$$$$$//$$ - \____ $$$|__/|________/|__/|_______/|__/ - \__/ -\end{lstlisting} -\caption{Experiments with the miter circuit from Fig.~\ref{primetest}. The first attempt of proving that 31 -is prime failed because the SAT solver found a creative way of factorizing 31 using integer overflow.} -\label{primesat} -\end{figure*} - -Often the opposite of the {\tt eval} command is needed, i.e. the circuits -output is given and we want to find the matching input signals. For small -circuits with only a few input bits this can be accomplished by trying all -possible input combinations, as it is done by the {\tt eval -table} command. -For larger circuits however, Yosys provides the {\tt sat} command that uses -a SAT \cite{CircuitSAT} solver \cite{MiniSAT} to solve this kind of problems. - -The {\tt sat} command works very similar to the {\tt eval} command. The main -difference is that it is now also possible to set output values and find the -corresponding input values. For Example: - -{\scriptsize -\begin{verbatim} - yosys [selstage]> sat -show s1,s2,d -set s1 s2 -set n2,n1 4'b1001 - - 11. Executing SAT pass (solving SAT problems in the circuit). - Full command line: sat -show s1,s2,d -set s1 s2 -set n2,n1 4'b1001 - - Setting up SAT problem: - Import set-constraint: \s1 = \s2 - Import set-constraint: { \n2 \n1 } = 4'1001 - Final constraint equation: { \n2 \n1 \s1 } = { 4'1001 \s2 } - Imported 3 cells to SAT database. - Import show expression: { \s1 \s2 \d } - - Solving problem with 81 variables and 207 clauses.. - SAT solving finished - model found: - - Signal Name Dec Hex Bin - -------------------- ---------- ---------- --------------- - \d 9 9 1001 - \s1 0 0 00 - \s2 0 0 00 -\end{verbatim} -} - -Note that the {\tt sat} command supports signal names in both arguments -to the {\tt -set} option. In the above example we used {\tt -set s1 s2} -to constraint {\tt s1} and {\tt s2} to be equal. When more complex -constraints are needed, a wrapper circuit must be constructed that -checks the constraints and signals if the constraint was met using an -extra output port, which then can be forced to a value using the {\tt --set} option. (Such a circuit that contains the circuit under test -plus additional constraint checking circuitry is called a {\it miter\/} -circuit.) - -Fig.~\ref{primetest} shows a miter circuit that is supposed to be used as a -prime number test. If {\tt ok} is 1 for all input values {\tt a} and {\tt b} -for a given {\tt p}, then {\tt p} is prime, or at least that is the idea. - -The Yosys shell session shown in Fig.~\ref{primesat} demonstrates that SAT -solvers can even find the unexpected solutions to a problem: Using integer -overflow there actually is a way of ``factorizing'' 31. The clean solution -would of course be to perform the test in 32 bits, for example by replacing -{\tt p != a*b} in the miter with {\tt p != \{16'd0,a\}*b}, or by using a -temporary variable for the 32 bit product {\tt a*b}. But as 31 fits well into -8 bits (and as the purpose of this document is to show off Yosys features) -we can also simply force the upper 8 bits of {\tt a} and {\tt b} to zero for -the {\tt sat} call, as is done in the second command in Fig.~\ref{primesat} -(line 31). - -The {\tt -prove} option used in this example works similar to {\tt -set}, but -tries to find a case in which the two arguments are not equal. If such a case is -not found, the property is proven to hold for all inputs that satisfy the other -constraints. - -It might be worth noting, that SAT solvers are not particularly efficient at -factorizing large numbers. But if a small factorization problem occurs as -part of a larger circuit problem, the Yosys SAT solver is perfectly capable -of solving it. - -\subsection{Solving sequential SAT problems} - -\begin{figure}[t!] -\begin{lstlisting}[basicstyle=\ttfamily\scriptsize] -yosys [memdemo]> sat -seq 6 -show y -show d -set-init-undef \ - -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3 - -6. Executing SAT pass (solving SAT problems in the circuit). -Full command line: sat -seq 6 -show y -show d -set-init-undef - -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3 - -Setting up time step 1: -Final constraint equation: { } = { } -Imported 29 cells to SAT database. - -Setting up time step 2: -Final constraint equation: { } = { } -Imported 29 cells to SAT database. - -Setting up time step 3: -Final constraint equation: { } = { } -Imported 29 cells to SAT database. - -Setting up time step 4: -Import set-constraint for timestep: \y = 4'0001 -Final constraint equation: \y = 4'0001 -Imported 29 cells to SAT database. - -Setting up time step 5: -Import set-constraint for timestep: \y = 4'0010 -Final constraint equation: \y = 4'0010 -Imported 29 cells to SAT database. - -Setting up time step 6: -Import set-constraint for timestep: \y = 4'0011 -Final constraint equation: \y = 4'0011 -Imported 29 cells to SAT database. - -Setting up initial state: -Final constraint equation: { \y \s2 \s1 \mem[3] \mem[2] \mem[1] - \mem[0] } = 24'xxxxxxxxxxxxxxxxxxxxxxxx - -Import show expression: \y -Import show expression: \d - -Solving problem with 10322 variables and 27881 clauses.. -SAT model found. maximizing number of undefs. -SAT solving finished - model found: - - Time Signal Name Dec Hex Bin - ---- -------------------- ---------- ---------- --------------- - init \mem[0] -- -- xxxx - init \mem[1] -- -- xxxx - init \mem[2] -- -- xxxx - init \mem[3] -- -- xxxx - init \s1 -- -- xx - init \s2 -- -- xx - init \y -- -- xxxx - ---- -------------------- ---------- ---------- --------------- - 1 \d 0 0 0000 - 1 \y -- -- xxxx - ---- -------------------- ---------- ---------- --------------- - 2 \d 1 1 0001 - 2 \y -- -- xxxx - ---- -------------------- ---------- ---------- --------------- - 3 \d 2 2 0010 - 3 \y 0 0 0000 - ---- -------------------- ---------- ---------- --------------- - 4 \d 3 3 0011 - 4 \y 1 1 0001 - ---- -------------------- ---------- ---------- --------------- - 5 \d -- -- 001x - 5 \y 2 2 0010 - ---- -------------------- ---------- ---------- --------------- - 6 \d -- -- xxxx - 6 \y 3 3 0011 -\end{lstlisting} -\caption{Solving a sequential SAT problem in the {\tt memdemo} module from Fig.~\ref{memdemo_src}.} -\label{memdemo_sat} -\end{figure} - -The SAT solver functionality in Yosys can not only be used to solve -combinatorial problems, but can also solve sequential problems. Let's consider -the entire {\tt memdemo} module from Fig.~\ref{memdemo_src} and suppose we -want to know which sequence of input values for {\tt d} will cause the output -{\tt y} to produce the sequence 1, 2, 3 from any initial state. -Fig.~\ref{memdemo_sat} show the solution to this question, as produced by -the following command: - -\begin{verbatim} - sat -seq 6 -show y -show d -set-init-undef \ - -max_undef -set-at 4 y 1 -set-at 5 y 2 -set-at 6 y 3 -\end{verbatim} - -The {\tt -seq 6} option instructs the {\tt sat} command to solve a sequential -problem in 6 time steps. (Experiments with lower number of steps have show that -at least 3 cycles are necessary to bring the circuit in a state from which -the sequence 1, 2, 3 can be produced.) - -The {\tt -set-init-undef} option tells the {\tt sat} command to initialize -all registers to the undef ({\tt x}) state. The way the {\tt x} state -is treated in Verilog will ensure that the solution will work for any -initial state. - -The {\tt -max\_undef} option instructs the {\tt sat} command to find a solution -with a maximum number of undefs. This way we can see clearly which inputs bits -are relevant to the solution. - -Finally the three {\tt -set-at} options add constraints for the {\tt y} -signal to play the 1, 2, 3 sequence, starting with time step 4. - -It is not surprising that the solution sets {\tt d = 0} in the first step, as -this is the only way of setting the {\tt s1} and {\tt s2} registers to a known -value. The input values for the other steps are a bit harder to work out -manually, but the SAT solver finds the correct solution in an instant. - -\medskip - -There is much more to write about the {\tt sat} command. For example, there is -a set of options that can be used to performs sequential proofs using temporal -induction \cite{tip}. The command {\tt help sat} can be used to print a list -of all options with short descriptions of their functions. - -\section{Conclusion} -\label{conclusion} - -Yosys provides a wide range of functions to analyze and investigate designs. For -many cases it is sufficient to simply display circuit diagrams, maybe use some -additional commands to narrow the scope of the circuit diagrams to the interesting -parts of the circuit. But some cases require more than that. For this applications -Yosys provides commands that can be used to further inspect the behavior of the -circuit, either by evaluating which output values are generated from certain input values -({\tt eval}) or by evaluation which input values and initial conditions can result -in a certain behavior at the outputs ({\tt sat}). The SAT command can even be used -to prove (or disprove) theorems regarding the circuit, in more advanced cases -with the additional help of a miter circuit. - -This features can be powerful tools for the circuit designer using Yosys as a -utility for building circuits and the software developer using Yosys as a -framework for new algorithms alike. - -\begin{thebibliography}{9} - -\bibitem{yosys} -Clifford Wolf. The Yosys Open SYnthesis Suite. -\url{http://www.clifford.at/yosys/} - -\bibitem{graphviz} -Graphviz - Graph Visualization Software. -\url{http://www.graphviz.org/} - -\bibitem{xdot} -xdot.py - an interactive viewer for graphs written in Graphviz's dot language. -\url{https://github.com/jrfonseca/xdot.py} - -\bibitem{CircuitSAT} -{\it Circuit satisfiability problem} on Wikipedia -\url{http://en.wikipedia.org/wiki/Circuit_satisfiability} - -\bibitem{MiniSAT} -MiniSat: a minimalistic open-source SAT solver. -\url{http://minisat.se/} - -\bibitem{tip} -Niklas Een and Niklas S\"orensson (2003). -Temporal Induction by Incremental SAT Solving. -\url{http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.8161} - -\end{thebibliography} - -\end{document} diff --git a/manual/APPNOTE_011_Design_Investigation/cmos_00.dot b/manual/APPNOTE_011_Design_Investigation/cmos_00.dot deleted file mode 100644 index 49c63008081..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/cmos_00.dot +++ /dev/null @@ -1,34 +0,0 @@ -digraph "cmos_demo" { -rankdir="LR"; -remincross=true; -n4 [ shape=octagon, label="a", color="black", fontcolor="black" ]; -n5 [ shape=octagon, label="b", color="black", fontcolor="black" ]; -n6 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -c10 [ shape=record, label="{{ A| B| Y}|$g0\nNOR|{}}" ]; -c11 [ shape=record, label="{{ A| Y}|$g1\nNOT|{}}" ]; -c12 [ shape=record, label="{{ A| Y}|$g2\nNOT|{}}" ]; -c13 [ shape=record, label="{{ A| B| Y}|$g3\nNOR|{}}" ]; -x0 [ shape=record, style=rounded, label=" 1:1 - 0:0 " ]; -x0:e -> c13:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -c14 [ shape=record, label="{{ A| B| Y}|$g4\nNOR|{}}" ]; -x1 [ shape=record, style=rounded, label=" 1:1 - 0:0 " ]; -x1:e -> c14:p8:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -x2 [ shape=record, style=rounded, label=" 0:0 - 0:0 " ]; -x2:e -> c14:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -n1 [ shape=diamond, label="$n4" ]; -n1:e -> c10:p9:w [color="black", label=""]; -n1:e -> c14:p7:w [color="black", label=""]; -n2 [ shape=diamond, label="$n5" ]; -n2:e -> c11:p9:w [color="black", label=""]; -n2:e -> c13:p7:w [color="black", label=""]; -n3 [ shape=diamond, label="$n6_1" ]; -n3:e -> c12:p9:w [color="black", label=""]; -n3:e -> c13:p8:w [color="black", label=""]; -n4:e -> c10:p8:w [color="black", label=""]; -n4:e -> c12:p7:w [color="black", label=""]; -n5:e -> c10:p7:w [color="black", label=""]; -n5:e -> c11:p7:w [color="black", label=""]; -n6:e -> x0:s0:w [color="black", label=""]; -n6:e -> x1:s0:w [color="black", label=""]; -n6:e -> x2:s0:w [color="black", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/cmos_01.dot b/manual/APPNOTE_011_Design_Investigation/cmos_01.dot deleted file mode 100644 index ea6f4403ca8..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/cmos_01.dot +++ /dev/null @@ -1,23 +0,0 @@ -digraph "cmos_demo" { -rankdir="LR"; -remincross=true; -n4 [ shape=octagon, label="a", color="black", fontcolor="black" ]; -n5 [ shape=octagon, label="b", color="black", fontcolor="black" ]; -n6 [ shape=octagon, label="y[0]", color="black", fontcolor="black" ]; -n7 [ shape=octagon, label="y[1]", color="black", fontcolor="black" ]; -c11 [ shape=record, label="{{ A| B}|$g0\nNOR|{ Y}}" ]; -c12 [ shape=record, label="{{ A}|$g1\nNOT|{ Y}}" ]; -c13 [ shape=record, label="{{ A}|$g2\nNOT|{ Y}}" ]; -c14 [ shape=record, label="{{ A| B}|$g3\nNOR|{ Y}}" ]; -c15 [ shape=record, label="{{ A| B}|$g4\nNOR|{ Y}}" ]; -c11:p10:e -> c15:p8:w [color="black", label=""]; -c12:p10:e -> c14:p8:w [color="black", label=""]; -c13:p10:e -> c14:p9:w [color="black", label=""]; -n4:e -> c11:p9:w [color="black", label=""]; -n4:e -> c13:p8:w [color="black", label=""]; -n5:e -> c11:p8:w [color="black", label=""]; -n5:e -> c12:p8:w [color="black", label=""]; -c15:p10:e -> n6:w [color="black", label=""]; -c14:p10:e -> n7:w [color="black", label=""]; -n7:e -> c15:p9:w [color="black", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/example.ys b/manual/APPNOTE_011_Design_Investigation/example.ys deleted file mode 100644 index b1e956088cc..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/example.ys +++ /dev/null @@ -1,11 +0,0 @@ -read_verilog example.v -show -format dot -prefix example_00 -proc -show -format dot -prefix example_01 -opt -show -format dot -prefix example_02 - -cd example -select t:$add -show -format dot -prefix example_03 - diff --git a/manual/APPNOTE_011_Design_Investigation/example_00.dot b/manual/APPNOTE_011_Design_Investigation/example_00.dot deleted file mode 100644 index 1e23ed0ead3..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/example_00.dot +++ /dev/null @@ -1,23 +0,0 @@ -digraph "example" { -rankdir="LR"; -remincross=true; -n4 [ shape=octagon, label="a", color="black", fontcolor="black" ]; -n5 [ shape=octagon, label="b", color="black", fontcolor="black" ]; -n6 [ shape=octagon, label="c", color="black", fontcolor="black" ]; -n7 [ shape=octagon, label="clk", color="black", fontcolor="black" ]; -n8 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -c12 [ shape=record, label="{{ A| B}|$2\n$add|{ Y}}" ]; -v0 [ label="2'00" ]; -c14 [ shape=record, label="{{ A| B| S}|$3\n$mux|{ Y}}" ]; -p1 [shape=box, style=rounded, label="PROC $1\nexample.v:3"]; -c12:p11:e -> c14:p10:w [color="black", style="setlinewidth(3)", label=""]; -c14:p11:e -> p1:w [color="black", style="setlinewidth(3)", label=""]; -n4:e -> c12:p9:w [color="black", label=""]; -n5:e -> c12:p10:w [color="black", label=""]; -n6:e -> c14:p13:w [color="black", label=""]; -n6:e -> p1:w [color="black", label=""]; -n7:e -> p1:w [color="black", label=""]; -p1:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; -n8:e -> p1:w [color="black", style="setlinewidth(3)", label=""]; -v0:e -> c14:p9:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/example_01.dot b/manual/APPNOTE_011_Design_Investigation/example_01.dot deleted file mode 100644 index e89292b51ac..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/example_01.dot +++ /dev/null @@ -1,33 +0,0 @@ -digraph "example" { -rankdir="LR"; -remincross=true; -n6 [ shape=octagon, label="a", color="black", fontcolor="black" ]; -n7 [ shape=octagon, label="b", color="black", fontcolor="black" ]; -n8 [ shape=octagon, label="c", color="black", fontcolor="black" ]; -n9 [ shape=octagon, label="clk", color="black", fontcolor="black" ]; -n10 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -c14 [ shape=record, label="{{ A| B}|$2\n$add|{ Y}}" ]; -c18 [ shape=record, label="{{ CLK| D}|$7\n$dff|{ Q}}" ]; -c20 [ shape=record, label="{{ A| B| S}|$5\n$mux|{ Y}}" ]; -v0 [ label="2'00" ]; -c21 [ shape=record, label="{{ A| B| S}|$3\n$mux|{ Y}}" ]; -x1 [shape=box, style=rounded, label="BUF"]; -x2 [shape=box, style=rounded, label="BUF"]; -n1 [ shape=diamond, label="$0\\y[1:0]" ]; -x2:e:e -> n1:w [color="black", style="setlinewidth(3)", label=""]; -c18:p17:e -> n10:w [color="black", style="setlinewidth(3)", label=""]; -n10:e -> c20:p11:w [color="black", style="setlinewidth(3)", label=""]; -c14:p13:e -> c21:p12:w [color="black", style="setlinewidth(3)", label=""]; -n3 [ shape=point ]; -c20:p13:e -> n3:w [color="black", style="setlinewidth(3)", label=""]; -n3:e -> c18:p16:w [color="black", style="setlinewidth(3)", label=""]; -n3:e -> x2:w:w [color="black", style="setlinewidth(3)", label=""]; -x1:e:e -> c20:p19:w [color="black", label=""]; -c21:p13:e -> c20:p12:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> c14:p11:w [color="black", label=""]; -n7:e -> c14:p12:w [color="black", label=""]; -n8:e -> c21:p19:w [color="black", label=""]; -n8:e -> x1:w:w [color="black", label=""]; -n9:e -> c18:p15:w [color="black", label=""]; -v0:e -> c21:p11:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/example_02.dot b/manual/APPNOTE_011_Design_Investigation/example_02.dot deleted file mode 100644 index f950ed2ed2c..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/example_02.dot +++ /dev/null @@ -1,20 +0,0 @@ -digraph "example" { -rankdir="LR"; -remincross=true; -n3 [ shape=octagon, label="a", color="black", fontcolor="black" ]; -n4 [ shape=octagon, label="b", color="black", fontcolor="black" ]; -n5 [ shape=octagon, label="c", color="black", fontcolor="black" ]; -n6 [ shape=octagon, label="clk", color="black", fontcolor="black" ]; -n7 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -c11 [ shape=record, label="{{ A| B}|$2\n$add|{ Y}}" ]; -c15 [ shape=record, label="{{ CLK| D}|$7\n$dff|{ Q}}" ]; -c17 [ shape=record, label="{{ A| B| S}|$5\n$mux|{ Y}}" ]; -c17:p10:e -> c15:p13:w [color="black", style="setlinewidth(3)", label=""]; -c11:p10:e -> c17:p9:w [color="black", style="setlinewidth(3)", label=""]; -n3:e -> c11:p8:w [color="black", label=""]; -n4:e -> c11:p9:w [color="black", label=""]; -n5:e -> c17:p16:w [color="black", label=""]; -n6:e -> c15:p12:w [color="black", label=""]; -c15:p14:e -> n7:w [color="black", style="setlinewidth(3)", label=""]; -n7:e -> c17:p8:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/example_03.dot b/manual/APPNOTE_011_Design_Investigation/example_03.dot deleted file mode 100644 index e19d24af78a..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/example_03.dot +++ /dev/null @@ -1,11 +0,0 @@ -digraph "example" { -rankdir="LR"; -remincross=true; -v0 [ label="a" ]; -v1 [ label="b" ]; -v2 [ label="$2_Y" ]; -c4 [ shape=record, label="{{ A| B}|$2\n$add|{ Y}}" ]; -v0:e -> c4:p1:w [color="black", label=""]; -v1:e -> c4:p2:w [color="black", label=""]; -c4:p3:e -> v2:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/make.sh b/manual/APPNOTE_011_Design_Investigation/make.sh deleted file mode 100644 index 3845dac6b31..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/make.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -set -ex -if false; then - rm -f *.dot - ../../yosys example.ys - ../../yosys -p 'proc; opt; show -format dot -prefix splice' splice.v - ../../yosys -p 'techmap; abc -liberty ../../techlibs/cmos/cmos_cells.lib;; show -format dot -prefix cmos_00' cmos.v - ../../yosys -p 'techmap; splitnets -ports; abc -liberty ../../techlibs/cmos/cmos_cells.lib;; show -lib ../../techlibs/cmos/cmos_cells.v -format dot -prefix cmos_01' cmos.v - ../../yosys -p 'opt; cd sumprod; select a:sumstuff; show -format dot -prefix sumprod_00' sumprod.v - ../../yosys -p 'opt; cd sumprod; select a:sumstuff %x; show -format dot -prefix sumprod_01' sumprod.v - ../../yosys -p 'opt; cd sumprod; select prod; show -format dot -prefix sumprod_02' sumprod.v - ../../yosys -p 'opt; cd sumprod; select prod %ci; show -format dot -prefix sumprod_03' sumprod.v - ../../yosys -p 'opt; cd sumprod; select prod %ci2; show -format dot -prefix sumprod_04' sumprod.v - ../../yosys -p 'opt; cd sumprod; select prod %ci3; show -format dot -prefix sumprod_05' sumprod.v - ../../yosys -p 'proc; opt; memory; opt; cd memdemo; show -format dot -prefix memdemo_00' memdemo.v - ../../yosys -p 'proc; opt; memory; opt; cd memdemo; show -format dot -prefix memdemo_01 y %ci2:+$dff[Q,D] %ci*:-$mux[S]:-$dff' memdemo.v - ../../yosys submod.ys - sed -i '/^label=/ d;' *.dot -fi -for dot_file in *.dot; do - pdf_file=${dot_file%.dot}.pdf - dot -Tpdf -o $pdf_file $dot_file -done diff --git a/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot b/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot deleted file mode 100644 index 0336a9aac2a..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/memdemo_00.dot +++ /dev/null @@ -1,138 +0,0 @@ -digraph "memdemo" { -rankdir="LR"; -remincross=true; -n24 [ shape=octagon, label="clk", color="black", fontcolor="black" ]; -n25 [ shape=octagon, label="d", color="black", fontcolor="black" ]; -n26 [ shape=diamond, label="mem[0]", color="black", fontcolor="black" ]; -n27 [ shape=diamond, label="mem[1]", color="black", fontcolor="black" ]; -n28 [ shape=diamond, label="mem[2]", color="black", fontcolor="black" ]; -n29 [ shape=diamond, label="mem[3]", color="black", fontcolor="black" ]; -n30 [ shape=diamond, label="s1", color="black", fontcolor="black" ]; -n31 [ shape=diamond, label="s2", color="black", fontcolor="black" ]; -n32 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -c36 [ shape=record, label="{{ A| B}|$28\n$add|{ Y}}" ]; -c37 [ shape=record, label="{{ A| B}|$31\n$add|{ Y}}" ]; -c38 [ shape=record, label="{{ A| B}|$34\n$add|{ Y}}" ]; -c39 [ shape=record, label="{{ A| B}|$37\n$add|{ Y}}" ]; -c41 [ shape=record, label="{{ A| B| S}|$110\n$mux|{ Y}}" ]; -x0 [ shape=record, style=rounded, label=" 1:1 - 0:0 " ]; -x0:e -> c41:p40:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -c42 [ shape=record, label="{{ A| B| S}|$113\n$mux|{ Y}}" ]; -x1 [ shape=record, style=rounded, label=" 0:0 - 0:0 " ]; -x1:e -> c42:p40:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -c43 [ shape=record, label="{{ A| B| S}|$116\n$mux|{ Y}}" ]; -x2 [ shape=record, style=rounded, label=" 0:0 - 0:0 " ]; -x2:e -> c43:p40:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -v3 [ label="1'1" ]; -c44 [ shape=record, label="{{ A| B}|$145\n$and|{ Y}}" ]; -v4 [ label="1'1" ]; -c45 [ shape=record, label="{{ A| B}|$175\n$and|{ Y}}" ]; -v5 [ label="1'1" ]; -c46 [ shape=record, label="{{ A| B}|$205\n$and|{ Y}}" ]; -v6 [ label="1'1" ]; -c47 [ shape=record, label="{{ A| B}|$235\n$and|{ Y}}" ]; -v7 [ label="2'00" ]; -c48 [ shape=record, label="{{ A| B}|$143\n$eq|{ Y}}" ]; -v8 [ label="2'01" ]; -c49 [ shape=record, label="{{ A| B}|$173\n$eq|{ Y}}" ]; -v9 [ label="2'10" ]; -c50 [ shape=record, label="{{ A| B}|$203\n$eq|{ Y}}" ]; -v10 [ label="2'11" ]; -c51 [ shape=record, label="{{ A| B}|$233\n$eq|{ Y}}" ]; -c52 [ shape=record, label="{{ A| B| S}|$147\n$mux|{ Y}}" ]; -c53 [ shape=record, label="{{ A| B| S}|$177\n$mux|{ Y}}" ]; -c54 [ shape=record, label="{{ A| B| S}|$207\n$mux|{ Y}}" ]; -c55 [ shape=record, label="{{ A| B| S}|$237\n$mux|{ Y}}" ]; -c59 [ shape=record, label="{{ CLK| D}|$66\n$dff|{ Q}}" ]; -c60 [ shape=record, label="{{ CLK| D}|$68\n$dff|{ Q}}" ]; -c61 [ shape=record, label="{{ CLK| D}|$70\n$dff|{ Q}}" ]; -c62 [ shape=record, label="{{ CLK| D}|$72\n$dff|{ Q}}" ]; -c63 [ shape=record, label="{{ CLK| D}|$59\n$dff|{ Q}}" ]; -c64 [ shape=record, label="{{ CLK| D}|$63\n$dff|{ Q}}" ]; -c65 [ shape=record, label="{{ CLK| D}|$64\n$dff|{ Q}}" ]; -c66 [ shape=record, label="{{ A}|$39\n$reduce_bool|{ Y}}" ]; -v11 [ label="4'0000" ]; -c67 [ shape=record, label="{{ A| B| S}|$40\n$mux|{ Y}}" ]; -x12 [ shape=record, style=rounded, label=" 3:2 - 1:0 | 1:0 - 1:0 " ]; -c67:p35:e -> x12:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -c68 [ shape=record, label="{{ A| B}|$38\n$xor|{ Y}}" ]; -x13 [ shape=record, style=rounded, label=" 1:0 - 3:2 | 1:0 - 1:0 " ]; -x13:e -> c68:p33:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -c36:p35:e -> c52:p33:w [color="black", style="setlinewidth(3)", label=""]; -c44:p35:e -> c52:p40:w [color="black", label=""]; -c45:p35:e -> c53:p40:w [color="black", label=""]; -c46:p35:e -> c54:p40:w [color="black", label=""]; -c47:p35:e -> c55:p40:w [color="black", label=""]; -c48:p35:e -> c44:p33:w [color="black", label=""]; -c49:p35:e -> c45:p33:w [color="black", label=""]; -c50:p35:e -> c46:p33:w [color="black", label=""]; -c51:p35:e -> c47:p33:w [color="black", label=""]; -c52:p35:e -> c59:p57:w [color="black", style="setlinewidth(3)", label=""]; -c53:p35:e -> c60:p57:w [color="black", style="setlinewidth(3)", label=""]; -c37:p35:e -> c53:p33:w [color="black", style="setlinewidth(3)", label=""]; -c54:p35:e -> c61:p57:w [color="black", style="setlinewidth(3)", label=""]; -c55:p35:e -> c62:p57:w [color="black", style="setlinewidth(3)", label=""]; -c66:p35:e -> c67:p40:w [color="black", label=""]; -c68:p35:e -> c67:p34:w [color="black", style="setlinewidth(3)", label=""]; -n24:e -> c59:p56:w [color="black", label=""]; -n24:e -> c60:p56:w [color="black", label=""]; -n24:e -> c61:p56:w [color="black", label=""]; -n24:e -> c62:p56:w [color="black", label=""]; -n24:e -> c63:p56:w [color="black", label=""]; -n24:e -> c64:p56:w [color="black", label=""]; -n24:e -> c65:p56:w [color="black", label=""]; -n25:e -> c52:p34:w [color="black", style="setlinewidth(3)", label=""]; -n25:e -> c53:p34:w [color="black", style="setlinewidth(3)", label=""]; -n25:e -> c54:p34:w [color="black", style="setlinewidth(3)", label=""]; -n25:e -> c55:p34:w [color="black", style="setlinewidth(3)", label=""]; -n25:e -> c66:p33:w [color="black", style="setlinewidth(3)", label=""]; -n25:e -> c68:p34:w [color="black", style="setlinewidth(3)", label=""]; -c59:p58:e -> n26:w [color="black", style="setlinewidth(3)", label=""]; -n26:e -> c38:p34:w [color="black", style="setlinewidth(3)", label=""]; -n26:e -> c39:p33:w [color="black", style="setlinewidth(3)", label=""]; -n26:e -> c42:p33:w [color="black", style="setlinewidth(3)", label=""]; -c60:p58:e -> n27:w [color="black", style="setlinewidth(3)", label=""]; -n27:e -> c36:p33:w [color="black", style="setlinewidth(3)", label=""]; -n27:e -> c39:p34:w [color="black", style="setlinewidth(3)", label=""]; -n27:e -> c42:p34:w [color="black", style="setlinewidth(3)", label=""]; -c61:p58:e -> n28:w [color="black", style="setlinewidth(3)", label=""]; -n28:e -> c36:p34:w [color="black", style="setlinewidth(3)", label=""]; -n28:e -> c37:p33:w [color="black", style="setlinewidth(3)", label=""]; -n28:e -> c43:p33:w [color="black", style="setlinewidth(3)", label=""]; -c62:p58:e -> n29:w [color="black", style="setlinewidth(3)", label=""]; -n29:e -> c37:p34:w [color="black", style="setlinewidth(3)", label=""]; -n29:e -> c38:p33:w [color="black", style="setlinewidth(3)", label=""]; -n29:e -> c43:p34:w [color="black", style="setlinewidth(3)", label=""]; -c38:p35:e -> c54:p33:w [color="black", style="setlinewidth(3)", label=""]; -c63:p58:e -> n30:w [color="black", style="setlinewidth(3)", label=""]; -n30:e -> x13:s1:w [color="black", style="setlinewidth(3)", label=""]; -c64:p58:e -> n31:w [color="black", style="setlinewidth(3)", label=""]; -n31:e -> x13:s0:w [color="black", style="setlinewidth(3)", label=""]; -c65:p58:e -> n32:w [color="black", style="setlinewidth(3)", label=""]; -c39:p35:e -> c55:p33:w [color="black", style="setlinewidth(3)", label=""]; -n5 [ shape=point ]; -x12:s0:e -> n5:w [color="black", style="setlinewidth(3)", label=""]; -n5:e -> c48:p34:w [color="black", style="setlinewidth(3)", label=""]; -n5:e -> c49:p34:w [color="black", style="setlinewidth(3)", label=""]; -n5:e -> c50:p34:w [color="black", style="setlinewidth(3)", label=""]; -n5:e -> c51:p34:w [color="black", style="setlinewidth(3)", label=""]; -n5:e -> c63:p57:w [color="black", style="setlinewidth(3)", label=""]; -n6 [ shape=point ]; -x12:s1:e -> n6:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> c64:p57:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> x0:s0:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> x1:s0:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> x2:s0:w [color="black", style="setlinewidth(3)", label=""]; -c41:p35:e -> c65:p57:w [color="black", style="setlinewidth(3)", label=""]; -c42:p35:e -> c41:p33:w [color="black", style="setlinewidth(3)", label=""]; -c43:p35:e -> c41:p34:w [color="black", style="setlinewidth(3)", label=""]; -v10:e -> c51:p33:w [color="black", style="setlinewidth(3)", label=""]; -v11:e -> c67:p33:w [color="black", style="setlinewidth(3)", label=""]; -v3:e -> c44:p34:w [color="black", label=""]; -v4:e -> c45:p34:w [color="black", label=""]; -v5:e -> c46:p34:w [color="black", label=""]; -v6:e -> c47:p34:w [color="black", label=""]; -v7:e -> c48:p33:w [color="black", style="setlinewidth(3)", label=""]; -v8:e -> c49:p33:w [color="black", style="setlinewidth(3)", label=""]; -v9:e -> c50:p33:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot b/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot deleted file mode 100644 index 2ad92c78b2f..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/memdemo_01.dot +++ /dev/null @@ -1,29 +0,0 @@ -digraph "memdemo" { -rankdir="LR"; -remincross=true; -n4 [ shape=diamond, label="mem[0]", color="black", fontcolor="black" ]; -n5 [ shape=diamond, label="mem[1]", color="black", fontcolor="black" ]; -n6 [ shape=diamond, label="mem[2]", color="black", fontcolor="black" ]; -n7 [ shape=diamond, label="mem[3]", color="black", fontcolor="black" ]; -n8 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -v0 [ label="$0\\s2[1:0] [1]" ]; -c13 [ shape=record, label="{{ A| B| S}|$110\n$mux|{ Y}}" ]; -v1 [ label="$0\\s2[1:0] [0]" ]; -c14 [ shape=record, label="{{ A| B| S}|$113\n$mux|{ Y}}" ]; -v2 [ label="$0\\s2[1:0] [0]" ]; -c15 [ shape=record, label="{{ A| B| S}|$116\n$mux|{ Y}}" ]; -v3 [ label="clk" ]; -c19 [ shape=record, label="{{ CLK| D}|$64\n$dff|{ Q}}" ]; -c13:p12:e -> c19:p17:w [color="black", style="setlinewidth(3)", label=""]; -c14:p12:e -> c13:p9:w [color="black", style="setlinewidth(3)", label=""]; -c15:p12:e -> c13:p10:w [color="black", style="setlinewidth(3)", label=""]; -n4:e -> c14:p9:w [color="black", style="setlinewidth(3)", label=""]; -n5:e -> c14:p10:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> c15:p9:w [color="black", style="setlinewidth(3)", label=""]; -n7:e -> c15:p10:w [color="black", style="setlinewidth(3)", label=""]; -c19:p18:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; -v0:e -> c13:p11:w [color="black", label=""]; -v1:e -> c14:p11:w [color="black", label=""]; -v2:e -> c15:p11:w [color="black", label=""]; -v3:e -> c19:p16:w [color="black", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/splice.dot b/manual/APPNOTE_011_Design_Investigation/splice.dot deleted file mode 100644 index 4657feed116..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/splice.dot +++ /dev/null @@ -1,39 +0,0 @@ -digraph "splice_demo" { -rankdir="LR"; -remincross=true; -n1 [ shape=octagon, label="a", color="black", fontcolor="black" ]; -n2 [ shape=octagon, label="b", color="black", fontcolor="black" ]; -n3 [ shape=octagon, label="c", color="black", fontcolor="black" ]; -n4 [ shape=octagon, label="d", color="black", fontcolor="black" ]; -n5 [ shape=octagon, label="e", color="black", fontcolor="black" ]; -n6 [ shape=octagon, label="f", color="black", fontcolor="black" ]; -n7 [ shape=octagon, label="x", color="black", fontcolor="black" ]; -n8 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -c11 [ shape=record, label="{{ A}|$2\n$neg|{ Y}}" ]; -x0 [ shape=record, style=rounded, label=" 1:0 - 3:2 | 1:0 - 1:0 " ]; -x0:e -> c11:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -x1 [ shape=record, style=rounded, label=" 3:0 - 7:4 " ]; -c11:p10:e -> x1:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -c12 [ shape=record, label="{{ A}|$1\n$not|{ Y}}" ]; -x2 [ shape=record, style=rounded, label=" 1:0 - 3:2 | 1:0 - 1:0 " ]; -x2:e -> c12:p9:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -x3 [ shape=record, style=rounded, label=" 3:2 - 1:0 | 1:0 - 3:2 " ]; -c12:p10:e -> x3:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -x4 [ shape=record, style=rounded, label=" 0:0 - 1:1 | 1:1 - 0:0 " ]; -x5 [ shape=record, style=rounded, label=" 1:0 - 3:2 | 1:0 - 1:0 " ]; -x6 [ shape=record, style=rounded, label=" 3:0 - 11:8 " ]; -x5:e -> x6:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -n1:e -> x4:s0:w [color="black", style="setlinewidth(3)", label=""]; -n1:e -> x4:s1:w [color="black", style="setlinewidth(3)", label=""]; -n1:e -> x5:s1:w [color="black", style="setlinewidth(3)", label=""]; -n2:e -> x5:s0:w [color="black", style="setlinewidth(3)", label=""]; -n3:e -> x0:s1:w [color="black", style="setlinewidth(3)", label=""]; -n4:e -> x0:s0:w [color="black", style="setlinewidth(3)", label=""]; -n5:e -> x2:s1:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> x2:s0:w [color="black", style="setlinewidth(3)", label=""]; -x4:e -> n7:w [color="black", style="setlinewidth(3)", label=""]; -x1:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; -x3:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; -x3:s1:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; -x6:s0:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/submod_00.dot b/manual/APPNOTE_011_Design_Investigation/submod_00.dot deleted file mode 100644 index 2e55268ee10..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/submod_00.dot +++ /dev/null @@ -1,45 +0,0 @@ -digraph "memdemo" { -rankdir="LR"; -remincross=true; -n5 [ shape=octagon, label="clk", color="black", fontcolor="black" ]; -n6 [ shape=octagon, label="d", color="black", fontcolor="black" ]; -n7 [ shape=diamond, label="mem[0]", color="black", fontcolor="black" ]; -n8 [ shape=diamond, label="mem[1]", color="black", fontcolor="black" ]; -n9 [ shape=diamond, label="mem[2]", color="black", fontcolor="black" ]; -n10 [ shape=diamond, label="mem[3]", color="black", fontcolor="black" ]; -n11 [ shape=diamond, label="s1", color="black", fontcolor="black" ]; -n12 [ shape=diamond, label="s2", color="black", fontcolor="black" ]; -n13 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -c17 [ shape=record, label="{{ CLK| D}|$59\n$dff|{ Q}}" ]; -c18 [ shape=record, label="{{ CLK| D}|$63\n$dff|{ Q}}" ]; -c20 [ shape=record, label="{{ clk| mem[0]| mem[1]| mem[2]| mem[3]| n1}|outstage\noutstage|{ y}}" ]; -c21 [ shape=record, label="{{ clk| d| n1}|scramble\nscramble|{ mem[0]| mem[1]| mem[2]| mem[3]}}" ]; -c23 [ shape=record, label="{{ d| s1| s2}|selstage\nselstage|{ n1| n2}}" ]; -n1 [ shape=point ]; -c23:p19:e -> n1:w [color="black", style="setlinewidth(3)", label=""]; -n1:e -> c17:p15:w [color="black", style="setlinewidth(3)", label=""]; -n1:e -> c21:p19:w [color="black", style="setlinewidth(3)", label=""]; -c21:p10:e -> n10:w [color="black", style="setlinewidth(3)", label=""]; -n10:e -> c20:p10:w [color="black", style="setlinewidth(3)", label=""]; -c17:p16:e -> n11:w [color="black", style="setlinewidth(3)", label=""]; -n11:e -> c23:p11:w [color="black", style="setlinewidth(3)", label=""]; -c18:p16:e -> n12:w [color="black", style="setlinewidth(3)", label=""]; -n12:e -> c23:p12:w [color="black", style="setlinewidth(3)", label=""]; -c20:p13:e -> n13:w [color="black", style="setlinewidth(3)", label=""]; -n2 [ shape=point ]; -c23:p22:e -> n2:w [color="black", style="setlinewidth(3)", label=""]; -n2:e -> c18:p15:w [color="black", style="setlinewidth(3)", label=""]; -n2:e -> c20:p19:w [color="black", style="setlinewidth(3)", label=""]; -n5:e -> c17:p14:w [color="black", label=""]; -n5:e -> c18:p14:w [color="black", label=""]; -n5:e -> c20:p5:w [color="black", label=""]; -n5:e -> c21:p5:w [color="black", label=""]; -n6:e -> c21:p6:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> c23:p6:w [color="black", style="setlinewidth(3)", label=""]; -c21:p7:e -> n7:w [color="black", style="setlinewidth(3)", label=""]; -n7:e -> c20:p7:w [color="black", style="setlinewidth(3)", label=""]; -c21:p8:e -> n8:w [color="black", style="setlinewidth(3)", label=""]; -n8:e -> c20:p8:w [color="black", style="setlinewidth(3)", label=""]; -c21:p9:e -> n9:w [color="black", style="setlinewidth(3)", label=""]; -n9:e -> c20:p9:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/submod_01.dot b/manual/APPNOTE_011_Design_Investigation/submod_01.dot deleted file mode 100644 index f8f8c008ac6..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/submod_01.dot +++ /dev/null @@ -1,87 +0,0 @@ -digraph "scramble" { -rankdir="LR"; -remincross=true; -n17 [ shape=octagon, label="clk", color="black", fontcolor="black" ]; -n18 [ shape=octagon, label="d", color="black", fontcolor="black" ]; -n19 [ shape=octagon, label="mem[0]", color="black", fontcolor="black" ]; -n20 [ shape=octagon, label="mem[1]", color="black", fontcolor="black" ]; -n21 [ shape=octagon, label="mem[2]", color="black", fontcolor="black" ]; -n22 [ shape=octagon, label="mem[3]", color="black", fontcolor="black" ]; -n23 [ shape=octagon, label="n1", color="black", fontcolor="black" ]; -c27 [ shape=record, label="{{ A| B}|$28\n$add|{ Y}}" ]; -c28 [ shape=record, label="{{ A| B}|$31\n$add|{ Y}}" ]; -c29 [ shape=record, label="{{ A| B}|$34\n$add|{ Y}}" ]; -c30 [ shape=record, label="{{ A| B}|$37\n$add|{ Y}}" ]; -v0 [ label="1'1" ]; -c31 [ shape=record, label="{{ A| B}|$145\n$and|{ Y}}" ]; -v1 [ label="1'1" ]; -c32 [ shape=record, label="{{ A| B}|$175\n$and|{ Y}}" ]; -v2 [ label="1'1" ]; -c33 [ shape=record, label="{{ A| B}|$205\n$and|{ Y}}" ]; -v3 [ label="1'1" ]; -c34 [ shape=record, label="{{ A| B}|$235\n$and|{ Y}}" ]; -v4 [ label="2'00" ]; -c35 [ shape=record, label="{{ A| B}|$143\n$eq|{ Y}}" ]; -v5 [ label="2'01" ]; -c36 [ shape=record, label="{{ A| B}|$173\n$eq|{ Y}}" ]; -v6 [ label="2'10" ]; -c37 [ shape=record, label="{{ A| B}|$203\n$eq|{ Y}}" ]; -v7 [ label="2'11" ]; -c38 [ shape=record, label="{{ A| B}|$233\n$eq|{ Y}}" ]; -c40 [ shape=record, label="{{ A| B| S}|$147\n$mux|{ Y}}" ]; -c41 [ shape=record, label="{{ A| B| S}|$177\n$mux|{ Y}}" ]; -c42 [ shape=record, label="{{ A| B| S}|$207\n$mux|{ Y}}" ]; -c43 [ shape=record, label="{{ A| B| S}|$237\n$mux|{ Y}}" ]; -c47 [ shape=record, label="{{ CLK| D}|$66\n$dff|{ Q}}" ]; -c48 [ shape=record, label="{{ CLK| D}|$68\n$dff|{ Q}}" ]; -c49 [ shape=record, label="{{ CLK| D}|$70\n$dff|{ Q}}" ]; -c50 [ shape=record, label="{{ CLK| D}|$72\n$dff|{ Q}}" ]; -c27:p26:e -> c40:p24:w [color="black", style="setlinewidth(3)", label=""]; -c36:p26:e -> c32:p24:w [color="black", label=""]; -c37:p26:e -> c33:p24:w [color="black", label=""]; -c38:p26:e -> c34:p24:w [color="black", label=""]; -c40:p26:e -> c47:p45:w [color="black", style="setlinewidth(3)", label=""]; -c41:p26:e -> c48:p45:w [color="black", style="setlinewidth(3)", label=""]; -c42:p26:e -> c49:p45:w [color="black", style="setlinewidth(3)", label=""]; -c43:p26:e -> c50:p45:w [color="black", style="setlinewidth(3)", label=""]; -n17:e -> c47:p44:w [color="black", label=""]; -n17:e -> c48:p44:w [color="black", label=""]; -n17:e -> c49:p44:w [color="black", label=""]; -n17:e -> c50:p44:w [color="black", label=""]; -n18:e -> c40:p25:w [color="black", style="setlinewidth(3)", label=""]; -n18:e -> c41:p25:w [color="black", style="setlinewidth(3)", label=""]; -n18:e -> c42:p25:w [color="black", style="setlinewidth(3)", label=""]; -n18:e -> c43:p25:w [color="black", style="setlinewidth(3)", label=""]; -c47:p46:e -> n19:w [color="black", style="setlinewidth(3)", label=""]; -n19:e -> c29:p25:w [color="black", style="setlinewidth(3)", label=""]; -n19:e -> c30:p24:w [color="black", style="setlinewidth(3)", label=""]; -c28:p26:e -> c41:p24:w [color="black", style="setlinewidth(3)", label=""]; -c48:p46:e -> n20:w [color="black", style="setlinewidth(3)", label=""]; -n20:e -> c27:p24:w [color="black", style="setlinewidth(3)", label=""]; -n20:e -> c30:p25:w [color="black", style="setlinewidth(3)", label=""]; -c49:p46:e -> n21:w [color="black", style="setlinewidth(3)", label=""]; -n21:e -> c27:p25:w [color="black", style="setlinewidth(3)", label=""]; -n21:e -> c28:p24:w [color="black", style="setlinewidth(3)", label=""]; -c50:p46:e -> n22:w [color="black", style="setlinewidth(3)", label=""]; -n22:e -> c28:p25:w [color="black", style="setlinewidth(3)", label=""]; -n22:e -> c29:p24:w [color="black", style="setlinewidth(3)", label=""]; -n23:e -> c35:p25:w [color="black", style="setlinewidth(3)", label=""]; -n23:e -> c36:p25:w [color="black", style="setlinewidth(3)", label=""]; -n23:e -> c37:p25:w [color="black", style="setlinewidth(3)", label=""]; -n23:e -> c38:p25:w [color="black", style="setlinewidth(3)", label=""]; -c29:p26:e -> c42:p24:w [color="black", style="setlinewidth(3)", label=""]; -c30:p26:e -> c43:p24:w [color="black", style="setlinewidth(3)", label=""]; -c31:p26:e -> c40:p39:w [color="black", label=""]; -c32:p26:e -> c41:p39:w [color="black", label=""]; -c33:p26:e -> c42:p39:w [color="black", label=""]; -c34:p26:e -> c43:p39:w [color="black", label=""]; -c35:p26:e -> c31:p24:w [color="black", label=""]; -v0:e -> c31:p25:w [color="black", label=""]; -v1:e -> c32:p25:w [color="black", label=""]; -v2:e -> c33:p25:w [color="black", label=""]; -v3:e -> c34:p25:w [color="black", label=""]; -v4:e -> c35:p24:w [color="black", style="setlinewidth(3)", label=""]; -v5:e -> c36:p24:w [color="black", style="setlinewidth(3)", label=""]; -v6:e -> c37:p24:w [color="black", style="setlinewidth(3)", label=""]; -v7:e -> c38:p24:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/submod_02.dot b/manual/APPNOTE_011_Design_Investigation/submod_02.dot deleted file mode 100644 index 1a672c484f1..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/submod_02.dot +++ /dev/null @@ -1,33 +0,0 @@ -digraph "outstage" { -rankdir="LR"; -remincross=true; -n4 [ shape=octagon, label="clk", color="black", fontcolor="black" ]; -n5 [ shape=octagon, label="mem[0]", color="black", fontcolor="black" ]; -n6 [ shape=octagon, label="mem[1]", color="black", fontcolor="black" ]; -n7 [ shape=octagon, label="mem[2]", color="black", fontcolor="black" ]; -n8 [ shape=octagon, label="mem[3]", color="black", fontcolor="black" ]; -n9 [ shape=octagon, label="n1", color="black", fontcolor="black" ]; -n10 [ shape=octagon, label="y", color="black", fontcolor="black" ]; -c15 [ shape=record, label="{{ A| B| S}|$110\n$mux|{ Y}}" ]; -x0 [ shape=record, style=rounded, label=" 1:1 - 0:0 " ]; -x0:e -> c15:p13:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -c16 [ shape=record, label="{{ A| B| S}|$113\n$mux|{ Y}}" ]; -x1 [ shape=record, style=rounded, label=" 0:0 - 0:0 " ]; -x1:e -> c16:p13:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -c17 [ shape=record, label="{{ A| B| S}|$116\n$mux|{ Y}}" ]; -x2 [ shape=record, style=rounded, label=" 0:0 - 0:0 " ]; -x2:e -> c17:p13:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", label=""]; -c21 [ shape=record, label="{{ CLK| D}|$64\n$dff|{ Q}}" ]; -c15:p14:e -> c21:p19:w [color="black", style="setlinewidth(3)", label=""]; -c21:p20:e -> n10:w [color="black", style="setlinewidth(3)", label=""]; -c16:p14:e -> c15:p11:w [color="black", style="setlinewidth(3)", label=""]; -c17:p14:e -> c15:p12:w [color="black", style="setlinewidth(3)", label=""]; -n4:e -> c21:p18:w [color="black", label=""]; -n5:e -> c16:p11:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> c16:p12:w [color="black", style="setlinewidth(3)", label=""]; -n7:e -> c17:p11:w [color="black", style="setlinewidth(3)", label=""]; -n8:e -> c17:p12:w [color="black", style="setlinewidth(3)", label=""]; -n9:e -> x0:s0:w [color="black", label=""]; -n9:e -> x1:s0:w [color="black", label=""]; -n9:e -> x2:s0:w [color="black", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/submod_03.dot b/manual/APPNOTE_011_Design_Investigation/submod_03.dot deleted file mode 100644 index 0dbbe3baa17..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/submod_03.dot +++ /dev/null @@ -1,26 +0,0 @@ -digraph "selstage" { -rankdir="LR"; -remincross=true; -n3 [ shape=octagon, label="d", color="black", fontcolor="black" ]; -n4 [ shape=octagon, label="n1", color="black", fontcolor="black" ]; -n5 [ shape=octagon, label="n2", color="black", fontcolor="black" ]; -n6 [ shape=octagon, label="s1", color="black", fontcolor="black" ]; -n7 [ shape=octagon, label="s2", color="black", fontcolor="black" ]; -c10 [ shape=record, label="{{ A}|$39\n$reduce_bool|{ Y}}" ]; -v0 [ label="4'0000" ]; -c13 [ shape=record, label="{{ A| B| S}|$40\n$mux|{ Y}}" ]; -x1 [ shape=record, style=rounded, label=" 3:2 - 1:0 | 1:0 - 1:0 " ]; -c13:p9:e -> x1:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -c14 [ shape=record, label="{{ A| B}|$38\n$xor|{ Y}}" ]; -x2 [ shape=record, style=rounded, label=" 1:0 - 3:2 | 1:0 - 1:0 " ]; -x2:e -> c14:p8:w [arrowhead=odiamond, arrowtail=odiamond, dir=both, color="black", style="setlinewidth(3)", label=""]; -c10:p9:e -> c13:p12:w [color="black", label=""]; -c14:p9:e -> c13:p11:w [color="black", style="setlinewidth(3)", label=""]; -n3:e -> c10:p8:w [color="black", style="setlinewidth(3)", label=""]; -n3:e -> c14:p11:w [color="black", style="setlinewidth(3)", label=""]; -x1:s0:e -> n4:w [color="black", style="setlinewidth(3)", label=""]; -x1:s1:e -> n5:w [color="black", style="setlinewidth(3)", label=""]; -n6:e -> x2:s1:w [color="black", style="setlinewidth(3)", label=""]; -n7:e -> x2:s0:w [color="black", style="setlinewidth(3)", label=""]; -v0:e -> c13:p8:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot deleted file mode 100644 index 06522dcc9ef..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_00.dot +++ /dev/null @@ -1,18 +0,0 @@ -digraph "sumprod" { -rankdir="LR"; -remincross=true; -v0 [ label="a" ]; -v1 [ label="b" ]; -v2 [ label="$1_Y" ]; -c4 [ shape=record, label="{{ A| B}|$1\n$add|{ Y}}" ]; -v3 [ label="$1_Y" ]; -v4 [ label="c" ]; -v5 [ label="sum" ]; -c5 [ shape=record, label="{{ A| B}|$2\n$add|{ Y}}" ]; -v0:e -> c4:p1:w [color="black", style="setlinewidth(3)", label=""]; -v1:e -> c4:p2:w [color="black", style="setlinewidth(3)", label=""]; -c4:p3:e -> v2:w [color="black", style="setlinewidth(3)", label=""]; -v3:e -> c5:p1:w [color="black", style="setlinewidth(3)", label=""]; -v4:e -> c5:p2:w [color="black", style="setlinewidth(3)", label=""]; -c5:p3:e -> v5:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot deleted file mode 100644 index aefe7a6da93..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_01.dot +++ /dev/null @@ -1,15 +0,0 @@ -digraph "sumprod" { -rankdir="LR"; -remincross=true; -n2 [ shape=octagon, label="a", color="black", fontcolor="black" ]; -n3 [ shape=octagon, label="b", color="black", fontcolor="black" ]; -n4 [ shape=octagon, label="c", color="black", fontcolor="black" ]; -n5 [ shape=octagon, label="sum", color="black", fontcolor="black" ]; -c9 [ shape=record, label="{{ A| B}|$1\n$add|{ Y}}" ]; -c10 [ shape=record, label="{{ A| B}|$2\n$add|{ Y}}" ]; -c9:p8:e -> c10:p6:w [color="black", style="setlinewidth(3)", label=""]; -n2:e -> c9:p6:w [color="black", style="setlinewidth(3)", label=""]; -n3:e -> c9:p7:w [color="black", style="setlinewidth(3)", label=""]; -n4:e -> c10:p7:w [color="black", style="setlinewidth(3)", label=""]; -c10:p8:e -> n5:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot deleted file mode 100644 index 4646c994786..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_02.dot +++ /dev/null @@ -1,5 +0,0 @@ -digraph "sumprod" { -rankdir="LR"; -remincross=true; -n1 [ shape=octagon, label="prod", color="black", fontcolor="black" ]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot deleted file mode 100644 index dcfea2b5609..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_03.dot +++ /dev/null @@ -1,11 +0,0 @@ -digraph "sumprod" { -rankdir="LR"; -remincross=true; -n1 [ shape=octagon, label="prod", color="black", fontcolor="black" ]; -v0 [ label="$3_Y" ]; -v1 [ label="c" ]; -c5 [ shape=record, label="{{ A| B}|$4\n$mul|{ Y}}" ]; -c5:p4:e -> n1:w [color="black", style="setlinewidth(3)", label=""]; -v0:e -> c5:p2:w [color="black", style="setlinewidth(3)", label=""]; -v1:e -> c5:p3:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot deleted file mode 100644 index e77c41aa2e1..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_04.dot +++ /dev/null @@ -1,11 +0,0 @@ -digraph "sumprod" { -rankdir="LR"; -remincross=true; -n2 [ shape=octagon, label="c", color="black", fontcolor="black" ]; -n3 [ shape=octagon, label="prod", color="black", fontcolor="black" ]; -c7 [ shape=record, label="{{ A| B}|$4\n$mul|{ Y}}" ]; -n1 [ shape=diamond, label="$3_Y" ]; -n1:e -> c7:p4:w [color="black", style="setlinewidth(3)", label=""]; -n2:e -> c7:p5:w [color="black", style="setlinewidth(3)", label=""]; -c7:p6:e -> n3:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot b/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot deleted file mode 100644 index b544412909c..00000000000 --- a/manual/APPNOTE_011_Design_Investigation/sumprod_05.dot +++ /dev/null @@ -1,15 +0,0 @@ -digraph "sumprod" { -rankdir="LR"; -remincross=true; -n2 [ shape=octagon, label="c", color="black", fontcolor="black" ]; -n3 [ shape=octagon, label="prod", color="black", fontcolor="black" ]; -v0 [ label="a" ]; -v1 [ label="b" ]; -c7 [ shape=record, label="{{ A| B}|$3\n$mul|{ Y}}" ]; -c8 [ shape=record, label="{{ A| B}|$4\n$mul|{ Y}}" ]; -c7:p6:e -> c8:p4:w [color="black", style="setlinewidth(3)", label=""]; -n2:e -> c8:p5:w [color="black", style="setlinewidth(3)", label=""]; -c8:p6:e -> n3:w [color="black", style="setlinewidth(3)", label=""]; -v0:e -> c7:p4:w [color="black", style="setlinewidth(3)", label=""]; -v1:e -> c7:p5:w [color="black", style="setlinewidth(3)", label=""]; -} diff --git a/manual/APPNOTE_012_Verilog_to_BTOR.tex b/manual/APPNOTE_012_Verilog_to_BTOR.tex deleted file mode 100644 index 1bc2778760e..00000000000 --- a/manual/APPNOTE_012_Verilog_to_BTOR.tex +++ /dev/null @@ -1,435 +0,0 @@ - -% IEEEtran howto: -% http://ftp.univie.ac.at/packages/tex/macros/latex/contrib/IEEEtran/IEEEtran_HOWTO.pdf -\documentclass[9pt,technote,a4paper]{IEEEtran} - -\usepackage[T1]{fontenc} % required for luximono! -\usepackage[scaled=0.8]{luximono} % typewriter font with bold face - -% To install the luximono font files: -% getnonfreefonts-sys --all or -% getnonfreefonts-sys luximono -% -% when there are trouble you might need to: -% - Create /etc/texmf/updmap.d/99local-luximono.cfg -% containing the single line: Map ul9.map -% - Run update-updmap followed by mktexlsr and updmap-sys -% -% This commands must be executed as root with a root environment -% (i.e. run "sudo su" and then execute the commands in the root -% shell, don't just prefix the commands with "sudo"). - -\usepackage[unicode,bookmarks=false]{hyperref} -\usepackage[english]{babel} -\usepackage[utf8]{inputenc} -\usepackage{amssymb} -\usepackage{amsmath} -\usepackage{amsfonts} -\usepackage{units} -\usepackage{nicefrac} -\usepackage{eurosym} -\usepackage{graphicx} -\usepackage{verbatim} -\usepackage{algpseudocode} -\usepackage{scalefnt} -\usepackage{xspace} -\usepackage{color} -\usepackage{colortbl} -\usepackage{multirow} -\usepackage{hhline} -\usepackage{listings} -\usepackage{float} - -\usepackage{tikz} -\usetikzlibrary{calc} -\usetikzlibrary{arrows} -\usetikzlibrary{scopes} -\usetikzlibrary{through} -\usetikzlibrary{shapes.geometric} - -\lstset{basicstyle=\ttfamily,frame=trBL,xleftmargin=2em,xrightmargin=1em,numbers=left} - -\begin{document} - -\title{Yosys Application Note 012: \\ Converting Verilog to BTOR} -\author{Ahmed Irfan and Clifford Wolf \\ April 2015} -\maketitle - -\begin{abstract} -Verilog-2005 is a powerful Hardware Description Language (HDL) that -can be used to easily create complex designs from small HDL code. -BTOR~\cite{btor} is a bit-precise word-level format for model -checking. It is a simple format and easy to parse. It allows to model -the model checking problem over the theory of bit-vectors with -one-dimensional arrays, thus enabling to model Verilog designs with -registers and memories. Yosys~\cite{yosys} is an Open-Source Verilog -synthesis tool that can be used to convert Verilog designs with simple -assertions to BTOR format. - -\end{abstract} - -\section{Installation} - -Yosys written in C++ (using features from C++11) and is tested on -modern Linux. It should compile fine on most UNIX systems with a -C++11 compiler. The README file contains useful information on -building Yosys and its prerequisites. - -Yosys is a large and feature-rich program with some dependencies. For -this work, we may deactivate other extra features such as {\tt TCL} -and {\tt ABC} support in the {\tt Makefile}. - -\bigskip - -This Application Note is based on GIT Rev. {\tt 082550f} from -2015-04-04 of Yosys~\cite{yosys}. - -\section{Quick Start} - -We assume that the Verilog design is synthesizable and we also assume -that the design does not have multi-dimensional memories. As BTOR -implicitly initializes registers to zero value and memories stay -uninitialized, we assume that the Verilog design does -not contain initial blocks. For more details about the BTOR format, -please refer to~\cite{btor}. - -We provide a shell script {\tt verilog2btor.sh} which can be used to -convert a Verilog design to BTOR. The script can be found in the -{\tt backends/btor} directory. The following example shows its usage: - -\begin{figure}[H] -\begin{lstlisting}[language=sh,numbers=none] -verilog2btor.sh fsm.v fsm.btor test -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{Using verilog2btor script} -\end{figure} - -The script {\tt verilog2btor.sh} takes three parameters. In the above -example, the first parameter {\tt fsm.v} is the input design, the second -parameter {\tt fsm.btor} is the file name of BTOR output, and the third -parameter {\tt test} is the name of top module in the design. - -To specify the properties (that need to be checked), we have two -options: -\begin{itemize} -\item We can use the Verilog {\tt assert} statement in the procedural block - or module body of the Verilog design, as shown in - Listing~\ref{specifying_property_assert}. This is the preferred option. -\item We can use a single-bit output wire, whose name starts with - {\tt safety}. The value of this output wire needs to be driven low - when the property is met, i.e. the solver will try to find a model - that makes the safety pin go high. This is demonstrated in - Listing~\ref{specifying_property_output}. -\end{itemize} - -\begin{figure}[H] -\begin{lstlisting}[language=Verilog,numbers=none] -module test(input clk, input rst, output y); - - reg [2:0] state; - - always @(posedge clk) begin - if (rst || state == 3) begin - state <= 0; - end else begin - assert(state < 3); - state <= state + 1; - end - end - - assign y = state[2]; - - assert property (y !== 1'b1); - -endmodule -\end{lstlisting} -\renewcommand{\figurename}{Listing} -\caption{Specifying property in Verilog design with {\tt assert}} -\label{specifying_property_assert} -\end{figure} - -\begin{figure}[H] -\begin{lstlisting}[language=Verilog,numbers=none] -module test(input clk, input rst, - output y, output safety1); - - reg [2:0] state; - - always @(posedge clk) begin - if (rst || state == 3) - state <= 0; - else - state <= state + 1; - end - - assign y = state[2]; - - assign safety1 = !(y !== 1'b1); - -endmodule -\end{lstlisting} -\renewcommand{\figurename}{Listing} -\caption{Specifying property in Verilog design with output wire} -\label{specifying_property_output} -\end{figure} - -We can run Boolector~\cite{boolector}~$1.4.1$\footnote{ -Newer version of Boolector do not support sequential models. -Boolector 1.4.1 can be built with picosat-951. Newer versions -of picosat have an incompatible API.} on the generated BTOR -file: - -\begin{figure}[H] -\begin{lstlisting}[language=sh,numbers=none] -$ boolector fsm.btor -unsat -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{Running boolector on BTOR file} -\end{figure} - -We can also use nuXmv~\cite{nuxmv}, but on BTOR designs it does not -support memories yet. With the next release of nuXmv, we will be also -able to verify designs with memories. - -\section{Detailed Flow} - -Yosys is able to synthesize Verilog designs up to the gate level. -We are interested in keeping registers and memories when synthesizing -the design. For this purpose, we describe a customized Yosys synthesis -flow, that is also provided by the {\tt verilog2btor.sh} script. -Listing~\ref{btor_script_memory} shows the Yosys commands that are -executed by {\tt verilog2btor.sh}. - -\begin{figure}[H] -\begin{lstlisting}[language=sh] -read_verilog -sv $1; -hierarchy -top $3; hierarchy -libdir $DIR; -hierarchy -check; -proc; opt; -opt_expr -mux_undef; opt; -rename -hide;;; -splice; opt; -memory_dff -wr_only; memory_collect;; -flatten;; -memory_unpack; -splitnets -driver; -setundef -zero -undriven; -opt;;; -write_btor $2; -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{Synthesis Flow for BTOR with memories} -\label{btor_script_memory} -\end{figure} - -Here is short description of what is happening in the script line by -line: - -\begin{enumerate} -\item Reading the input file. -\item Setting the top module in the hierarchy and trying to read - automatically the files which are given as {\tt include} in the file - read in first line. -\item Checking the design hierarchy. -\item Converting processes to multiplexers (muxs) and flip-flops. -\item Removing undef signals from muxs. -\item Hiding all signal names that are not used as module ports. -\item Explicit type conversion, by introducing slice and concat cells - in the circuit. -\item Converting write memories to synchronous memories, and - collecting the memories to multi-port memories. -\item Flattening the design to get only one module. -\item Separating read and write memories. -\item Splitting the signals that are partially assigned -\item Setting undef to zero value. -\item Final optimization pass. -\item Writing BTOR file. -\end{enumerate} - -For detailed description of the commands mentioned above, please refer -to the Yosys documentation, or run {\tt yosys -h \it command\_name}. - -The script presented earlier can be easily modified to have a BTOR -file that does not contain memories. This is done by removing the line -number~8 and 10, and introduces a new command {\tt memory} at line -number~8. Listing~\ref{btor_script_without_memory} shows the -modified Yosys script file: - -\begin{figure}[H] -\begin{lstlisting}[language=sh,numbers=none] -read_verilog -sv $1; -hierarchy -top $3; hierarchy -libdir $DIR; -hierarchy -check; -proc; opt; -opt_expr -mux_undef; opt; -rename -hide;;; -splice; opt; -memory;; -flatten;; -splitnets -driver; -setundef -zero -undriven; -opt;;; -write_btor $2; -\end{lstlisting} - \renewcommand{\figurename}{Listing} -\caption{Synthesis Flow for BTOR without memories} -\label{btor_script_without_memory} -\end{figure} - -\section{Example} - -Here is an example Verilog design that we want to convert to BTOR: - -\begin{figure}[H] -\begin{lstlisting}[language=Verilog,numbers=none] -module array(input clk); - - reg [7:0] counter; - reg [7:0] mem [7:0]; - - always @(posedge clk) begin - counter <= counter + 8'd1; - mem[counter] <= counter; - end - - assert property (!(counter > 8'd0) || - mem[counter - 8'd1] == counter - 8'd1); - -endmodule -\end{lstlisting} -\renewcommand{\figurename}{Listing} -\caption{Example - Verilog Design} -\label{example_verilog} -\end{figure} - -The generated BTOR file that contain memories, using the script shown -in Listing~\ref{btor_script_memory}: -\begin{figure}[H] -\begin{lstlisting}[numbers=none] -1 var 1 clk -2 array 8 3 -3 var 8 $auto$rename.cc:150:execute$20 -4 const 8 00000001 -5 sub 8 3 4 -6 slice 3 5 2 0 -7 read 8 2 6 -8 slice 3 3 2 0 -9 add 8 3 4 -10 const 8 00000000 -11 ugt 1 3 10 -12 not 1 11 -13 const 8 11111111 -14 slice 1 13 0 0 -15 one 1 -16 eq 1 1 15 -17 and 1 16 14 -18 write 8 3 2 8 3 -19 acond 8 3 17 18 2 -20 anext 8 3 2 19 -21 eq 1 7 5 -22 or 1 12 21 -23 const 1 1 -24 one 1 -25 eq 1 23 24 -26 cond 1 25 22 24 -27 root 1 -26 -28 cond 8 1 9 3 -29 next 8 3 28 -\end{lstlisting} -\renewcommand{\figurename}{Listing} -\caption{Example - Converted BTOR with memory} -\label{example_btor} -\end{figure} - -And the BTOR file obtained by the script shown in -Listing~\ref{btor_script_without_memory}, which expands the memory -into individual elements: -\begin{figure}[H] -\begin{lstlisting}[numbers=none,escapechar=@] -1 var 1 clk -2 var 8 mem[0] -3 var 8 $auto$rename.cc:150:execute$20 -4 slice 3 3 2 0 -5 slice 1 4 0 0 -6 not 1 5 -7 slice 1 4 1 1 -8 not 1 7 -9 slice 1 4 2 2 -10 not 1 9 -11 and 1 8 10 -12 and 1 6 11 -13 cond 8 12 3 2 -14 cond 8 1 13 2 -15 next 8 2 14 -16 const 8 00000001 -17 add 8 3 16 -18 const 8 00000000 -19 ugt 1 3 18 -20 not 1 19 -21 var 8 mem[2] -22 and 1 7 10 -23 and 1 6 22 -24 cond 8 23 3 21 -25 cond 8 1 24 21 -26 next 8 21 25 -27 sub 8 3 16 - -@\vbox to 0pt{\vss\vdots\vskip3pt}@ -54 cond 1 53 50 52 -55 root 1 -54 - -@\vbox to 0pt{\vss\vdots\vskip3pt}@ -77 cond 8 76 3 44 -78 cond 8 1 77 44 -79 next 8 44 78 -\end{lstlisting} -\renewcommand{\figurename}{Listing} -\caption{Example - Converted BTOR without memory} -\label{example_btor} -\end{figure} - -\section{Limitations} - -BTOR does not support initialization of memories and registers, i.e. they are -implicitly initialized to value zero, so the initial block for -memories need to be removed when converting to BTOR. It should -also be kept in consideration that BTOR does not support the {\tt x} or {\tt z} -values of Verilog. - -Another thing to bear in mind is that Yosys will convert multi-dimensional -memories to one-dimensional memories and address decoders. Therefore -out-of-bounds memory accesses can yield unexpected results. - -\section{Conclusion} - -Using the described flow, we can use Yosys to generate word-level -verification benchmarks with or without memories from Verilog designs. - -\begin{thebibliography}{9} - -\bibitem{yosys} -Clifford Wolf. The Yosys Open SYnthesis Suite. \\ -\url{http://www.clifford.at/yosys/} - -\bibitem{boolector} -Robert Brummayer and Armin Biere, Boolector: An Efficient SMT Solver for Bit-Vectors and Arrays\\ -\url{http://fmv.jku.at/boolector/} - -\bibitem{btor} -Robert Brummayer and Armin Biere and Florian Lonsing, BTOR: -Bit-Precise Modelling of Word-Level Problems for Model Checking\\ -\url{http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf} - -\bibitem{nuxmv} -Roberto Cavada and Alessandro Cimatti and Michele Dorigatti and -Alberto Griggio and Alessandro Mariotti and Andrea Micheli and Sergio -Mover and Marco Roveri and Stefano Tonetta, The nuXmv Symbolic Model -Checker\\ -\url{https://es-static.fbk.eu/tools/nuxmv/index.php} - -\end{thebibliography} - - -\end{document} diff --git a/manual/CHAPTER_Appnotes.tex b/manual/CHAPTER_Appnotes.tex deleted file mode 100644 index e0d093290e4..00000000000 --- a/manual/CHAPTER_Appnotes.tex +++ /dev/null @@ -1,29 +0,0 @@ - -\chapter{Application Notes} -\label{chapter:appnotes} - -% \begin{fixme} -% This appendix will cover some typical use-cases of Yosys in the form of application notes. -% \end{fixme} -% -% \section{Synthesizing using a Cell Library in Liberty Format} -% \section{Reverse Engineering the MOS6502 from an NMOS Transistor Netlist} -% \section{Reconfigurable Coarse-Grain Synthesis using Intersynth} - -This appendix contains copies of the Yosys application notes. - -\begin{itemize} -\item Yosys AppNote 010: Converting Verilog to BLIF \dotfill Page \pageref{app:010} \hskip2cm\null -\item Yosys AppNote 011: Interactive Design Investigation \dotfill Page \pageref{app:011} \hskip2cm\null -\item Yosys AppNote 012: Converting Verilog to BTOR \dotfill Page \pageref{app:012} \hskip2cm\null -\end{itemize} - -\eject\label{app:010} -\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_010_Verilog_to_BLIF.pdf} - -\eject\label{app:011} -\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_011_Design_Investigation.pdf} - -\eject\label{app:012} -\includepdf[pages=-,pagecommand=\thispagestyle{plain}]{APPNOTE_012_Verilog_to_BTOR.pdf} - diff --git a/manual/CHAPTER_Approach.tex b/manual/CHAPTER_Approach.tex deleted file mode 100644 index 4b170ee0ae8..00000000000 --- a/manual/CHAPTER_Approach.tex +++ /dev/null @@ -1,145 +0,0 @@ - -\chapter{Approach} -\label{chapter:approach} - -Yosys is a tool for synthesising (behavioural) Verilog HDL code to target architecture netlists. Yosys aims at a wide -range of application domains and thus must be flexible and easy to adapt to new tasks. This chapter covers the general -approach followed in the effort to implement this tool. - -\section{Data- and Control-Flow} - -The data- and control-flow of a typical synthesis tool is very similar to the data- and control-flow of a typical -compiler: different subsystems are called in a predetermined order, each consuming the data generated by the -last subsystem and generating the data for the next subsystem (see Fig.~\ref{fig:approach_flow}). - -\begin{figure}[b] - \hfil - \begin{tikzpicture} - \path (-1.5,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=orange!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Frontend} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=orange!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Backend} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - - \path (-3,-0.5) coordinate (cursor); - \draw (cursor) -- node[below] {HDL} ++(3,0) coordinate (cursor); - \draw[|-|] (cursor) -- node[below] {Internal Format(s)} ++(8,0) coordinate (cursor); - \draw (cursor) -- node[below] {Netlist} ++(3,0); - - \path (-3,3.5) coordinate (cursor); - \draw[-] (cursor) -- node[above] {High-Level} ++(3,0) coordinate (cursor); - \draw[-] (cursor) -- ++(8,0) coordinate (cursor); - \draw[->] (cursor) -- node[above] {Low-Level} ++(3,0); - - \end{tikzpicture} - \caption{General data- and control-flow of a synthesis tool} - \label{fig:approach_flow} -\end{figure} - -The first subsystem to be called is usually called a {\it frontend}. It does not process the data generated by -another subsystem but instead reads the user input---in the case of a HDL synthesis tool, the behavioural -HDL code. - -The subsystems that consume data from previous subsystems and produce data for the next subsystems (usually in the -same or a similar format) are called {\it passes}. - -The last subsystem that is executed transforms the data generated by the last pass into a suitable output -format and writes it to a disk file. This subsystem is usually called the {\it backend}. - -In Yosys all frontends, passes and backends are directly available as commands in the synthesis script. Thus -the user can easily create a custom synthesis flow just by calling passes in the right order in a synthesis -script. - -\section{Internal Formats in Yosys} - -Yosys uses two different internal formats. The first is used to store an abstract syntax tree (AST) of a Verilog -input file. This format is simply called {\it AST} and is generated by the Verilog Frontend. This data structure -is consumed by a subsystem called {\it AST Frontend}\footnote{In Yosys the term {\it pass} is only used to -refer to commands that operate on the RTLIL data structure.}. This AST Frontend then generates a design in Yosys' -main internal format, the Register-Transfer-Level-Intermediate-Language (RTLIL) representation. It does that -by first performing a number of simplifications within the AST representation and then generating RTLIL from -the simplified AST data structure. - -The RTLIL representation is used by all passes as input and outputs. This has the following advantages over -using different representational formats between different passes: - -\begin{itemize} -\item The passes can be rearranged in a different order and passes can be removed or inserted. -\item Passes can simply pass-thru the parts of the design they don't change without the need - to convert between formats. In fact Yosys passes output the same data structure they received - as input and performs all changes in place. -\item All passes use the same interface, thus reducing the effort required to understand a pass - when reading the Yosys source code, e.g.~when adding additional features. -\end{itemize} - -The RTLIL representation is basically a netlist representation with the following additional features: - -\begin{itemize} -\item An internal cell library with fixed-function cells to represent RTL datapath and register cells as well -as logical gate-level cells (single-bit gates and registers). -\item Support for multi-bit values that can use individual bits from wires as well as constant bits to -represent coarse-grain netlists. -\item Support for basic behavioural constructs (if-then-else structures and multi-case switches with -a sensitivity list for updating the outputs). -\item Support for multi-port memories. -\end{itemize} - -The use of RTLIL also has the disadvantage of having a very powerful format -between all passes, even when doing gate-level synthesis where the more -advanced features are not needed. In order to reduce complexity for passes that -operate on a low-level representation, these passes check the features used in -the input RTLIL and fail to run when unsupported high-level constructs are -used. In such cases a pass that transforms the higher-level constructs to -lower-level constructs must be called from the synthesis script first. - -\section{Typical Use Case} -\label{sec:typusecase} - -The following example script may be used in a synthesis flow to convert the behavioural Verilog code -from the input file {\tt design.v} to a gate-level netlist {\tt synth.v} using the cell library -described by the Liberty file \citeweblink{LibertyFormat} {\tt cells.lib}: - -\begin{lstlisting}[language=sh,numbers=left,frame=single] -# read input file to internal representation -read_verilog design.v - -# convert high-level behavioral parts ("processes") to d-type flip-flops and muxes -proc - -# perform some simple optimizations -opt - -# convert high-level memory constructs to d-type flip-flops and multiplexers -memory - -# perform some simple optimizations -opt - -# convert design to (logical) gate-level netlists -techmap - -# perform some simple optimizations -opt - -# map internal register types to the ones from the cell library -dfflibmap -liberty cells.lib - -# use ABC to map remaining logic to cells from the cell library -abc -liberty cells.lib - -# cleanup -opt - -# write results to output file -write_verilog synth.v -\end{lstlisting} - -A detailed description of the commands available in Yosys can be found in App.~\ref{commandref}. - diff --git a/manual/CHAPTER_Auxlibs.tex b/manual/CHAPTER_Auxlibs.tex deleted file mode 100644 index 440ea13754c..00000000000 --- a/manual/CHAPTER_Auxlibs.tex +++ /dev/null @@ -1,35 +0,0 @@ - -\chapter{Auxiliary Libraries} - -The Yosys source distribution contains some auxiliary libraries that are bundled -with Yosys. - -\section{SHA1} - -The files in {\tt libs/sha1/} provide a public domain SHA1 implementation written -by Steve Reid, Bruce Guenter, and Volker Grabsch. It is used for generating -unique names when specializing parameterized modules. - -\section{BigInt} - -The files in {\tt libs/bigint/} provide a library for performing arithmetic with -arbitrary length integers. It is written by Matt McCutchen \citeweblink{bigint}. - -The BigInt library is used for evaluating constant expressions, e.g.~using the {\tt -ConstEval} class provided in {\tt kernel/consteval.h}. - -\section{SubCircuit} -\label{sec:SubCircuit} - -The files in {\tt libs/subcircuit} provide a library for solving the subcircuit -isomorphism problem. It is written by Clifford Wolf and based on the Ullmann -Subgraph Isomorphism Algorithm \cite{UllmannSubgraphIsomorphism}. It is used by -the {\tt extract} pass (see {\tt help extract} or Sec.~\ref{cmd:extract}). - -\section{ezSAT} - -The files in {\tt libs/ezsat} provide a library for simplifying generating CNF -formulas for SAT solvers. It also contains bindings of MiniSAT. The ezSAT -library is written by Clifford Wolf. It is used by the {\tt sat} pass (see -{\tt help sat} or Sec.~\ref{cmd:sat}). - diff --git a/manual/CHAPTER_Auxprogs.tex b/manual/CHAPTER_Auxprogs.tex deleted file mode 100644 index f09b18f7610..00000000000 --- a/manual/CHAPTER_Auxprogs.tex +++ /dev/null @@ -1,26 +0,0 @@ - -\chapter{Auxiliary Programs} - -Besides the main {\tt yosys} executable, the Yosys distribution contains a set -of additional helper programs. - -\section{yosys-config} - -The {\tt yosys-config} tool (an auto-generated shell-script) can be used to -query compiler options and other information needed for building loadable -modules for Yosys. FIXME: See Sec.~\ref{chapter:prog} for details. - -\section{yosys-filterlib} -\label{sec:filterlib} - -The {\tt yosys-filterlib} tool is a small utility that can be used to strip -or extract information from a Liberty file. See Sec.~\ref{sec:techmap_extern} -for details. - -\section{yosys-abc} - -This is a fork of ABC \citeweblink{ABC} with a small set of custom modifications -that have not yet been accepted upstream. Not all versions of Yosys work with -all versions of ABC. So Yosys comes with its own yosys-abc to avoid -compatibility issues between the two. - diff --git a/manual/CHAPTER_Basics.tex b/manual/CHAPTER_Basics.tex deleted file mode 100644 index 5c60b7305a3..00000000000 --- a/manual/CHAPTER_Basics.tex +++ /dev/null @@ -1,839 +0,0 @@ - -\chapter{Basic Principles} -\label{chapter:basics} - -This chapter contains a short introduction to the basic principles of digital -circuit synthesis. - -\section{Levels of Abstraction} - -Digital circuits can be represented at different levels of abstraction. -During the design process a circuit is usually first specified using a higher -level abstraction. Implementation can then be understood as finding a -functionally equivalent representation at a lower abstraction level. When -this is done automatically using software, the term {\it synthesis} is used. - -So synthesis is the automatic conversion of a high-level representation of a -circuit to a functionally equivalent low-level representation of a circuit. -Figure~\ref{fig:Basics_abstractions} lists the different levels of abstraction -and how they relate to different kinds of synthesis. - -\begin{figure}[b!] - \hfil - \begin{tikzpicture} - \tikzstyle{lvl} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=15em] - \node[lvl] (sys) {System Level}; - \node[lvl] (hl) [below of=sys] {High Level}; - \node[lvl] (beh) [below of=hl] {Behavioral Level}; - \node[lvl] (rtl) [below of=beh] {Register-Transfer Level (RTL)}; - \node[lvl] (lg) [below of=rtl] {Logical Gate Level}; - \node[lvl] (pg) [below of=lg] {Physical Gate Level}; - \node[lvl] (sw) [below of=pg] {Switch Level}; - - \draw[dotted] (sys.east) -- ++(1,0) coordinate (sysx); - \draw[dotted] (hl.east) -- ++(1,0) coordinate (hlx); - \draw[dotted] (beh.east) -- ++(1,0) coordinate (behx); - \draw[dotted] (rtl.east) -- ++(1,0) coordinate (rtlx); - \draw[dotted] (lg.east) -- ++(1,0) coordinate (lgx); - \draw[dotted] (pg.east) -- ++(1,0) coordinate (pgx); - \draw[dotted] (sw.east) -- ++(1,0) coordinate (swx); - - \draw[gray,|->] (sysx) -- node[right] {System Design} (hlx); - \draw[|->|] (hlx) -- node[right] {High Level Synthesis (HLS)} (behx); - \draw[->|] (behx) -- node[right] {Behavioral Synthesis} (rtlx); - \draw[->|] (rtlx) -- node[right] {RTL Synthesis} (lgx); - \draw[->|] (lgx) -- node[right] {Logic Synthesis} (pgx); - \draw[gray,->|] (pgx) -- node[right] {Cell Library} (swx); - - \draw[dotted] (behx) -- ++(5,0) coordinate (a); - \draw[dotted] (pgx) -- ++(5,0) coordinate (b); - \draw[|->|] (a) -- node[right] {Yosys} (b); - \end{tikzpicture} - \caption{Different levels of abstraction and synthesis.} - \label{fig:Basics_abstractions} -\end{figure} - -Regardless of the way a lower level representation of a circuit is -obtained (synthesis or manual design), the lower level representation is usually -verified by comparing simulation results of the lower level and the higher level -representation \footnote{In recent years formal equivalence -checking also became an important verification method for validating RTL and -lower abstraction representation of the design.}. -Therefore even if no synthesis is used, there must still be a simulatable -representation of the circuit in all levels to allow for verification of the -design. - -Note: The exact meaning of terminology such as ``High-Level'' is of course not -fixed over time. For example the HDL ``ABEL'' was first introduced in 1985 as ``A High-Level -Design Language for Programmable Logic Devices'' \cite{ABEL}, but would not -be considered a ``High-Level Language'' today. - -\subsection{System Level} - -The System Level abstraction of a system only looks at its biggest building -blocks like CPUs and computing cores. At this level the circuit is usually described -using traditional programming languages like C/C++ or Matlab. Sometimes special -software libraries are used that are aimed at simulation circuits on the system -level, such as SystemC. - -Usually no synthesis tools are used to automatically transform a system level -representation of a circuit to a lower-level representation. But system level -design tools exist that can be used to connect system level building blocks. - -The IEEE 1685-2009 standard defines the IP-XACT file format that can be used to -represent designs on the system level and building blocks that can be used in -such system level designs. \cite{IP-XACT} - -\subsection{High Level} - -The high-level abstraction of a system (sometimes referred to as {\it -algorithmic} level) is also often represented using traditional programming -languages, but with a reduced feature set. For example when representing a -design at the high level abstraction in C, pointers can only be used to mimic -concepts that can be found in hardware, such as memory interfaces. Full -featured dynamic memory management is not allowed as it has no corresponding -concept in digital circuits. - -Tools exist to synthesize high level code (usually in the form of C/C++/SystemC -code with additional metadata) to behavioural HDL code (usually in the form of -Verilog or VHDL code). Aside from the many commercial tools for high level synthesis -there are also a number of FOSS tools for high level synthesis -\citeweblink{C_to_Verilog} \citeweblink{LegUp}. - -\subsection{Behavioural Level} - -At the behavioural abstraction level a language aimed at hardware description such -as Verilog or VHDL is used to describe the circuit, but so-called {\it behavioural -modelling} is used in at least part of the circuit description. In behavioural -modelling there must be a language feature that allows for imperative programming to be used to -describe data paths and registers. This is the {\tt always}-block in Verilog and -the {\tt process}-block in VHDL. - -In behavioural modelling, code fragments are provided together with a {\it -sensitivity list}; a list of signals and conditions. In simulation, the code -fragment is executed whenever a signal in the sensitivity list changes its -value or a condition in the sensitivity list is triggered. A synthesis tool -must be able to transfer this representation into an appropriate datapath followed -by the appropriate types of register. - -For example consider the following Verilog code fragment: - -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -always @(posedge clk) - y <= a + b; -\end{lstlisting} - -In simulation the statement \lstinline[language=Verilog]{y <= a + b} is executed whenever -a positive edge on the signal \lstinline[language=Verilog]{clk} is detected. The synthesis -result however will contain an adder that calculates the sum \lstinline[language=Verilog]{a + b} -all the time, followed by a d-type flip-flop with the adder output on its D-input and the -signal \lstinline[language=Verilog]{y} on its Q-output. - -Usually the imperative code fragments used in behavioural modelling can contain -statements for conditional execution (\lstinline[language=Verilog]{if}- and -\lstinline[language=Verilog]{case}-statements in Verilog) as well as loops, -as long as those loops can be completely unrolled. - -Interestingly there seems to be no other FOSS Tool that is capable of -performing Verilog or VHDL behavioural syntheses besides Yosys (see -App.~\ref{chapter:sota}). - -\subsection{Register-Transfer Level (RTL)} - -On the Register-Transfer Level the design is represented by combinatorial data -paths and registers (usually d-type flip flops). The following Verilog code fragment -is equivalent to the previous Verilog example, but is in RTL representation: - -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -assign tmp = a + b; // combinatorial data path - -always @(posedge clk) // register - y <= tmp; -\end{lstlisting} - -A design in RTL representation is usually stored using HDLs like Verilog and VHDL. But only -a very limited subset of features is used, namely minimalistic {\tt always}-blocks (Verilog) -or {\tt process}-blocks (VHDL) that model the register type used and unconditional assignments -for the datapath logic. The use of HDLs on this level simplifies simulation as no additional -tools are required to simulate a design in RTL representation. - -Many optimizations and analyses can be performed best at the RTL level. Examples include FSM -detection and optimization, identification of memories or other larger building blocks -and identification of shareable resources. - -Note that RTL is the first abstraction level in which the circuit is represented as a -graph of circuit elements (registers and combinatorial cells) and signals. Such a graph, -when encoded as list of cells and connections, is called a netlist. - -RTL synthesis is easy as each circuit node element in the netlist can simply be replaced -with an equivalent gate-level circuit. However, usually the term {\it RTL synthesis} does -not only refer to synthesizing an RTL netlist to a gate level netlist but also to performing -a number of highly sophisticated optimizations within the RTL representation, such as -the examples listed above. - -A number of FOSS tools exist that can perform isolated tasks within the domain of RTL -synthesis steps. But there seems to be no FOSS tool that covers a wide range of RTL -synthesis operations. - -\subsection{Logical Gate Level} - -At the logical gate level the design is represented by a netlist that uses only -cells from a small number of single-bit cells, such as basic logic gates (AND, -OR, NOT, XOR, etc.) and registers (usually D-Type Flip-flops). - -A number of netlist formats exists that can be used on this level, e.g.~the Electronic Design -Interchange Format (EDIF), but for ease of simulation often a HDL netlist is used. The latter -is a HDL file (Verilog or VHDL) that only uses the most basic language constructs for instantiation -and connecting of cells. - -There are two challenges in logic synthesis: First finding opportunities for optimizations -within the gate level netlist and second the optimal (or at least good) mapping of the logic -gate netlist to an equivalent netlist of physically available gate types. - -The simplest approach to logic synthesis is {\it two-level logic synthesis}, where a logic function -is converted into a sum-of-products representation, e.g.~using a Karnaugh map. -This is a simple approach, but has exponential worst-case effort and cannot make efficient use of -physical gates other than AND/NAND-, OR/NOR- and NOT-Gates. - -Therefore modern logic synthesis tools utilize much more complicated {\it multi-level logic -synthesis} algorithms \cite{MultiLevelLogicSynth}. Most of these algorithms convert the -logic function to a Binary-Decision-Diagram (BDD) or And-Inverter-Graph (AIG) and work from that -representation. The former has the advantage that it has a unique normalized form. The latter has -much better worst case performance and is therefore better suited for the synthesis of large -logic functions. - -Good FOSS tools exists for multi-level logic synthesis \citeweblink{ABC} -\citeweblink{AIGER} \citeweblink{MVSIS}. - -Yosys contains basic logic synthesis functionality but can also use ABC -\citeweblink{ABC} for the logic synthesis step. Using ABC is recommended. - -\subsection{Physical Gate Level} - -On the physical gate level only gates are used that are physically available on -the target architecture. In some cases this may only be NAND, NOR and NOT gates as well as -D-Type registers. In other cases this might include cells that are more complex than the cells -used at the logical gate level (e.g.~complete half-adders). In the case of an FPGA-based -design the physical gate level representation is a netlist of LUTs with optional output -registers, as these are the basic building blocks of FPGA logic cells. - -For the synthesis tool chain this abstraction is usually the lowest level. In -case of an ASIC-based design the cell library might contain further information on -how the physical cells map to individual switches (transistors). - -\subsection{Switch Level} - -A switch level representation of a circuit is a netlist utilizing single transistors as cells. -Switch level modelling is possible in Verilog and VHDL, but is seldom used in modern designs, -as in modern digital ASIC or FPGA flows the physical gates are considered the atomic build blocks -of the logic circuit. - -\subsection{Yosys} - -Yosys is a Verilog HDL synthesis tool. This means that it takes a behavioural -design description as input and generates an RTL, logical gate or physical gate -level description of the design as output. Yosys' main strengths are behavioural -and RTL synthesis. A wide range of commands (synthesis passes) exist -within Yosys that can be used to perform a wide range of synthesis tasks within -the domain of behavioural, rtl and logic synthesis. Yosys is designed to be -extensible and therefore is a good basis for implementing custom synthesis -tools for specialised tasks. - -\section{Features of Synthesizable Verilog} - -The subset of Verilog \cite{Verilog2005} that is synthesizable is specified in -a separate IEEE standards document, the IEEE standard 1364.1-2002 \cite{VerilogSynth}. -This standard also describes how certain language constructs are to be interpreted in -the scope of synthesis. - -This section provides a quick overview of the most important features of -synthesizable Verilog, structured in order of increasing complexity. - -\subsection{Structural Verilog} - -{\it Structural Verilog} (also known as {\it Verilog Netlists}) is a Netlist in -Verilog syntax. Only the following language constructs are used in this case: - -\begin{itemize} -\item Constant values -\item Wire and port declarations -\item Static assignments of signals to other signals -\item Cell instantiations -\end{itemize} - -Many tools (especially at the back end of the synthesis chain) only support -structural Verilog as input. ABC is an example of such a tool. Unfortunately -there is no standard specifying what {\it Structural Verilog} actually is, -leading to some confusion about what syntax constructs are supported in -structural Verilog when it comes to features such as attributes or multi-bit -signals. - -\subsection{Expressions in Verilog} - -In all situations where Verilog accepts a constant value or signal name, -expressions using arithmetic operations such as -\lstinline[language=Verilog]{+}, \lstinline[language=Verilog]{-} and \lstinline[language=Verilog]{*}, -boolean operations such as -\lstinline[language=Verilog]{&} (AND), \lstinline[language=Verilog]{|} (OR) and \lstinline[language=Verilog]{^} (XOR) -and many others (comparison operations, unary operator, etc.) can also be used. - -During synthesis these operators are replaced by cells that implement the respective function. - -Many FOSS tools that claim to be able to process Verilog in fact only support -basic structural Verilog and simple expressions. Yosys can be used to convert -full featured synthesizable Verilog to this simpler subset, thus enabling such -applications to be used with a richer set of Verilog features. - -\subsection{Behavioural Modelling} - -Code that utilizes the Verilog {\tt always} statement is using {\it Behavioural -Modelling}. In behavioural modelling, a circuit is described by means of imperative -program code that is executed on certain events, namely any change, a rising -edge, or a falling edge of a signal. This is a very flexible construct during -simulation but is only synthesizable when one of the following is modelled: - -\begin{itemize} -\item {\bf Asynchronous or latched logic} \\ -In this case the sensitivity list must contain all expressions that are used within -the {\tt always} block. The syntax \lstinline[language=Verilog]{@*} can be used -for these cases. Examples of this kind include: - -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -// asynchronous -always @* begin - if (add_mode) - y <= a + b; - else - y <= a - b; -end - -// latched -always @* begin - if (!hold) - y <= a + b; -end -\end{lstlisting} - -Note that latched logic is often considered bad style and in many cases just -the result of sloppy HDL design. Therefore many synthesis tools generate warnings -whenever latched logic is generated. - -\item {\bf Synchronous logic (with optional synchronous reset)} \\ -This is logic with d-type flip-flops on the output. In this case the sensitivity -list must only contain the respective clock edge. Example: -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -// counter with synchronous reset -always @(posedge clk) begin - if (reset) - y <= 0; - else - y <= y + 1; -end -\end{lstlisting} - -\item {\bf Synchronous logic with asynchronous reset} \\ -This is logic with d-type flip-flops with asynchronous resets on the output. In -this case the sensitivity list must only contain the respective clock and reset edges. -The values assigned in the reset branch must be constant. Example: -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -// counter with asynchronous reset -always @(posedge clk, posedge reset) begin - if (reset) - y <= 0; - else - y <= y + 1; -end -\end{lstlisting} -\end{itemize} - -Many synthesis tools support a wider subset of flip-flops that can be modelled -using {\tt always}-statements (including Yosys). But only the ones listed above -are covered by the Verilog synthesis standard and when writing new designs one -should limit herself or himself to these cases. - -In behavioural modelling, blocking assignments (=) and non-blocking assignments -(<=) can be used. The concept of blocking vs.~non-blocking assignment is one -of the most misunderstood constructs in Verilog \cite{Cummings00}. - -The blocking assignment behaves exactly like an assignment in any imperative -programming language, while with the non-blocking assignment the right hand side -of the assignment is evaluated immediately but the actual update of the left -hand side register is delayed until the end of the time-step. For example the Verilog -code \lstinline[language=Verilog]{a <= b; b <= a;} exchanges the values of -the two registers. See Sec.~\ref{sec:blocking_nonblocking} for a more -detailed description of this behaviour. - -\subsection{Functions and Tasks} - -Verilog supports {\it Functions} and {\it Tasks} to bundle statements that are -used in multiple places (similar to {\it Procedures} in imperative programming). -Both constructs can be implemented easily by substituting the function/task-call -with the body of the function or task. - -\subsection{Conditionals, Loops and Generate-Statements} - -Verilog supports \lstinline[language=Verilog]{if-else}-statements and -\lstinline[language=Verilog]{for}-loops inside \lstinline[language=Verilog]{always}-statements. - -It also supports both features in \lstinline[language=Verilog]{generate}-statements -on the module level. This can be used to selectively enable or disable parts of the -module based on the module parameters (\lstinline[language=Verilog]{if-else}) -or to generate a set of similar subcircuits (\lstinline[language=Verilog]{for}). - -While the \lstinline[language=Verilog]{if-else}-statement -inside an always-block is part of behavioural modelling, the three other cases -are (at least for a synthesis tool) part of a built-in macro processor. Therefore it must -be possible for the synthesis tool to completely unroll all loops and evaluate the -condition in all \lstinline[language=Verilog]{if-else}-statement in -\lstinline[language=Verilog]{generate}-statements using const-folding. - -Examples for this can be found in Fig.~\ref{fig:StateOfTheArt_for} and -Fig.~\ref{fig:StateOfTheArt_gen} in App.~\ref{chapter:sota}. - -\subsection{Arrays and Memories} - -Verilog supports arrays. This is in general a synthesizable language feature. -In most cases arrays can be synthesized by generating addressable memories. -However, when complex or asynchronous access patterns are used, it is not -possible to model an array as memory. In these cases the array must -be modelled using individual signals for each word and all accesses to the array -must be implemented using large multiplexers. - -In some cases it would be possible to model an array using memories, but it -is not desired. Consider the following delay circuit: -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -module (clk, in_data, out_data); - -parameter BITS = 8; -parameter STAGES = 4; - -input clk; -input [BITS-1:0] in_data; -output [BITS-1:0] out_data; -reg [BITS-1:0] ffs [STAGES-1:0]; - -integer i; -always @(posedge clk) begin - ffs[0] <= in_data; - for (i = 1; i < STAGES; i = i+1) - ffs[i] <= ffs[i-1]; -end - -assign out_data = ffs[STAGES-1]; - -endmodule -\end{lstlisting} - -This could be implemented using an addressable memory with {\tt STAGES} input -and output ports. A better implementation would be to use a simple chain of flip-flops -(a so-called shift register). -This better implementation can either be obtained by first creating a memory-based -implementation and then optimizing it based on the static address signals for all ports -or directly identifying such situations in the language front end and converting -all memory accesses to direct accesses to the correct signals. - -\section{Challenges in Digital Circuit Synthesis} - -This section summarizes the most important challenges in digital circuit -synthesis. Tools can be characterized by how well they address these topics. - -\subsection{Standards Compliance} - -The most important challenge is compliance with the HDL standards in question (in case -of Verilog the IEEE Standards 1364.1-2002 and 1364-2005). This can be broken down in two -items: - -\begin{itemize} -\item Completeness of implementation of the standard -\item Correctness of implementation of the standard -\end{itemize} - -Completeness is mostly important to guarantee compatibility -with existing HDL code. Once a design has been verified and tested, HDL designers -are very reluctant regarding changes to the design, even if it is only about -a few minor changes to work around a missing feature in a new synthesis tool. - -Correctness is crucial. In some areas this is obvious (such as -correct synthesis of basic behavioural models). But it is also crucial for the -areas that concern minor details of the standard, such as the exact rules -for handling signed expressions, even when the HDL code does not target -different synthesis tools. This is because (unlike software source code that -is only processed by compilers), in most design flows HDL code is not only -processed by the synthesis tool but also by one or more simulators and sometimes -even a formal verification tool. It is key for this verification process -that all these tools use the same interpretation for the HDL code. - -\subsection{Optimizations} - -Generally it is hard to give a one-dimensional description of how well a synthesis tool -optimizes the design. First of all because not all optimizations are applicable to all -designs and all synthesis tasks. Some optimizations work (best) on a coarse-grained level -(with complex cells such as adders or multipliers) and others work (best) on a fine-grained -level (single bit gates). Some optimizations target area and others target speed. -Some work well on large designs while others don't scale well and can only be applied -to small designs. - -A good tool is capable of applying a wide range of optimizations at different -levels of abstraction and gives the designer control over which optimizations -are performed (or skipped) and what the optimization goals are. - -\subsection{Technology Mapping} - -Technology mapping is the process of converting the design into a netlist of -cells that are available in the target architecture. In an ASIC flow this might -be the process-specific cell library provided by the fab. In an FPGA flow this -might be LUT cells as well as special function units such as dedicated multipliers. -In a coarse-grain flow this might even be more complex special function units. - -An open and vendor independent tool is especially of interest if it supports -a wide range of different types of target architectures. - -\section{Script-Based Synthesis Flows} - -A digital design is usually started by implementing a high-level or -system-level simulation of the desired function. This description is then -manually transformed (or re-implemented) into a synthesizable lower-level -description (usually at the behavioural level) and the equivalence of the -two representations is verified by simulating both and comparing the simulation -results. - -Then the synthesizable description is transformed to lower-level -representations using a series of tools and the results are again verified -using simulation. This process is illustrated in Fig.~\ref{fig:Basics_flow}. - -\begin{figure}[t!] - \hfil - \begin{tikzpicture} - \tikzstyle{manual} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=8em, node distance=10em] - \tikzstyle{auto} = [draw, fill=orange!10, rectangle, minimum height=2em, minimum width=8em, node distance=10em] - - \node[manual] (sys) {\begin{minipage}{8em} - \center - System Level \\ - Model - \end{minipage}}; - \node[manual] (beh) [right of=sys] {\begin{minipage}{8em} - \center - Behavioral \\ - Model - \end{minipage}}; - \node[auto] (rtl) [right of=beh] {\begin{minipage}{8em} - \center - RTL \\ - Model - \end{minipage}}; - \node[auto] (gates) [right of=rtl] {\begin{minipage}{8em} - \center - Gate-Level \\ - Model - \end{minipage}}; - - \draw[-latex] (beh) edge[double, bend left] node[above] {synthesis} (rtl); - \draw[-latex] (rtl) edge[double, bend left] node[above] {synthesis} (gates); - - \draw[latex-latex] (sys) edge[bend right] node[below] {verify} (beh); - \draw[latex-latex] (beh) edge[bend right] node[below] {verify} (rtl); - \draw[latex-latex] (rtl) edge[bend right] node[below] {verify} (gates); - \end{tikzpicture} - \caption{Typical design flow. Green boxes represent manually created models. Orange boxes represent - models generated by synthesis tools.} - \label{fig:Basics_flow} -\end{figure} - -In this example the System Level Model and the Behavioural Model are both -manually written design files. After the equivalence of system level model -and behavioural model has been verified, the lower level representations of the -design can be generated using synthesis tools. Finally the RTL Model and -the Gate-Level Model are verified and the design process is finished. - -However, in any real-world design effort there will be multiple iterations for -this design process. The reason for this can be the late change of a design -requirement or the fact that the analysis of a low-abstraction model (e.g.~gate-level -timing analysis) revealed that a design change is required in order to meet -the design requirements (e.g.~maximum possible clock speed). - -Whenever the behavioural model or the system level model is -changed their equivalence must be re-verified by re-running the simulations -and comparing the results. Whenever the behavioural model is changed the -synthesis must be re-run and the synthesis results must be re-verified. - -In order to guarantee reproducibility it is important to be able to re-run all -automatic steps in a design project with a fixed set of settings easily. -Because of this, usually all programs used in a synthesis flow can be -controlled using scripts. This means that all functions are available via -text commands. When such a tool provides a GUI, this is complementary to, -and not instead of, a command line interface. - -Usually a synthesis flow in an UNIX/Linux environment would be controlled by a -shell script that calls all required tools (synthesis and simulation/verification -in this example) in the correct order. Each of these tools would be called with -a script file containing commands for the respective tool. All settings required -for the tool would be provided by these script files so that no manual interaction -would be necessary. These script files are considered design sources and should -be kept under version control just like the source code of the system level and the -behavioural model. - -\section{Methods from Compiler Design} - -Some parts of synthesis tools involve problem domains that are traditionally known from -compiler design. This section addresses some of these domains. - -\subsection{Lexing and Parsing} - -The best known concepts from compiler design are probably {\it lexing} and {\it parsing}. -These are two methods that together can be used to process complex computer languages -easily. \cite{Dragonbook} - -A {\it lexer} consumes single characters from the input and generates a stream of {\it lexical -tokens} that consist of a {\it type} and a {\it value}. For example the Verilog input -``\lstinline[language=Verilog]{assign foo = bar + 42;}'' might be translated by the lexer -to the list of lexical tokens given in Tab.~\ref{tab:Basics_tokens}. - -\begin{table}[t] -\hfil -\begin{tabular}{ll} -Token-Type & Token-Value \\ -\hline -\tt TOK\_ASSIGN & - \\ -\tt TOK\_IDENTIFIER & ``{\tt foo}'' \\ -\tt TOK\_EQ & - \\ -\tt TOK\_IDENTIFIER & ``{\tt bar}'' \\ -\tt TOK\_PLUS & - \\ -\tt TOK\_NUMBER & 42 \\ -\tt TOK\_SEMICOLON & - \\ -\end{tabular} -\caption{Exemplary token list for the statement ``\lstinline[language=Verilog]{assign foo = bar + 42;}''.} -\label{tab:Basics_tokens} -\end{table} - -The lexer is usually generated by a lexer generator (e.g.~{\tt flex} \citeweblink{flex}) from a -description file that is using regular expressions to specify the text pattern that should match -the individual tokens. - -The lexer is also responsible for skipping ignored characters (such as whitespace outside string -constants and comments in the case of Verilog) and converting the original text snippet to a token -value. - -Note that individual keywords use different token types (instead of a keyword type with different -token values). This is because the parser usually can only use the Token-Type to make a decision on -the grammatical role of a token. - -The parser then transforms the list of tokens into a parse tree that closely resembles the productions -from the computer languages grammar. As the lexer, the parser is also typically generated by a code -generator (e.g.~{\tt bison} \citeweblink{bison}) from a grammar description in Backus-Naur Form (BNF). - -Let's consider the following BNF (in Bison syntax): - -\begin{lstlisting}[numbers=left,frame=single] -assign_stmt: TOK_ASSIGN TOK_IDENTIFIER TOK_EQ expr TOK_SEMICOLON; -expr: TOK_IDENTIFIER | TOK_NUMBER | expr TOK_PLUS expr; -\end{lstlisting} - -\begin{figure}[b!] - \hfil - \begin{tikzpicture} - \tikzstyle{node} = [draw, fill=green!10, ellipse, minimum height=2em, minimum width=8em, node distance=10em] - - \draw (+0,+1) node[node] (n1) {\tt assign\_stmt}; - - \draw (-6,-1) node[node] (n11) {\tt TOK\_ASSIGN}; - \draw (-3,-2) node[node] (n12) {\tt TOK\_IDENTIFIER}; - \draw (+0,-1) node[node] (n13) {\tt TOK\_EQ}; - \draw (+3,-2) node[node] (n14) {\tt expr}; - \draw (+6,-1) node[node] (n15) {\tt TOK\_SEMICOLON}; - - \draw (-1,-4) node[node] (n141) {\tt expr}; - \draw (+3,-4) node[node] (n142) {\tt TOK\_PLUS}; - \draw (+7,-4) node[node] (n143) {\tt expr}; - - \draw (-1,-5.5) node[node] (n1411) {\tt TOK\_IDENTIFIER}; - \draw (+7,-5.5) node[node] (n1431) {\tt TOK\_NUMBER}; - - \draw[-latex] (n1) -- (n11); - \draw[-latex] (n1) -- (n12); - \draw[-latex] (n1) -- (n13); - \draw[-latex] (n1) -- (n14); - \draw[-latex] (n1) -- (n15); - - \draw[-latex] (n14) -- (n141); - \draw[-latex] (n14) -- (n142); - \draw[-latex] (n14) -- (n143); - - \draw[-latex] (n141) -- (n1411); - \draw[-latex] (n143) -- (n1431); - \end{tikzpicture} - \caption{Example parse tree for the Verilog expression ``\lstinline[language=Verilog]{assign foo = bar + 42;}''.} - \label{fig:Basics_parsetree} -\end{figure} - -The parser converts the token list to the parse tree in Fig.~\ref{fig:Basics_parsetree}. Note that the parse -tree never actually exists as a whole as data structure in memory. Instead the parser calls user-specified -code snippets (so-called {\it reduce-functions}) for all inner nodes of the parse tree in depth-first order. - -In some very simple applications (e.g.~code generation for stack machines) it is possible to perform the -task at hand directly in the reduce functions. But usually the reduce functions are only used to build an in-memory -data structure with the relevant information from the parse tree. This data structure is called an {\it abstract -syntax tree} (AST). - -The exact format for the abstract syntax tree is application specific (while the format of the parse tree and token -list are mostly dictated by the grammar of the language at hand). Figure~\ref{fig:Basics_ast} illustrates what an -AST for the parse tree in Fig.~\ref{fig:Basics_parsetree} could look like. - -Usually the AST is then converted into yet another representation that is more suitable for further processing. -In compilers this is often an assembler-like three-address-code intermediate representation. \cite{Dragonbook} - -\begin{figure}[t] - \hfil - \begin{tikzpicture} - \tikzstyle{node} = [draw, fill=green!10, ellipse, minimum height=2em, minimum width=8em, node distance=10em] - - \draw (+0,+0) node[node] (n1) {\tt ASSIGN}; - - \draw (-2,-2) node[node] (n11) {\tt ID: foo}; - \draw (+2,-2) node[node] (n12) {\tt PLUS}; - - \draw (+0,-4) node[node] (n121) {\tt ID: bar}; - \draw (+4,-4) node[node] (n122) {\tt CONST: 42}; - - \draw[-latex] (n1) -- (n11); - \draw[-latex] (n1) -- (n12); - - \draw[-latex] (n12) -- (n121); - \draw[-latex] (n12) -- (n122); - \end{tikzpicture} - \caption{Example abstract syntax tree for the Verilog expression ``\lstinline[language=Verilog]{assign foo = bar + 42;}''.} - \label{fig:Basics_ast} -\end{figure} - -\subsection{Multi-Pass Compilation} - -Complex problems are often best solved when split up into smaller problems. This is certainly true -for compilers as well as for synthesis tools. The components responsible for solving the smaller problems can -be connected in two different ways: through {\it Single-Pass Pipelining} and by using {\it Multiple Passes}. - -Traditionally a parser and lexer are connected using the pipelined approach: The lexer provides a function that -is called by the parser. This function reads data from the input until a complete lexical token has been read. Then -this token is returned to the parser. So the lexer does not first generate a complete list of lexical tokens -and then pass it to the parser. Instead they run concurrently and the parser can consume tokens as -the lexer produces them. - -The single-pass pipelining approach has the advantage of lower memory footprint (at no time must the complete design -be kept in memory) but has the disadvantage of tighter coupling between the interacting components. - -Therefore single-pass pipelining should only be used when the lower memory footprint is required or the -components are also conceptually tightly coupled. The latter certainly is the case for a parser and its lexer. -But when data is passed between two conceptually loosely coupled components it is often -beneficial to use a multi-pass approach. - -In the multi-pass approach the first component processes all the data and the result is stored in a in-memory -data structure. Then the second component is called with this data. This reduces complexity, as only one -component is running at a time. It also improves flexibility as components can be exchanged easier. - -Most modern compilers are multi-pass compilers. - -\iffalse -\subsection{Static Single Assignment Form} - -In imperative programming (and behavioural HDL design) it is possible to assign the same variable multiple times. -This can either mean that the variable is independently used in two different contexts or that the final value -of the variable depends on a condition. - -The following examples show C code in which one variable is used independently in two different contexts: - -\begin{minipage}{7.7cm} -\begin{lstlisting}[numbers=left,frame=single,language=C++] -void demo1() -{ - int a = 1; - printf("%d\n", a); - - a = 2; - printf("%d\n", a); -} -\end{lstlisting} -\end{minipage} -\hfil -\begin{minipage}{7.7cm} -\begin{lstlisting}[frame=single,language=C++] -void demo1() -{ - int a = 1; - printf("%d\n", a); - - int b = 2; - printf("%d\n", b); -} -\end{lstlisting} -\end{minipage} - -\begin{minipage}{7.7cm} -\begin{lstlisting}[numbers=left,frame=single,language=C++] -void demo2(bool foo) -{ - int a; - if (foo) { - a = 23; - printf("%d\n", a); - } else { - a = 42; - printf("%d\n", a); - } -} -\end{lstlisting} -\end{minipage} -\hfil -\begin{minipage}{7.7cm} -\begin{lstlisting}[frame=single,language=C++] -void demo2(bool foo) -{ - int a, b; - if (foo) { - a = 23; - printf("%d\n", a); - } else { - b = 42; - printf("%d\n", b); - } -} -\end{lstlisting} -\end{minipage} - -In both examples the left version (only variable \lstinline[language=C++]{a}) and the right version (variables -\lstinline[language=Verilog]{a} and \lstinline[language=Verilog]{b}) are equivalent. Therefore it is -desired for further processing to bring the code in an equivalent form for both cases. - -In the following example the variable is assigned twice but it cannot be easily replaced by two variables: - -\begin{lstlisting}[frame=single,language=C++] -void demo3(bool foo) -{ - int a = 23 - if (foo) - a = 42; - printf("%d\n", a); -} -\end{lstlisting} - -Static single assignment (SSA) form is a representation of imperative code that uses identical representations -for the left and right version of demos 1 and 2, but can still represent demo 3. In SSA form each assignment -assigns a new variable (usually written with an index). But it also introduces a special $\Phi$-function to -merge the different instances of a variable when needed. In C-pseudo-code the demo 3 would be written as follows -using SSA from: - -\begin{lstlisting}[frame=single,language=C++] -void demo3(bool foo) -{ - int a_1, a_2, a_3; - a_1 = 23 - if (foo) - a_2 = 42; - a_3 = phi(a_1, a_2); - printf("%d\n", a_3); -} -\end{lstlisting} - -The $\Phi$-function is usually interpreted as ``these variables must be stored -in the same memory location'' during code generation. Most modern compilers for imperative languages -such as C/C++ use SSA form for at least some of its passes as it is very easy to manipulate and analyse. -\fi - diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex deleted file mode 100644 index d4572a88a29..00000000000 --- a/manual/CHAPTER_CellLib.tex +++ /dev/null @@ -1,923 +0,0 @@ - -\chapter{Internal Cell Library} -\label{chapter:celllib} - -Most of the passes in Yosys operate on netlists, i.e.~they only care about the RTLIL::Wire and RTLIL::Cell -objects in an RTLIL::Module. This chapter discusses the cell types used by Yosys to represent a behavioural -design internally. - -This chapter is split in two parts. In the first part the internal RTL cells are covered. These cells -are used to represent the design on a coarse grain level. Like in the original HDL code on this level the -cells operate on vectors of signals and complex cells like adders exist. In the second part the internal -gate cells are covered. These cells are used to represent the design on a fine-grain gate-level. All cells -from this category operate on single bit signals. - -\section{RTL Cells} - -Most of the RTL cells closely resemble the operators available in HDLs such as -Verilog or VHDL. Therefore Verilog operators are used in the following sections -to define the behaviour of the RTL cells. - -Note that all RTL cells have parameters indicating the size of inputs and outputs. When -passes modify RTL cells they must always keep the values of these parameters in sync with -the size of the signals connected to the inputs and outputs. - -Simulation models for the RTL cells can be found in the file {\tt techlibs/common/simlib.v} in the Yosys -source tree. - -\subsection{Unary Operators} - -All unary RTL cells have one input port \B{A} and one output port \B{Y}. They also -have the following parameters: - -\begin{itemize} -\item \B{A\_SIGNED} \\ -Set to a non-zero value if the input \B{A} is signed and therefore should be sign-extended -when needed. - -\item \B{A\_WIDTH} \\ -The width of the input port \B{A}. - -\item \B{Y\_WIDTH} \\ -The width of the output port \B{Y}. -\end{itemize} - -Table~\ref{tab:CellLib_unary} lists all cells for unary RTL operators. - -\begin{table}[t!] -\hfil -\begin{tabular}{ll} -Verilog & Cell Type \\ -\hline -\lstinline[language=Verilog]; Y = ~A ; & {\tt \$not} \\ -\lstinline[language=Verilog]; Y = +A ; & {\tt \$pos} \\ -\lstinline[language=Verilog]; Y = -A ; & {\tt \$neg} \\ -\hline -\lstinline[language=Verilog]; Y = &A ; & {\tt \$reduce\_and} \\ -\lstinline[language=Verilog]; Y = |A ; & {\tt \$reduce\_or} \\ -\lstinline[language=Verilog]; Y = ^A ; & {\tt \$reduce\_xor} \\ -\lstinline[language=Verilog]; Y = ~^A ; & {\tt \$reduce\_xnor} \\ -\hline -\lstinline[language=Verilog]; Y = |A ; & {\tt \$reduce\_bool} \\ -\lstinline[language=Verilog]; Y = !A ; & {\tt \$logic\_not} -\end{tabular} -\caption{Cell types for unary operators with their corresponding Verilog expressions.} -\label{tab:CellLib_unary} -\end{table} - -For the unary cells that output a logical value ({\tt \$reduce\_and}, {\tt \$reduce\_or}, -{\tt \$reduce\_xor}, {\tt \$reduce\_xnor}, {\tt \$reduce\_bool}, {\tt \$logic\_not}), -when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended, -and only the least significant bit varies. - -Note that {\tt \$reduce\_or} and {\tt \$reduce\_bool} actually represent the same -logic function. But the HDL frontends generate them in different situations. A -{\tt \$reduce\_or} cell is generated when the prefix {\tt |} operator is being used. A -{\tt \$reduce\_bool} cell is generated when a bit vector is used as a condition in -an {\tt if}-statement or {\tt ?:}-expression. - -\subsection{Binary Operators} - -All binary RTL cells have two input ports \B{A} and \B{B} and one output port \B{Y}. They -also have the following parameters: - -\begin{itemize} -\item \B{A\_SIGNED} \\ -Set to a non-zero value if the input \B{A} is signed and therefore should be sign-extended -when needed. - -\item \B{A\_WIDTH} \\ -The width of the input port \B{A}. - -\item \B{B\_SIGNED} \\ -Set to a non-zero value if the input \B{B} is signed and therefore should be sign-extended -when needed. - -\item \B{B\_WIDTH} \\ -The width of the input port \B{B}. - -\item \B{Y\_WIDTH} \\ -The width of the output port \B{Y}. -\end{itemize} - -Table~\ref{tab:CellLib_binary} lists all cells for binary RTL operators. - -\begin{table}[t!] -\hfil -\begin{tabular}[t]{ll} -Verilog & Cell Type \\ -\hline -\lstinline[language=Verilog]; Y = A & B; & {\tt \$and} \\ -\lstinline[language=Verilog]; Y = A | B; & {\tt \$or} \\ -\lstinline[language=Verilog]; Y = A ^ B; & {\tt \$xor} \\ -\lstinline[language=Verilog]; Y = A ~^ B; & {\tt \$xnor} \\ -\hline -\lstinline[language=Verilog]; Y = A << B; & {\tt \$shl} \\ -\lstinline[language=Verilog]; Y = A >> B; & {\tt \$shr} \\ -\lstinline[language=Verilog]; Y = A <<< B; & {\tt \$sshl} \\ -\lstinline[language=Verilog]; Y = A >>> B; & {\tt \$sshr} \\ -\hline -\lstinline[language=Verilog]; Y = A && B; & {\tt \$logic\_and} \\ -\lstinline[language=Verilog]; Y = A || B; & {\tt \$logic\_or} \\ -\hline -\lstinline[language=Verilog]; Y = A === B; & {\tt \$eqx} \\ -\lstinline[language=Verilog]; Y = A !== B; & {\tt \$nex} \\ -\end{tabular} -\hfil -\begin{tabular}[t]{ll} -Verilog & Cell Type \\ -\hline -\lstinline[language=Verilog]; Y = A < B; & {\tt \$lt} \\ -\lstinline[language=Verilog]; Y = A <= B; & {\tt \$le} \\ -\lstinline[language=Verilog]; Y = A == B; & {\tt \$eq} \\ -\lstinline[language=Verilog]; Y = A != B; & {\tt \$ne} \\ -\lstinline[language=Verilog]; Y = A >= B; & {\tt \$ge} \\ -\lstinline[language=Verilog]; Y = A > B; & {\tt \$gt} \\ -\hline -\lstinline[language=Verilog]; Y = A + B; & {\tt \$add} \\ -\lstinline[language=Verilog]; Y = A - B; & {\tt \$sub} \\ -\lstinline[language=Verilog]; Y = A * B; & {\tt \$mul} \\ -\lstinline[language=Verilog]; Y = A / B; & {\tt \$div} \\ -\lstinline[language=Verilog]; Y = A % B; & {\tt \$mod} \\ -\multicolumn{1}{c}{\tt [N/A]} & {\tt \$divfloor} \\ -\multicolumn{1}{c}{\tt [N/A]} & {\tt \$modfoor} \\ -\lstinline[language=Verilog]; Y = A ** B; & {\tt \$pow} \\ -\end{tabular} -\caption{Cell types for binary operators with their corresponding Verilog expressions.} -\label{tab:CellLib_binary} -\end{table} - -The {\tt \$shl} and {\tt \$shr} cells implement logical shifts, whereas the {\tt \$sshl} and -{\tt \$sshr} cells implement arithmetic shifts. The {\tt \$shl} and {\tt \$sshl} cells implement -the same operation. All four of these cells interpret the second operand as unsigned, and require -\B{B\_SIGNED} to be zero. - -Two additional shift operator cells are available that do not directly correspond to any operator -in Verilog, {\tt \$shift} and {\tt \$shiftx}. The {\tt \$shift} cell performs a right logical shift -if the second operand is positive (or unsigned), and a left logical shift if it is negative. -The {\tt \$shiftx} cell performs the same operation as the {\tt \$shift} cell, but the vacated bit -positions are filled with undef (x) bits, and corresponds to the Verilog indexed part-select expression. - -For the binary cells that output a logical value ({\tt \$logic\_and}, {\tt \$logic\_or}, -{\tt \$eqx}, {\tt \$nex}, {\tt \$lt}, {\tt \$le}, {\tt \$eq}, {\tt \$ne}, {\tt \$ge}, -{\tt \$gt}), when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended, -and only the least significant bit varies. - -Division and modulo cells are available in two rounding modes. The original {\tt \$div} and {\tt \$mod} -cells are based on truncating division, and correspond to the semantics of the verilog {\tt /} and -{\tt \%} operators. The {\tt \$divfloor} and {\tt \$modfloor} cells represent flooring division and -flooring modulo, the latter of which is also known as ``remainder'' in several languages. See -table~\ref{tab:CellLib_divmod} for a side-by-side comparison between the different semantics. - -\begin{table}[h] -\hfil -\begin{tabular}{lr|rr|rr} -\multirow{2}{*}{Division} & \multirow{2}{*}{Result} & \multicolumn{2}{c|}{Truncating} & \multicolumn{2}{c}{Flooring} \\ - & & {\tt \$div} & {\tt \$mod} & {\tt \$divfloor} & {\tt \$modfloor} \\ -\hline -{\tt -10 / 3} & {\tt -3.3} & {\tt -3} & {\tt -1} & {\tt -4} & {\tt 2} \\ -{\tt 10 / -3} & {\tt -3.3} & {\tt -3} & {\tt 1} & {\tt -4} & {\tt -2} \\ -{\tt -10 / -3} & {\tt 3.3} & {\tt 3} & {\tt -1} & {\tt 3} & {\tt -1} \\ -{\tt 10 / 3} & {\tt 3.3} & {\tt 3} & {\tt 1} & {\tt 3} & {\tt 1} \\ -\end{tabular} -\caption{Comparison between different rounding modes for division and modulo cells.} -\label{tab:CellLib_divmod} -\end{table} - -\subsection{Multiplexers} - -Multiplexers are generated by the Verilog HDL frontend for {\tt -?:}-expressions. Multiplexers are also generated by the {\tt proc} pass to map the decision trees -from RTLIL::Process objects to logic. - -The simplest multiplexer cell type is {\tt \$mux}. Cells of this type have a \B{WIDTH} parameter -and data inputs \B{A} and \B{B} and a data output \B{Y}, all of the specified width. This cell also -has a single bit control input \B{S}. If \B{S} is 0 the value from the \B{A} input is sent to -the output, if it is 1 the value from the \B{B} input is sent to the output. So the {\tt \$mux} -cell implements the function \lstinline[language=Verilog]; Y = S ? B : A;. - -The {\tt \$pmux} cell is used to multiplex between many inputs using a one-hot select signal. Cells -of this type have a \B{WIDTH} and a \B{S\_WIDTH} parameter and inputs \B{A}, \B{B}, and \B{S} and -an output \B{Y}. The \B{S} input is \B{S\_WIDTH} bits wide. The \B{A} input and the output are both -\B{WIDTH} bits wide and the \B{B} input is \B{WIDTH}*\B{S\_WIDTH} bits wide. When all bits of -\B{S} are zero, the value from \B{A} input is sent to the output. If the $n$'th bit from \B{S} is -set, the value $n$'th \B{WIDTH} bits wide slice of the \B{B} input is sent to the output. When more -than one bit from \B{S} is set the output is undefined. Cells of this type are used to model -``parallel cases'' (defined by using the {\tt parallel\_case} attribute or detected by -an optimization). - -The {\tt \$tribuf} cell is used to implement tristate logic. Cells of this type have a \B{WIDTH} -parameter and inputs \B{A} and \B{EN} and an output \B{Y}. The \B{A} input and \B{Y} output are -\B{WIDTH} bits wide, and the \B{EN} input is one bit wide. When \B{EN} is 0, the output \B{Y} -is not driven. When \B{EN} is 1, the value from \B{A} input is sent to the \B{Y} output. Therefore, -the {\tt \$tribuf} cell implements the function \lstinline[language=Verilog]; Y = EN ? A : 'bz;. - -Behavioural code with cascaded {\tt if-then-else}- and {\tt case}-statements -usually results in trees of multiplexer cells. Many passes (from various -optimizations to FSM extraction) heavily depend on these multiplexer trees to -understand dependencies between signals. Therefore optimizations should not -break these multiplexer trees (e.g.~by replacing a multiplexer between a -calculated signal and a constant zero with an {\tt \$and} gate). - -\subsection{Registers} - -SR-type latches are represented by {\tt \$sr} cells. These cells have input ports -\B{SET} and \B{CLR} and an output port \B{Q}. They have the following parameters: - -\begin{itemize} -\item \B{WIDTH} \\ -The width of inputs \B{SET} and \B{CLR} and output \B{Q}. - -\item \B{SET\_POLARITY} \\ -The set input bits are active-high if this parameter has the value {\tt 1'b1} and active-low -if this parameter is {\tt 1'b0}. - -\item \B{CLR\_POLARITY} \\ -The reset input bits are active-high if this parameter has the value {\tt 1'b1} and active-low -if this parameter is {\tt 1'b0}. -\end{itemize} - -Both set and reset inputs have separate bits for every output bit. -When both the set and reset inputs of an {\tt \$sr} cell are active for a given bit -index, the reset input takes precedence. - -D-type flip-flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK}, -an input port \B{D} and an output port \B{Q}. The following parameters are available for {\tt \$dff} -cells: - -\begin{itemize} -\item \B{WIDTH} \\ -The width of input \B{D} and output \B{Q}. - -\item \B{CLK\_POLARITY} \\ -Clock is active on the positive edge if this parameter has the value {\tt 1'b1} and on the negative -edge if this parameter is {\tt 1'b0}. -\end{itemize} - -D-type flip-flops with asynchronous reset are represented by {\tt \$adff} cells. As the {\tt \$dff} -cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{ARST} -input port for the reset pin and the following additional two parameters: - -\begin{itemize} -\item \B{ARST\_POLARITY} \\ -The asynchronous reset is active-high if this parameter has the value {\tt 1'b1} and active-low -if this parameter is {\tt 1'b0}. - -\item \B{ARST\_VALUE} \\ -The state of \B{Q} will be set to this value when the reset is active. -\end{itemize} - -\begin{sloppypar} -Usually these cells are generated by the {\tt proc} pass using the information -in the designs RTLIL::Process objects. -\end{sloppypar} - -D-type flip-flops with synchronous reset are represented by {\tt \$sdff} cells. As the {\tt \$dff} -cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{SRST} -input port for the reset pin and the following additional two parameters: - -\begin{itemize} -\item \B{SRST\_POLARITY} \\ -The synchronous reset is active-high if this parameter has the value {\tt 1'b1} and active-low -if this parameter is {\tt 1'b0}. - -\item \B{SRST\_VALUE} \\ -The state of \B{Q} will be set to this value when the reset is active. -\end{itemize} - -Note that the {\tt \$adff} and {\tt \$sdff} cells can only be used when the reset value is constant. - -D-type flip-flops with asynchronous set and reset are represented by {\tt \$dffsr} cells. -As the {\tt \$dff} cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have -multi-bit \B{SET} and \B{CLR} input ports and the corresponding polarity parameters, like -{\tt \$sr} cells. - -D-type flip-flops with enable are represented by {\tt \$dffe}, {\tt \$adffe}, {\tt \$dffsre}, -{\tt \$sdffe}, and {\tt \$sdffce} cells, which are enhanced variants of {\tt \$dff}, {\tt \$adff}, {\tt \$dffsr}, -{\tt \$sdff} (with reset over enable) and {\tt \$sdff} (with enable over reset) -cells, respectively. They have the same ports and parameters as their base cell. -In addition they also have a single-bit \B{EN} input port for the enable pin and the following parameter: - -\begin{itemize} -\item \B{EN\_POLARITY} \\ -The enable input is active-high if this parameter has the value {\tt 1'b1} and active-low -if this parameter is {\tt 1'b0}. -\end{itemize} - -D-type latches are represented by {\tt \$dlatch} cells. These cells have an enable port \B{EN}, -an input port \B{D}, and an output port \B{Q}. The following parameters are available for {\tt \$dlatch} cells: - -\begin{itemize} -\item \B{WIDTH} \\ -The width of input \B{D} and output \B{Q}. - -\item \B{EN\_POLARITY} \\ -The enable input is active-high if this parameter has the value {\tt 1'b1} and active-low -if this parameter is {\tt 1'b0}. -\end{itemize} - -The latch is transparent when the \B{EN} input is active. - -D-type latches with reset are represented by {\tt \$adlatch} cells. In addition to {\tt \$dlatch} -ports and parameters, they also have a single-bit \B{ARST} input port for the reset pin and the following additional parameters: - -\begin{itemize} -\item \B{ARST\_POLARITY} \\ -The asynchronous reset is active-high if this parameter has the value {\tt 1'b1} and active-low -if this parameter is {\tt 1'b0}. - -\item \B{ARST\_VALUE} \\ -The state of \B{Q} will be set to this value when the reset is active. -\end{itemize} - -D-type latches with set and reset are represented by {\tt \$dlatchsr} cells. -In addition to {\tt \$dlatch} ports and parameters, they also have multi-bit -\B{SET} and \B{CLR} input ports and the corresponding polarity parameters, like -{\tt \$sr} cells. - -\subsection{Memories} -\label{sec:memcells} - -Memories are either represented using RTLIL::Memory objects, {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit} -cells, or by {\tt \$mem} cells alone. - -In the first alternative the RTLIL::Memory objects hold the general metadata for the memory (bit width, -size in number of words, etc.) and for each port a {\tt \$memrd} (read port) or {\tt \$memwr} (write port) -cell is created. Having individual cells for read and write ports has the advantage that they can be -consolidated using resource sharing passes. In some cases this drastically reduces the number of required -ports on the memory cell. In this alternative, memory initialization data is represented by {\tt \$meminit} cells, -which allow delaying constant folding for initialization addresses and data until after the frontend finishes. - -The {\tt \$memrd} cells have a clock input \B{CLK}, an enable input \B{EN}, an -address input \B{ADDR}, and a data output \B{DATA}. They also have the -following parameters: - -\begin{itemize} -\item \B{MEMID} \\ -The name of the RTLIL::Memory object that is associated with this read port. - -\item \B{ABITS} \\ -The number of address bits (width of the \B{ADDR} input port). - -\item \B{WIDTH} \\ -The number of data bits (width of the \B{DATA} output port). - -\item \B{CLK\_ENABLE} \\ -When this parameter is non-zero, the clock is used. Otherwise this read port is asynchronous and -the \B{CLK} input is not used. - -\item \B{CLK\_POLARITY} \\ -Clock is active on the positive edge if this parameter has the value {\tt 1'b1} and on the negative -edge if this parameter is {\tt 1'b0}. - -\item \B{TRANSPARENT} \\ -If this parameter is set to {\tt 1'b1}, a read and write to the same address in the same cycle will -return the new value. Otherwise the old value is returned. -\end{itemize} - -The {\tt \$memwr} cells have a clock input \B{CLK}, an enable input \B{EN} (one -enable bit for each data bit), an address input \B{ADDR} and a data input -\B{DATA}. They also have the following parameters: - -\begin{itemize} -\item \B{MEMID} \\ -The name of the RTLIL::Memory object that is associated with this write port. - -\item \B{ABITS} \\ -The number of address bits (width of the \B{ADDR} input port). - -\item \B{WIDTH} \\ -The number of data bits (width of the \B{DATA} output port). - -\item \B{CLK\_ENABLE} \\ -When this parameter is non-zero, the clock is used. Otherwise this write port is asynchronous and -the \B{CLK} input is not used. - -\item \B{CLK\_POLARITY} \\ -Clock is active on positive edge if this parameter has the value {\tt 1'b1} and on the negative -edge if this parameter is {\tt 1'b0}. - -\item \B{PRIORITY} \\ -The cell with the higher integer value in this parameter wins a write conflict. -\end{itemize} - -The {\tt \$meminit} cells have an address input \B{ADDR} and a data input \B{DATA}, with the width -of the \B{DATA} port equal to \B{WIDTH} parameter times \B{WORDS} parameter. Both of the inputs -must resolve to a constant for synthesis to succeed. - -\begin{itemize} -\item \B{MEMID} \\ -The name of the RTLIL::Memory object that is associated with this initialization cell. - -\item \B{ABITS} \\ -The number of address bits (width of the \B{ADDR} input port). - -\item \B{WIDTH} \\ -The number of data bits per memory location. - -\item \B{WORDS} \\ -The number of consecutive memory locations initialized by this cell. - -\item \B{PRIORITY} \\ -The cell with the higher integer value in this parameter wins an initialization conflict. -\end{itemize} - -The HDL frontend models a memory using RTLIL::Memory objects and asynchronous -{\tt \$memrd} and {\tt \$memwr} cells. The {\tt memory} pass (i.e.~its various sub-passes) migrates -{\tt \$dff} cells into the {\tt \$memrd} and {\tt \$memwr} cells making them synchronous, then -converts them to a single {\tt \$mem} cell and (optionally) maps this cell type -to {\tt \$dff} cells for the individual words and multiplexer-based address decoders for the read and -write interfaces. When the last step is disabled or not possible, a {\tt \$mem} cell is left in the design. - -The {\tt \$mem} cell provides the following parameters: - -\begin{itemize} -\item \B{MEMID} \\ -The name of the original RTLIL::Memory object that became this {\tt \$mem} cell. - -\item \B{SIZE} \\ -The number of words in the memory. - -\item \B{ABITS} \\ -The number of address bits. - -\item \B{WIDTH} \\ -The number of data bits per word. - -\item \B{INIT} \\ -The initial memory contents. - -\item \B{RD\_PORTS} \\ -The number of read ports on this memory cell. - -\item \B{RD\_CLK\_ENABLE} \\ -This parameter is \B{RD\_PORTS} bits wide, containing a clock enable bit for each read port. - -\item \B{RD\_CLK\_POLARITY} \\ -This parameter is \B{RD\_PORTS} bits wide, containing a clock polarity bit for each read port. - -\item \B{RD\_TRANSPARENT} \\ -This parameter is \B{RD\_PORTS} bits wide, containing a transparent bit for each read port. - -\item \B{WR\_PORTS} \\ -The number of write ports on this memory cell. - -\item \B{WR\_CLK\_ENABLE} \\ -This parameter is \B{WR\_PORTS} bits wide, containing a clock enable bit for each write port. - -\item \B{WR\_CLK\_POLARITY} \\ -This parameter is \B{WR\_PORTS} bits wide, containing a clock polarity bit for each write port. -\end{itemize} - -The {\tt \$mem} cell has the following ports: - -\begin{itemize} -\item \B{RD\_CLK} \\ -This input is \B{RD\_PORTS} bits wide, containing all clock signals for the read ports. - -\item \B{RD\_EN} \\ -This input is \B{RD\_PORTS} bits wide, containing all enable signals for the read ports. - -\item \B{RD\_ADDR} \\ -This input is \B{RD\_PORTS}*\B{ABITS} bits wide, containing all address signals for the read ports. - -\item \B{RD\_DATA} \\ -This input is \B{RD\_PORTS}*\B{WIDTH} bits wide, containing all data signals for the read ports. - -\item \B{WR\_CLK} \\ -This input is \B{WR\_PORTS} bits wide, containing all clock signals for the write ports. - -\item \B{WR\_EN} \\ -This input is \B{WR\_PORTS}*\B{WIDTH} bits wide, containing all enable signals for the write ports. - -\item \B{WR\_ADDR} \\ -This input is \B{WR\_PORTS}*\B{ABITS} bits wide, containing all address signals for the write ports. - -\item \B{WR\_DATA} \\ -This input is \B{WR\_PORTS}*\B{WIDTH} bits wide, containing all data signals for the write ports. -\end{itemize} - -The {\tt memory\_collect} pass can be used to convert discrete {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit} cells -belonging to the same memory to a single {\tt \$mem} cell, whereas the {\tt memory\_unpack} pass performs the inverse operation. -The {\tt memory\_dff} pass can combine asynchronous memory ports that are fed by or feeding registers into synchronous memory ports. -The {\tt memory\_bram} pass can be used to recognize {\tt \$mem} cells that can be implemented with a block RAM resource on an FPGA. -The {\tt memory\_map} pass can be used to implement {\tt \$mem} cells as basic logic: word-wide DFFs and address decoders. - -\subsection{Finite State Machines} - -\begin{fixme} -Add a brief description of the {\tt \$fsm} cell type. -\end{fixme} - -\subsection{Specify rules} - -\begin{fixme} -Add information about {\tt \$specify2}, {\tt \$specify3}, and {\tt \$specrule} cells. -\end{fixme} - -\subsection{Formal verification cells} - -\begin{fixme} -Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$live}, {\tt \$fair}, {\tt \$cover}, {\tt \$equiv}, -{\tt \$initstate}, {\tt \$anyconst}, {\tt \$anyseq}, {\tt \$allconst}, {\tt \$allseq} cells. -\end{fixme} - -\begin{fixme} -Add information about {\tt \$ff} and {\tt \$\_FF\_} cells. -\end{fixme} - -\section{Gates} -\label{sec:celllib_gates} - -For gate level logic networks, fixed function single bit cells are used that do -not provide any parameters. - -Simulation models for these cells can be found in the file {\tt techlibs/common/simcells.v} in the Yosys -source tree. - -\begin{table}[t] -\hfil -\begin{tabular}[t]{ll} -Verilog & Cell Type \\ -\hline -\lstinline[language=Verilog]; Y = A; & {\tt \$\_BUF\_} \\ -\lstinline[language=Verilog]; Y = ~A; & {\tt \$\_NOT\_} \\ -\lstinline[language=Verilog]; Y = A & B; & {\tt \$\_AND\_} \\ -\lstinline[language=Verilog]; Y = ~(A & B); & {\tt \$\_NAND\_} \\ -\lstinline[language=Verilog]; Y = A & ~B; & {\tt \$\_ANDNOT\_} \\ -\lstinline[language=Verilog]; Y = A | B; & {\tt \$\_OR\_} \\ -\lstinline[language=Verilog]; Y = ~(A | B); & {\tt \$\_NOR\_} \\ -\lstinline[language=Verilog]; Y = A | ~B; & {\tt \$\_ORNOT\_} \\ -\lstinline[language=Verilog]; Y = A ^ B; & {\tt \$\_XOR\_} \\ -\lstinline[language=Verilog]; Y = ~(A ^ B); & {\tt \$\_XNOR\_} \\ -\lstinline[language=Verilog]; Y = ~((A & B) | C); & {\tt \$\_AOI3\_} \\ -\lstinline[language=Verilog]; Y = ~((A | B) & C); & {\tt \$\_OAI3\_} \\ -\lstinline[language=Verilog]; Y = ~((A & B) | (C & D)); & {\tt \$\_AOI4\_} \\ -\lstinline[language=Verilog]; Y = ~((A | B) & (C | D)); & {\tt \$\_OAI4\_} \\ -\lstinline[language=Verilog]; Y = S ? B : A; & {\tt \$\_MUX\_} \\ -\lstinline[language=Verilog]; Y = ~(S ? B : A); & {\tt \$\_NMUX\_} \\ -(see below) & {\tt \$\_MUX4\_} \\ -(see below) & {\tt \$\_MUX8\_} \\ -(see below) & {\tt \$\_MUX16\_} \\ -\lstinline[language=Verilog]; Y = EN ? A : 1'bz; & {\tt \$\_TBUF\_} \\ -\hline -\lstinline[language=Verilog]; always @(negedge C) Q <= D; & {\tt \$\_DFF\_N\_} \\ -\lstinline[language=Verilog]; always @(posedge C) Q <= D; & {\tt \$\_DFF\_P\_} \\ -\lstinline[language=Verilog]; always @* if (!E) Q <= D; & {\tt \$\_DLATCH\_N\_} \\ -\lstinline[language=Verilog]; always @* if (E) Q <= D; & {\tt \$\_DLATCH\_P\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (main list)} -\label{tab:CellLib_gates} -\end{table} - -\begin{table}[t] -\hfil -\begin{tabular}[t]{llll} -$ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\ -\hline -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_NN0\_}, {\tt \$\_SDFF\_NN0\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_NN1\_}, {\tt \$\_SDFF\_NN1\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_NP0\_}, {\tt \$\_SDFF\_NP0\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_NP1\_}, {\tt \$\_SDFF\_NP1\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PN0\_}, {\tt \$\_SDFF\_PN0\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PN1\_}, {\tt \$\_SDFF\_PN1\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PP0\_}, {\tt \$\_SDFF\_PP0\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PP1\_}, {\tt \$\_SDFF\_PP1\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (FFs with reset)} -\label{tab:CellLib_gates_adff} -\end{table} - -\begin{table}[t] -\hfil -\begin{tabular}[t]{lll} -$ClkEdge$ & $EnLvl$ & Cell Type \\ -\hline -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NN\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NP\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PN\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PP\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (FFs with enable)} -\label{tab:CellLib_gates_dffe} -\end{table} - -\begin{table}[t] -\begin{tabular}[t]{lllll} -$ClkEdge$ & $RstLvl$ & $RstVal$ & $EnLvl$ & Cell Type \\ -\hline -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NN0N\_}, {\tt \$\_SDFFE\_NN0N\_}, {\tt \$\_SDFFCE\_NN0N\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NN0P\_}, {\tt \$\_SDFFE\_NN0P\_}, {\tt \$\_SDFFCE\_NN0P\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NN1N\_}, {\tt \$\_SDFFE\_NN1N\_}, {\tt \$\_SDFFCE\_NN1N\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NN1P\_}, {\tt \$\_SDFFE\_NN1P\_}, {\tt \$\_SDFFCE\_NN1P\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NP0N\_}, {\tt \$\_SDFFE\_NP0N\_}, {\tt \$\_SDFFCE\_NP0N\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NP0P\_}, {\tt \$\_SDFFE\_NP0P\_}, {\tt \$\_SDFFCE\_NP0P\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NP1N\_}, {\tt \$\_SDFFE\_NP1N\_}, {\tt \$\_SDFFCE\_NP1N\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NP1P\_}, {\tt \$\_SDFFE\_NP1P\_}, {\tt \$\_SDFFCE\_NP1P\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PN0N\_}, {\tt \$\_SDFFE\_PN0N\_}, {\tt \$\_SDFFCE\_PN0N\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PN0P\_}, {\tt \$\_SDFFE\_PN0P\_}, {\tt \$\_SDFFCE\_PN0P\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PN1N\_}, {\tt \$\_SDFFE\_PN1N\_}, {\tt \$\_SDFFCE\_PN1N\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PN1P\_}, {\tt \$\_SDFFE\_PN1P\_}, {\tt \$\_SDFFCE\_PN1P\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PP0N\_}, {\tt \$\_SDFFE\_PP0N\_}, {\tt \$\_SDFFCE\_PP0N\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PP0P\_}, {\tt \$\_SDFFE\_PP0P\_}, {\tt \$\_SDFFCE\_PP0P\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PP1N\_}, {\tt \$\_SDFFE\_PP1N\_}, {\tt \$\_SDFFCE\_PP1N\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PP1P\_}, {\tt \$\_SDFFE\_PP1P\_}, {\tt \$\_SDFFCE\_PP1P\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (FFs with reset and enable)} -\label{tab:CellLib_gates_adffe} -\end{table} - -\begin{table}[t] -\hfil -\begin{tabular}[t]{llll} -$ClkEdge$ & $SetLvl$ & $RstLvl$ & Cell Type \\ -\hline -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NNN\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NNP\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NPN\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NPP\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PNN\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PNP\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PPN\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PPP\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (FFs with set and reset)} -\label{tab:CellLib_gates_dffsr} -\end{table} - -\begin{table}[t] -\hfil -\begin{tabular}[t]{lllll} -$ClkEdge$ & $SetLvl$ & $RstLvl$ & $EnLvl$ & Cell Type \\ -\hline -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSRE\_NNNN\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSRE\_NNNP\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSRE\_NNPN\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSRE\_NNPP\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSRE\_NPNN\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSRE\_NPNP\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSRE\_NPPN\_} \\ -\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSRE\_NPPP\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSRE\_PNNN\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSRE\_PNNP\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSRE\_PNPN\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSRE\_PNPP\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSRE\_PPNN\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSRE\_PPNP\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSRE\_PPPN\_} \\ -\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSRE\_PPPP\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (FFs with set and reset and enable)} -\label{tab:CellLib_gates_dffsre} -\end{table} - -\begin{table}[t] -\hfil -\begin{tabular}[t]{llll} -$EnLvl$ & $RstLvl$ & $RstVal$ & Cell Type \\ -\hline -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DLATCH\_NN0\_} \\ -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DLATCH\_NN1\_} \\ -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DLATCH\_NP0\_} \\ -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DLATCH\_NP1\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DLATCH\_PN0\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DLATCH\_PN1\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DLATCH\_PP0\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DLATCH\_PP1\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (latches with reset)} -\label{tab:CellLib_gates_adlatch} -\end{table} - -\begin{table}[t] -\hfil -\begin{tabular}[t]{llll} -$EnLvl$ & $SetLvl$ & $RstLvl$ & Cell Type \\ -\hline -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DLATCHSR\_NNN\_} \\ -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DLATCHSR\_NNP\_} \\ -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DLATCHSR\_NPN\_} \\ -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DLATCHSR\_NPP\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DLATCHSR\_PNN\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DLATCHSR\_PNP\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DLATCHSR\_PPN\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DLATCHSR\_PPP\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (latches with set and reset)} -\label{tab:CellLib_gates_dlatchsr} -\end{table} - -\begin{table}[t] -\hfil -\begin{tabular}[t]{llll} -$SetLvl$ & $RstLvl$ & Cell Type \\ -\hline -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_SR\_NN\_} \\ -\lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_SR\_NP\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_SR\_PN\_} \\ -\lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_SR\_PP\_} \\ -\end{tabular} -\caption{Cell types for gate level logic networks (SR latches)} -\label{tab:CellLib_gates_sr} -\end{table} - -Tables~\ref{tab:CellLib_gates}, \ref{tab:CellLib_gates_dffe}, \ref{tab:CellLib_gates_adff}, \ref{tab:CellLib_gates_adffe}, \ref{tab:CellLib_gates_dffsr}, \ref{tab:CellLib_gates_dffsre}, \ref{tab:CellLib_gates_adlatch}, \ref{tab:CellLib_gates_dlatchsr} and \ref{tab:CellLib_gates_sr} list all cell types used for gate level logic. The cell types -{\tt \$\_BUF\_}, {\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, -{\tt \$\_OR\_}, {\tt \$\_NOR\_}, {\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_}, -{\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, {\tt \$\_OAI4\_}, -{\tt \$\_MUX\_}, {\tt \$\_MUX4\_}, {\tt \$\_MUX8\_}, {\tt \$\_MUX16\_} and {\tt \$\_NMUX\_} are used to model combinatorial logic. -The cell type {\tt \$\_TBUF\_} is used to model tristate logic. - -The {\tt \$\_MUX4\_}, {\tt \$\_MUX8\_} and {\tt \$\_MUX16\_} cells are used to model wide muxes, and correspond to the following Verilog code: - -\begin{lstlisting}[language=Verilog] -// $_MUX4_ -assign Y = T ? (S ? D : C) : - (S ? B : A); -// $_MUX8_ -assign Y = U ? T ? (S ? H : G) : - (S ? F : E) : - T ? (S ? D : C) : - (S ? B : A); -// $_MUX16_ -assign Y = V ? U ? T ? (S ? P : O) : - (S ? N : M) : - T ? (S ? L : K) : - (S ? J : I) : - U ? T ? (S ? H : G) : - (S ? F : E) : - T ? (S ? D : C) : - (S ? B : A); -\end{lstlisting} - -The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops. - -The cell types {\tt \$\_DFFE\_[NP][NP]\_} -implement d-type flip-flops with enable. The values in the table for these cell types relate to the -following Verilog code template. - -\begin{lstlisting}[mathescape,language=Verilog] - always @($ClkEdge$ C) - if (EN == $EnLvl$) - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_DFF\_[NP][NP][01]\_} implement -d-type flip-flops with asynchronous reset. The values in the table for these cell types relate to the -following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; -if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge; -otherwise. - -\begin{lstlisting}[mathescape,language=Verilog] - always @($ClkEdge$ C, $RstEdge$ R) - if (R == $RstLvl$) - Q <= $RstVal$; - else - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_SDFF\_[NP][NP][01]\_} implement -d-type flip-flops with synchronous reset. The values in the table for these cell types relate to the -following Verilog code template: - -\begin{lstlisting}[mathescape,language=Verilog] - always @($ClkEdge$ C) - if (R == $RstLvl$) - Q <= $RstVal$; - else - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_DFFE\_[NP][NP][01][NP]\_} implement -d-type flip-flops with asynchronous reset and enable. The values in the table for these cell types relate to the -following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; -if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge; -otherwise. - -\begin{lstlisting}[mathescape,language=Verilog] - always @($ClkEdge$ C, $RstEdge$ R) - if (R == $RstLvl$) - Q <= $RstVal$; - else if (EN == $EnLvl$) - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_SDFFE\_[NP][NP][01][NP]\_} implement d-type flip-flops -with synchronous reset and enable, with reset having priority over enable. -The values in the table for these cell types relate to the -following Verilog code template: - -\begin{lstlisting}[mathescape,language=Verilog] - always @($ClkEdge$ C) - if (R == $RstLvl$) - Q <= $RstVal$; - else if (EN == $EnLvl$) - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_SDFFCE\_[NP][NP][01][NP]\_} implement d-type flip-flops -with synchronous reset and enable, with enable having priority over reset. -The values in the table for these cell types relate to the -following Verilog code template: - -\begin{lstlisting}[mathescape,language=Verilog] - always @($ClkEdge$ C) - if (EN == $EnLvl$) - if (R == $RstLvl$) - Q <= $RstVal$; - else - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_DFFSR\_[NP][NP][NP]\_} implement -d-type flip-flops with asynchronous set and reset. The values in the table for these cell types relate to the -following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; -if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge; -otherwise, and \lstinline[mathescape,language=Verilog];$SetEdge$; is \lstinline[language=Verilog];posedge; -if \lstinline[mathescape,language=Verilog];$SetLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge; -otherwise. - -\begin{lstlisting}[mathescape,language=Verilog] - always @($ClkEdge$ C, $RstEdge$ R, $SetEdge$ S) - if (R == $RstLvl$) - Q <= 0; - else if (S == $SetLvl$) - Q <= 1; - else - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_DFFSRE\_[NP][NP][NP][NP]\_} implement -d-type flip-flops with asynchronous set and reset and enable. The values in the table for these cell types relate to the -following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; -if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge; -otherwise, and \lstinline[mathescape,language=Verilog];$SetEdge$; is \lstinline[language=Verilog];posedge; -if \lstinline[mathescape,language=Verilog];$SetLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge; -otherwise. - -\begin{lstlisting}[mathescape,language=Verilog] - always @($ClkEdge$ C, $RstEdge$ R, $SetEdge$ S) - if (R == $RstLvl$) - Q <= 0; - else if (S == $SetLvl$) - Q <= 1; - else if (E == $EnLvl$) - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_DLATCH\_N\_} and {\tt \$\_DLATCH\_P\_} represent d-type latches. - -The cell types {\tt \$\_DLATCH\_[NP][NP][01]\_} implement -d-type latches with reset. The values in the table for these cell types relate to the -following Verilog code template: - -\begin{lstlisting}[mathescape,language=Verilog] - always @* - if (R == $RstLvl$) - Q <= $RstVal$; - else if (E == $EnLvl$) - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_DLATCHSR\_[NP][NP][NP]\_} implement -d-type latches with set and reset. The values in the table for these cell types relate to the -following Verilog code template: - -\begin{lstlisting}[mathescape,language=Verilog] - always @* - if (R == $RstLvl$) - Q <= 0; - else if (S == $SetLvl$) - Q <= 1; - else if (E == $EnLvl$) - Q <= D; -\end{lstlisting} - -The cell types {\tt \$\_SR\_[NP][NP]\_} implement -sr-type latches. The values in the table for these cell types relate to the -following Verilog code template: - -\begin{lstlisting}[mathescape,language=Verilog] - always @* - if (R == $RstLvl$) - Q <= 0; - else if (S == $SetLvl$) - Q <= 1; -\end{lstlisting} - -In most cases gate level logic networks are created from RTL networks using the {\tt techmap} pass. The flip-flop cells -from the gate level logic network can be mapped to physical flip-flop cells from a Liberty file using the {\tt dfflibmap} -pass. The combinatorial logic cells can be mapped to physical cells from a Liberty file via ABC \citeweblink{ABC} -using the {\tt abc} pass. - -\begin{fixme} -Add information about {\tt \$slice} and {\tt \$concat} cells. -\end{fixme} - -\begin{fixme} -Add information about {\tt \$lut} and {\tt \$sop} cells. -\end{fixme} - -\begin{fixme} -Add information about {\tt \$alu}, {\tt \$macc}, {\tt \$fa}, and {\tt \$lcu} cells. -\end{fixme} diff --git a/manual/CHAPTER_Eval.tex b/manual/CHAPTER_Eval.tex deleted file mode 100644 index f719618d50e..00000000000 --- a/manual/CHAPTER_Eval.tex +++ /dev/null @@ -1,209 +0,0 @@ - -\chapter{Evaluation, Conclusion, Future Work} -\label{chapter:eval} - -The Yosys source tree contains over 200 test cases\footnote{Most of this test -cases are copied from HANA \citeweblink{HANA} or the ASIC-WORLD website -\citeweblink{ASIC-WORLD}.} which are used in the {\tt make test} make-target. -Besides these there is an external Yosys benchmark and test case package that -contains a few larger designs \citeweblink{YosysTestsGit}. This package -contains the designs listed in Tab.~\ref{tab:yosys-test-designs}. - -\begin{table} - \hfil - \begin{tabular}{lrrp{8.5cm}} - Test-Design & Source & Gates\footnotemark & Description / Comments \\ - \hline - {\tt aes\_core} & IWLS2005 & $ 41{,}837 $ & \footnotesize AES Cipher written by Rudolf Usselmann \\ - {\tt i2c} & IWLS2005 & $ 1{,}072 $ & \footnotesize WISHBONE compliant I2C Master by Richard Herveille \\ - {\tt openmsp430} & OpenCores & $ 7{,}173 $ & \footnotesize MSP430 compatible CPU by Olivier Girard \\ - {\tt or1200} & OpenCores & $ 42{,}675 $ & \footnotesize The OpenRISC 1200 CPU by Damjan Lampret \\ - {\tt sasc} & IWLS2005 & $ 456 $ & \footnotesize Simple Async. Serial Comm. Device by Rudolf Usselmann \\ - {\tt simple\_spi} & IWLS2005 & $ 690 $ & \footnotesize MC68HC11E based SPI interface by Richard Herveille \\ - {\tt spi} & IWLS2005 & $ 2{,}478 $ & \footnotesize SPI IP core by Simon Srot \\ - {\tt ss\_pcm} & IWLS2005 & $ 279 $ & \footnotesize PCM IO Slave by Rudolf Usselmann \\ - {\tt systemcaes} & IWLS2005 & $ 6{,}893 $ & \footnotesize AES core (using SystemC to Verilog) by Javier Castillo \\ - {\tt usb\_phy} & IWLS2005 & $ 515 $ & \footnotesize USB 1.1 PHY by Rudolf Usselmann \\ - \end{tabular} - \caption{Tests included in the yosys-tests package.} - \label{tab:yosys-test-designs} -\end{table} - -\footnotetext{ -Number of gates determined using the Yosys synthesis script ``{\tt hierarchy -top \$top; proc; opt; memory; opt; techmap; opt; abc; opt; flatten \$top; hierarchy -top \$top; abc; opt; select -count */c:*}''. -} - -\section{Correctness of Synthesis Results} - -The following measures were taken to increase the confidence in the correctness of the Yosys synthesis results: - -\begin{itemize} -\item Yosys comes with a large selection\footnote{At the time of this writing -269 test cases.} of small test cases that are evaluated when the command {\tt -make test} is executed. During development of Yosys it was shown that this -collection of test cases is sufficient to catch most bugs. The following more -sophisticated test procedures only caught a few additional bugs. Whenever this -happened, an appropriate test case was added to the collection of small test -cases for {\tt make test} to ensure better testability of the feature in -question in the future. - -\item The designs listed in Tab.~\ref{tab:yosys-test-designs} where validated -using the formal verification tool Synopsys Formality\citeweblink{Formality}. -The Yosys synthesis scripts used to synthesize the individual designs for this -test are slightly different per design in order to broaden the coverage of -Yosys features. The large majority of all errors encountered using these tests -are false-negatives, mostly related to FSM encoding or signal naming in large -array logic (such as in memory blocks). Therefore the {\tt fsm\_recode} pass -was extended so it can be used to generate TCL commands for Synopsys Formality -that describe the relationship between old and new state encodings. Also the -method used to generate signal and cell names in the Verilog backend was -slightly modified in order to improve the automatic matching of net names in -Synopsys Formality. With these changes in place all designs in Tab.~\ref{tab:yosys-test-designs} -validate successfully using Formality. - -\item VlogHammer \citeweblink{VlogHammer} is a set of scripts that -auto-generate a large collection of test cases\footnote{At the time of this -writing over 6600 test cases.} and synthesize them using Yosys and the -following freely available proprietary synthesis tools. -\begin{itemize} -\item Xilinx Vivado WebPack (2013.2) \citeweblink{XilinxWebPACK} -\item Xilinx ISE (XST) WebPack (14.5) \citeweblink{XilinxWebPACK} -\item Altera Quartus II Web Edition (13.0) \citeweblink{QuartusWeb} -\end{itemize} -The built-in SAT solver of Yosys is used to formally -verify the Yosys RTL- and Gate-Level netlists against the netlists generated by -this other tools.\footnote{A SAT solver is a program that can solve the boolean -satisfiability problem. The built-in SAT solver in Yosys can be used for formal -equivalence checking, amongst other things. See Sec.~\ref{cmd:sat} for details.} -When differences are found, the input pattern that result in -different outputs are used for simulating the original Verilog code as well as -the synthesis results using the following Verilog simulators. -\begin{itemize} -\item Xilinx ISIM (from Xilinx ISE 14.5 \citeweblink{XilinxWebPACK}) -\item Modelsim 10.1d (from Quartus II 13.0 \citeweblink{QuartusWeb}) -\item Icarus Verilog (no specific version) -\end{itemize} -The set of tests performed by VlogHammer systematically verify the correct -behaviour of -\begin{itemize} -\item Yosys Verilog Frontend and RTL generation -\item Yosys Gate-Level Technology Mapping -\item Yosys SAT Models for RTL- and Gate-Level cells -\item Yosys Constant Evaluator Models for RTL- and Gate-Level cells -\end{itemize} -against the reference provided by the other tools. A few bugs related to sign -extensions and bit-width extensions where found (and have been fixed meanwhile) -using this approach. This test also revealed a small number of bugs in the -other tools (i.e.~Vivado, XST, Quartus, ISIM and Icarus Verilog; no bugs where -found in Modelsim using vlogHammer so far). -\end{itemize} - -Although complex software can never be expected to be fully bug-free -\cite{MURPHY}, it has been shown that Yosys is mature and feature-complete -enough to handle most real-world cases correctly. - -\section{Quality of synthesis results} - -In this section an attempt to evaluate the quality of Yosys synthesis results is made. To this end the -synthesis results of a commercial FPGA synthesis tool when presented with the original HDL code vs.~when -presented with the Yosys synthesis result are compared. - -The OpenMSP430 and the OpenRISC 1200 test cases were synthesized using the following Yosys synthesis script: - -\begin{lstlisting}[numbers=left,frame=single,mathescape] -hierarchy -check -proc; opt; fsm; opt; memory; opt -techmap; opt; abc; opt -\end{lstlisting} - -The original RTL and the Yosys output where both passed to the Xilinx XST 14.5 -FPGA synthesis tool. The following setting where used for XST: - -\begin{lstlisting}[numbers=left,frame=single,mathescape] --p artix7 --use_dsp48 NO --iobuf NO --ram_extract NO --rom_extract NO --fsm_extract YES --fsm_encoding Auto -\end{lstlisting} - -The results of this comparison is summarized in Tab.~\ref{tab:synth-test}. The -used FPGA resources (registers and LUTs) and performance (maximum frequency as -reported by XST) are given per module (indentation indicates module hierarchy, -the numbers are including all contained modules). - -For most modules the results are very similar between XST and Yosys. XST is -used in both cases for the final mapping of logic to LUTs. So this comparison -only compares the high-level synthesis functions (such as FSM extraction and -encoding) of Yosys and XST. - -\begin{table} - \def\nomhz{--- \phantom{MHz}} - \def\P#1 {(#1\hbox to 0px{)\hss}} - \hfil - \begin{tabular}{l|rrr|rrr} - & \multicolumn{3}{c|}{Without Yosys} & \multicolumn{3}{c}{With Yosys} \\ - Module & Regs & LUTs & Max. Freq. & Regs & LUTs & Max. Freq. \\ - \hline - {\tt openMSP430} & 689 & 2210 & 71 MHz & 719 & 2779 & 53 MHz \\ - {\tt \hskip1em omsp\_clock\_module} & 21 & 30 & 645 MHz & 21 & 30 & 644 MHz \\ - {\tt \hskip1em \hskip1em omsp\_sync\_cell} & 2 & --- & 1542 MHz & 2 & --- & 1542 MHz \\ - {\tt \hskip1em \hskip1em omsp\_sync\_reset} & 2 & --- & 1542 MHz & 2 & --- & 1542 MHz \\ - {\tt \hskip1em omsp\_dbg} & 143 & 344 & 292 MHz & 149 & 430 & 353 MHz \\ - {\tt \hskip1em \hskip1em omsp\_dbg\_uart} & 76 & 135 & 377 MHz & 79 & 139 & 389 MHz \\ - {\tt \hskip1em omsp\_execution\_unit} & 266 & 911 & 80 MHz & 266 & 1034 & 137 MHz \\ - {\tt \hskip1em \hskip1em omsp\_alu} & --- & 202 & \nomhz & --- & 263 & \nomhz \\ - {\tt \hskip1em \hskip1em omsp\_register\_file} & 231 & 478 & 285 MHz & 231 & 506 & 293 MHz \\ - {\tt \hskip1em omsp\_frontend} & 115 & 340 & 178 MHz & 118 & 527 & 206 MHz \\ - {\tt \hskip1em omsp\_mem\_backbone} & 38 & 141 & 1087 MHz & 38 & 144 & 1087 MHz \\ - {\tt \hskip1em omsp\_multiplier} & 73 & 397 & 129 MHz & 102 & 1053 & 55 MHz \\ - {\tt \hskip1em omsp\_sfr} & 6 & 18 & 1023 MHz & 6 & 20 & 1023 MHz \\ - {\tt \hskip1em omsp\_watchdog} & 24 & 53 & 362 MHz & 24 & 70 & 360 MHz \\ - \hline - {\tt or1200\_top} & 7148 & 9969 & 135 MHz & 7173 & 10238 & 108 MHz \\ - {\tt \hskip1em or1200\_alu} & --- & 681 & \nomhz & --- & 641 & \nomhz \\ - {\tt \hskip1em or1200\_cfgr} & --- & 11 & \nomhz & --- & 11 & \nomhz \\ - {\tt \hskip1em or1200\_ctrl} & 175 & 186 & 464 MHz & 174 & 279 & 377 MHz \\ - {\tt \hskip1em or1200\_except} & 241 & 451 & 313 MHz & 241 & 353 & 301 MHz \\ - {\tt \hskip1em or1200\_freeze} & 6 & 18 & 507 MHz & 6 & 16 & 515 MHz \\ - {\tt \hskip1em or1200\_if} & 68 & 143 & 806 MHz & 68 & 139 & 790 MHz \\ - {\tt \hskip1em or1200\_lsu} & 8 & 138 & \nomhz & 12 & 205 & 1306 MHz \\ - {\tt \hskip1em \hskip1em or1200\_mem2reg} & --- & 60 & \nomhz & --- & 66 & \nomhz \\ - {\tt \hskip1em \hskip1em or1200\_reg2mem} & --- & 29 & \nomhz & --- & 29 & \nomhz \\ - {\tt \hskip1em or1200\_mult\_mac} & 394 & 2209 & 240 MHz & 394 & 2230 & 241 MHz \\ - {\tt \hskip1em \hskip1em or1200\_amultp2\_32x32} & 256 & 1783 & 240 MHz & 256 & 1770 & 241 MHz \\ - {\tt \hskip1em or1200\_operandmuxes} & 65 & 129 & 1145 MHz & 65 & 129 & 1145 MHz \\ - {\tt \hskip1em or1200\_rf} & 1041 & 1722 & 822 MHz & 1042 & 1722 & 581 MHz \\ - {\tt \hskip1em or1200\_sprs} & 18 & 432 & 724 MHz & 18 & 469 & 722 MHz \\ - {\tt \hskip1em or1200\_wbmux} & 33 & 93 & \nomhz & 33 & 78 & \nomhz \\ - {\tt \hskip1em or1200\_dc\_top} & --- & 5 & \nomhz & --- & 5 & \nomhz \\ - {\tt \hskip1em or1200\_dmmu\_top} & 2445 & 1004 & \nomhz & 2445 & 1043 & \nomhz \\ - {\tt \hskip1em \hskip1em or1200\_dmmu\_tlb} & 2444 & 975 & \nomhz & 2444 & 1013 & \nomhz \\ - {\tt \hskip1em or1200\_du} & 67 & 56 & 859 MHz & 67 & 56 & 859 MHz \\ - {\tt \hskip1em or1200\_ic\_top} & 39 & 100 & 527 MHz & 41 & 136 & 514 MHz \\ - {\tt \hskip1em \hskip1em or1200\_ic\_fsm} & 40 & 42 & 408 MHz & 40 & 75 & 484 MHz \\ - {\tt \hskip1em or1200\_pic} & 38 & 50 & 1169 MHz & 38 & 50 & 1177 MHz \\ - {\tt \hskip1em or1200\_tt} & 64 & 112 & 370 MHz & 64 & 186 & 437 MHz \\ - \end{tabular} - \caption{Synthesis results (as reported by XST) for OpenMSP430 and OpenRISC 1200} - \label{tab:synth-test} -\end{table} - -\section{Conclusion and Future Work} - -Yosys is capable of correctly synthesizing real-world Verilog designs. The -generated netlists are of a decent quality. However, in cases where dedicated -hardware resources should be used for certain functions it is of course -necessary to implement proper technology mapping for these functions in -Yosys. This can be as easy as calling the {\tt techmap} pass with an -architecture-specific mapping file in the synthesis script. As no such thing -has been done in the above tests, it is only natural that the resulting designs -cannot benefit from these dedicated hardware resources. - -Therefore future work includes the implementation of architecture-specific -technology mappings besides additional frontends (VHDL), backends (EDIF), -and above all else, application specific passes. After all, this was -the main motivation for the development of Yosys in the first place. - diff --git a/manual/CHAPTER_Eval/grep-it.sh b/manual/CHAPTER_Eval/grep-it.sh deleted file mode 100644 index 0f4f95ae595..00000000000 --- a/manual/CHAPTER_Eval/grep-it.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash - -openmsp430_mods=" -omsp_alu -omsp_clock_module -omsp_dbg -omsp_dbg_uart -omsp_execution_unit -omsp_frontend -omsp_mem_backbone -omsp_multiplier -omsp_register_file -omsp_sfr -omsp_sync_cell -omsp_sync_reset -omsp_watchdog -openMSP430" - -or1200_mods=" -or1200_alu -or1200_amultp2_32x32 -or1200_cfgr -or1200_ctrl -or1200_dc_top -or1200_dmmu_tlb -or1200_dmmu_top -or1200_du -or1200_except -or1200_fpu -or1200_freeze -or1200_ic_fsm -or1200_ic_ram -or1200_ic_tag -or1200_ic_top -or1200_if -or1200_immu_tlb -or1200_lsu -or1200_mem2reg -or1200_mult_mac -or1200_operandmuxes -or1200_pic -or1200_pm -or1200_qmem_top -or1200_reg2mem -or1200_rf -or1200_sb -or1200_sprs -or1200_top -or1200_tt -or1200_wbmux" - -grep_regs() { - x=$(grep '^ Number of Slice Registers:' $1.syr | sed 's/.*: *//;' | cut -f1 -d' ') - echo $x | sed 's,^ *$,-1,' -} - -grep_luts() { - x=$(grep '^ Number of Slice LUTs:' $1.syr | sed 's/.*: *//;' | cut -f1 -d' ') - echo $x | sed 's,^ *$,-1,' -} - -grep_freq() { - x=$(grep 'Minimum period.*Maximum Frequency' $1.syr | sed 's/\.[0-9]*MHz.*//;' | cut -f3 -d:) - echo $x | sed 's,^ *$,-1,' -} - -for mod in $openmsp430_mods $or1200_mods; do - printf '%-30s s,$, \\& %6d \\& %6d \\& %4d MHz \\& %6d \\& %6d \\& %4d MHz \\\\\\\\,;\n' "/${mod//_/\\\\_}}/" \ - $(grep_regs ${mod}) $(grep_luts ${mod}) $(grep_freq ${mod}) \ - $(grep_regs ${mod}_ys) $(grep_luts ${mod}_ys) $(grep_freq ${mod}_ys) -done - -# for mod in $openmsp430_mods $or1200_mods; do -# [ $mod = "or1200_top" -o $mod = "or1200_dmmu_top" -o $mod = or1200_dmmu_tlb -o $mod = or1200_immu_tlb ] && continue -# regs=$(grep_regs ${mod}) regs_ys=$(grep_regs ${mod}_ys) -# luts=$(grep_luts ${mod}) luts_ys=$(grep_luts ${mod}_ys) -# freq=$(grep_freq ${mod}) freq_ys=$(grep_freq ${mod}_ys) -# if [ $regs -gt 0 -a $regs_ys -gt 0 ]; then regs_p=$(( 100*regs_ys / regs )); else regs_p=NaN; fi -# if [ $luts -gt 0 -a $luts_ys -gt 0 ]; then luts_p=$(( 100*luts_ys / luts )); else luts_p=NaN; fi -# if [ $freq -gt 0 -a $freq_ys -gt 0 ]; then freq_p=$(( 100*freq_ys / freq )); else freq_p=NaN; fi -# printf '%-30s %3s %3s %3s\n' $mod $regs_p $luts_p $freq_p -# -# done - diff --git a/manual/CHAPTER_Eval/openmsp430.prj b/manual/CHAPTER_Eval/openmsp430.prj deleted file mode 100644 index cb8cd2714eb..00000000000 --- a/manual/CHAPTER_Eval/openmsp430.prj +++ /dev/null @@ -1,14 +0,0 @@ -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_sync_cell.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_sync_reset.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_register_file.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_dbg_uart.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_alu.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_watchdog.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_sfr.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_multiplier.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_mem_backbone.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_frontend.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_execution_unit.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_dbg.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/omsp_clock_module.v" -verilog work "../../../../../Work/yosys-tests/openmsp430/rtl/openMSP430.v" diff --git a/manual/CHAPTER_Eval/openmsp430_ys.prj b/manual/CHAPTER_Eval/openmsp430_ys.prj deleted file mode 100644 index 0009c99dcab..00000000000 --- a/manual/CHAPTER_Eval/openmsp430_ys.prj +++ /dev/null @@ -1 +0,0 @@ -verilog work "openmsp430_ys.v" diff --git a/manual/CHAPTER_Eval/or1200.prj b/manual/CHAPTER_Eval/or1200.prj deleted file mode 100644 index 9496874e082..00000000000 --- a/manual/CHAPTER_Eval/or1200.prj +++ /dev/null @@ -1,37 +0,0 @@ -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_spram.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_reg2mem.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_mem2reg.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_dpram.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_amultp2_32x32.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_wbmux.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_sprs.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_rf.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_operandmuxes.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_mult_mac.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_lsu.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_immu_tlb.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_if.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ic_tag.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ic_ram.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ic_fsm.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_genpc.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_freeze.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_fpu.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_except.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_dmmu_tlb.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ctrl.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_cfgr.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_alu.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_wb_biu.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_tt.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_sb.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_qmem_top.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_pm.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_pic.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_immu_top.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_ic_top.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_du.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_dmmu_top.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_dc_top.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_cpu.v" -verilog work "../../../../../Work/yosys-tests/or1200/rtl/or1200_top.v" diff --git a/manual/CHAPTER_Eval/or1200_ys.prj b/manual/CHAPTER_Eval/or1200_ys.prj deleted file mode 100644 index 4dd5f41a079..00000000000 --- a/manual/CHAPTER_Eval/or1200_ys.prj +++ /dev/null @@ -1 +0,0 @@ -verilog work "or1200_ys.v" diff --git a/manual/CHAPTER_Eval/run-it.sh b/manual/CHAPTER_Eval/run-it.sh deleted file mode 100644 index b4a67cebd01..00000000000 --- a/manual/CHAPTER_Eval/run-it.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -openmsp430_mods=" -omsp_alu -omsp_clock_module -omsp_dbg -omsp_dbg_uart -omsp_execution_unit -omsp_frontend -omsp_mem_backbone -omsp_multiplier -omsp_register_file -omsp_sfr -omsp_sync_cell -omsp_sync_reset -omsp_watchdog -openMSP430" - -or1200_mods=" -or1200_alu -or1200_amultp2_32x32 -or1200_cfgr -or1200_ctrl -or1200_dc_top -or1200_dmmu_tlb -or1200_dmmu_top -or1200_du -or1200_except -or1200_fpu -or1200_freeze -or1200_ic_fsm -or1200_ic_ram -or1200_ic_tag -or1200_ic_top -or1200_if -or1200_immu_tlb -or1200_lsu -or1200_mem2reg -or1200_mult_mac -or1200_operandmuxes -or1200_pic -or1200_pm -or1200_qmem_top -or1200_reg2mem -or1200_rf -or1200_sb -or1200_sprs -or1200_top -or1200_tt -or1200_wbmux" - -yosys_cmds="hierarchy -check; proc; opt; fsm; opt; memory; opt; techmap; opt; abc; opt" - -yosys -p "$yosys_cmds" -o openmsp430_ys.v $( cut -f2 -d'"' openmsp430.prj ) -yosys -p "$yosys_cmds" -o or1200_ys.v $( cut -f2 -d'"' or1200.prj ) - -. /opt/Xilinx/14.5/ISE_DS/settings64.sh - -run_single() { - prj_file=$1 top_module=$2 out_file=$3 - sed "s/@prj_file@/$prj_file/g; s/@out_file@/$out_file/g; s/@top_module@/$top_module/g;" < settings.xst > ${out_file}.xst - xst -ifn ${out_file}.xst -ofn ${out_file}.syr -} - -for mod in $openmsp430_mods; do - run_single openmsp430.prj ${mod} ${mod} - run_single openmsp430_ys.prj ${mod} ${mod}_ys -done - -for mod in $or1200_mods; do - run_single or1200.prj ${mod} ${mod} - run_single or1200_ys.prj ${mod} ${mod}_ys -done - diff --git a/manual/CHAPTER_Eval/settings.xst b/manual/CHAPTER_Eval/settings.xst deleted file mode 100644 index 2f381d09d2a..00000000000 --- a/manual/CHAPTER_Eval/settings.xst +++ /dev/null @@ -1,2 +0,0 @@ -run -ifn @prj_file@ -ofn @out_file@ -ofmt NGC -top @top_module@ -p artix7 --use_dsp48 NO -iobuf NO -ram_extract NO -rom_extract NO -fsm_extract YES -fsm_encoding Auto diff --git a/manual/CHAPTER_Intro.tex b/manual/CHAPTER_Intro.tex deleted file mode 100644 index 76e5d847bdb..00000000000 --- a/manual/CHAPTER_Intro.tex +++ /dev/null @@ -1,98 +0,0 @@ - -\chapter{Introduction} -\label{chapter:intro} - -This document presents the Free and Open Source (FOSS) Verilog HDL synthesis tool ``Yosys''. -Its design and implementation as well as its performance on real-world designs -is discussed in this document. - -\section{History of Yosys} - -A Hardware Description Language (HDL) is a computer language used to describe -circuits. A HDL synthesis tool is a computer program that takes a formal -description of a circuit written in an HDL as input and generates a netlist -that implements the given circuit as output. - -Currently the most widely used and supported HDLs for digital circuits are -Verilog \cite{Verilog2005}\cite{VerilogSynth} and -VHDL\footnote{VHDL is an acronym for ``VHSIC hardware description language'' -and VHSIC is an acronym for ``Very-High-Speed Integrated -Circuits''.} \cite{VHDL}\cite{VHDLSynth}. -Both HDLs are used for test and verification purposes as well as logic -synthesis, resulting in a set of synthesizable and a set of non-synthesizable -language features. In this document we only look at the synthesizable subset -of the language features. - -In recent work on heterogeneous coarse-grain reconfigurable -logic \cite{intersynth} the need for a custom application-specific HDL synthesis -tool emerged. It was soon realised that a synthesis tool that understood Verilog -or VHDL would be preferred over a synthesis tool for a custom HDL. Given an -existing Verilog or VHDL front end, the work for writing the necessary -additional features and integrating them in an existing tool can be estimated to be -about the same as writing a new tool with support for a minimalistic custom HDL. - -The proposed custom HDL synthesis tool should be licensed under a Free -and Open Source Software (FOSS) licence. So an existing FOSS Verilog or VHDL -synthesis tool would have been needed as basis to build upon. The main advantages -of choosing Verilog or VHDL is the ability to synthesize existing HDL code and -to mitigate the requirement for circuit-designers to learn a new language. In order to take full advantage of any existing FOSS Verilog or VHDL tool, -such a tool would have to provide a feature-complete implementation of the -synthesizable HDL subset. - -Basic RTL synthesis is a well understood field \cite{LogicSynthesis}. Lexing, -parsing and processing of computer languages \cite{Dragonbook} is a thoroughly -researched field. All the information required to write such tools has been openly -available for a long time, and it is therefore likely that a FOSS HDL synthesis tool -with a feature-complete Verilog or VHDL front end must exist which can be used as a basis for a custom RTL synthesis tool. - -Due to the author's preference for Verilog over VHDL it was decided early -on to go for Verilog instead of VHDL\footnote{A quick investigation into FOSS -VHDL tools yielded similar grim results for FOSS VHDL synthesis tools.}. -So the existing FOSS Verilog synthesis tools were evaluated (see -App.~\ref{chapter:sota}). The results of this evaluation are utterly -devastating. Therefore a completely new Verilog synthesis tool was implemented -and is recommended as basis for custom synthesis tools. This is the tool that -is discussed in this document. - -\section{Structure of this Document} - -The structure of this document is as follows: - -Chapter~\ref{chapter:intro} is this introduction. - -Chapter~\ref{chapter:basics} covers a short introduction to the world of HDL -synthesis. Basic principles and the terminology are outlined in this chapter. - -Chapter~\ref{chapter:approach} gives the quickest possible outline to how the -problem of implementing a HDL synthesis tool is approached in the case of -Yosys. - -Chapter~\ref{chapter:overview} contains a more detailed overview of the -implementation of Yosys. This chapter covers the data structures used in -Yosys to represent a design in detail and is therefore recommended reading -for everyone who is interested in understanding the Yosys internals. - -Chapter~\ref{chapter:celllib} covers the internal cell library used by Yosys. -This is especially important knowledge for anyone who wants to understand the -intermediate netlists used internally by Yosys. - -Chapter~ \ref{chapter:prog} gives a tour to the internal APIs of Yosys. This -is recommended reading for everyone who actually wants to read or write -Yosys source code. The chapter concludes with an example loadable module -for Yosys. - -Chapters~\ref{chapter:verilog}, \ref{chapter:opt}, and \ref{chapter:techmap} -cover three important pieces of the synthesis pipeline: The Verilog frontend, -the optimization passes and the technology mapping to the target architecture, -respectively. - -Chapter~\ref{chapter:eval} covers the evaluation of the performance -(correctness and quality) of Yosys on real-world input data. -The chapter concludes the main part of this document with conclusions and -outlook to future work. - -Various appendices, including a command reference manual -(App.~\ref{commandref}) and an evaluation of pre-existing FOSS Verilog -synthesis tools (App.~\ref{chapter:sota}) complete this document. - - diff --git a/manual/CHAPTER_Optimize.tex b/manual/CHAPTER_Optimize.tex deleted file mode 100644 index eee92ef5c5a..00000000000 --- a/manual/CHAPTER_Optimize.tex +++ /dev/null @@ -1,324 +0,0 @@ - -\chapter{Optimizations} -\label{chapter:opt} - -Yosys employs a number of optimizations to generate better and cleaner results. -This chapter outlines these optimizations. - -\section{Simple Optimizations} - -The Yosys pass {\tt opt} runs a number of simple optimizations. This includes removing unused -signals and cells and const folding. It is recommended to run this pass after each major step -in the synthesis script. At the time of this writing the {\tt opt} pass executes the following -passes that each perform a simple optimization: - -\begin{itemize} -\item Once at the beginning of {\tt opt}: -\begin{itemize} -\item {\tt opt\_expr} -\item {\tt opt\_merge -nomux} -\end{itemize} -\item Repeat until result is stable: -\begin{itemize} -\item {\tt opt\_muxtree} -\item {\tt opt\_reduce} -\item {\tt opt\_merge} -\item {\tt opt\_rmdff} -\item {\tt opt\_clean} -\item {\tt opt\_expr} -\end{itemize} -\end{itemize} - -The following section describes each of the {\tt opt\_*} passes. - -\subsection{The opt\_expr pass} - -This pass performs const folding on the internal combinational cell types -described in Chap.~\ref{chapter:celllib}. This means a cell with all constant -inputs is replaced with the constant value this cell drives. In some cases -this pass can also optimize cells with some constant inputs. - -\begin{table} - \hfil - \begin{tabular}{cc|c} - A-Input & B-Input & Replacement \\ - \hline - any & 0 & 0 \\ - 0 & any & 0 \\ - 1 & 1 & 1 \\ - \hline - X/Z & X/Z & X \\ - 1 & X/Z & X \\ - X/Z & 1 & X \\ - \hline - any & X/Z & 0 \\ - X/Z & any & 0 \\ - \hline - $a$ & 1 & $a$ \\ - 1 & $b$ & $b$ \\ - \end{tabular} - \caption{Const folding rules for {\tt\$\_AND\_} cells as used in {\tt opt\_expr}.} - \label{tab:opt_expr_and} -\end{table} - -Table~\ref{tab:opt_expr_and} shows the replacement rules used for optimizing -an {\tt\$\_AND\_} gate. The first three rules implement the obvious const folding -rules. Note that `any' might include dynamic values calculated by other parts -of the circuit. The following three lines propagate undef (X) states. -These are the only three cases in which it is allowed to propagate an undef -according to Sec.~5.1.10 of IEEE Std. 1364-2005 \cite{Verilog2005}. - -The next two lines assume the value 0 for undef states. These two rules are only -used if no other substitutions are possible in the current module. If other substitutions -are possible they are performed first, in the hope that the `any' will change to -an undef value or a 1 and therefore the output can be set to undef. - -The last two lines simply replace an {\tt\$\_AND\_} gate with one constant-1 -input with a buffer. - -Besides this basic const folding the {\tt opt\_expr} pass can replace 1-bit wide -{\tt \$eq} and {\tt \$ne} cells with buffers or not-gates if one input is constant. - -The {\tt opt\_expr} pass is very conservative regarding optimizing {\tt \$mux} cells, -as these cells are often used to model decision-trees and breaking these trees can -interfere with other optimizations. - -\subsection{The opt\_muxtree pass} - -This pass optimizes trees of multiplexer cells by analyzing the select inputs. -Consider the following simple example: - -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -module uut(a, y); -input a; -output [1:0] y = a ? (a ? 1 : 2) : 3; -endmodule -\end{lstlisting} - -The output can never be 2, as this would require \lstinline[language=Verilog];a; -to be 1 for the outer multiplexer and 0 for the inner multiplexer. The {\tt -opt\_muxtree} pass detects this contradiction and replaces the inner multiplexer -with a constant 1, yielding the logic for \lstinline[language=Verilog];y = a ? 1 : 3;. - -\subsection{The opt\_reduce pass} - -\begin{sloppypar} -This is a simple optimization pass that identifies and consolidates identical input -bits to {\tt \$reduce\_and} and {\tt \$reduce\_or} cells. It also sorts the input -bits to ease identification of shareable {\tt \$reduce\_and} and {\tt \$reduce\_or} cells -in other passes. -\end{sloppypar} - -This pass also identifies and consolidates identical inputs to multiplexer cells. In this -case the new shared select bit is driven using a {\tt \$reduce\_or} cell that combines -the original select bits. - -Lastly this pass consolidates trees of {\tt \$reduce\_and} cells and trees of -{\tt \$reduce\_or} cells to single large {\tt \$reduce\_and} or {\tt \$reduce\_or} cells. - -These three simple optimizations are performed in a loop until a stable result is -produced. - -\subsection{The opt\_rmdff pass} - -This pass identifies single-bit d-type flip-flops ({\tt \$\_DFF\_*}, {\tt \$dff}, and {\tt -\$adff} cells) with a constant data input and replaces them with a constant driver. - -\subsection{The opt\_clean pass} - -This pass identifies unused signals and cells and removes them from the design. It also -creates an \B{unused\_bits} attribute on wires with unused bits. This attribute can be -used for debugging or by other optimization passes. - -\subsection{The opt\_merge pass} - -This pass performs trivial resource sharing. This means that this pass identifies cells -with identical inputs and replaces them with a single instance of the cell. - -The option {\tt -nomux} can be used to disable resource sharing for multiplexer -cells ({\tt \$mux} and {\tt \$pmux}. This can be useful as -it prevents multiplexer trees to be merged, which might prevent {\tt opt\_muxtree} -to identify possible optimizations. - -\section{FSM Extraction and Encoding} - -The {\tt fsm} pass performs finite-state-machine (FSM) extraction and recoding. The {\tt fsm} -pass simply executes the following other passes: - -\begin{itemize} -\item Identify and extract FSMs: -\begin{itemize} -\item {\tt fsm\_detect} -\item {\tt fsm\_extract} -\end{itemize} - -\item Basic optimizations: -\begin{itemize} -\item {\tt fsm\_opt} -\item {\tt opt\_clean} -\item {\tt fsm\_opt} -\end{itemize} - -\item Expanding to nearby gate-logic (if called with {\tt -expand}): -\begin{itemize} -\item {\tt fsm\_expand} -\item {\tt opt\_clean} -\item {\tt fsm\_opt} -\end{itemize} - -\item Re-code FSM states (unless called with {\tt -norecode}): -\begin{itemize} -\item {\tt fsm\_recode} -\end{itemize} - -\item Print information about FSMs: -\begin{itemize} -\item {\tt fsm\_info} -\end{itemize} - -\item Export FSMs in KISS2 file format (if called with {\tt -export}): -\begin{itemize} -\item {\tt fsm\_export} -\end{itemize} - -\item Map FSMs to RTL cells (unless called with {\tt -nomap}): -\begin{itemize} -\item {\tt fsm\_map} -\end{itemize} -\end{itemize} - -The {\tt fsm\_detect} pass identifies FSM state registers and marks them using the -\B{fsm\_encoding}{\tt = "auto"} attribute. The {\tt fsm\_extract} extracts all -FSMs marked using the \B{fsm\_encoding} attribute (unless \B{fsm\_encoding} is -set to {\tt "none"}) and replaces the corresponding RTL cells with a {\tt \$fsm} -cell. All other {\tt fsm\_*} passes operate on these {\tt \$fsm} cells. The -{\tt fsm\_map} call finally replaces the {\tt \$fsm} cells with RTL cells. - -Note that these optimizations operate on an RTL netlist. I.e.~the {\tt fsm} pass -should be executed after the {\tt proc} pass has transformed all -{\tt RTLIL::Process} objects to RTL cells. - -The algorithms used for FSM detection and extraction are influenced by a more -general reported technique \cite{fsmextract}. - -\subsection{FSM Detection} - -The {\tt fsm\_detect} pass identifies FSM state registers. It sets the -\B{fsm\_encoding}{\tt = "auto"} attribute on any (multi-bit) wire that matches -the following description: - -\begin{itemize} -\item Does not already have the \B{fsm\_encoding} attribute. -\item Is not an output of the containing module. -\item Is driven by single {\tt \$dff} or {\tt \$adff} cell. -\item The \B{D}-Input of this {\tt \$dff} or {\tt \$adff} cell is driven by a multiplexer -tree that only has constants or the old state value on its leaves. -\item The state value is only used in the said multiplexer tree or by simple relational -cells that compare the state value to a constant (usually {\tt \$eq} cells). -\end{itemize} - -This heuristic has proven to work very well. It is possible to overwrite it by setting -\B{fsm\_encoding}{\tt = "auto"} on registers that should be considered FSM state registers -and setting \B{fsm\_encoding}{\tt = "none"} on registers that match the above criteria -but should not be considered FSM state registers. - -Note however that marking state registers with \B{fsm\_encoding} that are not -suitable for FSM recoding can cause synthesis to fail or produce invalid -results. - -\subsection{FSM Extraction} - -The {\tt fsm\_extract} pass operates on all state signals marked with the -\B{fsm\_encoding} ({\tt != "none"}) attribute. For each state signal the following -information is determined: - -\begin{itemize} -\item The state registers -\item The asynchronous reset state if the state registers use asynchronous reset -\item All states and the control input signals used in the state transition functions -\item The control output signals calculated from the state signals and control inputs -\item A table of all state transitions and corresponding control inputs- and outputs -\end{itemize} - -The state registers (and asynchronous reset state, if applicable) is simply determined -by identifying the driver for the state signal. - -From there the {\tt \$mux}-tree driving the state register inputs is -recursively traversed. All select inputs are control signals and the leaves of the -{\tt \$mux}-tree are the states. The algorithm fails if a non-constant leaf -that is not the state signal itself is found. - -The list of control outputs is initialized with the bits from the state signal. -It is then extended by adding all values that are calculated by cells that -compare the state signal with a constant value. - -In most cases this will cover all uses of the state register, thus rendering the -state encoding arbitrary. If however a design uses e.g.~a single bit of the state -value to drive a control output directly, this bit of the state signal will be -transformed to a control output of the same value. - -Finally, a transition table for the FSM is generated. This is done by using the -{\tt ConstEval} C++ helper class (defined in {\tt kernel/consteval.h}) that can -be used to evaluate parts of the design. The {\tt ConstEval} class can be asked -to calculate a given set of result signals using a set of signal-value -assignments. It can also be passed a list of stop-signals that abort the {\tt -ConstEval} algorithm if the value of a stop-signal is needed in order to -calculate the result signals. - -The {\tt fsm\_extract} pass uses the {\tt ConstEval} class in the following way -to create a transition table. For each state: - -\begin{enumerate} -\item Create a {\tt ConstEval} object for the module containing the FSM -\item Add all control inputs to the list of stop signals -\item Set the state signal to the current state -\item Try to evaluate the next state and control output \label{enum:fsm_extract_cealg_try} -\item If step~\ref{enum:fsm_extract_cealg_try} was not successful: -\begin{itemize} -\item Recursively goto step~\ref{enum:fsm_extract_cealg_try} with the offending stop-signal set to 0. -\item Recursively goto step~\ref{enum:fsm_extract_cealg_try} with the offending stop-signal set to 1. -\end{itemize} -\item If step~\ref{enum:fsm_extract_cealg_try} was successful: Emit transition -\end{enumerate} - -Finally a {\tt \$fsm} cell is created with the generated transition table and added to the -module. This new cell is connected to the control signals and the old drivers for the -control outputs are disconnected. - -\subsection{FSM Optimization} - -The {\tt fsm\_opt} pass performs basic optimizations on {\tt \$fsm} cells (not including state -recoding). The following optimizations are performed (in this order): - -\begin{itemize} -\item Unused control outputs are removed from the {\tt \$fsm} cell. The attribute \B{unused\_bits} -(that is usually set by the {\tt opt\_clean} pass) is used to determine which control -outputs are unused. -\item Control inputs that are connected to the same driver are merged. -\item When a control input is driven by a control output, the control input is removed and the transition -table altered to give the same performance without the external feedback path. -\item Entries in the transition table that yield the same output and only -differ in the value of a single control input bit are merged and the different bit is removed -from the sensitivity list (turned into a don't-care bit). -\item Constant inputs are removed and the transition table is altered to give an unchanged behaviour. -\item Unused inputs are removed. -\end{itemize} - -\subsection{FSM Recoding} - -The {\tt fsm\_recode} pass assigns new bit pattern to the states. Usually this -also implies a change in the width of the state signal. At the moment of this -writing only one-hot encoding with all-zero for the reset state is supported. - -The {\tt fsm\_recode} pass can also write a text file with the changes performed -by it that can be used when verifying designs synthesized by Yosys using Synopsys -Formality \citeweblink{Formality}. - -\section{Logic Optimization} - -Yosys can perform multi-level combinational logic optimization on gate-level netlists using the -external program ABC \citeweblink{ABC}. The {\tt abc} pass extracts the combinational gate-level -parts of the design, passes it through ABC, and re-integrates the results. The {\tt abc} pass -can also be used to perform other operations using ABC, such as technology mapping (see -Sec.~\ref{sec:techmap_extern} for details). - diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex deleted file mode 100644 index ed8b4cd4955..00000000000 --- a/manual/CHAPTER_Overview.tex +++ /dev/null @@ -1,555 +0,0 @@ - -\chapter{Implementation Overview} -\label{chapter:overview} - -Yosys is an extensible open source hardware synthesis tool. It is aimed at -designers who are looking for an easily accessible, universal, and -vendor-independent synthesis tool, as well as scientists who do research in -electronic design automation (EDA) and are looking for an open synthesis -framework that can be used to test algorithms on complex real-world designs. - -Yosys can synthesize a large subset of Verilog 2005 and has been tested with a -wide range of real-world designs, including the OpenRISC 1200 CPU -\citeweblink{OR1200}, the openMSP430 CPU \citeweblink{openMSP430}, the -OpenCores I$^2$C master \citeweblink{i2cmaster} and the k68 CPU \citeweblink{k68}. - -As of this writing a Yosys VHDL frontend is in development. - -Yosys is written in C++ (using some features from the new C++11 standard). This -chapter describes some of the fundamental Yosys data structures. For the sake -of simplicity the C++ type names used in the Yosys implementation are used in -this chapter, even though the chapter only explains the conceptual idea behind -it and can be used as reference to implement a similar system in any language. - -\section{Simplified Data Flow} - -Figure~\ref{fig:Overview_flow} shows the simplified data flow within Yosys. -Rectangles in the figure represent program modules and ellipses internal -data structures that are used to exchange design data between the program -modules. - -Design data is read in using one of the frontend modules. The high-level HDL -frontends for Verilog and VHDL code generate an abstract syntax tree (AST) that -is then passed to the AST frontend. Note that both HDL frontends use the same -AST representation that is powerful enough to cover the Verilog HDL and VHDL -language. - -The AST Frontend then compiles the AST to Yosys's main internal data format, -the RTL Intermediate Language (RTLIL). A more detailed description of this format -is given in the next section. - -There is also a text representation of the RTLIL data structure that can be -parsed using the RTLIL Frontend. - -The design data may then be transformed using a series of passes that all -operate on the RTLIL representation of the design. - -Finally the design in RTLIL representation is converted back to text by one -of the backends, namely the Verilog Backend for generating Verilog netlists -and the RTLIL Backend for writing the RTLIL data in the same format that is -understood by the RTLIL Frontend. - -With the exception of the AST Frontend, which is called by the high-level HDL -frontends and can't be called directly by the user, all program modules are -called by the user (usually using a synthesis script that contains text -commands for Yosys). - -By combining passes in different ways and/or adding additional passes to Yosys -it is possible to adapt Yosys to a wide range of applications. For this to be -possible it is key that (1) all passes operate on the same data structure -(RTLIL) and (2) that this data structure is powerful enough to represent the design -in different stages of the synthesis. - -\begin{figure}[t] - \hfil - \begin{tikzpicture} - \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=15em] - \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=15em] - \node[process] (vlog) {Verilog Frontend}; - \node[process, dashed, fill=green!5] (vhdl) [right of=vlog] {VHDL Frontend}; - \node[process] (ilang) [right of=vhdl] {RTLIL Frontend}; - \node[data] (ast) [below of=vlog, node distance=5em, xshift=7.5em] {AST}; - \node[process] (astfe) [below of=ast, node distance=5em] {AST Frontend}; - \node[data] (rtlil) [below of=astfe, node distance=5em, xshift=7.5em] {RTLIL}; - \node[process] (pass) [right of=rtlil, node distance=5em, xshift=7.5em] {Passes}; - \node[process] (vlbe) [below of=rtlil, node distance=7em, xshift=-13em] {Verilog Backend}; - \node[process] (ilangbe) [below of=rtlil, node distance=7em, xshift=0em] {RTLIL Backend}; - \node[process, dashed, fill=green!5] (otherbe) [below of=rtlil, node distance=7em, xshift=+13em] {Other Backends}; - - \draw[-latex] (vlog) -- (ast); - \draw[-latex] (vhdl) -- (ast); - \draw[-latex] (ast) -- (astfe); - \draw[-latex] (astfe) -- (rtlil); - \draw[-latex] (ilang) -- (rtlil); - \draw[latex-latex] (rtlil) -- (pass); - \draw[-latex] (rtlil) -- (vlbe); - \draw[-latex] (rtlil) -- (ilangbe); - \draw[-latex] (rtlil) -- (otherbe); - \end{tikzpicture} - \caption{Yosys simplified data flow (ellipses: data structures, rectangles: program modules)} - \label{fig:Overview_flow} -\end{figure} - -\section{The RTL Intermediate Language} - -All frontends, passes and backends in Yosys operate on a design in RTLIL representation. -The only exception are the high-level frontends that use the AST representation as an intermediate step before generating RTLIL -data. - -In order to avoid reinventing names for the RTLIL classes, they are simply referred to by their full C++ name, i.e.~including -the {\tt RTLIL::} namespace prefix, in this document. - -Figure~\ref{fig:Overview_RTLIL} shows a simplified Entity-Relationship Diagram (ER Diagram) of RTLIL. In $1:N$ relationships the arrow -points from the $N$ side to the $1$. For example one RTLIL::Design contains $N$ (zero to many) instances of RTLIL::Module. -A two-pointed arrow indicates a $1:1$ relationship. - -The RTLIL::Design is the root object of the RTLIL data structure. There is always one ``current design'' in memory -which passes operate on, frontends add data to and backends convert to exportable formats. But in some cases passes -internally generate additional RTLIL::Design objects. For example when a pass is reading an auxiliary Verilog file such -as a cell library, it might create an additional RTLIL::Design object and call the Verilog frontend with this -other object to parse the cell library. - -\begin{figure}[t] - \hfil - \begin{tikzpicture} - \tikzstyle{entity} = [draw, fill=gray!10, rectangle, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}] - \node[entity] (design) {RTLIL::Design}; - \node[entity] (module) [right of=design, node distance=11em] {RTLIL::Module} edge [-latex] node[above] {\tiny 1 \hskip3em N} (design); - - \node[entity] (process) [fill=green!10, right of=module, node distance=10em] {RTLIL::Process} (process.west) edge [-latex] (module); - \node[entity] (memory) [fill=red!10, below of=process] {RTLIL::Memory} edge [-latex] (module); - \node[entity] (wire) [fill=blue!10, above of=process] {RTLIL::Wire} (wire.west) edge [-latex] (module); - \node[entity] (cell) [fill=blue!10, above of=wire] {RTLIL::Cell} (cell.west) edge [-latex] (module); - - \node[entity] (case) [fill=green!10, right of=process, node distance=10em] {RTLIL::CaseRule} edge [latex-latex] (process); - \node[entity] (sync) [fill=green!10, above of=case] {RTLIL::SyncRule} edge [-latex] (process); - \node[entity] (switch) [fill=green!10, below of=case] {RTLIL::SwitchRule} edge [-latex] (case); - \draw[latex-] (switch.east) -- ++(1em,0) |- (case.east); - \end{tikzpicture} - \caption{Simplified RTLIL Entity-Relationship Diagram} - \label{fig:Overview_RTLIL} -\end{figure} - -There is only one active RTLIL::Design object that is used by all frontends, -passes and backends called by the user, e.g.~using a synthesis script. The RTLIL::Design then contains -zero to many RTLIL::Module objects. This corresponds to modules in Verilog or entities in VHDL. Each -module in turn contains objects from three different categories: - -\begin{itemize} -\item RTLIL::Cell and RTLIL::Wire objects represent classical netlist data. -\item RTLIL::Process objects represent the decision trees (if-then-else statements, etc.) and synchronization -declarations (clock signals and sensitivity) from Verilog {\tt always} and VHDL {\tt process} blocks. -\item RTLIL::Memory objects represent addressable memories (arrays). -\end{itemize} - -\begin{sloppypar} -Usually the output of the synthesis procedure is a netlist, i.e. all -RTLIL::Process and RTLIL::Memory objects must be replaced by RTLIL::Cell and -RTLIL::Wire objects by synthesis passes. -\end{sloppypar} - -All features of the HDL that cannot be mapped directly to these RTLIL classes must be -transformed to an RTLIL-compatible representation by the HDL frontend. This includes -Verilog-features such as generate-blocks, loops and parameters. - -The following sections contain a more detailed description of the different -parts of RTLIL and rationale behind some of the design decisions. - -\subsection{RTLIL Identifiers} - -All identifiers in RTLIL (such as module names, port names, signal names, cell -types, etc.) follow the following naming convention: they must either start with -a backslash (\textbackslash) or a dollar sign (\$). - -Identifiers starting with a backslash are public visible identifiers. Usually -they originate from one of the HDL input files. For example the signal name ``{\tt \textbackslash sig42}'' -is most likely a signal that was declared using the name ``{\tt sig42}'' in an HDL input file. -On the other hand the signal name ``{\tt \$sig42}'' is an auto-generated signal name. The backends -convert all identifiers that start with a dollar sign to identifiers that do not collide with -identifiers that start with a backslash. - -This has three advantages: - -\begin{itemize} -\item First, it is impossible that an auto-generated identifier collides with -an identifier that was provided by the user. -\item Second, the information about which identifiers were originally -provided by the user is always available which can help guide some optimizations. For example the ``opt\_rmunused'' -tries to preserve signals with a user-provided name but doesn't hesitate to delete signals that have -auto-generated names when they just duplicate other signals. -\item Third, the delicate job of finding suitable auto-generated public visible -names is deferred to one central location. Internally auto-generated names that -may hold important information for Yosys developers can be used without -disturbing external tools. For example the Verilog backend assigns names in the form {\tt \_{\it integer}\_}. -\end{itemize} - -Whitespace and control characters (any character with an ASCII code 32 or less) are not allowed -in RTLIL identifiers; most frontends and backends cannot support these characters in identifiers. - -In order to avoid programming errors, the RTLIL data structures check if all identifiers start -with either a backslash or a dollar sign, and contain no whitespace or control characters. -Violating these rules results in a runtime error. - -All RTLIL identifiers are case sensitive. - -Some transformations, such as flattening, may have to change identifiers provided by the user -to avoid name collisions. When that happens, attribute ``{\tt hdlname}`` is attached to the object -with the changed identifier. This attribute contains one name (if emitted directly by the frontend, -or is a result of disambiguation) or multiple names separated by spaces (if a result of flattening). -All names specified in the ``{\tt hdlname}`` attribute are public and do not include the leading -``\textbackslash``. - -\subsection{RTLIL::Design and RTLIL::Module} - -The RTLIL::Design object is basically just a container for RTLIL::Module objects. In addition to -a list of RTLIL::Module objects the RTLIL::Design also keeps a list of {\it selected objects}, i.e. -the objects that passes should operate on. In most cases the whole design is selected and therefore -passes operate on the whole design. But this mechanism can be useful for more complex synthesis jobs -in which only parts of the design should be affected by certain passes. - -Besides the objects shown in the ER diagram in Fig.~\ref{fig:Overview_RTLIL} an RTLIL::Module object -contains the following additional properties: - -\begin{itemize} -\item The module name -\item A list of attributes -\item A list of connections between wires -\item An optional frontend callback used to derive parametrized variations of the module -\end{itemize} - -The attributes can be Verilog attributes imported by the Verilog frontend or attributes assigned -by passes. They can be used to store additional metadata about modules or just mark them to be -used by certain part of the synthesis script but not by others. - -Verilog and VHDL both support parametric modules (known as ``generic entities'' in VHDL). The RTLIL -format does not support parametric modules itself. Instead each module contains a callback function -into the AST frontend to generate a parametrized variation of the RTLIL::Module as needed. This -callback then returns the auto-generated name of the parametrized variation of the module. (A hash -over the parameters and the module name is used to prohibit the same parametrized variation from being -generated twice. For modules with only a few parameters, a name directly containing all parameters -is generated instead of a hash string.) - -\subsection{RTLIL::Cell and RTLIL::Wire} - -A module contains zero to many RTLIL::Cell and RTLIL::Wire objects. Objects of -these types are used to model netlists. Usually the goal of all synthesis efforts is to convert -all modules to a state where the functionality of the module is implemented only by cells -from a given cell library and wires to connect these cells with each other. Note that module -ports are just wires with a special property. - -An RTLIL::Wire object has the following properties: - -\begin{itemize} -\item The wire name -\item A list of attributes -\item A width (buses are just wires with a width > 1) -\item Bus direction (MSB to LSB or vice versa) -\item Lowest valid bit index (LSB or MSB depending on bus direction) -\item If the wire is a port: port number and direction (input/output/inout) -\end{itemize} - -As with modules, the attributes can be Verilog attributes imported by the -Verilog frontend or attributes assigned by passes. - -In Yosys, busses (signal vectors) are represented using a single wire object -with a width > 1. So Yosys does not convert signal vectors to individual signals. -This makes some aspects of RTLIL more complex but enables Yosys to be used for -coarse grain synthesis where the cells of the target architecture operate on -entire signal vectors instead of single bit wires. - -In Verilog and VHDL, busses may have arbitrary bounds, and LSB can have either -the lowest or the highest bit index. In RTLIL, bit 0 always corresponds to LSB; -however, information from the HDL frontend is preserved so that the bus will be -correctly indexed in error messages, backend output, constraint files, etc. - -An RTLIL::Cell object has the following properties: - -\begin{itemize} -\item The cell name and type -\item A list of attributes -\item A list of parameters (for parametric cells) -\item Cell ports and the connections of ports to wires and constants -\end{itemize} - -The connections of ports to wires are coded by assigning an RTLIL::SigSpec -to each cell port. The RTLIL::SigSpec data type is described in the next section. - -\subsection{RTLIL::SigSpec} - -A ``signal'' is everything that can be applied to a cell port. I.e. - -\begin{itemize} -\item Any constant value of arbitrary bit-width \\ -\null\hskip1em For example: \lstinline[language=Verilog]{1337, 16'b0000010100111001, 1'b1, 1'bx} -\item All bits of a wire or a selection of bits from a wire \\ -\null\hskip1em For example: \lstinline[language=Verilog]{mywire, mywire[24], mywire[15:8]} -\item Concatenations of the above \\ -\null\hskip1em For example: \lstinline[language=Verilog]|{16'd1337, mywire[15:8]}| -\end{itemize} - -The RTLIL::SigSpec data type is used to represent signals. The RTLIL::Cell -object contains one RTLIL::SigSpec for each cell port. - -In addition, connections between wires are represented using a pair of -RTLIL::SigSpec objects. Such pairs are needed in different locations. Therefore -the type name RTLIL::SigSig was defined for such a pair. - -\subsection{RTLIL::Process} - -When a high-level HDL frontend processes behavioural code it splits it up into -data path logic (e.g.~the expression {\tt a + b} is replaced by the output of an -adder that takes {\tt a} and {\tt b} as inputs) and an RTLIL::Process that models -the control logic of the behavioural code. Let's consider a simple example: - -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -module ff_with_en_and_async_reset(clock, reset, enable, d, q); -input clock, reset, enable, d; -output reg q; -always @(posedge clock, posedge reset) - if (reset) - q <= 0; - else if (enable) - q <= d; -endmodule -\end{lstlisting} - -In this example there is no data path and therefore the RTLIL::Module generated by -the frontend only contains a few RTLIL::Wire objects and an RTLIL::Process. -The RTLIL::Process in RTLIL syntax: - -\begin{lstlisting}[numbers=left,frame=single,language=rtlil] -process $proc$ff_with_en_and_async_reset.v:4$1 - assign $0\q[0:0] \q - switch \reset - case 1'1 - assign $0\q[0:0] 1'0 - case - switch \enable - case 1'1 - assign $0\q[0:0] \d - case - end - end - sync posedge \clock - update \q $0\q[0:0] - sync posedge \reset - update \q $0\q[0:0] -end -\end{lstlisting} - -This RTLIL::Process contains two RTLIL::SyncRule objects, two RTLIL::SwitchRule -objects and five RTLIL::CaseRule objects. The wire {\tt \$0\textbackslash{}q[0:0]} -is an automatically created wire that holds the next value of {\tt \textbackslash{}q}. The lines -$2 \dots 12$ describe how {\tt \$0\textbackslash{}q[0:0]} should be calculated. The -lines $13 \dots 16$ describe how the value of {\tt \$0\textbackslash{}q[0:0]} is used -to update {\tt \textbackslash{}q}. - -An RTLIL::Process is a container for zero or more RTLIL::SyncRule objects and -exactly one RTLIL::CaseRule object, which is called the {\it root case}. - -An RTLIL::SyncRule object contains an (optional) synchronization condition (signal and edge-type) and zero or -more assignments (RTLIL::SigSig). The {\tt always} synchronization condition is used to break combinatorial -loops when a latch should be inferred instead. - -An RTLIL::CaseRule is a container for zero or more assignments (RTLIL::SigSig) -and zero or more RTLIL::SwitchRule objects. An RTLIL::SwitchRule objects is a -container for zero or more RTLIL::CaseRule objects. - -In the above example the lines $2 \dots 12$ are the root case. Here {\tt \$0\textbackslash{}q[0:0]} is first -assigned the old value {\tt \textbackslash{}q} as default value (line 2). The root case -also contains an RTLIL::SwitchRule object (lines $3 \dots 12$). Such an object is very similar to the C {\tt switch} -statement as it uses a control signal ({\tt \textbackslash{}reset} in this case) to determine -which of its cases should be active. The RTLIL::SwitchRule object then contains one RTLIL::CaseRule -object per case. In this example there is a case\footnote{The -syntax {\tt 1'1} in the RTLIL code specifies a constant with a length of one bit (the first ``1''), -and this bit is a one (the second ``1'').} for {\tt \textbackslash{}reset == 1} that causes -{\tt \$0\textbackslash{}q[0:0]} to be set (lines 4 and 5) and a default case that in turn contains a switch that -sets {\tt \$0\textbackslash{}q[0:0]} to the value of {\tt \textbackslash{}d} if {\tt -\textbackslash{}enable} is active (lines $6 \dots 11$). - -A case can specify zero or more compare values that will determine whether it matches. Each of the compare values -must be the exact same width as the control signal. When more than one compare value is specified, the case matches -if any of them matches the control signal; when zero compare values are specified, the case always matches (i.e. -it is the default case). - -A switch prioritizes cases from first to last: multiple cases can match, but only the first matched case becomes -active. This normally synthesizes to a priority encoder. The {\tt parallel\_case} attribute allows passes to assume -that no more than one case will match, and {\tt full\_case} attribute allows passes to assume that exactly one -case will match; if these invariants are ever dynamically violated, the behavior is undefined. These attributes -are useful when an invariant invisible to the synthesizer causes the control signal to never take certain -bit patterns. - -The lines $13 \dots 16$ then cause {\tt \textbackslash{}q} to be updated whenever there is -a positive clock edge on {\tt \textbackslash{}clock} or {\tt \textbackslash{}reset}. - -In order to generate such a representation, the language frontend must be able to handle blocking -and nonblocking assignments correctly. However, the language frontend does not need to identify -the correct type of storage element for the output signal or generate multiplexers for the -decision tree. This is done by passes that work on the RTLIL representation. Therefore it is -relatively easy to substitute these steps with other algorithms that target different target -architectures or perform optimizations or other transformations on the decision trees before -further processing them. - -One of the first actions performed on a design in RTLIL representation in most -synthesis scripts is identifying asynchronous resets. This is usually done using the {\tt proc\_arst} -pass. This pass transforms the above example to the following RTLIL::Process: - -\begin{lstlisting}[numbers=left,frame=single,language=rtlil] -process $proc$ff_with_en_and_async_reset.v:4$1 - assign $0\q[0:0] \q - switch \enable - case 1'1 - assign $0\q[0:0] \d - case - end - sync posedge \clock - update \q $0\q[0:0] - sync high \reset - update \q 1'0 -end -\end{lstlisting} - -This pass has transformed the outer RTLIL::SwitchRule into a modified RTLIL::SyncRule object -for the {\tt \textbackslash{}reset} signal. Further processing converts the RTLIL::Process -into e.g.~a d-type flip-flop with asynchronous reset and a multiplexer for the enable signal: - -\begin{lstlisting}[numbers=left,frame=single,language=rtlil] -cell $adff $procdff$6 - parameter \ARST_POLARITY 1'1 - parameter \ARST_VALUE 1'0 - parameter \CLK_POLARITY 1'1 - parameter \WIDTH 1 - connect \ARST \reset - connect \CLK \clock - connect \D $0\q[0:0] - connect \Q \q -end -cell $mux $procmux$3 - parameter \WIDTH 1 - connect \A \q - connect \B \d - connect \S \enable - connect \Y $0\q[0:0] -end -\end{lstlisting} - -Different combinations of passes may yield different results. Note that {\tt \$adff} and {\tt -\$mux} are internal cell types that still need to be mapped to cell types from the -target cell library. - -Some passes refuse to operate on modules that still contain RTLIL::Process objects as the -presence of these objects in a module increases the complexity. Therefore the passes to translate -processes to a netlist of cells are usually called early in a synthesis script. The {\tt proc} -pass calls a series of other passes that together perform this conversion in a way that is suitable -for most synthesis tasks. - -\subsection{RTLIL::Memory} - -For every array (memory) in the HDL code an RTLIL::Memory object is created. A -memory object has the following properties: - -\begin{itemize} -\item The memory name -\item A list of attributes -\item The width of an addressable word -\item The size of the memory in number of words -\end{itemize} - -All read accesses to the memory are transformed to {\tt \$memrd} cells and all write accesses to -{\tt \$memwr} cells by the language frontend. These cells consist of independent read- and write-ports -to the memory. Memory initialization is transformed to {\tt \$meminit} cells by the language frontend. -The \B{MEMID} parameter on these cells is used to link them together and to the RTLIL::Memory object they belong to. - -The rationale behind using separate cells for the individual ports versus -creating a large multiport memory cell right in the language frontend is that -the separate {\tt \$memrd} and {\tt \$memwr} cells can be consolidated using resource sharing. -As resource sharing is a non-trivial optimization problem where different synthesis tasks -can have different requirements it lends itself to do the optimisation in separate passes and merge -the RTLIL::Memory objects and {\tt \$memrd} and {\tt \$memwr} cells to multiport memory blocks after resource sharing is completed. - -The {\tt memory} pass performs this conversion and can (depending on the options passed -to it) transform the memories directly to d-type flip-flops and address logic or yield -multiport memory blocks (represented using {\tt \$mem} cells). - -See Sec.~\ref{sec:memcells} for details about the memory cell types. - -\section{Command Interface and Synthesis Scripts} - -Yosys reads and processes commands from synthesis scripts, command line arguments and -an interactive command prompt. Yosys commands consist of a command name and an optional -whitespace separated list of arguments. Commands are terminated using the newline character -or a semicolon ({\tt ;}). Empty lines and lines starting with the hash sign ({\tt \#}) are ignored. -See Sec.~\ref{sec:typusecase} for an example synthesis script. - -The command {\tt help} can be used to access the command reference manual. - -Most commands can operate not only on the entire design but also specifically on {\it selected} -parts of the design. For example the command {\tt dump} will print all selected objects -in the current design while {\tt dump foobar} will only print the module {\tt foobar} -and {\tt dump *} will print the entire design regardless of the current selection. - -The selection mechanism is very powerful. For example the command {\tt dump */t:\$add -\%x:+[A] */w:* \%i} will print all wires that are connected to the \B{A} port of -a {\tt \$add} cell. Detailed documentation of the select framework can be -found in the command reference for the {\tt select} command. - -\section{Source Tree and Build System} - -The Yosys source tree is organized into the following top-level directories: - -\begin{itemize} - -\item {\tt backends/} \\ -This directory contains a subdirectory for each of the backend modules. - -\item {\tt frontends/} \\ -This directory contains a subdirectory for each of the frontend modules. - -\item {\tt kernel/} \\ -This directory contains all the core functionality of Yosys. This includes the -functions and definitions for working with the RTLIL data structures ({\tt -rtlil.h} and {\tt rtlil.cc}), the main() function ({\tt driver.cc}), the -internal framework for generating log messages ({\tt log.h} and {\tt log.cc}), -the internal framework for registering and calling passes ({\tt register.h} and -{\tt register.cc}), some core commands that are not really passes ({\tt -select.cc}, {\tt show.cc}, \dots) and a couple of other small utility libraries. - -\item {\tt passes/} \\ -This directory contains a subdirectory for each pass or group of passes. For example as -of this writing the directory {\tt passes/opt/} contains the code for seven -passes: {\tt opt}, {\tt opt\_expr}, {\tt opt\_muxtree}, {\tt opt\_reduce}, -{\tt opt\_rmdff}, {\tt opt\_rmunused} and {\tt opt\_merge}. - -\item {\tt techlibs/} \\ -This directory contains simulation models and standard implementations for the -cells from the internal cell library. - -\item {\tt tests/} \\ -This directory contains a couple of test cases. Most of the smaller tests are executed -automatically when {\tt make test} is called. The larger tests must be executed -manually. Most of the larger tests require downloading external HDL source code -and/or external tools. The tests range from comparing simulation results of the synthesized -design to the original sources to logic equivalence checking of entire CPU cores. - -\end{itemize} - -\begin{sloppypar} -The top-level Makefile includes {\tt frontends/*/Makefile.inc}, {\tt passes/*/Makefile.inc} -and {\tt backends/*/Makefile.inc}. So when extending Yosys it is enough to create -a new directory in {\tt frontends/}, {\tt passes/} or {\tt backends/} with your sources -and a {\tt Makefile.inc}. The Yosys kernel automatically detects all commands linked with -Yosys. So it is not needed to add additional commands to a central list of commands. -\end{sloppypar} - -Good starting points for reading example source code to learn how to write passes -are {\tt passes/opt/opt\_rmdff.cc} and {\tt passes/opt/opt\_merge.cc}. - -See the top-level README file for a quick {\it Getting Started} guide and build -instructions. The Yosys build is based solely on Makefiles. - -Users of the Qt Creator IDE can generate a QT Creator project file using {\tt -make qtcreator}. Users of the Eclipse IDE can use the ``Makefile Project with -Existing Code'' project type in the Eclipse ``New Project'' dialog (only -available after the CDT plugin has been installed) to create an Eclipse project -in order to programming extensions to Yosys or just browse the Yosys code base. - diff --git a/manual/CHAPTER_Prog.tex b/manual/CHAPTER_Prog.tex deleted file mode 100644 index 3cbc95a195d..00000000000 --- a/manual/CHAPTER_Prog.tex +++ /dev/null @@ -1,26 +0,0 @@ - -\chapter{Programming Yosys Extensions} -\label{chapter:prog} - -This chapter contains some bits and pieces of information about programming -yosys extensions. Also consult the section on programming in the ``Yosys -Presentation'' (can be downloaded from the Yosys website as PDF) and don't -be afraid to ask questions on the Yosys Subreddit. - -\section{The ``CodingReadme'' File} - -The following is an excerpt of the {\tt CodingReadme} file from the Yosys source tree. - -\lstinputlisting[title=CodingReadme,rangeprefix=--,rangesuffix=--,includerangemarker=false,linerange=snip-snap,numbers=left,frame=single]{../CodingReadme} - -\section{The ``stubsnets'' Example Module} - -The following is the complete code of the ``stubsnets'' example module. It is included in the Yosys source distribution as {\tt manual/CHAPTER\_Prog/stubnets.cc}. - - -\lstinputlisting[title=stubnets.cc,numbers=left,frame=single,language=C++]{CHAPTER_Prog/stubnets.cc} - -\lstinputlisting[title=Makefile,numbers=left,frame=single,language=make]{CHAPTER_Prog/Makefile} - -\lstinputlisting[title=test.v,numbers=left,frame=single,language=Verilog]{CHAPTER_Prog/test.v} - diff --git a/manual/CHAPTER_StateOfTheArt.tex b/manual/CHAPTER_StateOfTheArt.tex deleted file mode 100644 index 2d0c77a01a5..00000000000 --- a/manual/CHAPTER_StateOfTheArt.tex +++ /dev/null @@ -1,289 +0,0 @@ - -\chapter{Evaluation of other OSS Verilog Synthesis Tools} -\label{chapter:sota} - -In this appendix\footnote{This appendix is an updated version of an -unpublished student research paper. \cite{VerilogFossEval}} -the existing FOSS Verilog synthesis tools\footnote{To the -author's best knowledge, all relevant tools that existed at the time of this -writing are included. But as there is no formal channel through which such -tools are published it is hard to give any guarantees in that matter.} are -evaluated. Extremely limited or application specific tools (e.g.~pure Verilog -Netlist parsers) as well as Verilog simulators are not included. These existing -solutions are tested using a set of representative Verilog code snippets. It is -shown that no existing FOSS tool implements even close to a sufficient subset -of Verilog to be usable as synthesis tool for a wide range existing Verilog code. - -The packages evaluated are: - -\begin{itemize} -\item Icarus Verilog \citeweblink{Icarus}\footnote{Icarus Verilog is mainly a simulation -tool but also supported synthesis up to version 0.8. Therefore version 0.8.7 is used -for this evaluation.)} -\item Verilog-to-Routing (VTR) / Odin-II \cite{vtr2012}\cite{Odin}\citeweblink{VTR} -\item HDL Analyzer and Netlist Architect (HANA) \citeweblink{HANA} -\item Verilog front-end to VIS (vl2mv) \cite{Cheng93vl2mv:a}\citeweblink{VIS} -\end{itemize} - -In each of the following sections Verilog modules that test a certain Verilog -language feature are presented and the support for these features is tested in all -the tools mentioned above. It is evaluated whether the tools under test -successfully generate netlists for the Verilog input and whether these netlists -match the simulation behavior of the designs using testbenches. - -All test cases are verified to be synthesizeable using Xilinx XST from the Xilinx -WebPACK \citeweblink{XilinxWebPACK} suite. - -Trivial features such as support for simple structural Verilog are not explicitly tested. - -Vl2mv and Odin-II generate output in the BLIF (Berkeley Logic Interchange -Format) and BLIF-MV (an extended version of BLIF) formats respectively. -ABC \citeweblink{ABC} is used to convert this output to Verilog for verification -using testbenches. - -Icarus Verilog generates EDIF (Electronic Design Interchange Format) output -utilizing LPM (Library of Parameterized Modules) cells. The EDIF files are -converted to Verilog using edif2ngd and netgen from Xilinx WebPACK. A -hand-written implementation of the LPM cells utilized by the generated netlists -is used for verification. - -Following these functional tests, a quick analysis of the extensibility of the tools -under test is provided in a separate section. - -The last section of this chapter finally concludes these series of evaluations -with a summary of the results. - -\begin{figure}[t!] - \begin{minipage}{7.7cm} - \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/always01_pub.v} - \end{minipage} - \hfill - \begin{minipage}{7.7cm} - \lstinputlisting[frame=single,language=Verilog]{CHAPTER_StateOfTheArt/always02_pub.v} - \end{minipage} - \caption{1st and 2nd Verilog always examples} - \label{fig:StateOfTheArt_always12} -\end{figure} - -\begin{figure}[!] - \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/always03.v} - \caption{3rd Verilog always example} - \label{fig:StateOfTheArt_always3} -\end{figure} - -\section{Always blocks and blocking vs.~nonblocking assignments} -\label{sec:blocking_nonblocking} - -The ``always''-block is one of the most fundamental non-trivial Verilog -language features. It can be used to model a combinatorial path (with optional -registers on the outputs) in a way that mimics a regular programming language. - -Within an always block, if- and case-statements can be used to model multiplexers. -Blocking assignments ($=$) and nonblocking assignments ($<=$) are used to populate the -leaf-nodes of these multiplexer trees. Unassigned leaf-nodes default to feedback -paths that cause the output register to hold the previous value. More advanced -synthesis tools often convert these feedback paths to register enable signals or -even generate circuits with clock gating. - -Registers assigned with nonblocking assignments ($<=$) behave differently from -variables in regular programming languages: In a simulation they are not -updated immediately after being assigned. Instead the right-hand sides are -evaluated and the results stored in temporary memory locations. After all -pending updates have been prepared in this way they are executed, thus yielding -semi-parallel execution of all nonblocking assignments. - -For synthesis this means that every occurrence of that register in an expression -addresses the output port of the corresponding register regardless of the question whether the register -has been assigned a new value in an earlier command in the same always block. -Therefore with nonblocking assignments the order of the assignments has no effect -on the resulting circuit as long as the left-hand sides of the assignments are -unique. - -The three example codes in Fig.~\ref{fig:StateOfTheArt_always12} and -Fig.~\ref{fig:StateOfTheArt_always3} use all these features and can thus be used -to test the synthesis tools capabilities to synthesize always blocks correctly. - -The first example is only using the most fundamental Verilog features. All -tools under test were able to successfully synthesize this design. - -\begin{figure}[b!] - \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/arrays01.v} - \caption{Verilog array example} - \label{fig:StateOfTheArt_arrays} -\end{figure} - -The 2nd example is functionally identical to the 1st one but is using an -if-statement inside the always block. Odin-II fails to synthesize it and -instead produces the following error message: - -\begin{verbatim} -ERROR: (File: always02.v) (Line number: 13) -You've defined the driver "count~0" twice -\end{verbatim} - -Vl2mv does not produce an error message but outputs an invalid synthesis result -that is not using the reset input at all. - -Icarus Verilog also doesn't produce an error message but generates an invalid output -for this 2nd example. The code generated by Icarus Verilog only implements the reset -path for the count register, effectively setting the output to constant 0. - -So of all tools under test only HANA was able to create correct synthesis results -for the 2nd example. - -The 3rd example is using blocking and nonblocking assignments and many if statements. -Odin also fails to synthesize this example: - -\begin{verbatim} -ERROR: (File: always03.v) (Line number: 8) -ODIN doesn't handle blocking statements in Sequential blocks -\end{verbatim} - -HANA, Icarus Verilog and vl2mv create invalid synthesis results for the 3rd example. - -So unfortunately none of the tools under test provide a complete and correct -implementation of blocking and nonblocking assignments. - -\section{Arrays for memory modelling} - -Verilog arrays are part of the synthesizeable subset of Verilog and are -commonly used to model addressable memory. The Verilog code in -Fig.~\ref{fig:StateOfTheArt_arrays} demonstrates this by implementing a single -port memory. - -For this design HANA, vl2m and ODIN-II generate error messages indicating that -arrays are not supported. - -\begin{figure}[t!] - \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/forgen01.v} - \caption{Verilog for loop example} - \label{fig:StateOfTheArt_for} -\end{figure} - -Icarus Verilog produces an invalid output that is using the address only for -reads. Instead of using the address input for writes, the generated design -simply loads the data to all memory locations whenever the write-enable input -is active, effectively turning the design into a single 4-bit D-Flip-Flop with -enable input. - -As all tools under test already fail this simple test, there is nothing to gain -by continuing tests on this aspect of Verilog synthesis such as synthesis of dual port -memories, correct handling of write collisions, and so forth. - -\begin{figure}[t!] - \lstinputlisting[numbers=left,frame=single,language=Verilog]{CHAPTER_StateOfTheArt/forgen02.v} - \caption{Verilog generate example} - \label{fig:StateOfTheArt_gen} -\end{figure} - -\section{For-loops and generate blocks} - -For-loops and generate blocks are more advanced Verilog features. These features -allow the circuit designer to add program code to her design that is evaluated -during synthesis to generate (parts of) the circuits description; something that -could only be done using a code generator otherwise. - -For-loops are only allowed in synthesizeable Verilog if they can be completely -unrolled. Then they can be a powerful tool to generate array logic or static -lookup tables. The code in Fig.~\ref{fig:StateOfTheArt_for} generates a circuit that -tests a 5 bit value for being a prime number using a static lookup table. - -Generate blocks can be used to model array logic in complex parametric designs. The -code in Fig.~\ref{fig:StateOfTheArt_gen} implements a ripple-carry adder with -parametric width from simple assign-statements and logic operations using a Verilog -generate block. - -All tools under test failed to synthesize both test cases. HANA creates invalid -output in both cases. Icarus Verilog creates invalid output for the first -test and fails with an error for the second case. The other two tools fail with -error messages for both tests. - -\section{Extensibility} - -This section briefly discusses the extensibility of the tools under test and -their internal data- and control-flow. As all tools under test already failed -to synthesize simple Verilog always-blocks correctly, not much resources have -been spent on evaluating the extensibility of these tools and therefore only a -very brief discussion of the topic is provided here. - -HANA synthesizes for a built-in library of standard cells using two passes over -an AST representation of the Verilog input. This approach executes fast but -limits the extensibility as everything happens in only two comparable complex -AST walks and there is no universal intermediate representation that is flexible -enough to be used in arbitrary optimizations. - -Odin-II and vl2m are both front ends to existing synthesis flows. As such they -only try to quickly convert the Verilog input into the internal representation -of their respective flows (BLIF). So extensibility is less of an issue here as -potential extensions would likely be implemented in other components of the -flow. - -Icarus Verilog is clearly designed to be a simulation tool rather than a -synthesis tool. The synthesis part of Icarus Verilog is an ad-hoc add-on to -Icarus Verilog that aims at converting an internal representation that is meant -for generation of a virtual machine based simulation code to netlists. - -\section{Summary and Outlook} - -Table~\ref{tab:StateOfTheArt_sum} summarizes the tests performed. Clearly none -of the tools under test make a serious attempt at providing a feature-complete -implementation of Verilog. It can be argued that Odin-II performed best in the -test as it never generated incorrect code but instead produced error messages -indicating that unsupported Verilog features where used in the Verilog input. - -In conclusion, to the best knowledge of the author, there is no FOSS Verilog -synthesis tool other than Yosys that is anywhere near feature completeness and -therefore there is no other candidate for a generic Verilog front end and/or -synthesis framework to be used as a basis for custom synthesis tools. - -Yosys could also replace vl2m and/or Odin-II in their respective flows or -function as a pre-compiler that can translate full-featured Verilog code to the -simple subset of Verilog that is understood by vl2m and Odin-II. - -Yosys is designed for extensibility. It can be used as-is to synthesize Verilog -code to netlists, but its main purpose is to be used as basis for custom tools. -Yosys is structured in a language dependent Verilog front end and language -independent synthesis code (which is in itself structured in independent -passes). This architecture will simplify implementing additional HDL front -ends and/or additional synthesis passes. - -Chapter~\ref{chapter:eval} contains a more detailed evaluation of Yosys using real-world -designs that are far out of reach for any of the other tools discussed in this appendix. - -\vskip2cm -\begin{table}[h] - % yosys hana vis icarus odin - % always01 ok ok ok ok ok - % always02 ok ok failed failed error - % always03 ok failed failed missing error - % arrays01 ok error error failed error - % forgen01 ok failed error failed error - % forgen02 ok failed error error error - \def\ok{\ding{52}} - \def\error{\ding{56}} - \def\failed{$\skull$} - \def\missing{$\skull$} - \rowcolors{2}{gray!25}{white} - \centerline{ - \begin{tabular}{|l|cccc|c|} - \hline - & \bf HANA & \bf VIS / vl2m & \bf Icarus Verilog & \bf Odin-II & \bf Yosys \\ - \hline - \tt always01 & \ok & \ok & \ok & \ok & \ok \\ - \tt always02 & \ok & \failed & \failed & \error & \ok \\ - \tt always03 & \failed & \failed & \missing & \error & \ok \\ - \tt arrays01 & \error & \error & \failed & \error & \ok \\ - \tt forgen01 & \failed & \error & \failed & \error & \ok \\ - \tt forgen02 & \failed & \error & \error & \error & \ok \\ - \hline - \end{tabular} - } - \centerline{ - \ding{52} \dots passed \hskip2em - \ding{56} \dots produced error \hskip2em - $\skull$ \dots incorrect output - } - \caption{Summary of all test results} - \label{tab:StateOfTheArt_sum} -\end{table} - diff --git a/manual/CHAPTER_StateOfTheArt/always01.v b/manual/CHAPTER_StateOfTheArt/always01.v deleted file mode 100644 index 4719ed47ee5..00000000000 --- a/manual/CHAPTER_StateOfTheArt/always01.v +++ /dev/null @@ -1,12 +0,0 @@ -module uut_always01(clock, reset, c3, c2, c1, c0); - -input clock, reset; -output c3, c2, c1, c0; -reg [3:0] count; - -assign {c3, c2, c1, c0} = count; - -always @(posedge clock) - count <= reset ? 0 : count + 1; - -endmodule diff --git a/manual/CHAPTER_StateOfTheArt/always01_pub.v b/manual/CHAPTER_StateOfTheArt/always01_pub.v deleted file mode 100644 index 6a6a4b23138..00000000000 --- a/manual/CHAPTER_StateOfTheArt/always01_pub.v +++ /dev/null @@ -1,14 +0,0 @@ -module uut_always01(clock, - reset, count); - -input clock, reset; -output [3:0] count; -reg [3:0] count; - -always @(posedge clock) - count <= reset ? - 0 : count + 1; - - - -endmodule diff --git a/manual/CHAPTER_StateOfTheArt/always02.v b/manual/CHAPTER_StateOfTheArt/always02.v deleted file mode 100644 index 63f1ce31799..00000000000 --- a/manual/CHAPTER_StateOfTheArt/always02.v +++ /dev/null @@ -1,15 +0,0 @@ -module uut_always02(clock, reset, c3, c2, c1, c0); - -input clock, reset; -output c3, c2, c1, c0; -reg [3:0] count; - -assign {c3, c2, c1, c0} = count; - -always @(posedge clock) begin - count <= count + 1; - if (reset) - count <= 0; -end - -endmodule diff --git a/manual/CHAPTER_StateOfTheArt/always02_pub.v b/manual/CHAPTER_StateOfTheArt/always02_pub.v deleted file mode 100644 index 91f1ca16d74..00000000000 --- a/manual/CHAPTER_StateOfTheArt/always02_pub.v +++ /dev/null @@ -1,14 +0,0 @@ -module uut_always02(clock, - reset, count); - -input clock, reset; -output [3:0] count; -reg [3:0] count; - -always @(posedge clock) begin - count <= count + 1; - if (reset) - count <= 0; -end - -endmodule diff --git a/manual/CHAPTER_StateOfTheArt/always03.v b/manual/CHAPTER_StateOfTheArt/always03.v deleted file mode 100644 index 53386acd6de..00000000000 --- a/manual/CHAPTER_StateOfTheArt/always03.v +++ /dev/null @@ -1,23 +0,0 @@ -module uut_always03(clock, in1, in2, in3, in4, in5, in6, in7, - out1, out2, out3); - -input clock, in1, in2, in3, in4, in5, in6, in7; -output out1, out2, out3; -reg out1, out2, out3; - -always @(posedge clock) begin - out1 = in1; - if (in2) - out1 = !out1; - out2 <= out1; - if (in3) - out2 <= out2; - if (in4) - if (in5) - out3 <= in6; - else - out3 <= in7; - out1 = out1 ^ out2; -end - -endmodule diff --git a/manual/CHAPTER_StateOfTheArt/arrays01.v b/manual/CHAPTER_StateOfTheArt/arrays01.v deleted file mode 100644 index bd0eda29425..00000000000 --- a/manual/CHAPTER_StateOfTheArt/arrays01.v +++ /dev/null @@ -1,16 +0,0 @@ -module uut_arrays01(clock, we, addr, wr_data, rd_data); - -input clock, we; -input [3:0] addr, wr_data; -output [3:0] rd_data; -reg [3:0] rd_data; - -reg [3:0] memory [15:0]; - -always @(posedge clock) begin - if (we) - memory[addr] <= wr_data; - rd_data <= memory[addr]; -end - -endmodule diff --git a/manual/CHAPTER_StateOfTheArt/cmp_tbdata.c b/manual/CHAPTER_StateOfTheArt/cmp_tbdata.c deleted file mode 100644 index b188144ddc5..00000000000 --- a/manual/CHAPTER_StateOfTheArt/cmp_tbdata.c +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include -#include - -int line = 0; -char buffer1[1024]; -char buffer2[1024]; - -void check(bool ok) -{ - if (ok) - return; - // fprintf(stderr, "Error in testbench output compare (line=%d):\n-%s\n+%s\n", line, buffer1, buffer2); - exit(1); -} - -int main(int argc, char **argv) -{ - FILE *f1, *f2; - bool eof1, eof2; - int i; - - check(argc == 3); - - f1 = fopen(argv[1], "r"); - f2 = fopen(argv[2], "r"); - - check(f1 && f2); - - while (!feof(f1) && !feof(f2)) - { - line++; - buffer1[0] = 0; - buffer2[0] = 0; - - eof1 = fgets(buffer1, 1024, f1) == NULL; - eof2 = fgets(buffer2, 1024, f2) == NULL; - - if (*buffer1 && buffer1[strlen(buffer1)-1] == '\n') - buffer1[strlen(buffer1)-1] = 0; - - if (*buffer2 && buffer2[strlen(buffer2)-1] == '\n') - buffer2[strlen(buffer2)-1] = 0; - - check(eof1 == eof2); - - for (i = 0; buffer1[i] || buffer2[i]; i++) - { - check(buffer1[i] != 0 && buffer2[i] != 0); - - // first argument is the reference. An 'z' or 'x' - // here means we don't care about the result. - if (buffer1[i] == 'z' || buffer1[i] == 'x') - continue; - - check(buffer1[i] == buffer2[i]); - } - } - - check(feof(f1) && feof(f2)); - - fclose(f1); - fclose(f2); - return 0; -} - diff --git a/manual/CHAPTER_StateOfTheArt/forgen01.v b/manual/CHAPTER_StateOfTheArt/forgen01.v deleted file mode 100644 index 70ee7e6674e..00000000000 --- a/manual/CHAPTER_StateOfTheArt/forgen01.v +++ /dev/null @@ -1,20 +0,0 @@ -module uut_forgen01(a, y); - -input [4:0] a; -output y; - -integer i, j; -reg [31:0] lut; - -initial begin - for (i = 0; i < 32; i = i+1) begin - lut[i] = i > 1; - for (j = 2; j*j <= i; j = j+1) - if (i % j == 0) - lut[i] = 0; - end -end - -assign y = lut[a]; - -endmodule diff --git a/manual/CHAPTER_StateOfTheArt/forgen02.v b/manual/CHAPTER_StateOfTheArt/forgen02.v deleted file mode 100644 index 14af070c38e..00000000000 --- a/manual/CHAPTER_StateOfTheArt/forgen02.v +++ /dev/null @@ -1,30 +0,0 @@ -module uut_forgen02(a, b, cin, y, cout); - -parameter WIDTH = 8; - -input [WIDTH-1:0] a, b; -input cin; - -output [WIDTH-1:0] y; -output cout; - -genvar i; -wire [WIDTH-1:0] carry; - -generate - for (i = 0; i < WIDTH; i=i+1) begin:adder - wire [2:0] D; - assign D[1:0] = { a[i], b[i] }; - if (i == 0) begin:chain - assign D[2] = cin; - end else begin:chain - assign D[2] = carry[i-1]; - end - assign y[i] = ^D; - assign carry[i] = &D[1:0] | (^D[1:0] & D[2]); - end -endgenerate - -assign cout = carry[WIDTH-1]; - -endmodule diff --git a/manual/CHAPTER_StateOfTheArt/iverilog-0.8.7-buildfixes.patch b/manual/CHAPTER_StateOfTheArt/iverilog-0.8.7-buildfixes.patch deleted file mode 100644 index 63a03e595ff..00000000000 --- a/manual/CHAPTER_StateOfTheArt/iverilog-0.8.7-buildfixes.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- ./elab_net.cc.orig 2012-10-27 22:11:05.345688820 +0200 -+++ ./elab_net.cc 2012-10-27 22:12:23.398075860 +0200 -@@ -29,6 +29,7 @@ - - # include - # include -+# include - - /* - * This is a state flag that determines whether an elaborate_net must ---- ./syn-rules.y.orig 2012-10-27 22:25:38.890020489 +0200 -+++ ./syn-rules.y 2012-10-27 22:25:49.146071350 +0200 -@@ -25,6 +25,7 @@ - # include "config.h" - - # include -+# include - - /* - * This file implements synthesis based on matching threads and diff --git a/manual/CHAPTER_StateOfTheArt/mvsis-1.3.6-buildfixes.patch b/manual/CHAPTER_StateOfTheArt/mvsis-1.3.6-buildfixes.patch deleted file mode 100644 index 4b44320f87a..00000000000 --- a/manual/CHAPTER_StateOfTheArt/mvsis-1.3.6-buildfixes.patch +++ /dev/null @@ -1,36 +0,0 @@ ---- ./helpers/config.sub.orig 2012-10-27 22:09:04.429089223 +0200 -+++ ./helpers/config.sub 2012-10-27 22:09:11.501124295 +0200 -@@ -158,6 +158,7 @@ - | sparc | sparclet | sparclite | sparc64) - basic_machine=$basic_machine-unknown - ;; -+ x86_64-pc) ;; - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. ---- ./src/base/ntki/ntkiFrames.c.orig 2012-10-27 22:09:26.961200963 +0200 -+++ ./src/base/ntki/ntkiFrames.c 2012-10-27 22:09:32.901230409 +0200 -@@ -23,7 +23,7 @@ - //////////////////////////////////////////////////////////////////////// - - static void Ntk_NetworkAddFrame( Ntk_Network_t * pNetNew, Ntk_Network_t * pNet, int iFrame ); --static void Ntk_NetworkReorderCiCo( Ntk_Network_t * pNet ); -+// static void Ntk_NetworkReorderCiCo( Ntk_Network_t * pNet ); - - extern int Ntk_NetworkVerifyVariables( Ntk_Network_t * pNet1, Ntk_Network_t * pNet2, int fVerbose ); - ---- ./src/graph/wn/wnStrashBin.c.orig 2012-10-27 22:27:29.966571294 +0200 -+++ ./src/graph/wn/wnStrashBin.c 2012-10-27 22:27:55.898699881 +0200 -@@ -76,8 +76,10 @@ - // assert( RetValue ); - - // clean the data of the nodes in the window -- Ntk_NetworkForEachNodeSpecial( pWnd->pNet, pNode ) -- pNode->pCopy = (Ntk_Node_t *)pNode->pData = NULL; -+ Ntk_NetworkForEachNodeSpecial( pWnd->pNet, pNode ) { -+ pNode->pData = NULL; -+ pNode->pCopy = NULL; -+ } - - // set the leaves - pgInputs = Sh_ManagerReadVars( pMan ); diff --git a/manual/CHAPTER_StateOfTheArt/simlib_hana.v b/manual/CHAPTER_StateOfTheArt/simlib_hana.v deleted file mode 100644 index 7fb54fa4904..00000000000 --- a/manual/CHAPTER_StateOfTheArt/simlib_hana.v +++ /dev/null @@ -1,1139 +0,0 @@ -/* -Copyright (C) 2009-2010 Parvez Ahmad -Written by Parvez Ahmad . - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . */ - - -module BUF (input in, output out); - -assign out = in; - -endmodule - -module TRIBUF(input in, enable, output out); - -assign out = enable ? in : 1'bz; - -endmodule - -module INV(input in, output out); - -assign out = ~in; - -endmodule - -module AND2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); - -assign out = ∈ - -endmodule - -module AND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); - -assign out = ∈ - -endmodule - -module AND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); - -assign out = ∈ - -endmodule - -module OR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); - -assign out = |in; - -endmodule - -module OR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); - -assign out = |in; - -endmodule - -module OR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); - -assign out = |in; - -endmodule - - -module NAND2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); - -assign out = ~∈ - -endmodule - -module NAND3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); - -assign out = ~∈ - -endmodule - -module NAND4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); - -assign out = ~∈ - -endmodule - -module NOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); - -assign out = ~|in; - -endmodule - -module NOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); - -assign out = ~|in; - -endmodule - -module NOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); - -assign out = ~|in; - -endmodule - - -module XOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); - -assign out = ^in; - -endmodule - -module XOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); - -assign out = ^in; - -endmodule - -module XOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); - -assign out = ^in; - -endmodule - - -module XNOR2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output out); - -assign out = ~^in; - -endmodule - -module XNOR3 #(parameter SIZE = 3) (input [SIZE-1:0] in, output out); - -assign out = ~^in; - -endmodule - -module XNOR4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output out); - -assign out = ~^in; - -endmodule - -module DEC1 (input in, enable, output reg [1:0] out); - -always @(in or enable) - if(!enable) - out = 2'b00; - else begin - case (in) - 1'b0 : out = 2'b01; - 1'b1 : out = 2'b10; - endcase - end -endmodule - -module DEC2 (input [1:0] in, input enable, output reg [3:0] out); - -always @(in or enable) - if(!enable) - out = 4'b0000; - else begin - case (in) - 2'b00 : out = 4'b0001; - 2'b01 : out = 4'b0010; - 2'b10 : out = 4'b0100; - 2'b11 : out = 4'b1000; - endcase - end -endmodule - -module DEC3 (input [2:0] in, input enable, output reg [7:0] out); - -always @(in or enable) - if(!enable) - out = 8'b00000000; - else begin - case (in) - 3'b000 : out = 8'b00000001; - 3'b001 : out = 8'b00000010; - 3'b010 : out = 8'b00000100; - 3'b011 : out = 8'b00001000; - 3'b100 : out = 8'b00010000; - 3'b101 : out = 8'b00100000; - 3'b110 : out = 8'b01000000; - 3'b111 : out = 8'b10000000; - endcase - end -endmodule - -module DEC4 (input [3:0] in, input enable, output reg [15:0] out); - -always @(in or enable) - if(!enable) - out = 16'b0000000000000000; - else begin - case (in) - 4'b0000 : out = 16'b0000000000000001; - 4'b0001 : out = 16'b0000000000000010; - 4'b0010 : out = 16'b0000000000000100; - 4'b0011 : out = 16'b0000000000001000; - 4'b0100 : out = 16'b0000000000010000; - 4'b0101 : out = 16'b0000000000100000; - 4'b0110 : out = 16'b0000000001000000; - 4'b0111 : out = 16'b0000000010000000; - 4'b1000 : out = 16'b0000000100000000; - 4'b1001 : out = 16'b0000001000000000; - 4'b1010 : out = 16'b0000010000000000; - 4'b1011 : out = 16'b0000100000000000; - 4'b1100 : out = 16'b0001000000000000; - 4'b1101 : out = 16'b0010000000000000; - 4'b1110 : out = 16'b0100000000000000; - 4'b1111 : out = 16'b1000000000000000; - endcase - end -endmodule -module DEC5 (input [4:0] in, input enable, output reg [31:0] out); - -always @(in or enable) - if(!enable) - out = 32'b00000000000000000000000000000000; - else begin - case (in) - 5'b00000 : out = 32'b00000000000000000000000000000001; - 5'b00001 : out = 32'b00000000000000000000000000000010; - 5'b00010 : out = 32'b00000000000000000000000000000100; - 5'b00011 : out = 32'b00000000000000000000000000001000; - 5'b00100 : out = 32'b00000000000000000000000000010000; - 5'b00101 : out = 32'b00000000000000000000000000100000; - 5'b00110 : out = 32'b00000000000000000000000001000000; - 5'b00111 : out = 32'b00000000000000000000000010000000; - 5'b01000 : out = 32'b00000000000000000000000100000000; - 5'b01001 : out = 32'b00000000000000000000001000000000; - 5'b01010 : out = 32'b00000000000000000000010000000000; - 5'b01011 : out = 32'b00000000000000000000100000000000; - 5'b01100 : out = 32'b00000000000000000001000000000000; - 5'b01101 : out = 32'b00000000000000000010000000000000; - 5'b01110 : out = 32'b00000000000000000100000000000000; - 5'b01111 : out = 32'b00000000000000001000000000000000; - 5'b10000 : out = 32'b00000000000000010000000000000000; - 5'b10001 : out = 32'b00000000000000100000000000000000; - 5'b10010 : out = 32'b00000000000001000000000000000000; - 5'b10011 : out = 32'b00000000000010000000000000000000; - 5'b10100 : out = 32'b00000000000100000000000000000000; - 5'b10101 : out = 32'b00000000001000000000000000000000; - 5'b10110 : out = 32'b00000000010000000000000000000000; - 5'b10111 : out = 32'b00000000100000000000000000000000; - 5'b11000 : out = 32'b00000001000000000000000000000000; - 5'b11001 : out = 32'b00000010000000000000000000000000; - 5'b11010 : out = 32'b00000100000000000000000000000000; - 5'b11011 : out = 32'b00001000000000000000000000000000; - 5'b11100 : out = 32'b00010000000000000000000000000000; - 5'b11101 : out = 32'b00100000000000000000000000000000; - 5'b11110 : out = 32'b01000000000000000000000000000000; - 5'b11111 : out = 32'b10000000000000000000000000000000; - endcase - end -endmodule - -module DEC6 (input [5:0] in, input enable, output reg [63:0] out); - -always @(in or enable) - if(!enable) - out = 64'b0000000000000000000000000000000000000000000000000000000000000000; - else begin - case (in) - 6'b000000 : out = 64'b0000000000000000000000000000000000000000000000000000000000000001; - 6'b000001 : out = 64'b0000000000000000000000000000000000000000000000000000000000000010; - 6'b000010 : out = 64'b0000000000000000000000000000000000000000000000000000000000000100; - 6'b000011 : out = 64'b0000000000000000000000000000000000000000000000000000000000001000; - 6'b000100 : out = 64'b0000000000000000000000000000000000000000000000000000000000010000; - 6'b000101 : out = 64'b0000000000000000000000000000000000000000000000000000000000100000; - 6'b000110 : out = 64'b0000000000000000000000000000000000000000000000000000000001000000; - 6'b000111 : out = 64'b0000000000000000000000000000000000000000000000000000000010000000; - 6'b001000 : out = 64'b0000000000000000000000000000000000000000000000000000000100000000; - 6'b001001 : out = 64'b0000000000000000000000000000000000000000000000000000001000000000; - 6'b001010 : out = 64'b0000000000000000000000000000000000000000000000000000010000000000; - 6'b001011 : out = 64'b0000000000000000000000000000000000000000000000000000100000000000; - 6'b001100 : out = 64'b0000000000000000000000000000000000000000000000000001000000000000; - 6'b001101 : out = 64'b0000000000000000000000000000000000000000000000000010000000000000; - 6'b001110 : out = 64'b0000000000000000000000000000000000000000000000000100000000000000; - 6'b001111 : out = 64'b0000000000000000000000000000000000000000000000001000000000000000; - 6'b010000 : out = 64'b0000000000000000000000000000000000000000000000010000000000000000; - 6'b010001 : out = 64'b0000000000000000000000000000000000000000000000100000000000000000; - 6'b010010 : out = 64'b0000000000000000000000000000000000000000000001000000000000000000; - 6'b010011 : out = 64'b0000000000000000000000000000000000000000000010000000000000000000; - 6'b010100 : out = 64'b0000000000000000000000000000000000000000000100000000000000000000; - 6'b010101 : out = 64'b0000000000000000000000000000000000000000001000000000000000000000; - 6'b010110 : out = 64'b0000000000000000000000000000000000000000010000000000000000000000; - 6'b010111 : out = 64'b0000000000000000000000000000000000000000100000000000000000000000; - 6'b011000 : out = 64'b0000000000000000000000000000000000000001000000000000000000000000; - 6'b011001 : out = 64'b0000000000000000000000000000000000000010000000000000000000000000; - 6'b011010 : out = 64'b0000000000000000000000000000000000000100000000000000000000000000; - 6'b011011 : out = 64'b0000000000000000000000000000000000001000000000000000000000000000; - 6'b011100 : out = 64'b0000000000000000000000000000000000010000000000000000000000000000; - 6'b011101 : out = 64'b0000000000000000000000000000000000100000000000000000000000000000; - 6'b011110 : out = 64'b0000000000000000000000000000000001000000000000000000000000000000; - 6'b011111 : out = 64'b0000000000000000000000000000000010000000000000000000000000000000; - - 6'b100000 : out = 64'b0000000000000000000000000000000100000000000000000000000000000000; - 6'b100001 : out = 64'b0000000000000000000000000000001000000000000000000000000000000000; - 6'b100010 : out = 64'b0000000000000000000000000000010000000000000000000000000000000000; - 6'b100011 : out = 64'b0000000000000000000000000000100000000000000000000000000000000000; - 6'b100100 : out = 64'b0000000000000000000000000001000000000000000000000000000000000000; - 6'b100101 : out = 64'b0000000000000000000000000010000000000000000000000000000000000000; - 6'b100110 : out = 64'b0000000000000000000000000100000000000000000000000000000000000000; - 6'b100111 : out = 64'b0000000000000000000000001000000000000000000000000000000000000000; - 6'b101000 : out = 64'b0000000000000000000000010000000000000000000000000000000000000000; - 6'b101001 : out = 64'b0000000000000000000000100000000000000000000000000000000000000000; - 6'b101010 : out = 64'b0000000000000000000001000000000000000000000000000000000000000000; - 6'b101011 : out = 64'b0000000000000000000010000000000000000000000000000000000000000000; - 6'b101100 : out = 64'b0000000000000000000100000000000000000000000000000000000000000000; - 6'b101101 : out = 64'b0000000000000000001000000000000000000000000000000000000000000000; - 6'b101110 : out = 64'b0000000000000000010000000000000000000000000000000000000000000000; - 6'b101111 : out = 64'b0000000000000000100000000000000000000000000000000000000000000000; - 6'b110000 : out = 64'b0000000000000001000000000000000000000000000000000000000000000000; - 6'b110001 : out = 64'b0000000000000010000000000000000000000000000000000000000000000000; - 6'b110010 : out = 64'b0000000000000100000000000000000000000000000000000000000000000000; - 6'b110011 : out = 64'b0000000000001000000000000000000000000000000000000000000000000000; - 6'b110100 : out = 64'b0000000000010000000000000000000000000000000000000000000000000000; - 6'b110101 : out = 64'b0000000000100000000000000000000000000000000000000000000000000000; - 6'b110110 : out = 64'b0000000001000000000000000000000000000000000000000000000000000000; - 6'b110111 : out = 64'b0000000010000000000000000000000000000000000000000000000000000000; - 6'b111000 : out = 64'b0000000100000000000000000000000000000000000000000000000000000000; - 6'b111001 : out = 64'b0000001000000000000000000000000000000000000000000000000000000000; - 6'b111010 : out = 64'b0000010000000000000000000000000000000000000000000000000000000000; - 6'b111011 : out = 64'b0000100000000000000000000000000000000000000000000000000000000000; - 6'b111100 : out = 64'b0001000000000000000000000000000000000000000000000000000000000000; - 6'b111101 : out = 64'b0010000000000000000000000000000000000000000000000000000000000000; - 6'b111110 : out = 64'b0100000000000000000000000000000000000000000000000000000000000000; - 6'b111111 : out = 64'b1000000000000000000000000000000000000000000000000000000000000000; - endcase - end -endmodule - - -module MUX2(input [1:0] in, input select, output reg out); - -always @( in or select) - case (select) - 0: out = in[0]; - 1: out = in[1]; - endcase -endmodule - - -module MUX4(input [3:0] in, input [1:0] select, output reg out); - -always @( in or select) - case (select) - 0: out = in[0]; - 1: out = in[1]; - 2: out = in[2]; - 3: out = in[3]; - endcase -endmodule - - -module MUX8(input [7:0] in, input [2:0] select, output reg out); - -always @( in or select) - case (select) - 0: out = in[0]; - 1: out = in[1]; - 2: out = in[2]; - 3: out = in[3]; - 4: out = in[4]; - 5: out = in[5]; - 6: out = in[6]; - 7: out = in[7]; - endcase -endmodule - -module MUX16(input [15:0] in, input [3:0] select, output reg out); - -always @( in or select) - case (select) - 0: out = in[0]; - 1: out = in[1]; - 2: out = in[2]; - 3: out = in[3]; - 4: out = in[4]; - 5: out = in[5]; - 6: out = in[6]; - 7: out = in[7]; - 8: out = in[8]; - 9: out = in[9]; - 10: out = in[10]; - 11: out = in[11]; - 12: out = in[12]; - 13: out = in[13]; - 14: out = in[14]; - 15: out = in[15]; - endcase -endmodule - -module MUX32(input [31:0] in, input [4:0] select, output reg out); - -always @( in or select) - case (select) - 0: out = in[0]; - 1: out = in[1]; - 2: out = in[2]; - 3: out = in[3]; - 4: out = in[4]; - 5: out = in[5]; - 6: out = in[6]; - 7: out = in[7]; - 8: out = in[8]; - 9: out = in[9]; - 10: out = in[10]; - 11: out = in[11]; - 12: out = in[12]; - 13: out = in[13]; - 14: out = in[14]; - 15: out = in[15]; - 16: out = in[16]; - 17: out = in[17]; - 18: out = in[18]; - 19: out = in[19]; - 20: out = in[20]; - 21: out = in[21]; - 22: out = in[22]; - 23: out = in[23]; - 24: out = in[24]; - 25: out = in[25]; - 26: out = in[26]; - 27: out = in[27]; - 28: out = in[28]; - 29: out = in[29]; - 30: out = in[30]; - 31: out = in[31]; - endcase -endmodule - -module MUX64(input [63:0] in, input [5:0] select, output reg out); - -always @( in or select) - case (select) - 0: out = in[0]; - 1: out = in[1]; - 2: out = in[2]; - 3: out = in[3]; - 4: out = in[4]; - 5: out = in[5]; - 6: out = in[6]; - 7: out = in[7]; - 8: out = in[8]; - 9: out = in[9]; - 10: out = in[10]; - 11: out = in[11]; - 12: out = in[12]; - 13: out = in[13]; - 14: out = in[14]; - 15: out = in[15]; - 16: out = in[16]; - 17: out = in[17]; - 18: out = in[18]; - 19: out = in[19]; - 20: out = in[20]; - 21: out = in[21]; - 22: out = in[22]; - 23: out = in[23]; - 24: out = in[24]; - 25: out = in[25]; - 26: out = in[26]; - 27: out = in[27]; - 28: out = in[28]; - 29: out = in[29]; - 30: out = in[30]; - 31: out = in[31]; - 32: out = in[32]; - 33: out = in[33]; - 34: out = in[34]; - 35: out = in[35]; - 36: out = in[36]; - 37: out = in[37]; - 38: out = in[38]; - 39: out = in[39]; - 40: out = in[40]; - 41: out = in[41]; - 42: out = in[42]; - 43: out = in[43]; - 44: out = in[44]; - 45: out = in[45]; - 46: out = in[46]; - 47: out = in[47]; - 48: out = in[48]; - 49: out = in[49]; - 50: out = in[50]; - 51: out = in[51]; - 52: out = in[52]; - 53: out = in[53]; - 54: out = in[54]; - 55: out = in[55]; - 56: out = in[56]; - 57: out = in[57]; - 58: out = in[58]; - 59: out = in[59]; - 60: out = in[60]; - 61: out = in[61]; - 62: out = in[62]; - 63: out = in[63]; - endcase -endmodule - -module ADD1(input in1, in2, cin, output out, cout); - -assign {cout, out} = in1 + in2 + cin; - -endmodule - -module ADD2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 + in2 + cin; - -endmodule - -module ADD4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 + in2 + cin; - -endmodule - -module ADD8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 + in2 + cin; - -endmodule - -module ADD16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 + in2 + cin; - -endmodule - -module ADD32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 + in2 + cin; - -endmodule -module ADD64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 + in2 + cin; - -endmodule - -module SUB1(input in1, in2, cin, output out, cout); - -assign {cout, out} = in1 - in2 - cin; - -endmodule - -module SUB2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 - in2 - cin; - -endmodule - -module SUB4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 - in2 - cin; - -endmodule - -module SUB8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 - in2 - cin; - -endmodule - -module SUB16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 - in2 - cin; - -endmodule - -module SUB32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 - in2 - cin; - -endmodule -module SUB64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, - input cin, output [SIZE-1:0] out, output cout); - -assign {cout, out} = in1 - in2 - cin; - -endmodule - -module MUL1 #(parameter SIZE = 1)(input in1, in2, output [2*SIZE-1:0] out); - -assign out = in1*in2; - -endmodule - -module MUL2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); - -assign out = in1*in2; - -endmodule - -module MUL4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); - -assign out = in1*in2; - -endmodule - -module MUL8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); - -assign out = in1*in2; - -endmodule - -module MUL16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); - -assign out = in1*in2; - -endmodule - -module MUL32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); - -assign out = in1*in2; - -endmodule - -module MUL64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, output [2*SIZE-1:0] out); - -assign out = in1*in2; - -endmodule - -module DIV1 #(parameter SIZE = 1)(input in1, in2, output out, rem); - -assign out = in1/in2; -assign rem = in1%in2; - -endmodule - -module DIV2 #(parameter SIZE = 2)(input [SIZE-1:0] in1, in2, - output [SIZE-1:0] out, rem); - -assign out = in1/in2; -assign rem = in1%in2; - -endmodule - -module DIV4 #(parameter SIZE = 4)(input [SIZE-1:0] in1, in2, - output [SIZE-1:0] out, rem); - -assign out = in1/in2; -assign rem = in1%in2; - -endmodule - -module DIV8 #(parameter SIZE = 8)(input [SIZE-1:0] in1, in2, - output [SIZE-1:0] out, rem); - -assign out = in1/in2; -assign rem = in1%in2; - -endmodule - -module DIV16 #(parameter SIZE = 16)(input [SIZE-1:0] in1, in2, - output [SIZE-1:0] out, rem); - -assign out = in1/in2; -assign rem = in1%in2; - -endmodule - -module DIV32 #(parameter SIZE = 32)(input [SIZE-1:0] in1, in2, - output [SIZE-1:0] out, rem); - -assign out = in1/in2; -assign rem = in1%in2; - -endmodule - -module DIV64 #(parameter SIZE = 64)(input [SIZE-1:0] in1, in2, - output [SIZE-1:0] out, rem); - -assign out = in1/in2; -assign rem = in1%in2; - -endmodule - -module FF (input d, clk, output reg q); -always @( posedge clk) - q <= d; -endmodule - - -module RFF(input d, clk, reset, output reg q); -always @(posedge clk or posedge reset) - if(reset) - q <= 0; - else - q <= d; -endmodule - -module SFF(input d, clk, set, output reg q); -always @(posedge clk or posedge set) - if(set) - q <= 1; - else - q <= d; -endmodule - -module RSFF(input d, clk, set, reset, output reg q); -always @(posedge clk or posedge reset or posedge set) - if(reset) - q <= 0; - else if(set) - q <= 1; - else - q <= d; -endmodule - -module SRFF(input d, clk, set, reset, output reg q); -always @(posedge clk or posedge set or posedge reset) - if(set) - q <= 1; - else if(reset) - q <= 0; - else - q <= d; -endmodule - -module LATCH(input d, enable, output reg q); -always @( d or enable) - if(enable) - q <= d; -endmodule - -module RLATCH(input d, reset, enable, output reg q); -always @( d or enable or reset) - if(enable) - if(reset) - q <= 0; - else - q <= d; -endmodule - -module LSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out); - -always @ (in, shift, val) begin - if(shift) - out = val; - else - out = in; -end - -endmodule - - -module LSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in, - input [SIZE-1:0] shift, input val, - output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in << shift; - if(val) - out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end -endmodule - -module LSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in, - input [2:0] shift, input val, output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in << shift; - if(val) - out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end -endmodule - - -module LSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in, - input [3:0] shift, input val, output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in << shift; - if(val) - out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end -endmodule - -module LSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in, - input [4:0] shift, input val, output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in << shift; - if(val) - out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end -endmodule - -module LSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in, - input [5:0] shift, input val, output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in << shift; - if(val) - out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end -endmodule - -module LSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in, - input [6:0] shift, input val, output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in << shift; - if(val) - out = out | ({SIZE-1 {1'b1} } >> (SIZE-1-shift)); -end -endmodule - -module RSHIFT1 #(parameter SIZE = 1)(input in, shift, val, output reg out); - -always @ (in, shift, val) begin - if(shift) - out = val; - else - out = in; -end - -endmodule - -module RSHIFT2 #(parameter SIZE = 2)(input [SIZE-1:0] in, - input [SIZE-1:0] shift, input val, - output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in >> shift; - if(val) - out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end - -endmodule - - -module RSHIFT4 #(parameter SIZE = 4)(input [SIZE-1:0] in, - input [2:0] shift, input val, - output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in >> shift; - if(val) - out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end -endmodule - -module RSHIFT8 #(parameter SIZE = 8)(input [SIZE-1:0] in, - input [3:0] shift, input val, - output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in >> shift; - if(val) - out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end - -endmodule - -module RSHIFT16 #(parameter SIZE = 16)(input [SIZE-1:0] in, - input [4:0] shift, input val, - output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in >> shift; - if(val) - out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end -endmodule - - -module RSHIFT32 #(parameter SIZE = 32)(input [SIZE-1:0] in, - input [5:0] shift, input val, - output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in >> shift; - if(val) - out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end -endmodule - -module RSHIFT64 #(parameter SIZE = 64)(input [SIZE-1:0] in, - input [6:0] shift, input val, - output reg [SIZE-1:0] out); - -always @(in or shift or val) begin - out = in >> shift; - if(val) - out = out | ({SIZE-1 {1'b1} } << (SIZE-1-shift)); -end -endmodule - -module CMP1 #(parameter SIZE = 1) (input in1, in2, - output reg equal, unequal, greater, lesser); - -always @ (in1 or in2) begin - if(in1 == in2) begin - equal = 1; - unequal = 0; - greater = 0; - lesser = 0; - end - else begin - equal = 0; - unequal = 1; - - if(in1 < in2) begin - greater = 0; - lesser = 1; - end - else begin - greater = 1; - lesser = 0; - end - end -end -endmodule - - -module CMP2 #(parameter SIZE = 2) (input [SIZE-1:0] in1, in2, - output reg equal, unequal, greater, lesser); - -always @ (in1 or in2) begin - if(in1 == in2) begin - equal = 1; - unequal = 0; - greater = 0; - lesser = 0; - end - else begin - equal = 0; - unequal = 1; - - if(in1 < in2) begin - greater = 0; - lesser = 1; - end - else begin - greater = 1; - lesser = 0; - end - end -end -endmodule - -module CMP4 #(parameter SIZE = 4) (input [SIZE-1:0] in1, in2, - output reg equal, unequal, greater, lesser); - -always @ (in1 or in2) begin - if(in1 == in2) begin - equal = 1; - unequal = 0; - greater = 0; - lesser = 0; - end - else begin - equal = 0; - unequal = 1; - - if(in1 < in2) begin - greater = 0; - lesser = 1; - end - else begin - greater = 1; - lesser = 0; - end - end -end -endmodule - -module CMP8 #(parameter SIZE = 8) (input [SIZE-1:0] in1, in2, - output reg equal, unequal, greater, lesser); - -always @ (in1 or in2) begin - if(in1 == in2) begin - equal = 1; - unequal = 0; - greater = 0; - lesser = 0; - end - else begin - equal = 0; - unequal = 1; - - if(in1 < in2) begin - greater = 0; - lesser = 1; - end - else begin - greater = 1; - lesser = 0; - end - end -end -endmodule - -module CMP16 #(parameter SIZE = 16) (input [SIZE-1:0] in1, in2, - output reg equal, unequal, greater, lesser); - -always @ (in1 or in2) begin - if(in1 == in2) begin - equal = 1; - unequal = 0; - greater = 0; - lesser = 0; - end - else begin - equal = 0; - unequal = 1; - - if(in1 < in2) begin - greater = 0; - lesser = 1; - end - else begin - greater = 1; - lesser = 0; - end - end -end -endmodule - -module CMP32 #(parameter SIZE = 32) (input [SIZE-1:0] in1, in2, - output reg equal, unequal, greater, lesser); - -always @ (in1 or in2) begin - if(in1 == in2) begin - equal = 1; - unequal = 0; - greater = 0; - lesser = 0; - end - else begin - equal = 0; - unequal = 1; - - if(in1 < in2) begin - greater = 0; - lesser = 1; - end - else begin - greater = 1; - lesser = 0; - end - end -end -endmodule - -module CMP64 #(parameter SIZE = 64) (input [SIZE-1:0] in1, in2, - output reg equal, unequal, greater, lesser); - -always @ (in1 or in2) begin - if(in1 == in2) begin - equal = 1; - unequal = 0; - greater = 0; - lesser = 0; - end - else begin - equal = 0; - unequal = 1; - - if(in1 < in2) begin - greater = 0; - lesser = 1; - end - else begin - greater = 1; - lesser = 0; - end - end -end -endmodule - -module VCC (output supply1 out); -endmodule - -module GND (output supply0 out); -endmodule - - -module INC1 #(parameter SIZE = 1) (input in, output [SIZE:0] out); - -assign out = in + 1; - -endmodule - -module INC2 #(parameter SIZE = 2) (input [SIZE-1:0] in, output [SIZE:0] out); - -assign out = in + 1; - -endmodule - -module INC4 #(parameter SIZE = 4) (input [SIZE-1:0] in, output [SIZE:0] out); -assign out = in + 1; - -endmodule - -module INC8 #(parameter SIZE = 8) (input [SIZE-1:0] in, output [SIZE:0] out); -assign out = in + 1; - -endmodule - -module INC16 #(parameter SIZE = 16) (input [SIZE-1:0] in, output [SIZE:0] out); -assign out = in + 1; - -endmodule - -module INC32 #(parameter SIZE = 32) (input [SIZE-1:0] in, output [SIZE:0] out); -assign out = in + 1; - -endmodule -module INC64 #(parameter SIZE = 64) (input [SIZE-1:0] in, output [SIZE:0] out); -assign out = in + 1; - -endmodule - diff --git a/manual/CHAPTER_StateOfTheArt/simlib_icarus.v b/manual/CHAPTER_StateOfTheArt/simlib_icarus.v deleted file mode 100644 index fdd7ef61fd4..00000000000 --- a/manual/CHAPTER_StateOfTheArt/simlib_icarus.v +++ /dev/null @@ -1,224 +0,0 @@ - -module cell0(Result0); -output Result0; -assign Result0 = 0; -endmodule - -module cell1(Result0); -output Result0; -assign Result0 = 1; -endmodule - -module ADD4( - DataA0, DataA1, DataA2, DataA3, - DataB0, DataB1, DataB2, DataB3, - Result0, Result1, Result2, Result3, Cout -); -input DataA0, DataA1, DataA2, DataA3; -input DataB0, DataB1, DataB2, DataB3; -output Result0, Result1, Result2, Result3, Cout; -assign {Cout, Result3, Result2, Result1, Result0} = {DataA3, DataA2, DataA1, DataA0} + {DataB3, DataB2, DataB1, DataB0}; -endmodule - -module BUF(DATA, RESULT); -input DATA; -output RESULT; -assign RESULT = DATA; -endmodule - -module INV(DATA, RESULT); -input DATA; -output RESULT; -assign RESULT = ~DATA; -endmodule - -module fd4( - Clock, - Data0, Data1, Data2, Data3, - Q0, Q1, Q2, Q3 -); -input Clock; -input Data0, Data1, Data2, Data3; -output reg Q0, Q1, Q2, Q3; -always @(posedge Clock) - {Q0, Q1, Q2, Q3} <= {Data0, Data1, Data2, Data3}; -endmodule - -module fdce1( - Clock, Enable, - Data0, - Q0 -); -input Clock, Enable; -input Data0; -output reg Q0; -always @(posedge Clock) - if (Enable) - Q0 <= Data0; -endmodule - -module fdce4( - Clock, Enable, - Data0, Data1, Data2, Data3, - Q0, Q1, Q2, Q3 -); -input Clock, Enable; -input Data0, Data1, Data2, Data3; -output reg Q0, Q1, Q2, Q3; -always @(posedge Clock) - if (Enable) - {Q0, Q1, Q2, Q3} <= {Data0, Data1, Data2, Data3}; -endmodule - -module mux4_1_2( - Sel0, - Data0x0, Data0x1, Data0x2, Data0x3, - Data1x0, Data1x1, Data1x2, Data1x3, - Result0, Result1, Result2, Result3 -); -input Sel0; -input Data0x0, Data0x1, Data0x2, Data0x3; -input Data1x0, Data1x1, Data1x2, Data1x3; -output Result0, Result1, Result2, Result3; -assign {Result0, Result1, Result2, Result3} = Sel0 ? {Data1x0, Data1x1, Data1x2, Data1x3} : {Data0x0, Data0x1, Data0x2, Data0x3}; -endmodule - -module mux1_1_2( - Sel0, - Data0x0, - Data1x0, - Result0 -); -input Sel0; -input Data0x0; -input Data1x0; -output Result0; -assign Result0 = Sel0 ? Data1x0 : Data0x0; -endmodule - -module xor2( - DATA0X0, - DATA1X0, - RESULT0 -); -input DATA0X0; -input DATA1X0; -output RESULT0; -assign RESULT0 = DATA1X0 ^ DATA0X0; -endmodule - -module fdce64( - Clock, Enable, - Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, Data29, Data30, Data31, Data32, Data33, Data34, Data35, Data36, Data37, Data38, Data39, Data40, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, Data49, Data50, Data51, Data52, Data53, Data54, Data55, Data56, Data57, Data58, Data59, Data60, Data61, Data62, Data63, - Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, Q16, Q17, Q18, Q19, Q20, Q21, Q22, Q23, Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31, Q32, Q33, Q34, Q35, Q36, Q37, Q38, Q39, Q40, Q41, Q42, Q43, Q44, Q45, Q46, Q47, Q48, Q49, Q50, Q51, Q52, Q53, Q54, Q55, Q56, Q57, Q58, Q59, Q60, Q61, Q62, Q63 -); -input Clock, Enable; -input Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, Data29, Data30, Data31, Data32, Data33, Data34, Data35, Data36, Data37, Data38, Data39, Data40, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, Data49, Data50, Data51, Data52, Data53, Data54, Data55, Data56, Data57, Data58, Data59, Data60, Data61, Data62, Data63; -output reg Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, Q16, Q17, Q18, Q19, Q20, Q21, Q22, Q23, Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31, Q32, Q33, Q34, Q35, Q36, Q37, Q38, Q39, Q40, Q41, Q42, Q43, Q44, Q45, Q46, Q47, Q48, Q49, Q50, Q51, Q52, Q53, Q54, Q55, Q56, Q57, Q58, Q59, Q60, Q61, Q62, Q63; -always @(posedge Clock) - if (Enable) - { Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, Q16, Q17, Q18, Q19, Q20, Q21, Q22, Q23, Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31, Q32, Q33, Q34, Q35, Q36, Q37, Q38, Q39, Q40, Q41, Q42, Q43, Q44, Q45, Q46, Q47, Q48, Q49, Q50, Q51, Q52, Q53, Q54, Q55, Q56, Q57, Q58, Q59, Q60, Q61, Q62, Q63 } <= { Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, Data24, Data25, Data26, Data27, Data28, Data29, Data30, Data31, Data32, Data33, Data34, Data35, Data36, Data37, Data38, Data39, Data40, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, Data49, Data50, Data51, Data52, Data53, Data54, Data55, Data56, Data57, Data58, Data59, Data60, Data61, Data62, Data63 }; -endmodule - -module mux4_4_16( - Sel0, Sel1, Sel2, Sel3, - Result0, Result1, Result2, Result3, - Data0x0, Data0x1, Data0x2, Data0x3, - Data1x0, Data1x1, Data1x2, Data1x3, - Data2x0, Data2x1, Data2x2, Data2x3, - Data3x0, Data3x1, Data3x2, Data3x3, - Data4x0, Data4x1, Data4x2, Data4x3, - Data5x0, Data5x1, Data5x2, Data5x3, - Data6x0, Data6x1, Data6x2, Data6x3, - Data7x0, Data7x1, Data7x2, Data7x3, - Data8x0, Data8x1, Data8x2, Data8x3, - Data9x0, Data9x1, Data9x2, Data9x3, - Data10x0, Data10x1, Data10x2, Data10x3, - Data11x0, Data11x1, Data11x2, Data11x3, - Data12x0, Data12x1, Data12x2, Data12x3, - Data13x0, Data13x1, Data13x2, Data13x3, - Data14x0, Data14x1, Data14x2, Data14x3, - Data15x0, Data15x1, Data15x2, Data15x3 -); -input Sel0, Sel1, Sel2, Sel3; -output Result0, Result1, Result2, Result3; -input Data0x0, Data0x1, Data0x2, Data0x3; -input Data1x0, Data1x1, Data1x2, Data1x3; -input Data2x0, Data2x1, Data2x2, Data2x3; -input Data3x0, Data3x1, Data3x2, Data3x3; -input Data4x0, Data4x1, Data4x2, Data4x3; -input Data5x0, Data5x1, Data5x2, Data5x3; -input Data6x0, Data6x1, Data6x2, Data6x3; -input Data7x0, Data7x1, Data7x2, Data7x3; -input Data8x0, Data8x1, Data8x2, Data8x3; -input Data9x0, Data9x1, Data9x2, Data9x3; -input Data10x0, Data10x1, Data10x2, Data10x3; -input Data11x0, Data11x1, Data11x2, Data11x3; -input Data12x0, Data12x1, Data12x2, Data12x3; -input Data13x0, Data13x1, Data13x2, Data13x3; -input Data14x0, Data14x1, Data14x2, Data14x3; -input Data15x0, Data15x1, Data15x2, Data15x3; -assign {Result0, Result1, Result2, Result3} = - {Sel3, Sel2, Sel1, Sel0} == 0 ? { Data0x0, Data0x1, Data0x2, Data0x3 } : - {Sel3, Sel2, Sel1, Sel0} == 1 ? { Data1x0, Data1x1, Data1x2, Data1x3 } : - {Sel3, Sel2, Sel1, Sel0} == 2 ? { Data2x0, Data2x1, Data2x2, Data2x3 } : - {Sel3, Sel2, Sel1, Sel0} == 3 ? { Data3x0, Data3x1, Data3x2, Data3x3 } : - {Sel3, Sel2, Sel1, Sel0} == 4 ? { Data4x0, Data4x1, Data4x2, Data4x3 } : - {Sel3, Sel2, Sel1, Sel0} == 5 ? { Data5x0, Data5x1, Data5x2, Data5x3 } : - {Sel3, Sel2, Sel1, Sel0} == 6 ? { Data6x0, Data6x1, Data6x2, Data6x3 } : - {Sel3, Sel2, Sel1, Sel0} == 7 ? { Data7x0, Data7x1, Data7x2, Data7x3 } : - {Sel3, Sel2, Sel1, Sel0} == 8 ? { Data8x0, Data8x1, Data8x2, Data8x3 } : - {Sel3, Sel2, Sel1, Sel0} == 9 ? { Data9x0, Data9x1, Data9x2, Data9x3 } : - {Sel3, Sel2, Sel1, Sel0} == 10 ? { Data10x0, Data10x1, Data10x2, Data10x3 } : - {Sel3, Sel2, Sel1, Sel0} == 11 ? { Data11x0, Data11x1, Data11x2, Data11x3 } : - {Sel3, Sel2, Sel1, Sel0} == 12 ? { Data12x0, Data12x1, Data12x2, Data12x3 } : - {Sel3, Sel2, Sel1, Sel0} == 13 ? { Data13x0, Data13x1, Data13x2, Data13x3 } : - {Sel3, Sel2, Sel1, Sel0} == 14 ? { Data14x0, Data14x1, Data14x2, Data14x3 } : - {Sel3, Sel2, Sel1, Sel0} == 15 ? { Data15x0, Data15x1, Data15x2, Data15x3 } : 'bx; -endmodule - -module mux1_5_32( - Sel0, Sel1, Sel2, Sel3, Sel4, - Data0x0, Data1x0, Data2x0, Data3x0, Data4x0, Data5x0, Data6x0, Data7x0, Data8x0, Data9x0, Data10x0, Data11x0, Data12x0, Data13x0, Data14x0, Data15x0, - Data16x0, Data17x0, Data18x0, Data19x0, Data20x0, Data21x0, Data22x0, Data23x0, Data24x0, Data25x0, Data26x0, Data27x0, Data28x0, Data29x0, Data30x0, Data31x0, - Result0 -); -input Sel0, Sel1, Sel2, Sel3, Sel4; -input Data0x0, Data1x0, Data2x0, Data3x0, Data4x0, Data5x0, Data6x0, Data7x0, Data8x0, Data9x0, Data10x0, Data11x0, Data12x0, Data13x0, Data14x0, Data15x0; -input Data16x0, Data17x0, Data18x0, Data19x0, Data20x0, Data21x0, Data22x0, Data23x0, Data24x0, Data25x0, Data26x0, Data27x0, Data28x0, Data29x0, Data30x0, Data31x0; -output Result0; -assign Result0 = - {Sel4, Sel3, Sel2, Sel1, Sel0} == 0 ? Data0x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 1 ? Data1x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 2 ? Data2x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 3 ? Data3x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 4 ? Data4x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 5 ? Data5x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 6 ? Data6x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 7 ? Data7x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 8 ? Data8x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 9 ? Data9x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 10 ? Data10x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 11 ? Data11x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 12 ? Data12x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 13 ? Data13x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 14 ? Data14x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 15 ? Data15x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 16 ? Data16x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 17 ? Data17x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 18 ? Data18x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 19 ? Data19x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 20 ? Data20x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 21 ? Data21x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 22 ? Data22x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 23 ? Data23x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 24 ? Data24x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 25 ? Data25x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 26 ? Data26x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 27 ? Data27x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 28 ? Data28x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 29 ? Data29x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 30 ? Data30x0 : - {Sel4, Sel3, Sel2, Sel1, Sel0} == 31 ? Data31x0 : 'bx; -endmodule - diff --git a/manual/CHAPTER_StateOfTheArt/simlib_yosys.v b/manual/CHAPTER_StateOfTheArt/simlib_yosys.v deleted file mode 100644 index 454c9a83f37..00000000000 --- a/manual/CHAPTER_StateOfTheArt/simlib_yosys.v +++ /dev/null @@ -1,166 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Clifford Wolf - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * --- - * - * The internal logic cell simulation library. - * - * This Verilog library contains simple simulation models for the internal - * logic cells (_NOT_, _AND_, ...) that are generated by the default technology - * mapper (see "stdcells.v" in this directory) and expected by the "abc" pass. - * - */ - -module _NOT_(A, Y); -input A; -output Y; -assign Y = ~A; -endmodule - -module _AND_(A, B, Y); -input A, B; -output Y; -assign Y = A & B; -endmodule - -module _OR_(A, B, Y); -input A, B; -output Y; -assign Y = A | B; -endmodule - -module _XOR_(A, B, Y); -input A, B; -output Y; -assign Y = A ^ B; -endmodule - -module _MUX_(A, B, S, Y); -input A, B, S; -output reg Y; -always @* begin - if (S) - Y = B; - else - Y = A; -end -endmodule - -module _DFF_N_(D, Q, C); -input D, C; -output reg Q; -always @(negedge C) begin - Q <= D; -end -endmodule - -module _DFF_P_(D, Q, C); -input D, C; -output reg Q; -always @(posedge C) begin - Q <= D; -end -endmodule - -module _DFF_NN0_(D, Q, C, R); -input D, C, R; -output reg Q; -always @(negedge C or negedge R) begin - if (R == 0) - Q <= 0; - else - Q <= D; -end -endmodule - -module _DFF_NN1_(D, Q, C, R); -input D, C, R; -output reg Q; -always @(negedge C or negedge R) begin - if (R == 0) - Q <= 1; - else - Q <= D; -end -endmodule - -module _DFF_NP0_(D, Q, C, R); -input D, C, R; -output reg Q; -always @(negedge C or posedge R) begin - if (R == 1) - Q <= 0; - else - Q <= D; -end -endmodule - -module _DFF_NP1_(D, Q, C, R); -input D, C, R; -output reg Q; -always @(negedge C or posedge R) begin - if (R == 1) - Q <= 1; - else - Q <= D; -end -endmodule - -module _DFF_PN0_(D, Q, C, R); -input D, C, R; -output reg Q; -always @(posedge C or negedge R) begin - if (R == 0) - Q <= 0; - else - Q <= D; -end -endmodule - -module _DFF_PN1_(D, Q, C, R); -input D, C, R; -output reg Q; -always @(posedge C or negedge R) begin - if (R == 0) - Q <= 1; - else - Q <= D; -end -endmodule - -module _DFF_PP0_(D, Q, C, R); -input D, C, R; -output reg Q; -always @(posedge C or posedge R) begin - if (R == 1) - Q <= 0; - else - Q <= D; -end -endmodule - -module _DFF_PP1_(D, Q, C, R); -input D, C, R; -output reg Q; -always @(posedge C or posedge R) begin - if (R == 1) - Q <= 1; - else - Q <= D; -end -endmodule - diff --git a/manual/CHAPTER_StateOfTheArt/sis-1.3.6-buildfixes.patch b/manual/CHAPTER_StateOfTheArt/sis-1.3.6-buildfixes.patch deleted file mode 100644 index ad957d6b8e3..00000000000 --- a/manual/CHAPTER_StateOfTheArt/sis-1.3.6-buildfixes.patch +++ /dev/null @@ -1,113 +0,0 @@ -Some minor build fixes for sis-1.3.6 as it can be downloaded from -http://www-cad.eecs.berkeley.edu/~pchong/sis.html or -http://embedded.eecs.berkeley.edu/Alumni/pchong/sis.html - -diff --git a/sis/io/read_kiss.c b/sis/io/read_kiss.c -index 814e526..c862892 100644 ---- a/sis/io/read_kiss.c -+++ b/sis/io/read_kiss.c -@@ -10,7 +10,6 @@ - #ifdef SIS - #include "sis.h" - --extern void read_error(); - extern int read_lineno; - extern char *read_filename; - -diff --git a/sis/pld/act_bdd.c b/sis/pld/act_bdd.c -index 4fb4415..a5cd74c 100644 ---- a/sis/pld/act_bdd.c -+++ b/sis/pld/act_bdd.c -@@ -141,6 +141,8 @@ char *name; - return p_vertex; - } - -+static int compare(); -+ - /* Or 2 ACT's*/ - act_t * - my_or_act_F(array_b,cover, array) -@@ -148,7 +150,6 @@ array_t *array_b; - array_t *array; - sm_row *cover; - { -- static int compare(); - int i; - act_t *up_vertex, *down_vertex, *vertex; - sm_element *p; -diff --git a/sis/pld/act_ite.c b/sis/pld/act_ite.c -index a35f2fb..7b824df 100644 ---- a/sis/pld/act_ite.c -+++ b/sis/pld/act_ite.c -@@ -125,6 +125,8 @@ node_t *fanin; - and the minimum column cover variables in cover, generates an ite for the - original function. */ - -+static int compare(); -+ - ite_vertex * - my_or_ite_F(array_b, cover, array, network) - array_t *array_b; -@@ -132,7 +134,6 @@ array_t *array; - sm_row *cover; - network_t *network; - { -- static int compare(); - int i; - ite_vertex *vertex; - sm_element *p; -diff --git a/sis/pld/xln_merge.c b/sis/pld/xln_merge.c -index 075e6c5..16f4d61 100644 ---- a/sis/pld/xln_merge.c -+++ b/sis/pld/xln_merge.c -@@ -284,6 +284,7 @@ array_t *match1_array, *match2_array; - - } - -+static sm_row *xln_merge_find_neighbor_of_row1_with_minimum_neighbors(); - - /*---------------------------------------------------------------------------------------------------- - An alternate to lindo option. Uses greedy merging. A node with minimum mergeable nodes is picked -@@ -296,7 +297,6 @@ xln_merge_nodes_without_lindo(coeff, cand_node_array, match1_array, match2_array - { - node_t *n1, *n2; - sm_row *row1, *row2; -- static sm_row *xln_merge_find_neighbor_of_row1_with_minimum_neighbors(); - - while (TRUE) { - row1 = sm_shortest_row(coeff); -diff --git a/sis/pld/xln_part_dec.c b/sis/pld/xln_part_dec.c -index 1c856bd..b78828a 100644 ---- a/sis/pld/xln_part_dec.c -+++ b/sis/pld/xln_part_dec.c -@@ -49,13 +49,14 @@ int size; - - - -+static int kernel_value(); -+ - int - split_node(network, node, size) - network_t *network; - node_t *node; - int size; - { -- static int kernel_value(); - int i, value = 1; - kern_node *sorted; - divisor_t *div, *best_div; -diff --git a/xsis/Makefile.am b/xsis/Makefile.am -index 196d98b..686fdf4 100644 ---- a/xsis/Makefile.am -+++ b/xsis/Makefile.am -@@ -1,8 +1,8 @@ - xsis_SOURCES_local = NetPlot.c NetPlot.h NetPlotP.h main.c xastg.c \ - xblif.c xcmd.c xhelp.c xsis.c xsis.h xutil.c \ - blif50.px ghost.px help50.px sis50.px --AM_CPPFLAGS = -I../sis/include -I@SIS_X_INCLUDES@ --AM_LDFLAGS = -L@SIS_X_LIBRARIES@ -+AM_CPPFLAGS = -I../sis/include -+AM_LDFLAGS = - LDADD = ../sis/libsis.a -lXaw -lXmu -lXt -lXext -lX11 -lm - - if SIS_COND_X diff --git a/manual/CHAPTER_StateOfTheArt/synth.sh b/manual/CHAPTER_StateOfTheArt/synth.sh deleted file mode 100755 index 3a7524a2918..00000000000 --- a/manual/CHAPTER_StateOfTheArt/synth.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash - -yosys_bin="/usr/local/synthesis/src/yosys/yosys" -hana_bin="/usr/local/synthesis/src/hana/bin/hana" -vl2mv_bin="/usr/local/synthesis/bin/vl2mv" -vis_bin="/usr/local/synthesis/bin/vis" -iverilog_bin="/usr/local/synthesis/bin/iverilog-0.8" -odin_bin="/usr/local/synthesis/src/vtr_release/ODIN_II/odin_II.exe" -abc_bin="/usr/local/synthesis/src/alanmi-abc-b5750272659f/abc" -edif2ngd="/opt/Xilinx/14.3/ISE_DS/ISE/bin/lin64/edif2ngd" -netgen="/opt/Xilinx/14.3/ISE_DS/ISE/bin/lin64/netgen" - -all_modes="yosys hana vis icarus odin" -all_sources="always01 always02 always03 arrays01 forgen01 forgen02" - -if [ "$*" == "ALL" ]; then - for mode in $all_modes; do - for src in $all_sources; do - echo "synth.sh $mode $src.v ${src}_${mode}.v" - ( set -x; bash synth.sh $mode $src.v ${src}_${mode}.v || rm -f ${src}_${mode}.v; ) > ${src}_${mode}.log 2>&1 - done - done - exit -fi - -mode="$1" -source="$2" -output="$3" -prefix="${output%.v}" - -help() { - echo "$0 ALL" >&2 - echo "$0 {yosys|hana|vis|icarus|odin} " >&2 - exit 1 -} - -if [ "$#" != 3 -o ! -f "$source" ]; then - help -fi - -set -ex - -case "$mode" in - yosys) - $yosys_bin -o $output -b "verilog -noattr" -p proc -p opt -p memory -p opt -p techmap -p opt $source ;; - hana) - $hana_bin -s $output $source ;; - vis) - $vl2mv_bin -o $prefix.mv $source - { echo "read_blif_mv $prefix.mv"; echo "write_verilog $output"; } | $abc_bin ;; - icarus) - rm -f $prefix.ngo $prefix.v - $iverilog_bin -t fpga -o $prefix.edif $source - $edif2ngd $prefix.edif $prefix.ngo - $netgen -ofmt verilog $prefix.ngo $prefix.v - sed -re '/timescale/ s,^,//,;' -i $prefix.v ;; - odin) - $odin_bin -o $prefix.blif -V $source - sed -re 's,top\^,,g; s,clock,_clock,g;' -i $prefix.blif - { echo "read_blif $prefix.blif"; echo "write_verilog $output"; } | $abc_bin ;; - *) - help -esac - diff --git a/manual/CHAPTER_StateOfTheArt/validate_tb.sh b/manual/CHAPTER_StateOfTheArt/validate_tb.sh deleted file mode 100755 index b6409eb1487..00000000000 --- a/manual/CHAPTER_StateOfTheArt/validate_tb.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -set -ex - -yosys_bin="/usr/local/synthesis/src/yosys/yosys" -iverilog_bin="iverilog" - -all_modes="yosys hana vis icarus odin" -all_sources="always01 always02 always03 arrays01 forgen01 forgen02" - -gcc -o cmp_tbdata cmp_tbdata.c - -for src in $all_sources; do - echo; echo - $yosys_bin -o ${src}_tb.v -b autotest ${src}.v - $iverilog_bin -o ${src}_tb ${src}_tb.v ${src}.v - ./${src}_tb > ${src}_tb.out - for mode in $all_modes; do - simlib="" - [ -f ${src}_${mode}.v ] || continue - [ -f simlib_${mode}.v ] && simlib="simlib_${mode}.v" - if $iverilog_bin -o ${src}_${mode}_tb -s testbench ${src}_tb.v ${src}_${mode}.v $simlib; then - ./${src}_${mode}_tb > ${src}_${mode}_tb.out - else - rm -f ${src}_${mode}_tb.out - fi - done -done - -set +x -echo; echo; echo - -{ - for mode in $all_modes; do - echo -en "\t$mode" - done; echo - - for src in $all_sources; do - echo -n "$src" - for mode in $all_modes; do - if [ -f ${src}_${mode}.v ]; then - if [ ! -s ${src}_${mode}_tb.out ]; then - echo -en "\tmissing" - elif ./cmp_tbdata ${src}_tb.out ${src}_${mode}_tb.out; then - echo -en "\tok" - else - echo -en "\tfailed" - fi - else - echo -en "\terror" - fi - done; echo - done -} | expand -t12 - diff --git a/manual/CHAPTER_Techmap.tex b/manual/CHAPTER_Techmap.tex deleted file mode 100644 index 13aa8e5a35f..00000000000 --- a/manual/CHAPTER_Techmap.tex +++ /dev/null @@ -1,102 +0,0 @@ - -\chapter{Technology Mapping} -\label{chapter:techmap} - -Previous chapters outlined how HDL code is transformed into an RTL netlist. The -RTL netlist is still based on abstract coarse-grain cell types like arbitrary -width adders and even multipliers. This chapter covers how an RTL netlist is -transformed into a functionally equivalent netlist utilizing the cell types -available in the target architecture. - -Technology mapping is often performed in two phases. In the first phase RTL cells -are mapped to an internal library of single-bit cells (see Sec.~\ref{sec:celllib_gates}). -In the second phase this netlist of internal gate types is transformed to a netlist -of gates from the target technology library. - -When the target architecture provides coarse-grain cells (such as block ram -or ALUs), these must be mapped to directly form the RTL netlist, as information -on the coarse-grain structure of the design is lost when it is mapped to -bit-width gate types. - -\section{Cell Substitution} - -The simplest form of technology mapping is cell substitution, as performed by -the {\tt techmap} pass. This pass, when provided with a Verilog file that -implements the RTL cell types using simpler cells, simply replaces the RTL -cells with the provided implementation. - -When no map file is provided, {\tt techmap} uses a built-in map file that -maps the Yosys RTL cell types to the internal gate library used by Yosys. -The curious reader may find this map file as {\tt techlibs/common/techmap.v} in -the Yosys source tree. - -Additional features have been added to {\tt techmap} to allow for conditional -mapping of cells (see {\tt help techmap} or Sec.~\ref{cmd:techmap}). This can -for example be useful if the target architecture supports hardware multipliers for -certain bit-widths but not for others. - -A usual synthesis flow would first use the {\tt techmap} pass to directly map -some RTL cells to coarse-grain cells provided by the target architecture (if -any) and then use techmap with the built-in default file to map the remaining -RTL cells to gate logic. - -\section{Subcircuit Substitution} - -Sometimes the target architecture provides cells that are more powerful than -the RTL cells used by Yosys. For example a cell in the target architecture that can -calculate the absolute-difference of two numbers does not match any single -RTL cell type but only combinations of cells. - -For these cases Yosys provides the {\tt extract} pass that can match a given set -of modules against a design and identify the portions of the design that are -identical (i.e.~isomorphic subcircuits) to any of the given modules. These -matched subcircuits are then replaced by instances of the given modules. - -The {\tt extract} pass also finds basic variations of the given modules, -such as swapped inputs on commutative cell types. - -In addition to this the {\tt extract} pass also has limited support for -frequent subcircuit mining, i.e.~the process of finding recurring subcircuits -in the design. This has a few applications, including the design of new -coarse-grain architectures \cite{intersynthFdlBookChapter}. - -The hard algorithmic work done by the {\tt extract} pass (solving the -isomorphic subcircuit problem and frequent subcircuit mining) is performed -using the SubCircuit library that can also be used stand-alone without Yosys -(see Sec.~\ref{sec:SubCircuit}). - -\section{Gate-Level Technology Mapping} -\label{sec:techmap_extern} - -On the gate-level the target architecture is usually described by a ``Liberty -file''. The Liberty file format is an industry standard format that can be -used to describe the behaviour and other properties of standard library cells -\citeweblink{LibertyFormat}. - -Mapping a design utilizing the Yosys internal gate library (e.g.~as a result -of mapping it to this representation using the {\tt techmap} pass) is -performed in two phases. - -First the register cells must be mapped to the registers that are available -on the target architectures. The target architecture might not provide all -variations of d-type flip-flops with positive and negative clock edge, -high-active and low-active asynchronous set and/or reset, etc. Therefore the -process of mapping the registers might add additional inverters to the design -and thus it is important to map the register cells first. - -Mapping of the register cells may be performed by using the {\tt dfflibmap} -pass. This pass expects a Liberty file as argument (using the {\tt -liberty} -option) and only uses the register cells from the Liberty file. - -Secondly the combinational logic must be mapped to the target architecture. -This is done using the external program ABC \citeweblink{ABC} via the -{\tt abc} pass by using the {\tt -liberty} option to the pass. Note that -in this case only the combinatorial cells are used from the cell library. - -Occasionally Liberty files contain trade secrets (such as sensitive timing -information) that cannot be shared freely. This complicates processes such as -reporting bugs in the tools involved. When the information in the Liberty file -used by Yosys and ABC are not part of the sensitive information, the additional -tool {\tt yosys-filterlib} (see Sec.~\ref{sec:filterlib}) can be used to strip -the sensitive information from the Liberty file. - diff --git a/manual/CHAPTER_Verilog.tex b/manual/CHAPTER_Verilog.tex deleted file mode 100644 index d4cc55647df..00000000000 --- a/manual/CHAPTER_Verilog.tex +++ /dev/null @@ -1,849 +0,0 @@ - -\chapter{The Verilog and AST Frontends} -\label{chapter:verilog} - -This chapter provides an overview of the implementation of the Yosys Verilog -and AST frontends. The Verilog frontend reads Verilog-2005 code and creates -an abstract syntax tree (AST) representation of the input. This AST representation -is then passed to the AST frontend that converts it to RTLIL data, as illustrated -in Fig.~\ref{fig:Verilog_flow}. - -\begin{figure}[b!] - \hfil - \begin{tikzpicture} - \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=5em, font={\ttfamily}] - \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}] - - \node[data] (n1) {Verilog Source}; - \node[process] (n2) [below of=n1] {Verilog Frontend}; - \node[data] (n3) [below of=n2] {AST}; - \node[process] (n4) [below of=n3] {AST Frontend}; - \node[data] (n5) [below of=n4] {RTLIL}; - - \draw[-latex] (n1) -- (n2); - \draw[-latex] (n2) -- (n3); - \draw[-latex] (n3) -- (n4); - \draw[-latex] (n4) -- (n5); - - \tikzstyle{details} = [draw, fill=yellow!5, rectangle, node distance=6cm, font={\ttfamily}] - - \node[details] (d1) [right of=n2] {\begin{minipage}{5cm} - \hfil - \begin{tikzpicture} - \tikzstyle{subproc} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=10em, node distance=3em, font={\ttfamily}] - \node (s0) {}; - \node[subproc] (s1) [below of=s0] {Preprocessor}; - \node[subproc] (s2) [below of=s1] {Lexer}; - \node[subproc] (s3) [below of=s2] {Parser}; - \node[node distance=3em] (s4) [below of=s3] {}; - \draw[-latex] (s0) -- (s1); - \draw[-latex] (s1) -- (s2); - \draw[-latex] (s2) -- (s3); - \draw[-latex] (s3) -- (s4); - \end{tikzpicture} - \end{minipage}}; - - \draw[dashed] (n2.north east) -- (d1.north west); - \draw[dashed] (n2.south east) -- (d1.south west); - - \node[details] (d2) [right of=n4] {\begin{minipage}{5cm} - \hfil - \begin{tikzpicture} - \tikzstyle{subproc} = [draw, fill=green!10, rectangle, minimum height=2em, minimum width=10em, node distance=3em, font={\ttfamily}] - \node (s0) {}; - \node[subproc] (s1) [below of=s0] {Simplifier}; - \node[subproc] (s2) [below of=s1] {RTLIL Generator}; - \node[node distance=3em] (s3) [below of=s2] {}; - \draw[-latex] (s0) -- (s1); - \draw[-latex] (s1) -- (s2); - \draw[-latex] (s2) -- (s3); - \end{tikzpicture} - \end{minipage}}; - - \draw[dashed] (n4.north east) -- (d2.north west); - \draw[dashed] (n4.south east) -- (d2.south west); - - \end{tikzpicture} - \caption{Simplified Verilog to RTLIL data flow} - \label{fig:Verilog_flow} -\end{figure} - - -\section{Transforming Verilog to AST} - -The {\it Verilog frontend} converts the Verilog sources to an internal AST representation that closely resembles -the structure of the original Verilog code. The Verilog frontend consists of three components, the -{\it Preprocessor}, the {\it Lexer} and the {\it Parser}. - -The source code to the Verilog frontend can be found in {\tt frontends/verilog/} in the Yosys source tree. - -\subsection{The Verilog Preprocessor} - -The Verilog preprocessor scans over the Verilog source code and interprets some of the Verilog compiler -directives such as \lstinline[language=Verilog]{`include}, \lstinline[language=Verilog]{`define} and -\lstinline[language=Verilog]{`ifdef}. - -It is implemented as a C++ function that is passed a file descriptor as input and returns the -pre-processed Verilog code as a \lstinline[language=C++]{std::string}. - -The source code to the Verilog Preprocessor can be found in {\tt -frontends/verilog/preproc.cc} in the Yosys source tree. - -\subsection{The Verilog Lexer} - -\begin{sloppypar} -The Verilog Lexer is written using the lexer generator {\it flex} \citeweblink{flex}. Its source code -can be found in {\tt frontends/verilog/verilog\_lexer.l} in the Yosys source tree. -The lexer does little more than identifying all keywords and literals -recognised by the Yosys Verilog frontend. -\end{sloppypar} - -The lexer keeps track of the current location in the Verilog source code using -some global variables. These variables are used by the constructor of AST nodes -to annotate each node with the source code location it originated from. - -\begin{sloppypar} -Finally the lexer identifies and handles special comments such as -``\lstinline[language=Verilog]{// synopsys translate_off}'' and -``\lstinline[language=Verilog]{// synopsys full_case}''. (It is recommended to -use \lstinline[language=Verilog]{`ifdef} constructs instead of the Synsopsys -translate\_on/off comments and attributes such as -\lstinline[language=Verilog]{(* full_case *)} over ``\lstinline[language=Verilog]{// synopsys full_case}'' -whenever possible.) -\end{sloppypar} - -\subsection{The Verilog Parser} - -The Verilog Parser is written using the parser generator {\it bison} \citeweblink{bison}. Its source code -can be found in {\tt frontends/verilog/verilog\_parser.y} in the Yosys source tree. - -It generates an AST using the \lstinline[language=C++]{AST::AstNode} data structure -defined in {\tt frontends/ast/ast.h}. An \lstinline[language=C++]{AST::AstNode} object has -the following properties: - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\begin{table}[b!] -\hfil -\begin{tabular}{>{\raggedright\arraybackslash}p{7cm}>{\raggedright\arraybackslash}p{8cm}} -AST Node Type & Corresponding Verilog Construct \\ -\hline -\hline -\arrayrulecolor{gray} -{\tt AST\_NONE} & This Node type should never be used. \\ -\hline -% -{\tt AST\_DESIGN} & This node type is used for the top node of the AST tree. It -has no corresponding Verilog construct. \\ -\hline -% -{\tt AST\_MODULE}, -{\tt AST\_TASK}, -{\tt AST\_FUNCTION} & -\lstinline[language=Verilog];module;, -\lstinline[language=Verilog];task; and -\lstinline[language=Verilog];function; \\ -\hline -% -{\tt AST\_WIRE} & -\lstinline[language=Verilog];input;, -\lstinline[language=Verilog];output;, -\lstinline[language=Verilog];wire;, -\lstinline[language=Verilog];reg; and -\lstinline[language=Verilog];integer; \\ -\hline -% -{\tt AST\_MEMORY} & -Verilog Arrays \\ -\hline -% -{\tt AST\_AUTOWIRE} & -Created by the simplifier when an undeclared signal name is used. \\ -\hline -% -{\tt AST\_PARAMETER}, -{\tt AST\_LOCALPARAM} & -\lstinline[language=Verilog];parameter; and -\lstinline[language=Verilog];localparam; \\ -\hline -% -{\tt AST\_PARASET} & -Parameter set in cell instantiation \\ -\hline -% -{\tt AST\_ARGUMENT} & -Port connection in cell instantiation \\ -\hline -% -{\tt AST\_RANGE} & -Bit-Index in a signal or element index in array \\ -\hline -% -{\tt AST\_CONSTANT} & -A literal value \\ -\hline -% -{\tt AST\_CELLTYPE} & -The type of cell in cell instantiation \\ -\hline -% -{\tt AST\_IDENTIFIER} & -An Identifier (signal name in expression or cell/task/etc. name in other contexts) \\ -\hline -% -{\tt AST\_PREFIX} & -Construct an identifier in the form {\tt [].} (used only in -advanced generate constructs) \\ -\hline -% -{\tt AST\_FCALL}, -{\tt AST\_TCALL} & -Call to function or task \\ -\hline -% -{\tt AST\_TO\_SIGNED}, -{\tt AST\_TO\_UNSIGNED} & -The \lstinline[language=Verilog];$signed(); and -\lstinline[language=Verilog];$unsigned(); functions \\ -\hline -\end{tabular} -\caption{AST node types with their corresponding Verilog constructs. \\ (continued on next page)} -\label{tab:Verilog_AstNodeType} -\end{table} - -\begin{table}[t!] -\ContinuedFloat -\hfil -\begin{tabular}{>{\raggedright\arraybackslash}p{7cm}>{\raggedright\arraybackslash}p{8cm}} -AST Node Type & Corresponding Verilog Construct \\ -\hline -\hline -\arrayrulecolor{gray} -{\tt AST\_CONCAT} -{\tt AST\_REPLICATE} & -The \lstinline[language=Verilog];{...}; and -\lstinline[language=Verilog];{...{...}}; operators \\ -\hline -% -{\tt AST\_BIT\_NOT}, -{\tt AST\_BIT\_AND}, -{\tt AST\_BIT\_OR}, -{\tt AST\_BIT\_XOR}, -{\tt AST\_BIT\_XNOR} & -The bitwise operators \break -\lstinline[language=Verilog];~;, -\lstinline[language=Verilog];&;, -\lstinline[language=Verilog];|;, -\lstinline[language=Verilog];^; and -\lstinline[language=Verilog];~^; \\ -\hline -% -{\tt AST\_REDUCE\_AND}, -{\tt AST\_REDUCE\_OR}, -{\tt AST\_REDUCE\_XOR}, -{\tt AST\_REDUCE\_XNOR} & -The unary reduction operators \break -\lstinline[language=Verilog];~;, -\lstinline[language=Verilog];&;, -\lstinline[language=Verilog];|;, -\lstinline[language=Verilog];^; and -\lstinline[language=Verilog];~^; \\ -\hline -% -{\tt AST\_REDUCE\_BOOL} & -Conversion from multi-bit value to boolean value -(equivalent to {\tt AST\_REDUCE\_OR}) \\ -\hline -% -{\tt AST\_SHIFT\_LEFT}, -{\tt AST\_SHIFT\_RIGHT}, -{\tt AST\_SHIFT\_SLEFT}, -{\tt AST\_SHIFT\_SRIGHT} & -The shift operators \break -\lstinline[language=Verilog];<<;, -\lstinline[language=Verilog];>>;, -\lstinline[language=Verilog];<<<; and -\lstinline[language=Verilog];>>>; \\ -\hline -% -{\tt AST\_LT}, -{\tt AST\_LE}, -{\tt AST\_EQ}, -{\tt AST\_NE}, -{\tt AST\_GE}, -{\tt AST\_GT} & -The relational operators \break -\lstinline[language=Verilog];<;, -\lstinline[language=Verilog];<=;, -\lstinline[language=Verilog];==;, -\lstinline[language=Verilog];!=;, -\lstinline[language=Verilog];>=; and -\lstinline[language=Verilog];>; \\ -\hline -% -{\tt AST\_ADD}, -{\tt AST\_SUB}, -{\tt AST\_MUL}, -{\tt AST\_DIV}, -{\tt AST\_MOD}, -{\tt AST\_POW} & -The binary operators \break -\lstinline[language=Verilog];+;, -\lstinline[language=Verilog];-;, -\lstinline[language=Verilog];*;, -\lstinline[language=Verilog];/;, -\lstinline[language=Verilog];%; and -\lstinline[language=Verilog];**; \\ -\hline -% -{\tt AST\_POS}, -{\tt AST\_NEG} & -The prefix operators -\lstinline[language=Verilog];+; and -\lstinline[language=Verilog];-; \\ -\hline -% -{\tt AST\_LOGIC\_AND}, -{\tt AST\_LOGIC\_OR}, -{\tt AST\_LOGIC\_NOT} & -The logic operators -\lstinline[language=Verilog];&&;, -\lstinline[language=Verilog];||; and -\lstinline[language=Verilog];!; \\ -\hline -% -{\tt AST\_TERNARY} & -The ternary \lstinline[language=Verilog];?:;-operator \\ -\hline -% -{\tt AST\_MEMRD} -{\tt AST\_MEMWR} & -Read and write memories. These nodes are generated by -the AST simplifier for writes/reads to/from Verilog arrays. \\ -\hline -% -{\tt AST\_ASSIGN} & -An \lstinline[language=Verilog];assign; statement \\ -\hline -% -{\tt AST\_CELL} & -A cell instantiation \\ -\hline -% -{\tt AST\_PRIMITIVE} & -A primitive cell (\lstinline[language=Verilog];and;, -\lstinline[language=Verilog];nand;, -\lstinline[language=Verilog];or;, etc.) \\ -\hline -% -{\tt AST\_ALWAYS}, -{\tt AST\_INITIAL} & -Verilog \lstinline[language=Verilog];always;- and \lstinline[language=Verilog];initial;-blocks \\ -\hline -% -{\tt AST\_BLOCK} & -A \lstinline[language=Verilog];begin;-\lstinline[language=Verilog];end;-block \\ -\hline -% -{\tt AST\_ASSIGN\_EQ}. -{\tt AST\_ASSIGN\_LE} & -Blocking (\lstinline[language=Verilog];=;) and nonblocking (\lstinline[language=Verilog];<=;) -assignments within an \lstinline[language=Verilog];always;- or \lstinline[language=Verilog];initial;-block \\ -\hline -% -{\tt AST\_CASE}. -{\tt AST\_COND}, -{\tt AST\_DEFAULT} & -The \lstinline[language=Verilog];case; (\lstinline[language=Verilog];if;) statements, conditions within a case -and the default case respectively \\ -\hline -% -{\tt AST\_FOR} & -A \lstinline[language=Verilog];for;-loop with an -\lstinline[language=Verilog];always;- or -\lstinline[language=Verilog];initial;-block \\ -\hline -% -{\tt AST\_GENVAR}, -{\tt AST\_GENBLOCK}, -{\tt AST\_GENFOR}, -{\tt AST\_GENIF} & -The \lstinline[language=Verilog];genvar; and -\lstinline[language=Verilog];generate; keywords and -\lstinline[language=Verilog];for; and \lstinline[language=Verilog];if; within a -generate block. \\ -\hline -% -{\tt AST\_POSEDGE}, -{\tt AST\_NEGEDGE}, -{\tt AST\_EDGE} & -Event conditions for \lstinline[language=Verilog];always; blocks. \\ -\hline -\end{tabular} -\caption{AST node types with their corresponding Verilog constructs. \\ (continuation from previous page)} -\label{tab:Verilog_AstNodeTypeCont} -\end{table} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\begin{itemize} -\item {\bf The node type} \\ -This enum (\lstinline[language=C++]{AST::AstNodeType}) specifies the role of the node. -Table~\ref{tab:Verilog_AstNodeType} contains a list of all node types. -\item {\bf The child nodes} \\ -This is a list of pointers to all children in the abstract syntax tree. -\item {\bf Attributes} \\ -As almost every AST node might have Verilog attributes assigned to it, the -\lstinline[language=C++]{AST::AstNode} has direct support for attributes. Note that the -attribute values are again AST nodes. -\item {\bf Node content} \\ -Each node might have additional content data. A series of member variables exist to hold such data. -For example the member \lstinline[language=C++]{std::string str} can hold a string value and is -used e.g.~in the {\tt AST\_IDENTIFIER} node type to store the identifier name. -\item {\bf Source code location} \\ -Each \lstinline[language=C++]{AST::AstNode} is automatically annotated with the current -source code location by the \lstinline[language=C++]{AST::AstNode} constructor. It is -stored in the \lstinline[language=C++]{std::string filename} and \lstinline[language=C++]{int linenum} -member variables. -\end{itemize} - -The \lstinline[language=C++]{AST::AstNode} constructor can be called with up to -two child nodes that are automatically added to the list of child nodes for the new object. -This simplifies the creation of AST nodes for simple expressions a bit. For example the bison -code for parsing multiplications: - -\begin{lstlisting}[numbers=left,frame=single] - basic_expr '*' attr basic_expr { - $$ = new AstNode(AST_MUL, $1, $4); - append_attr($$, $3); - } | -\end{lstlisting} - -The generated AST data structure is then passed directly to the AST frontend -that performs the actual conversion to RTLIL. - -Note that the Yosys command {\tt read\_verilog} provides the options {\tt -yydebug} -and {\tt -dump\_ast} that can be used to print the parse tree or abstract syntax tree -respectively. - -\section{Transforming AST to RTLIL} - -The {\it AST Frontend} converts a set of modules in AST representation to -modules in RTLIL representation and adds them to the current design. This is done -in two steps: {\it simplification} and {\it RTLIL generation}. - -The source code to the AST frontend can be found in {\tt frontends/ast/} in the Yosys source tree. - -\subsection{AST Simplification} - -A full-featured AST is too complex to be transformed into RTLIL directly. Therefore it must -first be brought into a simpler form. This is done by calling the \lstinline[language=C++]{AST::AstNode::simplify()} -method of all {\tt AST\_MODULE} nodes in the AST. This initiates a recursive process that performs the following transformations -on the AST data structure: - -\begin{itemize} -\item Inline all task and function calls. -\item Evaluate all \lstinline[language=Verilog]{generate}-statements and unroll all \lstinline[language=Verilog]{for}-loops. -\item Perform const folding where it is necessary (e.g.~in the value part of {\tt AST\_PARAMETER}, {\tt AST\_LOCALPARAM}, -{\tt AST\_PARASET} and {\tt AST\_RANGE} nodes). -\item Replace {\tt AST\_PRIMITIVE} nodes with appropriate {\tt AST\_ASSIGN} nodes. -\item Replace dynamic bit ranges in the left-hand-side of assignments with {\tt AST\_CASE} nodes with {\tt AST\_COND} children -for each possible case. -\item Detect array access patterns that are too complicated for the {\tt RTLIL::Memory} abstraction and replace them -with a set of signals and cases for all reads and/or writes. -\item Otherwise replace array accesses with {\tt AST\_MEMRD} and {\tt AST\_MEMWR} nodes. -\end{itemize} - -In addition to these transformations, the simplifier also annotates the AST with additional information that is needed -for the RTLIL generator, namely: - -\begin{itemize} -\item All ranges (width of signals and bit selections) are not only const folded but (when a constant value -is found) are also written to member variables in the {\tt AST\_RANGE} node. -\item All identifiers are resolved and all {\tt AST\_IDENTIFIER} nodes are annotated with a pointer to the AST node -that contains the declaration of the identifier. If no declaration has been found, an {\tt AST\_AUTOWIRE} node -is created and used for the annotation. -\end{itemize} - -This produces an AST that is fairly easy to convert to the RTLIL format. - -\subsection{Generating RTLIL} - -After AST simplification, the \lstinline[language=C++]{AST::AstNode::genRTLIL()} method of each {\tt AST\_MODULE} node -in the AST is called. This initiates a recursive process that generates equivalent RTLIL data for the AST data. - -The \lstinline[language=C++]{AST::AstNode::genRTLIL()} method returns an \lstinline[language=C++]{RTLIL::SigSpec} structure. -For nodes that represent expressions (operators, constants, signals, etc.), the cells needed to implement the calculation -described by the expression are created and the resulting signal is returned. That way it is easy to generate the circuits -for large expressions using depth-first recursion. For nodes that do not represent an expression (such as {\tt -AST\_CELL}), the corresponding circuit is generated and an empty \lstinline[language=C++]{RTLIL::SigSpec} is returned. - -\section{Synthesizing Verilog always Blocks} - -For behavioural Verilog code (code utilizing \lstinline[language=Verilog]{always}- and -\lstinline[language=Verilog]{initial}-blocks) it is necessary to also generate \lstinline[language=C++]{RTLIL::Process} -objects. This is done in the following way: - -\begin{itemize} -\item Whenever \lstinline[language=C++]{AST::AstNode::genRTLIL()} encounters an \lstinline[language=Verilog]{always}- -or \lstinline[language=Verilog]{initial}-block, it creates an instance of -\lstinline[language=Verilog]{AST_INTERNAL::ProcessGenerator}. This object then generates the -\lstinline[language=C++]{RTLIL::Process} object for the block. It also calls \lstinline[language=C++]{AST::AstNode::genRTLIL()} -for all right-hand-side expressions contained within the block. -% -\begin{sloppypar} -\item First the \lstinline[language=Verilog]{AST_INTERNAL::ProcessGenerator} creates a list of all signals assigned -within the block. It then creates a set of temporary signals using the naming scheme {\tt \$\it\tt -\textbackslash\it } for each of the assigned signals. -\end{sloppypar} -% -\item Then an \lstinline[language=C++]{RTLIL::Process} is created that assigns all intermediate values for each left-hand-side -signal to the temporary signal in its \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} tree. -% -\item Finally a \lstinline[language=C++]{RTLIL::SyncRule} is created for the \lstinline[language=C++]{RTLIL::Process} that -assigns the temporary signals for the final values to the actual signals. -% -\item Calls to \lstinline[language=C++]{AST::AstNode::genRTLIL()} are generated for right hand sides as needed. When blocking -assignments are used, \lstinline[language=C++]{AST::AstNode::genRTLIL()} is configured using global variables to use -the temporary signals that hold the correct intermediate values whenever one of the previously assigned signals is used -in an expression. -\end{itemize} - -Unfortunately the generation of a correct \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} -tree for behavioural code is a non-trivial task. The AST frontend solves the problem using the approach described on the following -pages. The following example illustrates what the algorithm is supposed to do. Consider the following Verilog code: - -\begin{lstlisting}[numbers=left,frame=single,language=Verilog] -always @(posedge clock) begin - out1 = in1; - if (in2) - out1 = !out1; - out2 <= out1; - if (in3) - out2 <= out2; - if (in4) - if (in5) - out3 <= in6; - else - out3 <= in7; - out1 = out1 ^ out2; -end -\end{lstlisting} - -This is translated by the Verilog and AST frontends into the following RTLIL code (attributes, cell parameters -and wire declarations not included): - -\begin{lstlisting}[numbers=left,frame=single,language=rtlil] -cell $logic_not $logic_not$:4$2 - connect \A \in1 - connect \Y $logic_not$:4$2_Y -end -cell $xor $xor$:13$3 - connect \A $1\out1[0:0] - connect \B \out2 - connect \Y $xor$:13$3_Y -end -process $proc$:1$1 - assign $0\out3[0:0] \out3 - assign $0\out2[0:0] $1\out1[0:0] - assign $0\out1[0:0] $xor$:13$3_Y - switch \in2 - case 1'1 - assign $1\out1[0:0] $logic_not$:4$2_Y - case - assign $1\out1[0:0] \in1 - end - switch \in3 - case 1'1 - assign $0\out2[0:0] \out2 - case - end - switch \in4 - case 1'1 - switch \in5 - case 1'1 - assign $0\out3[0:0] \in6 - case - assign $0\out3[0:0] \in7 - end - case - end - sync posedge \clock - update \out1 $0\out1[0:0] - update \out2 $0\out2[0:0] - update \out3 $0\out3[0:0] -end -\end{lstlisting} - -Note that the two operators are translated into separate cells outside the generated process. The signal -\lstinline[language=Verilog]{out1} is assigned using blocking assignments and therefore \lstinline[language=Verilog]{out1} -has been replaced with a different signal in all expressions after the initial assignment. The signal -\lstinline[language=Verilog]{out2} is assigned using nonblocking assignments and therefore is not substituted -on the right-hand-side expressions. - -The \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} -tree must be interpreted the following way: - -\begin{itemize} -\item On each case level (the body of the process is the {\it root case}), first the actions on this level are -evaluated and then the switches within the case are evaluated. (Note that the last assignment on line 13 of the -Verilog code has been moved to the beginning of the RTLIL process to line 13 of the RTLIL listing.) - -I.e.~the special cases deeper in the switch hierarchy override the defaults on the upper levels. The assignments -in lines 12 and 22 of the RTLIL code serve as an example for this. - -Note that in contrast to this, the order within the \lstinline[language=C++]{RTLIL::SwitchRule} objects -within a \lstinline[language=C++]{RTLIL::CaseRule} is preserved with respect to the original AST and -Verilog code. -% -\item \begin{sloppypar} -The whole \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} tree -describes an asynchronous circuit. I.e.~the decision tree formed by the switches can be seen independently for -each assigned signal. Whenever one assigned signal changes, all signals that depend on the changed signals -are to be updated. For example the assignments in lines 16 and 18 in the RTLIL code in fact influence the assignment -in line 12, even though they are in the ``wrong order''. -\end{sloppypar} -\end{itemize} - -The only synchronous part of the process is in the \lstinline[language=C++]{RTLIL::SyncRule} object generated at line -35 in the RTLIL code. The sync rule is the only part of the process where the original signals are assigned. The -synchronization event from the original Verilog code has been translated into the synchronization type ({\tt posedge}) -and signal ({\tt \textbackslash clock}) for the \lstinline[language=C++]{RTLIL::SyncRule} object. In the case of -this simple example the \lstinline[language=C++]{RTLIL::SyncRule} object is later simply transformed into a set of -d-type flip-flops and the \lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} tree -to a decision tree using multiplexers. - -\begin{sloppypar} -In more complex examples (e.g.~asynchronous resets) the part of the -\lstinline[language=C++]{RTLIL::CaseRule}/\lstinline[language=C++]{RTLIL::SwitchRule} -tree that describes the asynchronous reset must first be transformed to the -correct \lstinline[language=C++]{RTLIL::SyncRule} objects. This is done by the {\tt proc\_adff} pass. -\end{sloppypar} - -\subsection{The ProcessGenerator Algorithm} - -The \lstinline[language=C++]{AST_INTERNAL::ProcessGenerator} uses the following internal state variables: - -\begin{itemize} -\item \begin{sloppypar} -\lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to} \\ -These two variables hold the replacement pattern that should be used by \lstinline[language=C++]{AST::AstNode::genRTLIL()} -for signals with blocking assignments. After initialization of \lstinline[language=C++]{AST_INTERNAL::ProcessGenerator} -these two variables are empty. -\end{sloppypar} -% -\item \lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} \\ -These two variables contain the mapping from left-hand-side signals ({\tt \textbackslash \it }) to the current -temporary signal for the same thing (initially {\tt \$0\textbackslash \it }). -% -\item \lstinline[language=C++]{current_case} \\ -A pointer to a \lstinline[language=C++]{RTLIL::CaseRule} object. Initially this is the root case of the -generated \lstinline[language=C++]{RTLIL::Process}. -\end{itemize} - -As the algorithm runs these variables are continuously modified as well as pushed -to the stack and later restored to their earlier values by popping from the stack. - -On startup the ProcessGenerator generates a new -\lstinline[language=C++]{RTLIL::Process} object with an empty root case and -initializes its state variables as described above. Then the \lstinline[language=C++]{RTLIL::SyncRule} objects -are created using the synchronization events from the {\tt AST\_ALWAYS} node and the initial values of -\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to}. Then the -AST for this process is evaluated recursively. - -During this recursive evaluation, three different relevant types of AST nodes can be discovered: -{\tt AST\_ASSIGN\_LE} (nonblocking assignments), {\tt AST\_ASSIGN\_EQ} (blocking assignments) and -{\tt AST\_CASE} (\lstinline[language=Verilog]{if} or \lstinline[language=Verilog]{case} statement). - -\subsubsection{Handling of Nonblocking Assignments} - -When an {\tt AST\_ASSIGN\_LE} node is discovered, the following actions are performed by the -ProcessGenerator: - -\begin{itemize} -\item The left-hand-side is evaluated using \lstinline[language=C++]{AST::AstNode::genRTLIL()} and mapped to -a temporary signal name using \lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to}. -% -\item The right-hand-side is evaluated using \lstinline[language=C++]{AST::AstNode::genRTLIL()}. For this call, -the values of \lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to} are used to -map blocking-assigned signals correctly. -% -\item Remove all assignments to the same left-hand-side as this assignment from the \lstinline[language=C++]{current_case} -and all cases within it. -% -\item Add the new assignment to the \lstinline[language=C++]{current_case}. -\end{itemize} - -\subsubsection{Handling of Blocking Assignments} - -When an {\tt AST\_ASSIGN\_EQ} node is discovered, the following actions are performed by -the ProcessGenerator: - -\begin{itemize} -\item Perform all the steps that would be performed for a nonblocking assignment (see above). -% -\item Remove the found left-hand-side (before lvalue mapping) from -\lstinline[language=C++]{subst_rvalue_from} and also remove the respective -bits from \lstinline[language=C++]{subst_rvalue_to}. -% -\item Append the found left-hand-side (before lvalue mapping) to \lstinline[language=C++]{subst_rvalue_from} -and append the found right-hand-side to \lstinline[language=C++]{subst_rvalue_to}. -\end{itemize} - -\subsubsection{Handling of Cases and if-Statements} - -\begin{sloppypar} -When an {\tt AST\_CASE} node is discovered, the following actions are performed by -the ProcessGenerator: - -\begin{itemize} -\item The values of \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to}, -\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} are pushed to the stack. -% -\item A new \lstinline[language=C++]{RTLIL::SwitchRule} object is generated, the selection expression is evaluated using -\lstinline[language=C++]{AST::AstNode::genRTLIL()} (with the use of \lstinline[language=C++]{subst_rvalue_from} and -\lstinline[language=C++]{subst_rvalue_to}) and added to the \lstinline[language=C++]{RTLIL::SwitchRule} object and the -object is added to the \lstinline[language=C++]{current_case}. -% -\item All lvalues assigned to within the {\tt AST\_CASE} node using blocking assignments are collected and -saved in the local variable \lstinline[language=C++]{this_case_eq_lvalue}. -% -\item New temporary signals are generated for all signals in \lstinline[language=C++]{this_case_eq_lvalue} and stored -in \lstinline[language=C++]{this_case_eq_ltemp}. -% -\item The signals in \lstinline[language=C++]{this_case_eq_lvalue} are mapped using \lstinline[language=C++]{subst_rvalue_from} -and \lstinline[language=C++]{subst_rvalue_to} and the resulting set of signals is stored in -\lstinline[language=C++]{this_case_eq_rvalue}. -\end{itemize} - -Then the following steps are performed for each {\tt AST\_COND} node within the {\tt AST\_CASE} node: - -\begin{itemize} -\item Set \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to}, -\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} to the values -that have been pushed to the stack. -% -\item Remove \lstinline[language=C++]{this_case_eq_lvalue} from -\lstinline[language=C++]{subst_lvalue_from}/\lstinline[language=C++]{subst_lvalue_to}. -% -\item Append \lstinline[language=C++]{this_case_eq_lvalue} to \lstinline[language=C++]{subst_lvalue_from} and append -\lstinline[language=C++]{this_case_eq_ltemp} to \lstinline[language=C++]{subst_lvalue_to}. -% -\item Push the value of \lstinline[language=C++]{current_case}. -% -\item Create a new \lstinline[language=C++]{RTLIL::CaseRule}. Set \lstinline[language=C++]{current_case} to the -new object and add the new object to the \lstinline[language=C++]{RTLIL::SwitchRule} created above. -% -\item Add an assignment from \lstinline[language=C++]{this_case_eq_rvalue} to \lstinline[language=C++]{this_case_eq_ltemp} -to the new \lstinline[language=C++]{current_case}. -% -\item Evaluate the compare value for this case using \lstinline[language=C++]{AST::AstNode::genRTLIL()} (with the use of -\lstinline[language=C++]{subst_rvalue_from} and \lstinline[language=C++]{subst_rvalue_to}) modify the new -\lstinline[language=C++]{current_case} accordingly. -% -\item Recursion into the children of the {\tt AST\_COND} node. -% -\item Restore \lstinline[language=C++]{current_case} by popping the old value from the stack. -\end{itemize} - -Finally the following steps are performed: - -\begin{itemize} -\item The values of \lstinline[language=C++]{subst_rvalue_from}, \lstinline[language=C++]{subst_rvalue_to}, -\lstinline[language=C++]{subst_lvalue_from} and \lstinline[language=C++]{subst_lvalue_to} are popped from the stack. -% -\item The signals from \lstinline[language=C++]{this_case_eq_lvalue} are removed from the -\lstinline[language=C++]{subst_rvalue_from}/\lstinline[language=C++]{subst_rvalue_to}-pair. -% -\item The value of \lstinline[language=C++]{this_case_eq_lvalue} is appended to \lstinline[language=C++]{subst_rvalue_from} -and the value of \lstinline[language=C++]{this_case_eq_ltemp} is appended to \lstinline[language=C++]{subst_rvalue_to}. -% -\item Map the signals in \lstinline[language=C++]{this_case_eq_lvalue} using -\lstinline[language=C++]{subst_lvalue_from}/\lstinline[language=C++]{subst_lvalue_to}. -% -\item Remove all assignments to signals in \lstinline[language=C++]{this_case_eq_lvalue} in \lstinline[language=C++]{current_case} -and all cases within it. -% -\item Add an assignment from \lstinline[language=C++]{this_case_eq_ltemp} to \lstinline[language=C++]{this_case_eq_lvalue} -to \lstinline[language=C++]{current_case}. -\end{itemize} -\end{sloppypar} - -\subsubsection{Further Analysis of the Algorithm for Cases and if-Statements} - -With respect to nonblocking assignments the algorithm is easy: later assignments invalidate earlier assignments. -For each signal assigned using nonblocking assignments exactly one temporary variable is generated (with the -{\tt \$0}-prefix) and this variable is used for all assignments of the variable. - -Note how all the \lstinline[language=C++]{_eq_}-variables become empty when no blocking assignments are used -and many of the steps in the algorithm can then be ignored as a result of this. - -For a variable with blocking assignments the algorithm shows the following behaviour: First a new temporary variable -is created. This new temporary variable is then registered as the assignment target for all assignments for this -variable within the cases for this {\tt AST\_CASE} node. Then for each case the new temporary variable is first -assigned the old temporary variable. This assignment is overwritten if the variable is actually assigned in this -case and is kept as a default value otherwise. - -This yields an \lstinline[language=C++]{RTLIL::CaseRule} that assigns the new temporary variable in all branches. -So when all cases have been processed a final assignment is added to the containing block that assigns the new -temporary variable to the old one. Note how this step always overrides a previous assignment to the old temporary -variable. Other than nonblocking assignments, the old assignment could still have an effect somewhere -in the design, as there have been calls to \lstinline[language=C++]{AST::AstNode::genRTLIL()} with a -\lstinline[language=C++]{subst_rvalue_from}/\lstinline[language=C++]{subst_rvalue_to}-tuple that contained -the right-hand-side of the old assignment. - -\subsection{The proc pass} - -The ProcessGenerator converts a behavioural model in AST representation to a behavioural model in -\lstinline[language=C++]{RTLIL::Process} representation. The actual conversion from a behavioural -model to an RTL representation is performed by the {\tt proc} pass and the passes it launches: - -\begin{itemize} -\item {\tt proc\_clean} and {\tt proc\_rmdead} \\ -These two passes just clean up the \lstinline[language=C++]{RTLIL::Process} structure. The {\tt proc\_clean} -pass removes empty parts (eg. empty assignments) from the process and {\tt proc\_rmdead} detects and removes -unreachable branches from the process's decision trees. -% -\item {\tt proc\_arst} \\ -This pass detects processes that describe d-type flip-flops with asynchronous -resets and rewrites the process to better reflect what they are modelling: -Before this pass, an asynchronous reset has two edge-sensitive sync rules and -one top-level \C{RTLIL::SwitchRule} for the reset path. After this pass the -sync rule for the reset is level-sensitive and the top-level -\C{RTLIL::SwitchRule} has been removed. -% -\item {\tt proc\_mux} \\ -This pass converts the \C{RTLIL::CaseRule}/\C{RTLIL::SwitchRule}-tree to a tree -of multiplexers per written signal. After this, the \C{RTLIL::Process} structure only contains -the \C{RTLIL::SyncRule}s that describe the output registers. -% -\item {\tt proc\_dff} \\ -This pass replaces the \C{RTLIL::SyncRule}s to d-type flip-flops (with -asynchronous resets if necessary). -% -\item {\tt proc\_clean} \\ -A final call to {\tt proc\_clean} removes the now empty \C{RTLIL::Process} objects. -\end{itemize} - -Performing these last processing steps in passes instead of in the Verilog frontend has two important benefits: - -First it improves the transparency of the process. Everything that happens in a separate pass is easier to debug, -as the RTLIL data structures can be easily investigated before and after each of the steps. - -Second it improves flexibility. This scheme can easily be extended to support other types of storage-elements, such -as sr-latches or d-latches, without having to extend the actual Verilog frontend. - -\section{Synthesizing Verilog Arrays} - -\begin{fixme} -Add some information on the generation of {\tt \$memrd} and {\tt \$memwr} cells -and how they are processed in the {\tt memory} pass. -\end{fixme} - -\section{Synthesizing Parametric Designs} - -\begin{fixme} -Add some information on the \lstinline[language=C++]{RTLIL::Module::derive()} method and how it -is used to synthesize parametric modules via the {\tt hierarchy} pass. -\end{fixme} - diff --git a/manual/PRESENTATION_ExAdv.tex b/manual/PRESENTATION_ExAdv.tex deleted file mode 100644 index ef8f64cec11..00000000000 --- a/manual/PRESENTATION_ExAdv.tex +++ /dev/null @@ -1,896 +0,0 @@ - -\section{Yosys by example -- Advanced Synthesis} - -\begin{frame} -\sectionpage -\end{frame} - -\begin{frame}{Overview} -This section contains 4 subsections: -\begin{itemize} -\item Using selections -\item Advanced uses of techmap -\item Coarse-grain synthesis -\item Automatic design changes -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Using selections} - -\begin{frame} -\subsectionpage -\subsectionpagesuffix -\end{frame} - -\subsubsection{Simple selections} - -\begin{frame}[fragile]{\subsubsecname} -Most Yosys commands make use of the ``selection framework'' of Yosys. It can be used -to apply commands only to part of the design. For example: - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -delete # will delete the whole design, but - -delete foobar # will only delete the module foobar. -\end{lstlisting} - -\bigskip -The {\tt select} command can be used to create a selection for subsequent -commands. For example: - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -select foobar # select the module foobar -delete # delete selected objects -select -clear # reset selection (select whole design) -\end{lstlisting} -\end{frame} - -\subsubsection{Selection by object name} - -\begin{frame}[fragile]{\subsubsecname} -The easiest way to select objects is by object name. This is usually only done -in synthesis scripts that are hand-tailored for a specific design. - -\bigskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -select foobar # select module foobar -select foo* # select all modules whose names start with foo -select foo*/bar* # select all objects matching bar* from modules matching foo* -select */clk # select objects named clk from all modules -\end{lstlisting} -\end{frame} - -\subsubsection{Module and design context} - -\begin{frame}[fragile]{\subsubsecname} -Commands can be executed in {\it module\/} or {\it design\/} context. Until now all -commands have been executed in design context. The {\tt cd} command can be used -to switch to module context. - -\bigskip -In module context all commands only effect the active module. Objects in the module -are selected without the {\tt /} prefix. For example: - -\bigskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -cd foo # switch to module foo -delete bar # delete object foo/bar - -cd mycpu # switch to module mycpu -dump reg_* # print details on all objects whose names start with reg_ - -cd .. # switch back to design -\end{lstlisting} - -\bigskip -Note: Most synthesis scripts never switch to module context. But it is a very powerful -tool for interactive design investigation. -\end{frame} - -\subsubsection{Selecting by object property or type} - -\begin{frame}[fragile]{\subsubsecname} -Special patterns can be used to select by object property or type. For example: - -\bigskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -select w:reg_* # select all wires whose names start with reg_ -select a:foobar # select all objects with the attribute foobar set -select a:foobar=42 # select all objects with the attribute foobar set to 42 -select A:blabla # select all modules with the attribute blabla set -select foo/t:$add # select all $add cells from the module foo -\end{lstlisting} - -\bigskip -A complete list of this pattern expressions can be found in the command -reference to the {\tt select} command. -\end{frame} - -\subsubsection{Combining selection} - -\begin{frame}[fragile]{\subsubsecname} -When more than one selection expression is used in one statement, then they are -pushed on a stack. The final elements on the stack are combined into a union: - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -select t:$dff r:WIDTH>1 # all cells of type $dff and/or with a parameter WIDTH > 1 -\end{lstlisting} - -\bigskip -Special \%-commands can be used to combine the elements on the stack: - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -select t:$dff r:WIDTH>1 %i # all cells of type $dff *AND* with a parameter WIDTH > 1 -\end{lstlisting} - -\medskip -\begin{block}{Examples for {\tt \%}-codes (see {\tt help select} for full list)} -{\tt \%u} \dotfill union of top two elements on stack -- pop 2, push 1 \\ -{\tt \%d} \dotfill difference of top two elements on stack -- pop 2, push 1 \\ -{\tt \%i} \dotfill intersection of top two elements on stack -- pop 2, push 1 \\ -{\tt \%n} \dotfill inverse of top element on stack -- pop 1, push 1 \\ -\end{block} -\end{frame} - -\subsubsection{Expanding selections} - -\begin{frame}[fragile]{\subsubsecname} -Selections of cells and wires can be expanded along connections using {\tt \%}-codes -for selecting input cones ({\tt \%ci}), output cones ({\tt \%co}), or both ({\tt \%x}). - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -# select all wires that are inputs to $add cells -select t:$add %ci w:* %i -\end{lstlisting} - -\bigskip -Additional constraints such as port names can be specified. - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -# select all wires that connect a "Q" output with a "D" input -select c:* %co:+[Q] w:* %i c:* %ci:+[D] w:* %i %i - -# select the multiplexer tree that drives the signal 'state' -select state %ci*:+$mux,$pmux[A,B,Y] -\end{lstlisting} - -\bigskip -See {\tt help select} for full documentation of this expressions. -\end{frame} - -\subsubsection{Incremental selection} - -\begin{frame}[fragile]{\subsubsecname} -Sometimes a selection can most easily be described by a series of add/delete operations. -The commands {\tt select -add} and {\tt select -del} respectively add or remove objects -from the current selection instead of overwriting it. - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -select -none # start with an empty selection -select -add reg_* # select a bunch of objects -select -del reg_42 # but not this one -select -add state %ci # and add mor stuff -\end{lstlisting} - -\bigskip -Within a select expression the token {\tt \%} can be used to push the previous selection -on the stack. - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -select t:$add t:$sub # select all $add and $sub cells -select % %ci % %d # select only the input wires to those cells -\end{lstlisting} -\end{frame} - -\subsubsection{Creating selection variables} - -\begin{frame}[fragile]{\subsubsecname} -Selections can be stored under a name with the {\tt select -set } -command. The stored selections can be used in later select expressions -using the syntax {\tt @}. - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -select -set cone_a state_a %ci*:-$dff # set @cone_a to the input cone of state_a -select -set cone_b state_b %ci*:-$dff # set @cone_b to the input cone of state_b -select @cone_a @cone_b %i # select the objects that are in both cones -\end{lstlisting} - -\bigskip -Remember that select expressions can also be used directly as arguments to most -commands. Some commands also except a single select argument to some options. -In those cases selection variables must be used to capture more complex selections. - -\medskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -dump @cone_a @cone_b - -select -set cone_ab @cone_a @cone_b %i -show -color red @cone_ab -color magenta @cone_a -color blue @cone_b -\end{lstlisting} -\end{frame} - -\begin{frame}[fragile]{\subsubsecname{} -- Example} -\begin{columns} -\column[t]{4cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/select.v} -\column[t]{7cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExAdv/select.ys} -\end{columns} -\hfil\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/select.pdf} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Advanced uses of techmap} - -\begin{frame} -\subsectionpage -\subsectionpagesuffix -\end{frame} - -\subsubsection{Introduction to techmap} - -\begin{frame}{\subsubsecname} -\begin{itemize} -\item -The {\tt techmap} command replaces cells in the design with implementations given -as Verilog code (called ``map files''). It can replace Yosys' internal cell -types (such as {\tt \$or}) as well as user-defined cell types. -\medskip\item -Verilog parameters are used extensively to customize the internal cell types. -\medskip\item -Additional special parameters are used by techmap to communicate meta-data to the -map files. -\medskip\item -Special wires are used to instruct techmap how to handle a module in the map file. -\medskip\item -Generate blocks and recursion are powerful tools for writing map files. -\end{itemize} -\end{frame} - -\begin{frame}[t]{\subsubsecname{} -- Example 1/2} -\vskip-0.2cm -To map the Verilog OR-reduction operator to 3-input OR gates: -\vskip-0.2cm -\begin{columns} -\column[t]{0.35\linewidth} -\lstinputlisting[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, lastline=24]{PRESENTATION_ExAdv/red_or3x1_map.v} -\column[t]{0.65\linewidth} -\lstinputlisting[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=25]{PRESENTATION_ExAdv/red_or3x1_map.v} -\end{columns} -\end{frame} - -\begin{frame}[t]{\subsubsecname{} -- Example 2/2} -\vbox to 0cm{ -\hfil\includegraphics[width=10cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/red_or3x1.pdf} -\vss -} -\begin{columns} -\column[t]{6cm} -\column[t]{4cm} -\vskip-0.6cm\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, firstline=4, lastline=4, frame=single]{PRESENTATION_ExAdv/red_or3x1_test.ys} -\vskip-0.2cm\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/red_or3x1_test.v} -\end{columns} -\end{frame} - -\subsubsection{Conditional techmap} - -\begin{frame}{\subsubsecname} -\begin{itemize} -\item In some cases only cells with certain properties should be substituted. -\medskip -\item The special wire {\tt \_TECHMAP\_FAIL\_} can be used to disable a module -in the map file for a certain set of parameters. -\medskip -\item The wire {\tt \_TECHMAP\_FAIL\_} must be set to a constant value. If it -is non-zero then the module is disabled for this set of parameters. -\medskip -\item Example use-cases: -\begin{itemize} -\item coarse-grain cell types that only operate on certain bit widths -\item memory resources for different memory geometries (width, depth, ports, etc.) -\end{itemize} -\end{itemize} -\end{frame} - -\begin{frame}[t]{\subsubsecname{} -- Example} -\vbox to 0cm{ -\vskip-0.5cm -\hfill\includegraphics[width=6cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/sym_mul.pdf} -\vss -} -\vskip-0.5cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/sym_mul_map.v} -\begin{columns} -\column[t]{6cm} -\vskip-0.5cm\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/sym_mul_test.v} -\column[t]{4cm} -\vskip-0.5cm\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys, lastline=4]{PRESENTATION_ExAdv/sym_mul_test.ys} -\end{columns} -\end{frame} - -\subsubsection{Scripting in map modules} - -\begin{frame}{\subsubsecname} -\begin{itemize} -\item The special wires {\tt \_TECHMAP\_DO\_*} can be used to run Yosys scripts -in the context of the replacement module. -\medskip -\item The wire that comes first in alphabetical oder is interpreted as string (must -be connected to constants) that is executed as script. Then the wire is removed. Repeat. -\medskip -\item You can even call techmap recursively! -\medskip -\item Example use-cases: -\begin{itemize} -\item Using always blocks in map module: call {\tt proc} -\item Perform expensive optimizations (such as {\tt freduce}) on cells where -this is known to work well. -\item Interacting with custom commands. -\end{itemize} -\end{itemize} - -\scriptsize -PROTIP: Commands such as {\tt shell}, {\tt show -pause}, and {\tt dump} can be use -in the {\tt \_TECHMAP\_DO\_*} scripts for debugging map modules. -\end{frame} - -\begin{frame}[t]{\subsubsecname{} -- Example} -\vbox to 0cm{ -\vskip4.2cm -\hskip0.5cm\includegraphics[width=10cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/mymul.pdf} -\vss -} -\vskip-0.6cm -\begin{columns} -\column[t]{6cm} -\vskip-0.6cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/mymul_map.v} -\column[t]{4.2cm} -\vskip-0.6cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/mymul_test.v} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys, lastline=5]{PRESENTATION_ExAdv/mymul_test.ys} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, frame=single, language=ys, firstline=7, lastline=12]{PRESENTATION_ExAdv/mymul_test.ys} -\end{columns} -\end{frame} - -\subsubsection{Handling constant inputs} - -\begin{frame}{\subsubsecname} -\begin{itemize} -\item The special parameters {\tt \_TECHMAP\_CONSTMSK\_\it \tt \_} and -{\tt \_TECHMAP\_CONSTVAL\_\it \tt \_} can be used to handle constant -input values to cells. -\medskip -\item The former contains 1-bits for all constant input bits on the port. -\medskip -\item The latter contains the constant bits or undef (x) for non-constant bits. -\medskip -\item Example use-cases: -\begin{itemize} -\item Converting arithmetic (for example multiply to shift) -\item Identify constant addresses or enable bits in memory interfaces. -\end{itemize} -\end{itemize} -\end{frame} - -\begin{frame}[t]{\subsubsecname{} -- Example} -\vbox to 0cm{ -\vskip5.2cm -\hskip6.5cm\includegraphics[width=5cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/mulshift.pdf} -\vss -} -\vskip-0.6cm -\begin{columns} -\column[t]{6cm} -\vskip-0.4cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/mulshift_map.v} -\column[t]{4.2cm} -\vskip-0.6cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/mulshift_test.v} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys, lastline=5]{PRESENTATION_ExAdv/mulshift_test.ys} -\end{columns} -\end{frame} - -\subsubsection{Handling shorted inputs} - -\begin{frame}{\subsubsecname} -\begin{itemize} -\item The special parameters {\tt \_TECHMAP\_BITS\_CONNMAP\_} and -{\tt \_TECHMAP\_CONNMAP\_\it \tt \_} can be used to handle shorted inputs. -\medskip -\item Each bit of the port correlates to an {\tt \_TECHMAP\_BITS\_CONNMAP\_} bits wide -number in {\tt \_TECHMAP\_CONNMAP\_\it \tt \_}. -\medskip -\item Each unique signal bit is assigned its own number. Identical fields in the {\tt -\_TECHMAP\_CONNMAP\_\it \tt \_} parameters mean shorted signal bits. -\medskip -\item The numbers 0-3 are reserved for {\tt 0}, {\tt 1}, {\tt x}, and {\tt z} respectively. -\medskip -\item Example use-cases: -\begin{itemize} -\item Detecting shared clock or control signals in memory interfaces. -\item In some cases this can be used for for optimization. -\end{itemize} -\end{itemize} -\end{frame} - -\begin{frame}[t]{\subsubsecname{} -- Example} -\vbox to 0cm{ -\vskip4.5cm -\hskip6.5cm\includegraphics[width=5cm,trim=0 0cm 0 0cm]{PRESENTATION_ExAdv/addshift.pdf} -\vss -} -\vskip-0.6cm -\begin{columns} -\column[t]{6cm} -\vskip-0.4cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/addshift_map.v} -\column[t]{4.2cm} -\vskip-0.6cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/addshift_test.v} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys, lastline=5]{PRESENTATION_ExAdv/addshift_test.ys} -\end{columns} -\end{frame} - -\subsubsection{Notes on using techmap} - -\begin{frame}{\subsubsecname} -\begin{itemize} -\item Don't use positional cell parameters in map modules. -\medskip -\item Don't try to implement basic logic optimization with techmap. \\ -{\small (So the OR-reduce using OR3X1 cells map was actually a bad example.)} -\medskip -\item You can use the {\tt \$\_\,\_}-prefix for internal cell types to avoid -collisions with the user-namespace. But always use two underscores or the -internal consistency checker will trigger on this cells. -\medskip -\item Techmap has two major use cases: -\begin{itemize} -\item Creating good logic-level representation of arithmetic functions. \\ -This also means using dedicated hardware resources such as half- and full-adder -cells in ASICS or dedicated carry logic in FPGAs. -\smallskip -\item Mapping of coarse-grain resources such as block memory or DSP cells. -\end{itemize} -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Coarse-grain synthesis} - -\begin{frame} -\subsectionpage -\subsectionpagesuffix -\end{frame} - -\subsubsection{Intro to coarse-grain synthesis} - -\begin{frame}[fragile]{\subsubsecname} -In coarse-grain synthesis the target architecture has cells of the same -complexity or larger complexity than the internal RTL representation. - -For example: -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog] - wire [15:0] a, b; - wire [31:0] c, y; - assign y = a * b + c; -\end{lstlisting} - -This circuit contains two cells in the RTL representation: one multiplier and -one adder. In some architectures this circuit can be implemented using -a single circuit element, for example an FPGA DSP core. Coarse grain synthesis -is this mapping of groups of circuit elements to larger components. - -\bigskip -Fine-grain synthesis would be matching the circuit elements to smaller -components, such as LUTs, gates, or half- and full-adders. -\end{frame} - -\subsubsection{The extract pass} - -\begin{frame}{\subsubsecname} -\begin{itemize} -\item Like the {\tt techmap} pass, the {\tt extract} pass is called with -a map file. It compares the circuits inside the modules of the map file -with the design and looks for sub-circuits in the design that match any -of the modules in the map file. -\bigskip -\item If a match is found, the {\tt extract} pass will replace the matching -subcircuit with an instance of the module from the map file. -\bigskip -\item In a way the {\tt extract} pass is the inverse of the techmap pass. -\end{itemize} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- Example 1/2} -\vbox to 0cm{ -\vskip2cm -\begin{tikzpicture} - \node at (0,0) {\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_00a.pdf}}; - \node at (3,-3) {\includegraphics[width=8cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_00b.pdf}}; - \draw[yshift=0.2cm,thick,-latex] (1,-1) -- (2,-2); -\end{tikzpicture} -\vss} -\vskip-1.2cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/macc_simple_test.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExAdv/macc_simple_xmap.v} -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=ys] -read_verilog macc_simple_test.v -hierarchy -check -top test - -extract -map macc_simple_xmap.v;; -\end{lstlisting} -\end{columns} -\end{frame} - -\begin{frame}[fragile]{\subsubsecname{} -- Example 2/2} -\hfil\begin{tabular}{cc} -\fbox{\hbox to 5cm {\lstinputlisting[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/macc_simple_test_01.v}}} & -\fbox{\hbox to 5cm {\lstinputlisting[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExAdv/macc_simple_test_02.v}}} \\ -$\downarrow$ & $\downarrow$ \\ -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_01a.pdf}} & -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_02a.pdf}} \\ -$\downarrow$ & $\downarrow$ \\ -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_01b.pdf}} & -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_simple_test_02b.pdf}} \\ -\end{tabular} -\end{frame} - -\subsubsection{The wrap-extract-unwrap method} - -\begin{frame}{\subsubsecname} -\scriptsize -Often a coarse-grain element has a constant bit-width, but can be used to -implement operations with a smaller bit-width. For example, a 18x25-bit multiplier -can also be used to implement 16x20-bit multiplication. - -\bigskip -A way of mapping such elements in coarse grain synthesis is the wrap-extract-unwrap method: - -\begin{itemize} -\item {\bf wrap} \\ -Identify candidate-cells in the circuit and wrap them in a cell with a constant -wider bit-width using {\tt techmap}. The wrappers use the same parameters as the original cell, so -the information about the original width of the ports is preserved. \\ -Then use the {\tt connwrappers} command to connect up the bit-extended in- and -outputs of the wrapper cells. -\item {\bf extract} \\ -Now all operations are encoded using the same bit-width as the coarse grain element. The {\tt -extract} command can be used to replace circuits with cells of the target architecture. -\item {\bf unwrap} \\ -The remaining wrapper cell can be unwrapped using {\tt techmap}. -\end{itemize} - -\bigskip -The following sides detail an example that shows how to map MACC operations of -arbitrary size to MACC cells with a 18x25-bit multiplier and a 48-bit adder (such as -the Xilinx DSP48 cells). -\end{frame} - -\subsubsection{Example: DSP48\_MACC} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 1/13} -Preconditioning: {\tt macc\_xilinx\_swap\_map.v} \\ -Make sure {\tt A} is the smaller port on all multipliers - -\begin{columns} -\column{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, lastline=15]{PRESENTATION_ExAdv/macc_xilinx_swap_map.v} -\column{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=16]{PRESENTATION_ExAdv/macc_xilinx_swap_map.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 2/13} -Wrapping multipliers: {\tt macc\_xilinx\_wrap\_map.v} - -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, lastline=23]{PRESENTATION_ExAdv/macc_xilinx_wrap_map.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=24, lastline=46]{PRESENTATION_ExAdv/macc_xilinx_wrap_map.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 3/13} -Wrapping adders: {\tt macc\_xilinx\_wrap\_map.v} - -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=48, lastline=67]{PRESENTATION_ExAdv/macc_xilinx_wrap_map.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=68, lastline=89]{PRESENTATION_ExAdv/macc_xilinx_wrap_map.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 4/13} -Extract: {\tt macc\_xilinx\_xmap.v} - -\lstinputlisting[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=1, lastline=17]{PRESENTATION_ExAdv/macc_xilinx_xmap.v} - -.. simply use the same wrapping commands on this module as on the design to create a template for the {\tt extract} command. -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 5/13} -Unwrapping multipliers: {\tt macc\_xilinx\_unwrap\_map.v} - -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=1, lastline=17]{PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=18, lastline=30]{PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 6/13} -Unwrapping adders: {\tt macc\_xilinx\_unwrap\_map.v} - -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=32, lastline=48]{PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{7pt}{8pt}\selectfont, language=verilog, firstline=49, lastline=61]{PRESENTATION_ExAdv/macc_xilinx_unwrap_map.v} -\end{columns} -\end{frame} - -\begin{frame}[fragile]{\subsubsecname{} -- 7/13} -\hfil\begin{tabular}{cc} -{\tt test1} & {\tt test2} \\ -\fbox{\hbox to 5cm {\lstinputlisting[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, firstline=1, lastline=6, language=verilog]{PRESENTATION_ExAdv/macc_xilinx_test.v}}} & -\fbox{\hbox to 5cm {\lstinputlisting[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, firstline=8, lastline=13, language=verilog]{PRESENTATION_ExAdv/macc_xilinx_test.v}}} \\ -$\downarrow$ & $\downarrow$ \\ -\end{tabular} -\vskip-0.5cm -\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - read_verilog macc_xilinx_test.v - hierarchy -check -\end{lstlisting} -\vskip-0.5cm -\hfil\begin{tabular}{cc} -$\downarrow$ & $\downarrow$ \\ -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1a.pdf}} & -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2a.pdf}} \\ -\end{tabular} -\end{frame} - -\begin{frame}[fragile]{\subsubsecname{} -- 8/13} -\hfil\begin{tabular}{cc} -{\tt test1} & {\tt test2} \\ -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1a.pdf}} & -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2a.pdf}} \\ -$\downarrow$ & $\downarrow$ \\ -\end{tabular} -\vskip-0.2cm -\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - techmap -map macc_xilinx_swap_map.v ;; -\end{lstlisting} -\vskip-0.2cm -\hfil\begin{tabular}{cc} -$\downarrow$ & $\downarrow$ \\ -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1b.pdf}} & -\fbox{\includegraphics[width=5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2b.pdf}} \\ -\end{tabular} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 9/13} -Wrapping in {\tt test1}: -\begin{columns} -\column[t]{5cm} -\vbox to 0cm{\fbox{\includegraphics[width=4.5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1b.pdf}}\vss} -\column[t]{6cm} -\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -techmap -map macc_xilinx_wrap_map.v - -connwrappers -unsigned $__mul_wrapper \ - Y Y_WIDTH \ - -unsigned $__add_wrapper \ - Y Y_WIDTH ;; -\end{lstlisting} -\end{columns} - -\vskip1cm -\hfil\includegraphics[width=\linewidth,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1c.pdf} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 10/13} -Wrapping in {\tt test2}: -\begin{columns} -\column[t]{5cm} -\vbox to 0cm{\fbox{\includegraphics[width=4.5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2b.pdf}}\vss} -\column[t]{6cm} -\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -techmap -map macc_xilinx_wrap_map.v - -connwrappers -unsigned $__mul_wrapper \ - Y Y_WIDTH \ - -unsigned $__add_wrapper \ - Y Y_WIDTH ;; -\end{lstlisting} -\end{columns} - -\vskip1cm -\hfil\includegraphics[width=\linewidth,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2c.pdf} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 11/13} -Extract in {\tt test1}: -\begin{columns} -\column[t]{4.5cm} -\vbox to 0cm{ -\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -design -push -read_verilog macc_xilinx_xmap.v -techmap -map macc_xilinx_swap_map.v -techmap -map macc_xilinx_wrap_map.v;; -design -save __macc_xilinx_xmap -design -pop -\end{lstlisting} -\vss} -\column[t]{5.5cm} -\vskip-1cm -\begin{lstlisting}[linewidth=5.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -extract -constports -ignore_parameters \ - -map %__macc_xilinx_xmap \ - -swap $__add_wrapper A,B ;; -\end{lstlisting} -\vbox to 0cm{\fbox{\includegraphics[width=4.5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1c.pdf}}\vss} -\end{columns} - -\vskip2cm -\hfil\includegraphics[width=11cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test1d.pdf} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 12/13} -Extract in {\tt test2}: -\begin{columns} -\column[t]{4.5cm} -\vbox to 0cm{ -\begin{lstlisting}[linewidth=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -design -push -read_verilog macc_xilinx_xmap.v -techmap -map macc_xilinx_swap_map.v -techmap -map macc_xilinx_wrap_map.v;; -design -save __macc_xilinx_xmap -design -pop -\end{lstlisting} -\vss} -\column[t]{5.5cm} -\vskip-1cm -\begin{lstlisting}[linewidth=5.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -extract -constports -ignore_parameters \ - -map %__macc_xilinx_xmap \ - -swap $__add_wrapper A,B ;; -\end{lstlisting} -\vbox to 0cm{\fbox{\includegraphics[width=4.5cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2c.pdf}}\vss} -\end{columns} - -\vskip2cm -\hfil\includegraphics[width=11cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2d.pdf} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname{} -- 13/13} -Unwrap in {\tt test2}: - -\hfil\begin{tikzpicture} -\node at (0,0) {\includegraphics[width=11cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2d.pdf}}; -\node at (0,-4) {\includegraphics[width=8cm,trim=1.5cm 1.5cm 1.5cm 1.5cm]{PRESENTATION_ExAdv/macc_xilinx_test2e.pdf}}; -\node at (1,-1.7) {\begin{lstlisting}[linewidth=5.5cm, frame=single, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -techmap -map macc_xilinx_unwrap_map.v ;; -\end{lstlisting}}; -\draw[-latex] (4,-0.7) .. controls (5,-1.7) .. (4,-2.7); -\end{tikzpicture} -\end{frame} - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Automatic design changes} - -\begin{frame} -\subsectionpage -\subsectionpagesuffix -\end{frame} - -\subsubsection{Changing the design from Yosys} - -\begin{frame}{\subsubsecname} -Yosys commands can be used to change the design in memory. Examples of this are: - -\begin{itemize} -\item {\bf Changes in design hierarchy} \\ -Commands such as {\tt flatten} and {\tt submod} can be used to change the design hierarchy, i.e. -flatten the hierarchy or moving parts of a module to a submodule. This has applications in synthesis -scripts as well as in reverse engineering and analysis. - -\item {\bf Behavioral changes} \\ -Commands such as {\tt techmap} can be used to make behavioral changes to the design, for example -changing asynchronous resets to synchronous resets. This has applications in design space exploration -(evaluation of various architectures for one circuit). -\end{itemize} -\end{frame} - -\subsubsection{Example: Async reset to sync reset} - -\begin{frame}[t, fragile]{\subsubsecname} -The following techmap map file replaces all positive-edge async reset flip-flops with -positive-edge sync reset flip-flops. The code is taken from the example Yosys script -for ASIC synthesis of the Amber ARMv2 CPU. - -\begin{columns} -\column[t]{6cm} -\vbox to 0cm{ -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog] -(* techmap_celltype = "$adff" *) -module adff2dff (CLK, ARST, D, Q); - - parameter WIDTH = 1; - parameter CLK_POLARITY = 1; - parameter ARST_POLARITY = 1; - parameter ARST_VALUE = 0; - - input CLK, ARST; - input [WIDTH-1:0] D; - output reg [WIDTH-1:0] Q; - - wire [1023:0] _TECHMAP_DO_ = "proc"; - - wire _TECHMAP_FAIL_ = !CLK_POLARITY || !ARST_POLARITY; -\end{lstlisting} -\vss} -\column[t]{4cm} -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog] -// ..continued.. - - - always @(posedge CLK) - if (ARST) - Q <= ARST_VALUE; - else - <= D; - -endmodule -\end{lstlisting} -\end{columns} - -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Summary} - -\begin{frame}{\subsecname} -\begin{itemize} -\item A lot can be achieved in Yosys just with the standard set of commands. -\item The commands {\tt techmap} and {\tt extract} can be used to prototype many complex synthesis tasks. -\end{itemize} - -\bigskip -\bigskip -\begin{center} -Questions? -\end{center} - -\bigskip -\bigskip -\begin{center} -\url{http://www.clifford.at/yosys/} -\end{center} -\end{frame} - diff --git a/manual/PRESENTATION_ExAdv/.gitignore b/manual/PRESENTATION_ExAdv/.gitignore deleted file mode 100644 index cf658897d52..00000000000 --- a/manual/PRESENTATION_ExAdv/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.dot diff --git a/manual/PRESENTATION_ExAdv/Makefile b/manual/PRESENTATION_ExAdv/Makefile deleted file mode 100644 index 993a9d9e10c..00000000000 --- a/manual/PRESENTATION_ExAdv/Makefile +++ /dev/null @@ -1,28 +0,0 @@ - -all: select.pdf red_or3x1.pdf sym_mul.pdf mymul.pdf mulshift.pdf addshift.pdf \ - macc_simple_xmap.pdf macc_xilinx_xmap.pdf - -select.pdf: select.v select.ys - ../../yosys select.ys - -red_or3x1.pdf: red_or3x1_* - ../../yosys red_or3x1_test.ys - -sym_mul.pdf: sym_mul_* - ../../yosys sym_mul_test.ys - -mymul.pdf: mymul_* - ../../yosys mymul_test.ys - -mulshift.pdf: mulshift_* - ../../yosys mulshift_test.ys - -addshift.pdf: addshift_* - ../../yosys addshift_test.ys - -macc_simple_xmap.pdf: macc_simple_*.v macc_simple_test.ys - ../../yosys macc_simple_test.ys - -macc_xilinx_xmap.pdf: macc_xilinx_*.v macc_xilinx_test.ys - ../../yosys macc_xilinx_test.ys - diff --git a/manual/PRESENTATION_ExAdv/macc_xilinx_test.ys b/manual/PRESENTATION_ExAdv/macc_xilinx_test.ys deleted file mode 100644 index f3e8af4f046..00000000000 --- a/manual/PRESENTATION_ExAdv/macc_xilinx_test.ys +++ /dev/null @@ -1,43 +0,0 @@ -read_verilog macc_xilinx_test.v -read_verilog -lib -icells macc_xilinx_unwrap_map.v -read_verilog -lib -icells macc_xilinx_xmap.v -hierarchy -check ;; - -show -prefix macc_xilinx_test1a -format pdf -notitle test1 -show -prefix macc_xilinx_test2a -format pdf -notitle test2 - -techmap -map macc_xilinx_swap_map.v;; - -show -prefix macc_xilinx_test1b -format pdf -notitle test1 -show -prefix macc_xilinx_test2b -format pdf -notitle test2 - -techmap -map macc_xilinx_wrap_map.v - -connwrappers -unsigned $__mul_wrapper Y Y_WIDTH \ - -unsigned $__add_wrapper Y Y_WIDTH;; - -show -prefix macc_xilinx_test1c -format pdf -notitle test1 -show -prefix macc_xilinx_test2c -format pdf -notitle test2 - -design -push -read_verilog macc_xilinx_xmap.v -techmap -map macc_xilinx_swap_map.v -techmap -map macc_xilinx_wrap_map.v;; -design -save __macc_xilinx_xmap -design -pop - -extract -constports -ignore_parameters \ - -map %__macc_xilinx_xmap \ - -swap $__add_wrapper A,B ;; - -show -prefix macc_xilinx_test1d -format pdf -notitle test1 -show -prefix macc_xilinx_test2d -format pdf -notitle test2 - -techmap -map macc_xilinx_unwrap_map.v;; - -show -prefix macc_xilinx_test1e -format pdf -notitle test1 -show -prefix macc_xilinx_test2e -format pdf -notitle test2 - -design -load __macc_xilinx_xmap -show -prefix macc_xilinx_xmap -format pdf -notitle - diff --git a/manual/PRESENTATION_ExOth.tex b/manual/PRESENTATION_ExOth.tex deleted file mode 100644 index 73f8bea2e5c..00000000000 --- a/manual/PRESENTATION_ExOth.tex +++ /dev/null @@ -1,227 +0,0 @@ - -\section{Yosys by example -- Beyond Synthesis} - -\begin{frame} -\sectionpage -\end{frame} - -\begin{frame}{Overview} -This section contains 2 subsections: -\begin{itemize} -\item Interactive Design Investigation -\item Symbolic Model Checking -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Interactive Design Investigation} - -\begin{frame} -\subsectionpage -\subsectionpagesuffix -\end{frame} - -\begin{frame}{\subsecname} -Yosys can also be used to investigate designs (or netlists created -from other tools). - -\begin{itemize} -\item -The selection mechanism (see slides ``Using Selections''), especially patterns such -as {\tt \%ci} and {\tt \%co}, can be used to figure out how parts of the design -are connected. - -\item -Commands such as {\tt submod}, {\tt expose}, {\tt splice}, \dots can be used -to transform the design into an equivalent design that is easier to analyse. - -\item -Commands such as {\tt eval} and {\tt sat} can be used to investigate the -behavior of the circuit. -\end{itemize} -\end{frame} - -\begin{frame}[t, fragile]{Example: Reorganizing a module} -\begin{columns} -\column[t]{4cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=verilog]{PRESENTATION_ExOth/scrambler.v} -\column[t]{7cm} -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single] -read_verilog scrambler.v - -hierarchy; proc;; - -cd scrambler -submod -name xorshift32 \ - xs %c %ci %D %c %ci:+[D] %D \ - %ci*:-$dff xs %co %ci %d -\end{lstlisting} -\end{columns} - -\hfil\includegraphics[width=11cm,trim=0 0cm 0 1.5cm]{PRESENTATION_ExOth/scrambler_p01.pdf} - -\hfil\includegraphics[width=11cm,trim=0 0cm 0 2cm]{PRESENTATION_ExOth/scrambler_p02.pdf} -\end{frame} - -\begin{frame}[t, fragile]{Example: Analysis of circuit behavior} -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -> read_verilog scrambler.v -> hierarchy; proc;; cd scrambler -> submod -name xorshift32 xs %c %ci %D %c %ci:+[D] %D %ci*:-$dff xs %co %ci %d - -> cd xorshift32 -> rename n2 in -> rename n1 out - -> eval -set in 1 -show out -Eval result: \out = 270369. - -> eval -set in 270369 -show out -Eval result: \out = 67634689. - -> sat -set out 632435482 -Signal Name Dec Hex Bin --------------------- ---------- ---------- ------------------------------------- -\in 745495504 2c6f5bd0 00101100011011110101101111010000 -\out 632435482 25b2331a 00100101101100100011001100011010 -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Symbolic Model Checking} - -\begin{frame} -\subsectionpage -\subsectionpagesuffix -\end{frame} - -\begin{frame}{\subsecname} -Symbolic Model Checking (SMC) is used to formally prove that a circuit has -(or has not) a given property. - -\bigskip -One application is Formal Equivalence Checking: Proving that two circuits -are identical. For example this is a very useful feature when debugging custom -passes in Yosys. - -\bigskip -Other applications include checking if a module conforms to interface -standards. - -\bigskip -The {\tt sat} command in Yosys can be used to perform Symbolic Model Checking. -\end{frame} - -\begin{frame}[t]{Example: Formal Equivalence Checking (1/2)} -Remember the following example? -\vskip1em - -\vbox to 0cm{ -\vskip-0.3cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/techmap_01_map.v} -}\vbox to 0cm{ -\vskip-0.5cm -\lstinputlisting[xleftmargin=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExSyn/techmap_01.v} -\lstinputlisting[xleftmargin=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/techmap_01.ys}} - -\vskip5cm\hskip5cm -Lets see if it is correct.. -\end{frame} - -\begin{frame}[t, fragile]{Example: Formal Equivalence Checking (2/2)} -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single] -# read test design -read_verilog techmap_01.v -hierarchy -top test - -# create two version of the design: test_orig and test_mapped -copy test test_orig -rename test test_mapped - -# apply the techmap only to test_mapped -techmap -map techmap_01_map.v test_mapped - -# create a miter circuit to test equivalence -miter -equiv -make_assert -make_outputs test_orig test_mapped miter -flatten miter - -# run equivalence check -sat -verify -prove-asserts -show-inputs -show-outputs miter -\end{lstlisting} - -\dots -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] -Solving problem with 945 variables and 2505 clauses.. -SAT proof finished - no model found: SUCCESS! -\end{lstlisting} -\end{frame} - -\begin{frame}[t, fragile]{Example: Symbolic Model Checking (1/2)} -\small -The following AXI4 Stream Master has a bug. But the bug is not exposed if the -slave keeps {\tt tready} asserted all the time. (Something a test bench might do.) - -\medskip -Symbolic Model Checking can be used to expose the bug and find a sequence -of values for {\tt tready} that yield the incorrect behavior. - -\vskip-1em -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{5pt}{6pt}\selectfont, language=verilog]{PRESENTATION_ExOth/axis_master.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{5pt}{6pt}\selectfont, language=verilog]{PRESENTATION_ExOth/axis_test.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{Example: Symbolic Model Checking (2/2)} -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single] -read_verilog -sv axis_master.v axis_test.v -hierarchy -top axis_test - -proc; flatten;; -sat -seq 50 -prove-asserts -\end{lstlisting} - -\bigskip -\dots with unmodified {\tt axis\_master.v}: -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] -Solving problem with 159344 variables and 442126 clauses.. -SAT proof finished - model found: FAIL! -\end{lstlisting} - -\bigskip -\dots with fixed {\tt axis\_master.v}: -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] -Solving problem with 159144 variables and 441626 clauses.. -SAT proof finished - no model found: SUCCESS! -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Summary} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Yosys provides useful features beyond synthesis. -\item The commands {\tt sat} and {\tt eval} can be used to analyse the behavior of a circuit. -\item The {\tt sat} command can also be used for symbolic model checking. -\item This can be useful for debugging and testing designs and Yosys extensions alike. -\end{itemize} - -\bigskip -\bigskip -\begin{center} -Questions? -\end{center} - -\bigskip -\bigskip -\begin{center} -\url{http://www.clifford.at/yosys/} -\end{center} -\end{frame} - diff --git a/manual/PRESENTATION_ExOth/.gitignore b/manual/PRESENTATION_ExOth/.gitignore deleted file mode 100644 index cf658897d52..00000000000 --- a/manual/PRESENTATION_ExOth/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.dot diff --git a/manual/PRESENTATION_ExOth/Makefile b/manual/PRESENTATION_ExOth/Makefile deleted file mode 100644 index 4864d8d5203..00000000000 --- a/manual/PRESENTATION_ExOth/Makefile +++ /dev/null @@ -1,16 +0,0 @@ - -all: scrambler_p01.pdf scrambler_p02.pdf equiv.log axis_test.log - -scrambler_p01.pdf: scrambler.ys scrambler.v - ../../yosys scrambler.ys - -scrambler_p02.pdf: scrambler_p01.pdf - -equiv.log: equiv.ys - ../../yosys -l equiv.log_new equiv.ys - mv equiv.log_new equiv.log - -axis_test.log: axis_test.ys axis_master.v axis_test.v - ../../yosys -l axis_test.log_new axis_test.ys - mv axis_test.log_new axis_test.log - diff --git a/manual/PRESENTATION_ExOth/equiv.ys b/manual/PRESENTATION_ExOth/equiv.ys deleted file mode 100644 index 8db0a88a5fb..00000000000 --- a/manual/PRESENTATION_ExOth/equiv.ys +++ /dev/null @@ -1,17 +0,0 @@ -# read test design -read_verilog ../PRESENTATION_ExSyn/techmap_01.v -hierarchy -top test - -# create two version of the design: test_orig and test_mapped -copy test test_orig -rename test test_mapped - -# apply the techmap only to test_mapped -techmap -map ../PRESENTATION_ExSyn/techmap_01_map.v test_mapped - -# create a miter circuit to test equivalence -miter -equiv -make_assert -make_outputs test_orig test_mapped miter -flatten miter - -# run equivalence check -sat -verify -prove-asserts -show-inputs -show-outputs miter diff --git a/manual/PRESENTATION_ExSyn.tex b/manual/PRESENTATION_ExSyn.tex deleted file mode 100644 index 655720ebc64..00000000000 --- a/manual/PRESENTATION_ExSyn.tex +++ /dev/null @@ -1,515 +0,0 @@ - -\section{Yosys by example -- Synthesis} - -\begin{frame} -\sectionpage -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Typical Phases of a Synthesis Flow} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Reading and elaborating the design -\item Higher-level synthesis and optimization -\begin{itemize} -\item Converting {\tt always}-blocks to logic and registers -\item Perform coarse-grain optimizations (resource sharing, const folding, ...) -\item Handling of memories and other coarse-grain blocks -\item Extracting and optimizing finite state machines -\end{itemize} -\item Convert remaining logic to bit-level logic functions -\item Perform optimizations on bit-level logic functions -\item Map bit-level logic gates and registers to cell library -\item Write results to output file -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Reading the design} - -\begin{frame}[fragile]{\subsecname} -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -read_verilog file1.v -read_verilog -I include_dir -D enable_foo -D WIDTH=12 file2.v -read_verilog -lib cell_library.v - -verilog_defaults -add -I include_dir -read_verilog file3.v -read_verilog file4.v -verilog_defaults -clear - -verilog_defaults -push -verilog_defaults -add -I include_dir -read_verilog file5.v -read_verilog file6.v -verilog_defaults -pop -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Design elaboration} - -\begin{frame}[fragile]{\subsecname} -During design elaboration Yosys figures out how the modules are hierarchically -connected. It also re-runs the AST parts of the Verilog frontend to create -all needed variations of parametric modules. - -\bigskip -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -# simplest form. at least this version should be used after reading all input files -# -hierarchy - -# recommended form. fails if parts of the design hierarchy are missing, removes -# everything that is unreachable from the top module, and marks the top module. -# -hierarchy -check -top top_module -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{The {\tt proc} command} - -\begin{frame}[fragile]{\subsecname} -The Verilog frontend converts {\tt always}-blocks to RTL netlists for the -expressions and ``processes'' for the control- and memory elements. - -\medskip -The {\tt proc} command transforms this ``processes'' to netlists of RTL -multiplexer and register cells. - -\medskip -The {\tt proc} command is actually a macro-command that calls the following -other commands: - -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -proc_clean # remove empty branches and processes -proc_rmdead # remove unreachable branches -proc_init # special handling of "initial" blocks -proc_arst # identify modeling of async resets -proc_mux # convert decision trees to multiplexer networks -proc_dff # extract registers from processes -proc_clean # if all went fine, this should remove all the processes -\end{lstlisting} - -\medskip -Many commands can not operate on modules with ``processes'' in them. Usually -a call to {\tt proc} is the first command in the actual synthesis procedure -after design elaboration. -\end{frame} - -\begin{frame}[fragile]{\subsecname{} -- Example 1/3} -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/proc_01.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/proc_01.ys} -\end{columns} -\hfil\includegraphics[width=8cm,trim=0 0cm 0 0cm]{PRESENTATION_ExSyn/proc_01.pdf} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Example 2/3} -\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -2.5cm]{PRESENTATION_ExSyn/proc_02.pdf}\vss} -\vskip-1cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/proc_02.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/proc_02.ys} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Example 3/3} -\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -1.5cm]{PRESENTATION_ExSyn/proc_03.pdf}\vss} -\vskip-1cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/proc_03.ys} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/proc_03.v} -\end{columns} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{The {\tt opt} command} - -\begin{frame}[fragile]{\subsecname} -The {\tt opt} command implements a series of simple optimizations. It also -is a macro command that calls other commands: - -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -opt_expr # const folding and simple expression rewriting -opt_merge -nomux # merging identical cells - -do - opt_muxtree # remove never-active branches from multiplexer tree - opt_reduce # consolidate trees of boolean ops to reduce functions - opt_merge # merging identical cells - opt_rmdff # remove/simplify registers with constant inputs - opt_clean # remove unused objects (cells, wires) from design - opt_expr # const folding and simple expression rewriting -while [changed design] -\end{lstlisting} - -The command {\tt clean} can be used as alias for {\tt opt\_clean}. And {\tt ;;} -can be used as shortcut for {\tt clean}. For example: - -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -proc; opt; memory; opt_expr;; fsm;; -\end{lstlisting} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Example 1/4} -\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -0.5cm]{PRESENTATION_ExSyn/opt_01.pdf}\vss} -\vskip-1cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/opt_01.ys} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/opt_01.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Example 2/4} -\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm 0cm]{PRESENTATION_ExSyn/opt_02.pdf}\vss} -\vskip-1cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/opt_02.ys} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/opt_02.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Example 3/4} -\vbox to 0cm{\includegraphics[width=\linewidth,trim=0cm 0cm 0cm -2cm]{PRESENTATION_ExSyn/opt_03.pdf}\vss} -\vskip-1cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/opt_03.ys} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/opt_03.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Example 4/4} -\vbox to 0cm{\hskip6cm\includegraphics[width=6cm,trim=0cm 0cm 0cm -3cm]{PRESENTATION_ExSyn/opt_04.pdf}\vss} -\vskip-1cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/opt_04.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/opt_04.ys} -\end{columns} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{When to use {\tt opt} or {\tt clean}} - -\begin{frame}{\subsecname} -Usually it does not hurt to call {\tt opt} after each regular command in the -synthesis script. But it increases the synthesis time, so it is favourable -to only call {\tt opt} when an improvement can be achieved. - -\bigskip -The designs in {\tt yosys-bigsim} are a good playground for experimenting with -the effects of calling {\tt opt} in various places of the flow. - -\bigskip -It generally is a good idea to call {\tt opt} before inherently expensive -commands such as {\tt sat} or {\tt freduce}, as the possible gain is much -higher in this cases as the possible loss. - -\bigskip -The {\tt clean} command on the other hand is very fast and many commands leave -a mess (dangling signal wires, etc). For example, most commands do not remove -any wires or cells. They just change the connections and depend on a later -call to clean to get rid of the now unused objects. So the occasional {\tt ;;} -is a good idea in every synthesis script. -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{The {\tt memory} command} - -\begin{frame}[fragile]{\subsecname} -In the RTL netlist, memory reads and writes are individual cells. This makes -consolidating the number of ports for a memory easier. The {\tt memory} -transforms memories to an implementation. Per default that is logic for address -decoders and registers. It also is a macro command that calls other commands: - -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -# this merges registers into the memory read- and write cells. -memory_dff - -# this collects all read and write cells for a memory and transforms them -# into one multi-port memory cell. -memory_collect - -# this takes the multi-port memory cell and transforms it to address decoder -# logic and registers. This step is skipped if "memory" is called with -nomap. -memory_map -\end{lstlisting} - -\bigskip -Usually it is preferred to use architecture-specific RAM resources for memory. -For example: - -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -memory -nomap; techmap -map my_memory_map.v; memory_map -\end{lstlisting} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Example 1/2} -\vbox to 0cm{\includegraphics[width=0.7\linewidth,trim=0cm 0cm 0cm -10cm]{PRESENTATION_ExSyn/memory_01.pdf}\vss} -\vskip-1cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/memory_01.ys} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/memory_01.v} -\end{columns} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Example 2/2} -\vbox to 0cm{\hfill\includegraphics[width=7.5cm,trim=0cm 0cm 0cm -5cm]{PRESENTATION_ExSyn/memory_02.pdf}\vss} -\vskip-1cm -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{8pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/memory_02.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/memory_02.ys} -\end{columns} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{The {\tt fsm} command} - -\begin{frame}[fragile]{\subsecname{}} -The {\tt fsm} command identifies, extracts, optimizes (re-encodes), and -re-synthesizes finite state machines. It again is a macro that calls -a series of other commands: - -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -fsm_detect # unless got option -nodetect -fsm_extract - -fsm_opt -clean -fsm_opt - -fsm_expand # if got option -expand -clean # if got option -expand -fsm_opt # if got option -expand - -fsm_recode # unless got option -norecode - -fsm_info - -fsm_export # if got option -export -fsm_map # unless got option -nomap -\end{lstlisting} -\end{frame} - -\begin{frame}{\subsecname{} -- details} -Some details on the most important commands from the {\tt fsm\_*} group: - -\bigskip -The {\tt fsm\_detect} command identifies FSM state registers and marks them -with the {\tt (* fsm\_encoding = "auto" *)} attribute, if they do not have the -{\tt fsm\_encoding} set already. Mark registers with {\tt (* fsm\_encoding = -"none" *)} to disable FSM optimization for a register. - -\bigskip -The {\tt fsm\_extract} command replaces the entire FSM (logic and state -registers) with a {\tt \$fsm} cell. - -\bigskip -The commands {\tt fsm\_opt} and {\tt fsm\_recode} can be used to optimize the -FSM. - -\bigskip -Finally the {\tt fsm\_map} command can be used to convert the (optimized) {\tt -\$fsm} cell back to logic and registers. -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{The {\tt techmap} command} - -\begin{frame}[t]{\subsecname} -\vbox to 0cm{\includegraphics[width=12cm,trim=-15cm 0cm 0cm -20cm]{PRESENTATION_ExSyn/techmap_01.pdf}\vss} -\vskip-0.8cm -The {\tt techmap} command replaces cells with implementations given as -verilog source. For example implementing a 32 bit adder using 16 bit adders: - -\vbox to 0cm{ -\vskip-0.3cm -\lstinputlisting[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/techmap_01_map.v} -}\vbox to 0cm{ -\vskip-0.5cm -\lstinputlisting[xleftmargin=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, frame=single, language=verilog]{PRESENTATION_ExSyn/techmap_01.v} -\lstinputlisting[xleftmargin=5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/techmap_01.ys} -} -\end{frame} - -\begin{frame}[t]{\subsecname{} -- stdcell mapping} -When {\tt techmap} is used without a map file, it uses a built-in map file -to map all RTL cell types to a generic library of built-in logic gates and registers. - -\bigskip -\begin{block}{The built-in logic gate types are:} -{\tt \$\_NOT\_ \$\_AND\_ \$\_OR\_ \$\_XOR\_ \$\_MUX\_} -\end{block} - -\bigskip -\begin{block}{The register types are:} -{\tt \$\_SR\_NN\_ \$\_SR\_NP\_ \$\_SR\_PN\_ \$\_SR\_PP\_ \\ -\$\_DFF\_N\_ \$\_DFF\_P\_ \\ -\$\_DFF\_NN0\_ \$\_DFF\_NN1\_ \$\_DFF\_NP0\_ \$\_DFF\_NP1\_ \\ -\$\_DFF\_PN0\_ \$\_DFF\_PN1\_ \$\_DFF\_PP0\_ \$\_DFF\_PP1\_ \\ -\$\_DFFSR\_NNN\_ \$\_DFFSR\_NNP\_ \$\_DFFSR\_NPN\_ \$\_DFFSR\_NPP\_ \\ -\$\_DFFSR\_PNN\_ \$\_DFFSR\_PNP\_ \$\_DFFSR\_PPN\_ \$\_DFFSR\_PPP\_ \\ -\$\_DLATCH\_N\_ \$\_DLATCH\_P\_} -\end{block} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{The {\tt abc} command} - -\begin{frame}{\subsecname} -The {\tt abc} command provides an interface to ABC\footnote[frame]{\url{http://www.eecs.berkeley.edu/~alanmi/abc/}}, -an open source tool for low-level logic synthesis. - -\medskip -The {\tt abc} command processes a netlist of internal gate types and can perform: -\begin{itemize} -\item logic minimization (optimization) -\item mapping of logic to standard cell library (liberty format) -\item mapping of logic to k-LUTs (for FPGA synthesis) -\end{itemize} - -\medskip -Optionally {\tt abc} can process registers from one clock domain and perform -sequential optimization (such as register balancing). - -\medskip -ABC is also controlled using scripts. An ABC script can be specified to use -more advanced ABC features. It is also possible to write the design with -{\tt write\_blif} and load the output file into ABC outside of Yosys. -\end{frame} - -\begin{frame}[fragile]{\subsecname{} -- Example} -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=verilog]{PRESENTATION_ExSyn/abc_01.v} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys, frame=single]{PRESENTATION_ExSyn/abc_01.ys} -\end{columns} -\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_ExSyn/abc_01.pdf} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Other special-purpose mapping commands} - -\begin{frame}{\subsecname} -\begin{block}{\tt dfflibmap} -This command maps the internal register cell types to the register types -described in a liberty file. -\end{block} - -\bigskip -\begin{block}{\tt hilomap} -Some architectures require special driver cells for driving a constant hi or lo -value. This command replaces simple constants with instances of such driver cells. -\end{block} - -\bigskip -\begin{block}{\tt iopadmap} -Top-level input/outputs must usually be implemented using special I/O-pad cells. -This command inserts this cells to the design. -\end{block} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Example Synthesis Script} - -\begin{frame}[fragile]{\subsecname} -\begin{columns} -\column[t]{4cm} -\begin{lstlisting}[basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont, language=ys] -# read and elaborate design -read_verilog cpu_top.v cpu_ctrl.v cpu_regs.v -read_verilog -D WITH_MULT cpu_alu.v -hierarchy -check -top cpu_top - -# high-level synthesis -proc; opt; fsm;; memory -nomap; opt - -# substitute block rams -techmap -map map_rams.v - -# map remaining memories -memory_map - -# low-level synthesis -techmap; opt; flatten;; abc -lut6 -techmap -map map_xl_cells.v - -# add clock buffers -select -set xl_clocks t:FDRE %x:+FDRE[C] t:FDRE %d -iopadmap -inpad BUFGP O:I @xl_clocks - -# add io buffers -select -set xl_nonclocks w:* t:BUFGP %x:+BUFGP[I] %d -iopadmap -outpad OBUF I:O -inpad IBUF O:I @xl_nonclocks - -# write synthesis results -write_edif synth.edif -\end{lstlisting} -\column[t]{6cm} -\vskip1cm -\begin{block}{Teaser / Outlook} -\small\parbox{6cm}{ -The weird {\tt select} expressions at the end of this script are discussed in -the next part (Section 3, ``Advanced Synthesis'') of this presentation.} -\end{block} -\end{columns} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Summary} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Yosys provides commands for each phase of the synthesis. -\item Each command solves a (more or less) simple problem. -\item Complex commands are often only front-ends to simple commands. -\item {\tt proc; opt; fsm; opt; memory; opt; techmap; opt; abc;;} -\end{itemize} - -\bigskip -\bigskip -\begin{center} -Questions? -\end{center} - -\bigskip -\bigskip -\begin{center} -\url{http://www.clifford.at/yosys/} -\end{center} -\end{frame} - diff --git a/manual/PRESENTATION_ExSyn/Makefile b/manual/PRESENTATION_ExSyn/Makefile deleted file mode 100644 index c34eae3ffcf..00000000000 --- a/manual/PRESENTATION_ExSyn/Makefile +++ /dev/null @@ -1,20 +0,0 @@ - -TARGETS += proc_01 proc_02 proc_03 -TARGETS += opt_01 opt_02 opt_03 opt_04 -TARGETS += memory_01 memory_02 -TARGETS += techmap_01 -TARGETS += abc_01 - -all: $(addsuffix .pdf,$(TARGETS)) - -define make_pdf_template -$(1).pdf: $(1)*.v $(1)*.ys - ../../yosys -p 'script $(1).ys; show -notitle -prefix $(1) -format pdf' -endef - -$(foreach trg,$(TARGETS),$(eval $(call make_pdf_template,$(trg)))) - -clean: - rm -f $(addsuffix .pdf,$(TARGETS)) - rm -f $(addsuffix .dot,$(TARGETS)) - diff --git a/manual/PRESENTATION_ExSyn/abc_01.v b/manual/PRESENTATION_ExSyn/abc_01.v deleted file mode 100644 index 3bc6863538d..00000000000 --- a/manual/PRESENTATION_ExSyn/abc_01.v +++ /dev/null @@ -1,10 +0,0 @@ -module test(input clk, a, b, c, - output reg y); - - reg [2:0] q1, q2; - always @(posedge clk) begin - q1 <= { a, b, c }; - q2 <= q1; - y <= ^q2; - end -endmodule diff --git a/manual/PRESENTATION_ExSyn/abc_01.ys b/manual/PRESENTATION_ExSyn/abc_01.ys deleted file mode 100644 index bb0b3780ff3..00000000000 --- a/manual/PRESENTATION_ExSyn/abc_01.ys +++ /dev/null @@ -1,5 +0,0 @@ -read_verilog abc_01.v -read_verilog -lib abc_01_cells.v -hierarchy -check -top test -proc; opt; techmap -abc -dff -liberty abc_01_cells.lib;; diff --git a/manual/PRESENTATION_ExSyn/abc_01_cells.lib b/manual/PRESENTATION_ExSyn/abc_01_cells.lib deleted file mode 100644 index bf6b34788d0..00000000000 --- a/manual/PRESENTATION_ExSyn/abc_01_cells.lib +++ /dev/null @@ -1,54 +0,0 @@ -// test comment -/* test comment */ -library(demo) { - cell(BUF) { - area: 6; - pin(A) { direction: input; } - pin(Y) { direction: output; - function: "A"; } - } - cell(NOT) { - area: 3; - pin(A) { direction: input; } - pin(Y) { direction: output; - function: "A'"; } - } - cell(NAND) { - area: 4; - pin(A) { direction: input; } - pin(B) { direction: input; } - pin(Y) { direction: output; - function: "(A*B)'"; } - } - cell(NOR) { - area: 4; - pin(A) { direction: input; } - pin(B) { direction: input; } - pin(Y) { direction: output; - function: "(A+B)'"; } - } - cell(DFF) { - area: 18; - ff(IQ, IQN) { clocked_on: C; - next_state: D; } - pin(C) { direction: input; - clock: true; } - pin(D) { direction: input; } - pin(Q) { direction: output; - function: "IQ"; } - } - cell(DFFSR) { - area: 18; - ff(IQ, IQN) { clocked_on: C; - next_state: D; - preset: S; - clear: R; } - pin(C) { direction: input; - clock: true; } - pin(D) { direction: input; } - pin(Q) { direction: output; - function: "IQ"; } - pin(S) { direction: input; } - pin(R) { direction: input; } - } -} diff --git a/manual/PRESENTATION_ExSyn/abc_01_cells.v b/manual/PRESENTATION_ExSyn/abc_01_cells.v deleted file mode 100644 index 44409479895..00000000000 --- a/manual/PRESENTATION_ExSyn/abc_01_cells.v +++ /dev/null @@ -1,40 +0,0 @@ - -module BUF(A, Y); -input A; -output Y = A; -endmodule - -module NOT(A, Y); -input A; -output Y = ~A; -endmodule - -module NAND(A, B, Y); -input A, B; -output Y = ~(A & B); -endmodule - -module NOR(A, B, Y); -input A, B; -output Y = ~(A | B); -endmodule - -module DFF(C, D, Q); -input C, D; -output reg Q; -always @(posedge C) - Q <= D; -endmodule - -module DFFSR(C, D, Q, S, R); -input C, D, S, R; -output reg Q; -always @(posedge C, posedge S, posedge R) - if (S) - Q <= 1'b1; - else if (R) - Q <= 1'b0; - else - Q <= D; -endmodule - diff --git a/manual/PRESENTATION_ExSyn/opt_01.v b/manual/PRESENTATION_ExSyn/opt_01.v deleted file mode 100644 index 5d3c1ea49fc..00000000000 --- a/manual/PRESENTATION_ExSyn/opt_01.v +++ /dev/null @@ -1,3 +0,0 @@ -module test(input A, B, output Y); -assign Y = A ? A ? B : 1'b1 : B; -endmodule diff --git a/manual/PRESENTATION_ExSyn/opt_01.ys b/manual/PRESENTATION_ExSyn/opt_01.ys deleted file mode 100644 index 34ed123bee4..00000000000 --- a/manual/PRESENTATION_ExSyn/opt_01.ys +++ /dev/null @@ -1,3 +0,0 @@ -read_verilog opt_01.v -hierarchy -check -top test -opt diff --git a/manual/PRESENTATION_ExSyn/opt_02.v b/manual/PRESENTATION_ExSyn/opt_02.v deleted file mode 100644 index 762fc1a8993..00000000000 --- a/manual/PRESENTATION_ExSyn/opt_02.v +++ /dev/null @@ -1,3 +0,0 @@ -module test(input A, output Y, Z); -assign Y = A == A, Z = A != A; -endmodule diff --git a/manual/PRESENTATION_ExSyn/opt_02.ys b/manual/PRESENTATION_ExSyn/opt_02.ys deleted file mode 100644 index fc92a636ed4..00000000000 --- a/manual/PRESENTATION_ExSyn/opt_02.ys +++ /dev/null @@ -1,3 +0,0 @@ -read_verilog opt_02.v -hierarchy -check -top test -opt diff --git a/manual/PRESENTATION_ExSyn/opt_03.v b/manual/PRESENTATION_ExSyn/opt_03.v deleted file mode 100644 index 134161bb80a..00000000000 --- a/manual/PRESENTATION_ExSyn/opt_03.v +++ /dev/null @@ -1,4 +0,0 @@ -module test(input [3:0] A, B, - output [3:0] Y, Z); -assign Y = A + B, Z = B + A; -endmodule diff --git a/manual/PRESENTATION_ExSyn/opt_03.ys b/manual/PRESENTATION_ExSyn/opt_03.ys deleted file mode 100644 index 282f06ddecc..00000000000 --- a/manual/PRESENTATION_ExSyn/opt_03.ys +++ /dev/null @@ -1,3 +0,0 @@ -read_verilog opt_03.v -hierarchy -check -top test -opt diff --git a/manual/PRESENTATION_ExSyn/opt_04.v b/manual/PRESENTATION_ExSyn/opt_04.v deleted file mode 100644 index 2ed44763953..00000000000 --- a/manual/PRESENTATION_ExSyn/opt_04.v +++ /dev/null @@ -1,19 +0,0 @@ -module test(input CLK, ARST, - output [7:0] Q1, Q2, Q3); - -wire NO_CLK = 0; - -always @(posedge CLK, posedge ARST) - if (ARST) - Q1 <= 42; - -always @(posedge NO_CLK, posedge ARST) - if (ARST) - Q2 <= 42; - else - Q2 <= 23; - -always @(posedge CLK) - Q3 <= 42; - -endmodule diff --git a/manual/PRESENTATION_ExSyn/opt_04.ys b/manual/PRESENTATION_ExSyn/opt_04.ys deleted file mode 100644 index f5ddae29f20..00000000000 --- a/manual/PRESENTATION_ExSyn/opt_04.ys +++ /dev/null @@ -1,3 +0,0 @@ -read_verilog opt_04.v -hierarchy -check -top test -proc; opt diff --git a/manual/PRESENTATION_Intro.tex b/manual/PRESENTATION_Intro.tex deleted file mode 100644 index af561d01b67..00000000000 --- a/manual/PRESENTATION_Intro.tex +++ /dev/null @@ -1,956 +0,0 @@ - -\section{Introduction to Yosys} - -\begin{frame} -\sectionpage -\end{frame} - -\iffalse -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Representations of (digital) Circuits} - -\begin{frame}[t]{\subsecname} -\begin{itemize} - \item Graphical - \begin{itemize} - \item \alert<1>{Schematic Diagram} - \item \alert<2>{Physical Layout} - \end{itemize} - \bigskip - \item Non-graphical - \begin{itemize} - \item \alert<3>{Netlists} - \item \alert<4>{Hardware Description Languages (HDLs)} - \end{itemize} -\end{itemize} -\bigskip -\begin{block}{Definition: -\only<1>{Schematic Diagram}% -\only<2>{Physical Layout}% -\only<3>{Netlists}% -\only<4>{Hardware Description Languages (HDLs)}} -\only<1>{ - Graphical representation of the circuit topology. Circuit elements - are represented by symbols and electrical connections by lines. The geometric - layout is for readability only. -}% -\only<2>{ - The actual physical geometry of the device (PCB or ASIC manufacturing masks). - This is the final product of the design process. -}% -\only<3>{ - A list of circuit elements and a list of connections. This is the raw circuit - topology. -}% -\only<4>{ - Computer languages (like programming languages) that can be used to describe - circuits. HDLs are much more powerful in describing huge circuits than - schematic diagrams. -}% -\end{block} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -\fi - -\subsection{Levels of Abstraction for Digital Circuits} - -\begin{frame}[t]{\subsecname} -\begin{itemize} - \item \alert<1>{System Level} - \item \alert<2>{High Level} - \item \alert<3>{Behavioral Level} - \item \alert<4>{Register-Transfer Level (RTL)} - \item \alert<5>{Logical Gate Level} - \item \alert<6>{Physical Gate Level} - \item \alert<7>{Switch Level} -\end{itemize} -\bigskip -\begin{block}{Definition: -\only<1>{System Level}% -\only<2>{High Level}% -\only<3>{Behavioral Level}% -\only<4>{Register-Transfer Level (RTL)}% -\only<5>{Logical Gate Level}% -\only<6>{Physical Gate Level}% -\only<7>{Switch Level}} -\only<1>{ - Overall view of the circuit. E.g. block-diagrams or instruction-set architecture descriptions. -}% -\only<2>{ - Functional implementation of circuit in high-level programming language (C, C++, SystemC, Matlab, Python, etc.). -}% -\only<3>{ - Cycle-accurate description of circuit in hardware description language (Verilog, VHDL, etc.). -}% -\only<4>{ - List of registers (flip-flops) and logic functions that calculate the next state from the previous one. Usually - a netlist utilizing high-level cells such as adders, multipliers, multiplexer, etc. -}% -\only<5>{ - Netlist of single-bit registers and basic logic gates (such as AND, OR, - NOT, etc.). Popular form: And-Inverter-Graphs (AIGs) with pairs of primary - inputs and outputs for each register bit. -}% -\only<6>{ - Netlist of cells that actually are available on the target architecture - (such as CMOS gates in an ASIC or LUTs in an FPGA). Optimized for - area, power, and/or speed (static timing or number of logic levels). -}% -\only<7>{ - Netlist of individual transistors. -}% -\end{block} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Digital Circuit Synthesis} - -\begin{frame}{\subsecname} - Synthesis Tools (such as Yosys) can transform HDL code to circuits: - - \bigskip - \begin{center} - \begin{tikzpicture}[scale=0.8, every node/.style={transform shape}] - \tikzstyle{lvl} = [draw, fill=MyBlue, rectangle, minimum height=2em, minimum width=15em] - \node[lvl] (sys) {System Level}; - \node[lvl] (hl) [below of=sys] {High Level}; - \node[lvl] (beh) [below of=hl] {Behavioral Level}; - \node[lvl] (rtl) [below of=beh] {Register-Transfer Level (RTL)}; - \node[lvl] (lg) [below of=rtl] {Logical Gate Level}; - \node[lvl] (pg) [below of=lg] {Physical Gate Level}; - \node[lvl] (sw) [below of=pg] {Switch Level}; - - \draw[dotted] (sys.east) -- ++(1,0) coordinate (sysx); - \draw[dotted] (hl.east) -- ++(1,0) coordinate (hlx); - \draw[dotted] (beh.east) -- ++(1,0) coordinate (behx); - \draw[dotted] (rtl.east) -- ++(1,0) coordinate (rtlx); - \draw[dotted] (lg.east) -- ++(1,0) coordinate (lgx); - \draw[dotted] (pg.east) -- ++(1,0) coordinate (pgx); - \draw[dotted] (sw.east) -- ++(1,0) coordinate (swx); - - \draw[gray,|->] (sysx) -- node[right] {System Design} (hlx); - \draw[|->|] (hlx) -- node[right] {High Level Synthesis (HLS)} (behx); - \draw[->|] (behx) -- node[right] {Behavioral Synthesis} (rtlx); - \draw[->|] (rtlx) -- node[right] {RTL Synthesis} (lgx); - \draw[->|] (lgx) -- node[right] {Logic Synthesis} (pgx); - \draw[gray,->|] (pgx) -- node[right] {Cell Library} (swx); - - \draw[dotted] (behx) -- ++(4,0) coordinate (a); - \draw[dotted] (pgx) -- ++(4,0) coordinate (b); - \draw[|->|] (a) -- node[right] {Yosys} (b); - \end{tikzpicture} - \end{center} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{What Yosys can and can't do} - -\begin{frame}{\subsecname} - -Things Yosys can do: -\begin{itemize} -\item Read and process (most of) modern Verilog-2005 code. -\item Perform all kinds of operations on netlist (RTL, Logic, Gate). -\item Perform logic optimizations and gate mapping with ABC\footnote[frame]{\url{http://www.eecs.berkeley.edu/~alanmi/abc/}}. -\end{itemize} - -\bigskip -Things Yosys can't do: -\begin{itemize} -\item Process high-level languages such as C/C++/SystemC. -\item Create physical layouts (place\&route). -\end{itemize} - -\bigskip -A typical flow combines Yosys with with a low-level implementation tool, such -as Qflow\footnote[frame]{\url{http://opencircuitdesign.com/qflow/}} for ASIC designs. - -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Yosys Data- and Control-Flow} - -\begin{frame}{\subsecname} - A (usually short) synthesis script controls Yosys. - - This scripts contain three types of commands: - \begin{itemize} - \item {\bf Frontends}, that read input files (usually Verilog). - \item {\bf Passes}, that perform transformations on the design in memory. - \item {\bf Backends}, that write the design in memory to a file (various formats are available: Verilog, BLIF, EDIF, SPICE, BTOR, \dots). - \end{itemize} - - \bigskip - \begin{center} - \begin{tikzpicture}[scale=0.6, every node/.style={transform shape}] - \path (-1.5,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=orange!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Frontend} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=green!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Pass} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - \draw[fill=orange!10] ($ (cursor) + (1,-3) $) rectangle node[rotate=90] {Backend} ++(1,3) coordinate (cursor); - \draw[-latex] ($ (cursor) + (0,-1.5) $) -- ++(1,0); - - \path (-3,-0.5) coordinate (cursor); - \draw (cursor) -- node[below] {HDL} ++(3,0) coordinate (cursor); - \draw[|-|] (cursor) -- node[below] {Internal Format (RTLIL)} ++(8,0) coordinate (cursor); - \draw (cursor) -- node[below] {Netlist} ++(3,0); - - \path (-3,3.5) coordinate (cursor); - \draw[-] (cursor) -- node[above] {High-Level} ++(3,0) coordinate (cursor); - \draw[-] (cursor) -- ++(8,0) coordinate (cursor); - \draw[->] (cursor) -- node[above] {Low-Level} ++(3,0); - \end{tikzpicture} - \end{center} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Program Components and Data Formats} - -\begin{frame}{\subsecname} - \begin{center} - \begin{tikzpicture}[scale=0.6, every node/.style={transform shape}] - \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=15em] - \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=15em] - \node[process] (vlog) {Verilog Frontend}; - \node[process, dashed, fill=green!5] (vhdl) [right of=vlog] {VHDL Frontend}; - \node[process] (ilang) [right of=vhdl] {Other Frontends}; - \node[data] (ast) [below of=vlog, node distance=5em, xshift=7.5em] {AST}; - \node[process] (astfe) [below of=ast, node distance=5em] {AST Frontend}; - \node[data] (rtlil) [below of=astfe, node distance=5em, xshift=7.5em] {RTLIL}; - \node[process] (pass) [right of=rtlil, node distance=5em, xshift=7.5em] {Passes}; - \node[process] (vlbe) [below of=rtlil, node distance=7em, xshift=-13em] {Verilog Backend}; - \node[process] (ilangbe) [below of=rtlil, node distance=7em, xshift=0em] {RTLIL Backend}; - \node[process, fill=green!5] (otherbe) [below of=rtlil, node distance=7em, xshift=+13em] {Other Backends}; - - \draw[-latex] (vlog) -- (ast); - \draw[-latex] (vhdl) -- (ast); - \draw[-latex] (ast) -- (astfe); - \draw[-latex] (astfe) -- (rtlil); - \draw[-latex] (ilang) -- (rtlil); - \draw[latex-latex] (rtlil) -- (pass); - \draw[-latex] (rtlil) -- (vlbe); - \draw[-latex] (rtlil) -- (ilangbe); - \draw[-latex] (rtlil) -- (otherbe); - \end{tikzpicture} - \end{center} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Example Project} - -\begin{frame}[t]{\subsecname} -The following slides cover an example project. This project contains three files: -\begin{itemize} -\item A simple ASIC synthesis script -\item A digital design written in Verilog -\item A simple CMOS cell library -\end{itemize} -\vfill -Direct link to the files: \\ \footnotesize -\url{https://github.com/cliffordwolf/yosys/tree/master/manual/PRESENTATION_Intro} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\begin{frame}[t]{\subsecname{} -- Synthesis Script} - -\setbeamercolor{alerted text}{fg=white,bg=red} - -\begin{minipage}[t]{6cm} -\tt\scriptsize -{\color{YosysGreen}\# read design}\\ -\boxalert<1>{read\_verilog counter.v}\\ -\boxalert<2>{hierarchy -check -top counter} - -\medskip -{\color{YosysGreen}\# the high-level stuff}\\ -\boxalert<3>{proc}; \boxalert<4>{opt}; \boxalert<5>{fsm}; \boxalert<6>{opt}; \boxalert<7>{memory}; \boxalert<8>{opt} - -\medskip -{\color{YosysGreen}\# mapping to internal cell library}\\ -\boxalert<9>{techmap}; \boxalert<10>{opt} -\end{minipage} -\begin{minipage}[t]{5cm} -\tt\scriptsize -{\color{YosysGreen}\# mapping flip-flops to mycells.lib}\\ -\boxalert<11>{dfflibmap -liberty mycells.lib} - -\medskip -{\color{YosysGreen}\# mapping logic to mycells.lib}\\ -\boxalert<12>{abc -liberty mycells.lib} - -\medskip -{\color{YosysGreen}\# cleanup}\\ -\boxalert<13>{clean} - -\medskip -{\color{YosysGreen}\# write synthesized design}\\ -\boxalert<14>{write\_verilog synth.v} -\end{minipage} - -\vskip1cm - -\begin{block}{Command: \tt -\only<1>{read\_verilog counter.v}% -\only<2>{hierarchy -check -top counter}% -\only<3>{proc}% -\only<4>{opt}% -\only<5>{fsm}% -\only<6>{opt}% -\only<7>{memory}% -\only<8>{opt}% -\only<9>{techmap}% -\only<10>{opt}% -\only<11>{dfflibmap -liberty mycells.lib}% -\only<12>{abc -liberty mycells.lib}% -\only<13>{clean}% -\only<14>{write\_verilog synth.v}} -\only<1>{ - Read Verilog source file and convert to internal representation. -}% -\only<2>{ - Elaborate the design hierarchy. Should always be the first - command after reading the design. Can re-run AST front-end. -}% -\only<3>{ - Convert ``processes'' (the internal representation of behavioral - Verilog code) into multiplexers and registers. -}% -\only<4>{ - Perform some basic optimizations and cleanups. -}% -\only<5>{ - Analyze and optimize finite state machines. -}% -\only<6>{ - Perform some basic optimizations and cleanups. -}% -\only<7>{ - Analyze memories and create circuits to implement them. -}% -\only<8>{ - Perform some basic optimizations and cleanups. -}% -\only<9>{ - Map coarse-grain RTL cells (adders, etc.) to fine-grain - logic gates (AND, OR, NOT, etc.). -}% -\only<10>{ - Perform some basic optimizations and cleanups. -}% -\only<11>{ - Map registers to available hardware flip-flops. -}% -\only<12>{ - Map logic to available hardware gates. -}% -\only<13>{ - Clean up the design (just the last step of {\tt opt}). -}% -\only<14>{ - Write final synthesis result to output file. -}% -\end{block} - -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\begin{frame}[fragile]{\subsecname{} -- Verilog Source: \tt counter.v} -\lstinputlisting[xleftmargin=1cm, language=Verilog]{PRESENTATION_Intro/counter.v} -\end{frame} - -\begin{frame}[fragile]{\subsecname{} -- Cell Library: \tt mycells.lib} -\begin{columns} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=liberty, lastline=20]{PRESENTATION_Intro/mycells.lib} -\column[t]{5cm} -\lstinputlisting[basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=liberty, firstline=21]{PRESENTATION_Intro/mycells.lib} -\end{columns} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Running the Synthesis Script} - -\begin{frame}[t, fragile]{\subsecname{} -- Step 1/4} -\begin{verbatim} -read_verilog counter.v -hierarchy -check -top counter -\end{verbatim} - -\vfill -\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_Intro/counter_00.pdf} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Step 2/4} -\begin{verbatim} -proc; opt; fsm; opt; memory; opt -\end{verbatim} - -\vfill -\includegraphics[width=\linewidth,trim=0 0cm 0 0cm]{PRESENTATION_Intro/counter_01.pdf} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Step 3/4} -\begin{verbatim} -techmap; opt -\end{verbatim} - -\vfill -\includegraphics[width=\linewidth,trim=0 0cm 0 2cm]{PRESENTATION_Intro/counter_02.pdf} -\end{frame} - -\begin{frame}[t, fragile]{\subsecname{} -- Step 4/4} -\begin{verbatim} -dfflibmap -liberty mycells.lib -abc -liberty mycells.lib -clean -\end{verbatim} - -\vfill\hfil -\includegraphics[width=10cm,trim=0 0cm 0 0cm]{PRESENTATION_Intro/counter_03.pdf} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{The synth command} - -\begin{frame}[fragile]{\subsecname{}} -Yosys contains a default (recommended example) synthesis script in form of the -{\tt synth} command. The following commands are executed by this synthesis command: - -\begin{columns} -\column[t]{5cm} -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -begin: - hierarchy -check [-top ] - -coarse: - proc - opt - wreduce - alumacc - share - opt - fsm - opt -fast - memory -nomap - opt_clean -\end{lstlisting} -\column[t]{5cm} -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] -fine: - opt -fast -full - memory_map - opt -full - techmap - opt -fast - -abc: - abc -fast - opt -fast -\end{lstlisting} -\end{columns} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Yosys Commands} - -\begin{frame}[fragile]{\subsecname{} 1/3 \hspace{0pt plus 1 filll} (excerpt)} -Command reference: -\begin{itemize} -\item Use ``{\tt help}'' for a command list and ``{\tt help \it command}'' for details. -\item Or run ``{\tt yosys -H}'' or ``{\tt yosys -h \it command}''. -\item Or go to \url{http://www.clifford.at/yosys/documentation.html}. -\end{itemize} - -\bigskip -Commands for design navigation and investigation: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - cd # a shortcut for 'select -module ' - ls # list modules or objects in modules - dump # print parts of the design in RTLIL format - show # generate schematics using graphviz - select # modify and view the list of selected objects -\end{lstlisting} - -\bigskip -Commands for executing scripts or entering interactive mode: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - shell # enter interactive command mode - history # show last interactive commands - script # execute commands from script file - tcl # execute a TCL script file -\end{lstlisting} -\end{frame} - -\begin{frame}[fragile]{\subsecname{} 2/3 \hspace{0pt plus 1 filll} (excerpt)} -Commands for reading and elaborating the design: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - read_rtlil # read modules from RTLIL file - read_verilog # read modules from Verilog file - hierarchy # check, expand and clean up design hierarchy -\end{lstlisting} - -\bigskip -Commands for high-level synthesis: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - proc # translate processes to netlists - fsm # extract and optimize finite state machines - memory # translate memories to basic cells - opt # perform simple optimizations -\end{lstlisting} - -\bigskip -Commands for technology mapping: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - techmap # generic technology mapper - abc # use ABC for technology mapping - dfflibmap # technology mapping of flip-flops - hilomap # technology mapping of constant hi- and/or lo-drivers - iopadmap # technology mapping of i/o pads (or buffers) - flatten # flatten design -\end{lstlisting} -\end{frame} - -\begin{frame}[fragile]{\subsecname{} 3/3 \hspace{0pt plus 1 filll} (excerpt)} -Commands for writing the results: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - write_blif # write design to BLIF file - write_btor # write design to BTOR file - write_edif # write design to EDIF netlist file - write_rtlil # write design to RTLIL file - write_spice # write design to SPICE netlist file - write_verilog # write design to Verilog file -\end{lstlisting} - -\bigskip -Script-Commands for standard synthesis tasks: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - synth # generic synthesis script - synth_xilinx # synthesis for Xilinx FPGAs -\end{lstlisting} - -\bigskip -Commands for model checking: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=ys] - sat # solve a SAT problem in the circuit - miter # automatically create a miter circuit - scc # detect strongly connected components (logic loops) -\end{lstlisting} - -\bigskip -... and many many more. -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{More Verilog Examples} - -\begin{frame}[fragile]{\subsecname{} 1/3} -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog] -module detectprime(a, y); - input [4:0] a; - output y; - - integer i, j; - reg [31:0] lut; - - initial begin - for (i = 0; i < 32; i = i+1) begin - lut[i] = i > 1; - for (j = 2; j*j <= i; j = j+1) - if (i % j == 0) - lut[i] = 0; - end - end - - assign y = lut[a]; -endmodule -\end{lstlisting} -\end{frame} - -\begin{frame}[fragile]{\subsecname{} 2/3} -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog] -module carryadd(a, b, y); - parameter WIDTH = 8; - input [WIDTH-1:0] a, b; - output [WIDTH-1:0] y; - - genvar i; - generate - for (i = 0; i < WIDTH; i = i+1) begin:STAGE - wire IN1 = a[i], IN2 = b[i]; - wire C, Y; - if (i == 0) - assign C = IN1 & IN2, Y = IN1 ^ IN2; - else - assign C = (IN1 & IN2) | ((IN1 | IN2) & STAGE[i-1].C), - Y = IN1 ^ IN2 ^ STAGE[i-1].C; - assign y[i] = Y; - end - endgenerate -endmodule -\end{lstlisting} -\end{frame} - -\begin{frame}[fragile]{\subsecname{} 3/3} -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{7pt}{8.5pt}\selectfont, language=Verilog] -module cam(clk, wr_enable, wr_addr, wr_data, rd_data, rd_addr, rd_match); - parameter WIDTH = 8; - parameter DEPTH = 16; - localparam ADDR_BITS = $clog2(DEPTH-1); - - input clk, wr_enable; - input [ADDR_BITS-1:0] wr_addr; - input [WIDTH-1:0] wr_data, rd_data; - output reg [ADDR_BITS-1:0] rd_addr; - output reg rd_match; - - integer i; - reg [WIDTH-1:0] mem [0:DEPTH-1]; - - always @(posedge clk) begin - rd_addr <= 'bx; - rd_match <= 0; - for (i = 0; i < DEPTH; i = i+1) - if (mem[i] == rd_data) begin - rd_addr <= i; - rd_match <= 1; - end - if (wr_enable) - mem[wr_addr] <= wr_data; - end -endmodule -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Currently unsupported Verilog-2005 language features} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Tri-state logic -\item The wor/wand wire types (maybe for 0.5) -\item Latched logic (is synthesized as logic with feedback loops) -\item Some non-synthesizable features that should be ignored in synthesis are not supported by the parser and cause a parser error (file a bug report if you encounter this problem) -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Verification of Yosys} - -\begin{frame}{\subsecname} -Continuously checking the correctness of Yosys and making sure that new features -do not break old ones is a high priority in Yosys. - -\bigskip -Two external test suites have been built for Yosys: VlogHammer and yosys-bigsim -(see next slides) - -\bigskip -In addition to that, yosys comes with $\approx\!200$ test cases used in ``{\tt make test}''. - -\bigskip -A debug build of Yosys also contains a lot of asserts and checks the integrity of -the internal state after each command. -\end{frame} - -\begin{frame}[fragile]{\subsecname{} -- VlogHammer} -VlogHammer is a Verilog regression test suite developed to test the different -subsystems in Yosys by comparing them to each other and to the output created -by some other tools (Xilinx Vivado, Xilinx XST, Altera Quartus II, ...). - -\bigskip -Yosys Subsystems tested: Verilog frontend, const folding, const eval, technology mapping, -simulation models, SAT models. - -\bigskip -Thousands of auto-generated test cases containing code such as: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog] -assign y9 = $signed(((+$signed((^(6'd2 ** a2))))<$unsigned($unsigned(((+a3)))))); -assign y10 = (-((+((+{2{(~^p13)}})))^~(!{{b5,b1,a0},(a1&p12),(a4+a3)}))); -assign y11 = (~&(-{(-3'sd3),($unsigned($signed($unsigned({p0,b4,b1}))))})); -\end{lstlisting} - -\bigskip -Some bugs in Yosys where found and fixed thanks to VlogHammer. Over 50 bugs in -the other tools used as external reference where found and reported so far. -\end{frame} - -\begin{frame}{\subsecname{} -- yosys-bigsim} -yosys-bigsim is a collection of real-world open-source Verilog designs and test -benches. yosys-bigsim compares the testbench outputs of simulations of the original -Verilog code and synthesis results. - -\bigskip -The following designs are included in yosys-bigsim (excerpt): -\begin{itemize} -\item {\tt openmsp430} -- an MSP430 compatible 16 bit CPU -\item {\tt aes\_5cycle\_2stage} -- an AES encryption core -\item {\tt softusb\_navre} -- an AVR compatible 8 bit CPU -\item {\tt amber23} -- an ARMv2 compatible 32 bit CPU -\item {\tt lm32} -- another 32 bit CPU from Lattice Semiconductor -\item {\tt verilog-pong} -- a hardware pong game with VGA output -\item {\tt elliptic\_curve\_group} -- ECG point-add and point-scalar-mul core -\item {\tt reed\_solomon\_decoder} -- a Reed-Solomon Error Correction Decoder -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Benefits of Open Source HDL Synthesis} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Cost (also applies to ``free as in free beer'' solutions) -\item Availability and Reproducibility -\item Framework- and all-in-one-aspects -\item Educational Tool -\end{itemize} - -\bigskip - -Yosys is open source under the ISC license. -\end{frame} - -\begin{frame}{\subsecname{} -- 1/3} -\begin{itemize} -\item Cost (also applies to ``free as in free beer'' solutions): \smallskip\par -Today the cost for a mask set in $\unit[180]{nm}$ technology is far less than -the cost for the design tools needed to design the mask layouts. Open Source -ASIC flows are an important enabler for ASIC-level Open Source Hardware. - -\bigskip -\item Availability and Reproducibility: \smallskip\par -If you are a researcher who is publishing, you want to use tools that everyone -else can also use. Even if most universities have access to all major -commercial tools, you usually do not have easy access to the version that was -used in a research project a couple of years ago. With Open Source tools you -can even release the source code of the tool you have used alongside your data. -\end{itemize} -\end{frame} - -\begin{frame}{\subsecname{} -- 2/3} -\begin{itemize} -\item Framework: \smallskip\par -Yosys is not only a tool. It is a framework that can be used as basis for other -developments, so researchers and hackers alike do not need to re-invent the -basic functionality. Extensibility was one of Yosys' design goals. - -\bigskip -\item All-in-one: \smallskip\par -Because of the framework characteristics of Yosys, an increasing number of features -become available in one tool. Yosys not only can be used for circuit synthesis but -also for formal equivalence checking, SAT solving, and for circuit analysis, to -name just a few other application domains. With proprietary software one needs to -learn a new tool for each of these applications. -\end{itemize} -\end{frame} - -\begin{frame}{\subsecname{} -- 3/3} -\begin{itemize} -\item Educational Tool: \smallskip\par -Proprietary synthesis tools are at times very secretive about their inner -workings. They often are ``black boxes''. Yosys is very open about its -internals and it is easy to observe the different steps of synthesis. -\end{itemize} - -\bigskip -\begin{block}{Yosys is licensed under the ISC license:} -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. -\end{block} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Typical Applications for Yosys} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Synthesis of final production designs -\item Pre-production synthesis (trial runs before investing in other tools) -\item Conversion of full-featured Verilog to simple Verilog -\item Conversion of Verilog to other formats (BLIF, BTOR, etc) -\item Demonstrating synthesis algorithms (e.g. for educational purposes) -\item Framework for experimenting with new algorithms -\item Framework for building custom flows\footnote[frame]{Not limited to synthesis -but also formal verification, reverse engineering, ...} -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Projects (that I know of) using Yosys} - -\begin{frame}{\subsecname{} -- (1/2)} -\begin{itemize} -\item Ongoing PhD project on coarse grain synthesis \\ -{\setlength{\parindent}{0.5cm}\footnotesize -Johann Glaser and Clifford Wolf. Methodology and Example-Driven Interconnect -Synthesis for Designing Heterogeneous Coarse-Grain Reconfigurable -Architectures. In Jan Haase, editor, \it Models, Methods, and Tools for Complex -Chip Design. Lecture Notes in Electrical Engineering. Volume 265, 2014, pp -201-221. Springer, 2013.} - -\bigskip -\item I know several people that use Yosys simply as Verilog frontend for other -flows (using either the BLIF and BTOR backends). - -\bigskip -\item I know some analog chip designers that use Yosys for small digital -control logic because it is simpler than setting up a commercial flow. -\end{itemize} -\end{frame} - -\begin{frame}{\subsecname{} -- (2/2)} -\begin{itemize} -\item Efabless -\begin{itemize} -\smallskip \item Not much information on the website (\url{http://efabless.com}) yet. -\smallskip \item Very cheap 180nm prototyping process (partnering with various fabs) -\smallskip \item A semiconductor company, NOT an EDA company -\smallskip \item Web-based design environment -\smallskip \item HDL Synthesis using Yosys -\smallskip \item Custom place\&route tool - -\bigskip -\item efabless is building an Open Source IC as reference design. \\ -\hskip1cm (to be announced soon: \url{http://www.openic.io}) -\end{itemize} -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Supported Platforms} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Main development OS: Kubuntu 14.04 -\item There is a PPA for ubuntu (not maintained by me) -\item Any current Debian-based system should work out of the box -\item When building on other Linux distributions: -\begin{itemize} -\item Needs compiler with some C++11 support -\item See README file for build instructions -\item Post to the subreddit if you get stuck -\end{itemize} -\item Ported to OS X (Darwin) and OpenBSD -\item Native win32 build with VisualStudio -\item Cross win32 build with MXE -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Other Open Source Tools} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Icarus Verilog \\ -\smallskip\hskip1cm{}Verilog Simulation (and also a good syntax checker) \\ -\smallskip\hskip1cm{}\url{http://iverilog.icarus.com/} - -\bigskip -\item Qflow (incl. TimberWolf, qrouter and Magic) \\ -\smallskip\hskip1cm{}A complete ASIC synthesis flow, using Yosys and ABC \\ -\smallskip\hskip1cm{}\url{http://opencircuitdesign.com/qflow/} - -\bigskip -\item ABC \\ -\smallskip\hskip1cm{}Logic optimization, technology mapping, and more \\ -\smallskip\hskip1cm{}\url{http://www.eecs.berkeley.edu/~alanmi/abc/} -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Yosys needs you} - -\begin{frame}{\subsecname} -\dots as an active user: -\begin{itemize} -\item Use Yosys for on your own projects -\item .. even if you are not using it as final synthesis tool -\item Join the discussion on the Subreddit -\item Report bugs and send in feature requests -\end{itemize} - -\bigskip -\dots as a developer: -\begin{itemize} -\item Use Yosys as environment for your (research) work -\item .. you might also want to look into ABC for logic-level stuff -\item Fork the project on github or create loadable plugins -\item We need a VHDL frontend or a good VHDL-to-Verilog converter -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Documentation, Downloads, Contacts} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Website: \\ -\smallskip\hskip1cm\url{http://www.clifford.at/yosys/} - -\bigskip -\item Manual, Command Reference, Application Notes: \\ -\smallskip\hskip1cm\url{http://www.clifford.at/yosys/documentation.html} - -\bigskip -\item Instead of a mailing list we have a SubReddit: \\ -\smallskip\hskip1cm\url{http://www.reddit.com/r/yosys/} - -\bigskip -\item Direct link to the source code: \\ -\smallskip\hskip1cm\url{https://github.com/cliffordwolf/yosys} -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Summary} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Yosys is a powerful tool and framework for Verilog synthesis. -\item It uses a command-based interface and can be controlled by scripts. -\item By combining existing commands and implementing new commands Yosys can -be used in a wide range of application far beyond simple synthesis. -\end{itemize} - -\bigskip -\bigskip -\begin{center} -Questions? -\end{center} - -\bigskip -\bigskip -\begin{center} -\url{http://www.clifford.at/yosys/} -\end{center} -\end{frame} - diff --git a/manual/PRESENTATION_Intro/.gitignore b/manual/PRESENTATION_Intro/.gitignore deleted file mode 100644 index d0c4618ac70..00000000000 --- a/manual/PRESENTATION_Intro/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -counter_00.dot -counter_01.dot -counter_02.dot -counter_03.dot diff --git a/manual/PRESENTATION_Intro/Makefile b/manual/PRESENTATION_Intro/Makefile deleted file mode 100644 index abc354e4691..00000000000 --- a/manual/PRESENTATION_Intro/Makefile +++ /dev/null @@ -1,10 +0,0 @@ - -all: counter_00.pdf counter_01.pdf counter_02.pdf counter_03.pdf - -counter_00.pdf: counter.v counter.ys mycells.lib - ../../yosys counter.ys - -counter_01.pdf: counter_00.pdf -counter_02.pdf: counter_00.pdf -counter_03.pdf: counter_00.pdf - diff --git a/manual/PRESENTATION_Intro/counter.ys b/manual/PRESENTATION_Intro/counter.ys deleted file mode 100644 index cc4e7cd3199..00000000000 --- a/manual/PRESENTATION_Intro/counter.ys +++ /dev/null @@ -1,27 +0,0 @@ -# read design -read_verilog counter.v -hierarchy -check -top counter - -show -notitle -stretch -format pdf -prefix counter_00 - -# the high-level stuff -proc; opt; memory; opt; fsm; opt - -show -notitle -stretch -format pdf -prefix counter_01 - -# mapping to internal cell library -techmap; opt - -splitnets -ports;; -show -notitle -stretch -format pdf -prefix counter_02 - -# mapping flip-flops to mycells.lib -dfflibmap -liberty mycells.lib - -# mapping logic to mycells.lib -abc -liberty mycells.lib - -# cleanup -clean - -show -notitle -stretch -lib mycells.v -format pdf -prefix counter_03 diff --git a/manual/PRESENTATION_Prog.tex b/manual/PRESENTATION_Prog.tex deleted file mode 100644 index 3b61361aff4..00000000000 --- a/manual/PRESENTATION_Prog.tex +++ /dev/null @@ -1,596 +0,0 @@ - -\section{Writing Yosys extensions in C++} - -\begin{frame} -\sectionpage -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Program Components and Data Formats} - -\begin{frame}{\subsecname} -\begin{center} -\begin{tikzpicture}[scale=0.6, every node/.style={transform shape}] - \tikzstyle{process} = [draw, fill=green!10, rectangle, minimum height=3em, minimum width=10em, node distance=15em] - \tikzstyle{data} = [draw, fill=blue!10, ellipse, minimum height=3em, minimum width=7em, node distance=15em] - \node[process] (vlog) {Verilog Frontend}; - \node[process, dashed, fill=green!5] (vhdl) [right of=vlog] {VHDL Frontend}; - \node[process] (ilang) [right of=vhdl] {Other Frontends}; - \node[data] (ast) [below of=vlog, node distance=5em, xshift=7.5em] {AST}; - \node[process] (astfe) [below of=ast, node distance=5em] {AST Frontend}; - \node[data] (rtlil) [below of=astfe, node distance=5em, xshift=7.5em] {RTLIL}; - \node[process] (pass) [right of=rtlil, node distance=5em, xshift=7.5em] {Passes}; - \node[process] (vlbe) [below of=rtlil, node distance=7em, xshift=-13em] {Verilog Backend}; - \node[process] (ilangbe) [below of=rtlil, node distance=7em, xshift=0em] {RTLIL Backend}; - \node[process, fill=green!5] (otherbe) [below of=rtlil, node distance=7em, xshift=+13em] {Other Backends}; - - \draw[-latex] (vlog) -- (ast); - \draw[-latex] (vhdl) -- (ast); - \draw[-latex] (ast) -- (astfe); - \draw[-latex] (astfe) -- (rtlil); - \draw[-latex] (ilang) -- (rtlil); - \draw[latex-latex] (rtlil) -- (pass); - \draw[-latex] (rtlil) -- (vlbe); - \draw[-latex] (rtlil) -- (ilangbe); - \draw[-latex] (rtlil) -- (otherbe); -\end{tikzpicture} -\end{center} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Simplified RTLIL Entity-Relationship Diagram} - -\begin{frame}{\subsecname} -Between passses and frontends/backends the design is stored in Yosys' internal -RTLIL (RTL Intermediate Language) format. For writing Yosys extensions it is -key to understand this format. - -\bigskip -\begin{center} -\begin{tikzpicture}[scale=0.6, every node/.style={transform shape}] - \tikzstyle{entity} = [draw, fill=gray!10, rectangle, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}] - \node[entity] (design) {RTLIL::Design}; - \node[entity] (module) [right of=design, node distance=11em] {RTLIL::Module} edge [-latex] node[above] {\tiny 1 \hskip3em N} (design); - - \node[entity] (process) [fill=green!10, right of=module, node distance=10em] {RTLIL::Process} (process.west) edge [-latex] (module); - \node[entity] (memory) [fill=red!10, below of=process] {RTLIL::Memory} edge [-latex] (module); - \node[entity] (wire) [fill=blue!10, above of=process] {RTLIL::Wire} (wire.west) edge [-latex] (module); - \node[entity] (cell) [fill=blue!10, above of=wire] {RTLIL::Cell} (cell.west) edge [-latex] (module); - - \node[entity] (case) [fill=green!10, right of=process, node distance=10em] {RTLIL::CaseRule} edge [latex-latex] (process); - \node[entity] (sync) [fill=green!10, above of=case] {RTLIL::SyncRule} edge [-latex] (process); - \node[entity] (switch) [fill=green!10, below of=case] {RTLIL::SwitchRule} edge [-latex] (case); - \draw[latex-] (switch.east) -- ++(1em,0) |- (case.east); -\end{tikzpicture} -\end{center} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{RTLIL without memories and processes} - -\begin{frame}[fragile]{\subsecname} -After the commands {\tt proc} and {\tt memory} (or {\tt memory -nomap}), we are -left with a much simpler version of RTLIL: - -\begin{center} -\begin{tikzpicture}[scale=0.6, every node/.style={transform shape}] - \tikzstyle{entity} = [draw, fill=gray!10, rectangle, minimum height=3em, minimum width=7em, node distance=5em, font={\ttfamily}] - \node[entity] (design) {RTLIL::Design}; - \node[entity] (module) [right of=design, node distance=11em] {RTLIL::Module} edge [-latex] node[above] {\tiny 1 \hskip3em N} (design); - - \node[entity] (wire) [fill=blue!10, right of=module, node distance=10em] {RTLIL::Wire} (wire.west) edge [-latex] (module); - \node[entity] (cell) [fill=blue!10, above of=wire] {RTLIL::Cell} (cell.west) edge [-latex] (module); -\end{tikzpicture} -\end{center} - -\bigskip -Many commands simply choose to only work on this simpler version: -\begin{lstlisting}[xleftmargin=0.5cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] -for (RTLIL::Module *module : design->selected_modules() { - if (module->has_memories_warn() || module->has_processes_warn()) - continue; - .... -} -\end{lstlisting} - -For simplicity we only discuss this version of RTLIL in this presentation. -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Using dump and show commands} - -\begin{frame}{\subsecname} -\begin{itemize} -\item The {\tt dump} command prints the design (or parts of it) in the text representation of RTLIL. - -\bigskip -\item The {\tt show} command visualizes how the components in the design are connected. -\end{itemize} - -\bigskip -When trying to understand what a command does, create a small test case and -look at the output of {\tt dump} and {\tt show} before and after the command -has been executed. -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{The RTLIL Data Structures} - -\begin{frame}{\subsecname} -The RTLIL data structures are simple structs utilizing {\tt pool<>} and -{\tt dict<>} containers (drop-in replacements for {\tt -std::unordered\_set<>} and {\tt std::unordered\_map<>}). - -\bigskip -\begin{itemize} -\item Most operations are performed directly on the RTLIL structs without -setter or getter functions. - -\bigskip -\item In debug builds a consistency checker is run over the in-memory design -between commands to make sure that the RTLIL representation is intact. - -\bigskip -\item Most RTLIL structs have helper methods that perform the most common operations. -\end{itemize} - -\bigskip -See {\tt yosys/kernel/rtlil.h} for details. -\end{frame} - -\subsubsection{RTLIL::IdString} - -\begin{frame}{\subsubsecname}{} -{\tt RTLIL::IdString} in many ways behave like a {\tt std::string}. It is used -for names of RTLIL objects. Internally a RTLIL::IdString object is only a -single integer. - -\medskip -The first character of a {\tt RTLIL::IdString} specifies if the name is {\it public\/} or {\it private\/}: - -\medskip -\begin{itemize} -\item {\tt RTLIL::IdString[0] == '\textbackslash\textbackslash'}: \\ -This is a public name. Usually this means it is a name that was declared in a Verilog file. - -\bigskip -\item {\tt RTLIL::IdString[0] == '\$'}: \\ -This is a private name. It was assigned by Yosys. -\end{itemize} - -\bigskip -Use the {\tt NEW\_ID} macro to create a new unique private name. -\end{frame} - -\subsubsection{RTLIL::Design and RTLIL::Module} - -\begin{frame}[t, fragile]{\subsubsecname} -The {\tt RTLIL::Design} and {\tt RTLIL::Module} structs are the top-level RTLIL -data structures. Yosys always operates on one active design, but can hold many designs in memory. - -\bigskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -struct RTLIL::Design { - dict modules_; - ... -}; - -struct RTLIL::Module { - RTLIL::IdString name; - dict wires_; - dict cells_; - std::vector connections_; - ... -}; -\end{lstlisting} - -(Use the various accessor functions instead of directly working with the {\tt *\_} members.) -\end{frame} - -\subsubsection{The RTLIL::Wire Structure} - -\begin{frame}[t, fragile]{\subsubsecname} -Each wire in the design is represented by a {\tt RTLIL::Wire} struct: - -\medskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -struct RTLIL::Wire { - RTLIL::IdString name; - int width, start_offset, port_id; - bool port_input, port_output; - ... -}; -\end{lstlisting} - -\medskip -\hfil\begin{tabular}{p{3cm}l} -{\tt width} \dotfill & The total number of bits. E.g. 10 for {\tt [9:0]}. \\ -{\tt start\_offset} \dotfill & The lowest bit index. E.g. 3 for {\tt [5:3]}. \\ -{\tt port\_id} \dotfill & Zero for non-ports. Positive index for ports. \\ -{\tt port\_input} \dotfill & True for {\tt input} and {\tt inout} ports. \\ -{\tt port\_output} \dotfill & True for {\tt output} and {\tt inout} ports. \\ -\end{tabular} -\end{frame} - -\subsubsection{RTLIL::State and RTLIL::Const} - -\begin{frame}[t, fragile]{\subsubsecname} -The {\tt RTLIL::State} enum represents a simple 1-bit logic level: - -\smallskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -enum RTLIL::State { - S0 = 0, - S1 = 1, - Sx = 2, // undefined value or conflict - Sz = 3, // high-impedance / not-connected - Sa = 4, // don't care (used only in cases) - Sm = 5 // marker (used internally by some passes) -}; -\end{lstlisting} - -\bigskip -The {\tt RTLIL::Const} struct represents a constant multi-bit value: - -\smallskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -struct RTLIL::Const { - std::vector bits; - ... -}; -\end{lstlisting} - -\bigskip -Notice that Yosys is not using special {\tt VCC} or {\tt GND} driver cells to represent constants. Instead -constants are part of the RTLIL representation itself. -\end{frame} - -\subsubsection{The RTLIL::SigSpec Structure} - -\begin{frame}[t, fragile]{\subsubsecname} -The {\tt RTLIL::SigSpec} struct represents a signal vector. Each bit can either be a bit from a wire -or a constant value. - -\bigskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -struct RTLIL::SigBit -{ - RTLIL::Wire *wire; - union { - RTLIL::State data; // used if wire == NULL - int offset; // used if wire != NULL - }; - ... -}; - -struct RTLIL::SigSpec { - std::vector bits_; // LSB at index 0 - ... -}; -\end{lstlisting} - -\bigskip -The {\tt RTLIL::SigSpec} struct has a ton of additional helper methods to compare, analyze, and -manipulate instances of {\tt RTLIL::SigSpec}. -\end{frame} - -\subsubsection{The RTLIL::Cell Structure} - -\begin{frame}[t, fragile]{\subsubsecname (1/2)} -The {\tt RTLIL::Cell} struct represents an instance of a module or library cell. - -\smallskip -The ports of the cell -are associated with {\tt RTLIL::SigSpec} instances and the parameters are associated with {\tt RTLIL::Const} -instances: - -\bigskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -struct RTLIL::Cell { - RTLIL::IdString name, type; - dict connections_; - dict parameters; - ... -}; -\end{lstlisting} - -\bigskip -The {\tt type} may refer to another module in the same design, a cell name from a cell library, or a -cell name from the internal cell library: - -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont] -$not $pos $neg $and $or $xor $xnor $reduce_and $reduce_or $reduce_xor $reduce_xnor -$reduce_bool $shl $shr $sshl $sshr $lt $le $eq $ne $eqx $nex $ge $gt $add $sub $mul $div $mod -$divfloor $modfloor $pow $logic_not $logic_and $logic_or $mux $pmux $slice $concat $lut $assert $sr $dff -$dffsr $adff $dlatch $dlatchsr $memrd $memwr $mem $fsm $_NOT_ $_AND_ $_OR_ $_XOR_ $_MUX_ $_SR_NN_ -$_SR_NP_ $_SR_PN_ $_SR_PP_ $_DFF_N_ $_DFF_P_ $_DFF_NN0_ $_DFF_NN1_ $_DFF_NP0_ $_DFF_NP1_ $_DFF_PN0_ -$_DFF_PN1_ $_DFF_PP0_ $_DFF_PP1_ $_DFFSR_NNN_ $_DFFSR_NNP_ $_DFFSR_NPN_ $_DFFSR_NPP_ $_DFFSR_PNN_ -$_DFFSR_PNP_ $_DFFSR_PPN_ $_DFFSR_PPP_ $_DLATCH_N_ $_DLATCH_P_ $_DLATCHSR_NNN_ $_DLATCHSR_NNP_ -$_DLATCHSR_NPN_ $_DLATCHSR_NPP_ $_DLATCHSR_PNN_ $_DLATCHSR_PNP_ $_DLATCHSR_PPN_ $_DLATCHSR_PPP_ -\end{lstlisting} -\end{frame} - -\begin{frame}[t, fragile]{\subsubsecname (2/2)} -Simulation models (i.e. {\it documentation\/} :-) for the internal cell library: - -\smallskip -\hskip2em {\tt yosys/techlibs/common/simlib.v} and \\ -\hskip2em {\tt yosys/techlibs/common/simcells.v} - -\bigskip -The lower-case cell types (such as {\tt \$and}) are parameterized cells of variable -width. This so-called {\it RTL Cells\/} are the cells described in {\tt simlib.v}. - -\bigskip -The upper-case cell types (such as {\tt \$\_AND\_}) are single-bit cells that are not -parameterized. This so-called {\it Internal Logic Gates} are the cells described -in {\tt simcells.v}. - -\bigskip -The consistency checker also checks the interfaces to the internal cell library. -If you want to use private cell types for your own purposes, use the {\tt \$\_\_}-prefix -to avoid name collisions. -\end{frame} - -\subsubsection{Connecting wires or constant drivers} - -\begin{frame}[t, fragile]{\subsubsecname} -Additional connections between wires or between wires and constants are modelled using -{\tt RTLIL::Module::connections}: - -\bigskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -typedef std::pair RTLIL::SigSig; - -struct RTLIL::Module { - ... - std::vector connections_; - ... -}; -\end{lstlisting} - -\bigskip -{\tt RTLIL::SigSig::first} is the driven signal and {\tt RTLIL::SigSig::second} is the driving signal. -Example usage (setting wire {\tt foo} to value {\tt 42}): -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -module->connect(module->wire("\\foo"), - RTLIL::SigSpec(42, module->wire("\\foo")->width)); -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Creating modules from scratch} - -\begin{frame}[t, fragile]{\subsecname} -Let's create the following module using the RTLIL API: - -\smallskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog] -module absval(input signed [3:0] a, output [3:0] y); - assign y = a[3] ? -a : a; -endmodule -\end{lstlisting} - -\smallskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -RTLIL::Module *module = new RTLIL::Module; -module->name = "\\absval"; - -RTLIL::Wire *a = module->addWire("\\a", 4); -a->port_input = true; -a->port_id = 1; - -RTLIL::Wire *y = module->addWire("\\y", 4); -y->port_output = true; -y->port_id = 2; - -RTLIL::Wire *a_inv = module->addWire(NEW_ID, 4); -module->addNeg(NEW_ID, a, a_inv, true); -module->addMux(NEW_ID, a, a_inv, RTLIL::SigSpec(a, 1, 3), y); - -module->fixup_ports(); -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Modifying modules} - -\begin{frame}{\subsecname} -Most commands modify existing modules, not create new ones. - -When modifying existing modules, stick to the following DOs and DON'Ts: - -\begin{itemize} -\item Do not remove wires. Simply disconnect them and let a successive {\tt clean} command worry about removing it. - -\item Use {\tt module->fixup\_ports()} after changing the {\tt port\_*} properties of wires. - -\item You can safely remove cells or change the {\tt connections} property of a cell, but be careful when -changing the size of the {\tt SigSpec} connected to a cell port. - -\item Use the {\tt SigMap} helper class (see next slide) when you need a unique handle for each signal bit. -\end{itemize} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Using the SigMap helper class} - -\begin{frame}[t, fragile]{\subsecname} -Consider the following module: - -\smallskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=Verilog] -module test(input a, output x, y); - assign x = a, y = a; -endmodule -\end{lstlisting} - -In this case {\tt a}, {\tt x}, and {\tt y} are all different names for the same signal. However: - -\smallskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -RTLIL::SigSpec a(module->wire("\\a")), x(module->wire("\\x")), - y(module->wire("\\y")); -log("%d %d %d\n", a == x, x == y, y == a); // will print "0 0 0" -\end{lstlisting} - -The {\tt SigMap} helper class can be used to map all such aliasing signals to a -unique signal from the group (usually the wire that is directly driven by a cell or port). - -\smallskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -SigMap sigmap(module); -log("%d %d %d\n", sigmap(a) == sigmap(x), sigmap(x) == sigmap(y), - sigmap(y) == sigmap(a)); // will print "1 1 1" -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Printing log messages} - -\begin{frame}[t, fragile]{\subsecname} -The {\tt log()} function is a {\tt printf()}-like function that can be used to create log messages. - -\medskip -Use {\tt log\_signal()} to create a C-string for a SigSpec object\footnote[frame]{The pointer returned -by {\tt log\_signal()} is automatically freed by the log framework at a later time.}: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -log("Mapped signal x: %s\n", log_signal(sigmap(x))); -\end{lstlisting} - -\medskip -Use {\tt log\_id()} to create a C-string for an {\tt RTLIL::IdString}: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -log("Name of this module: %s\n", log_id(module->name)); -\end{lstlisting} - -\medskip -Use {\tt log\_header()} and {\tt log\_push()}/{\tt log\_pop()} to structure log messages: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -log_header(design, "Doing important stuff!\n"); -log_push(); -for (int i = 0; i < 10; i++) - log("Log message #%d.\n", i); -log_pop(); -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Error handling} - -\begin{frame}[t, fragile]{\subsecname} -Use {\tt log\_error()} to report a non-recoverable error: - -\medskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -if (design->modules.count(module->name) != 0) - log_error("A module with the name %s already exists!\n", - RTLIL::id2cstr(module->name)); -\end{lstlisting} - -\bigskip -Use {\tt log\_cmd\_error()} to report a recoverable error: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -if (design->selection_stack.back().empty()) - log_cmd_error("This command can't operator on an empty selection!\n"); -\end{lstlisting} - -\bigskip -Use {\tt log\_assert()} and {\tt log\_abort()} instead of {\tt assert()} and {\tt abort()}. -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Creating a command} - -\begin{frame}[t, fragile]{\subsecname} -Simply create a global instance of a class derived from {\tt Pass} to create -a new yosys command: - -\bigskip -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont, language=C++] -#include "kernel/yosys.h" -USING_YOSYS_NAMESPACE - -struct MyPass : public Pass { - MyPass() : Pass("my_cmd", "just a simple test") { } - virtual void execute(std::vector args, RTLIL::Design *design) - { - log("Arguments to my_cmd:\n"); - for (auto &arg : args) - log(" %s\n", arg.c_str()); - - log("Modules in current design:\n"); - for (auto mod : design->modules()) - log(" %s (%d wires, %d cells)\n", log_id(mod), - GetSize(mod->wires()), GetSize(mod->cells())); - } -} MyPass; -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Creating a plugin} - -\begin{frame}[fragile]{\subsecname} -Yosys can be extended by adding additional C++ code to the Yosys code base, or -by loading plugins into Yosys. - -\bigskip -Use the following command to compile a Yosys plugin: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] -yosys-config --exec --cxx --cxxflags --ldflags \ - -o my_cmd.so -shared my_cmd.cc --ldlibs -\end{lstlisting} - -\bigskip -Or shorter: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] -yosys-config --build my_cmd.so my_cmd.cc -\end{lstlisting} - -\bigskip -Load the plugin using the yosys {\tt -m} option: -\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{8pt}{10pt}\selectfont] -yosys -m ./my_cmd.so -p 'my_cmd foo bar' -\end{lstlisting} -\end{frame} - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -\subsection{Summary} - -\begin{frame}{\subsecname} -\begin{itemize} -\item Writing Yosys extensions is very straight-forward. -\item \dots and even simpler if you don't need RTLIL::Memory or RTLIL::Process objects. - -\bigskip -\item Writing synthesis software? Consider learning the Yosys API and make your work -part of the Yosys framework. -\end{itemize} - -\bigskip -\bigskip -\begin{center} -Questions? -\end{center} - -\bigskip -\bigskip -\begin{center} -\url{http://www.clifford.at/yosys/} -\end{center} -\end{frame} - diff --git a/manual/PRESENTATION_Prog/Makefile b/manual/PRESENTATION_Prog/Makefile deleted file mode 100644 index 7e3cf814b44..00000000000 --- a/manual/PRESENTATION_Prog/Makefile +++ /dev/null @@ -1,21 +0,0 @@ - -all: test0.log test1.log test2.log - -CXXFLAGS=$(shell ../../yosys-config --cxxflags) -DATDIR=$(shell ../../yosys-config --datdir) - -my_cmd.so: my_cmd.cc - ../../yosys-config --exec --cxx $(subst $(DATDIR),../../share,$(CXXFLAGS)) --ldflags -o my_cmd.so -shared my_cmd.cc --ldlibs - -test0.log: my_cmd.so - ../../yosys -Ql test0.log_new -m ./my_cmd.so -p 'my_cmd foo bar' absval_ref.v - mv test0.log_new test0.log - -test1.log: my_cmd.so - ../../yosys -Ql test1.log_new -m ./my_cmd.so -p 'clean; test1; dump' absval_ref.v - mv test1.log_new test1.log - -test2.log: my_cmd.so - ../../yosys -Ql test2.log_new -m ./my_cmd.so -p 'test2' sigmap_test.v - mv test2.log_new test2.log - diff --git a/manual/appnotes.sh b/manual/appnotes.sh deleted file mode 100755 index 0ae52862ec1..00000000000 --- a/manual/appnotes.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -set -ex -for job in APPNOTE_010_Verilog_to_BLIF APPNOTE_011_Design_Investigation APPNOTE_012_Verilog_to_BTOR -do - [ -f $job.ok -a $job.ok -nt $job.tex ] && continue - if [ -f $job/make.sh ]; then - cd $job - bash make.sh - cd .. - fi - old_md5=$([ -f $job.aux ] && md5sum < $job.aux || true) - while - pdflatex -shell-escape -halt-on-error $job.tex || exit - new_md5=$(md5sum < $job.aux) - [ "$old_md5" != "$new_md5" ] - do - old_md5="$new_md5" - done - touch $job.ok -done - diff --git a/manual/clean.sh b/manual/clean.sh deleted file mode 100755 index addc34ed183..00000000000 --- a/manual/clean.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -for f in $( find . -name .gitignore ); do sed -Ee "s,^,find ${f%.gitignore} -name ',; s,$,' | xargs rm -f,;" $f; done | bash -v diff --git a/manual/command-reference-manual.tex b/manual/command-reference-manual.tex deleted file mode 100644 index 988f034b4bc..00000000000 --- a/manual/command-reference-manual.tex +++ /dev/null @@ -1,7628 +0,0 @@ -% Generated using the yosys 'help -write-tex-command-reference-manual' command. - -\section{abc -- use ABC for technology mapping} -\label{cmd:abc} -\begin{lstlisting}[numbers=left,frame=single] - abc [options] [selection] - -This pass uses the ABC tool [1] for technology mapping of yosys's internal gate -library to a target architecture. - - -exe - use the specified command instead of "/yosys-abc" to execute ABC. - This can e.g. be used to call a specific version of ABC or a wrapper. - - -script - use the specified ABC script file instead of the default script. - - if starts with a plus sign (+), then the rest of the filename - string is interpreted as the command string to be passed to ABC. The - leading plus sign is removed and all commas (,) in the string are - replaced with blanks before the string is passed to ABC. - - if no -script parameter is given, the following scripts are used: - - for -liberty without -constr: - strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; - &nf {D}; &put - - for -liberty with -constr: - strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; - &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p - - for -lut/-luts (only one LUT size): - strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2; - lutpack {S} - - for -lut/-luts (different LUT sizes): - strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2 - - for -sop: - strash; ifraig; scorr; dc2; dretime; strash; dch -f; - cover {I} {P} - - otherwise: - strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; - &nf {D}; &put - - -fast - use different default scripts that are slightly faster (at the cost - of output quality): - - for -liberty without -constr: - strash; dretime; map {D} - - for -liberty with -constr: - strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; - stime -p - - for -lut/-luts: - strash; dretime; if - - for -sop: - strash; dretime; cover -I {I} -P {P} - - otherwise: - strash; dretime; map - - -liberty - generate netlists for the specified cell library (using the liberty - file format). - - -constr - pass this file with timing constraints to ABC. use with -liberty. - - a constr file contains two lines: - set_driving_cell - set_load - - the set_driving_cell statement defines which cell type is assumed to - drive the primary inputs and the set_load statement sets the load in - femtofarads for each primary output. - - -D - set delay target. the string {D} in the default scripts above is - replaced by this option when used, and an empty string otherwise. - this also replaces 'dretime' with 'dretime; retime -o {D}' in the - default scripts above. - - -I - maximum number of SOP inputs. - (replaces {I} in the default scripts above) - - -P - maximum number of SOP products. - (replaces {P} in the default scripts above) - - -S - maximum number of LUT inputs shared. - (replaces {S} in the default scripts above, default: -S 1) - - -lut - generate netlist using luts of (max) the specified width. - - -lut : - generate netlist using luts of (max) the specified width . All - luts with width <= have constant cost. for luts larger than - the area cost doubles with each additional input bit. the delay cost - is still constant for all lut widths. - - -luts ,,,:,.. - generate netlist using luts. Use the specified costs for luts with 1, - 2, 3, .. inputs. - - -sop - map to sum-of-product cells and inverters - - -g type1,type2,... - Map to the specified list of gate types. Supported gates types are: - AND, NAND, OR, NOR, XOR, XNOR, ANDNOT, ORNOT, MUX, - NMUX, AOI3, OAI3, AOI4, OAI4. - (The NOT gate is always added to this list automatically.) - - The following aliases can be used to reference common sets of gate types: - simple: AND OR XOR MUX - cmos2: NAND NOR - cmos3: NAND NOR AOI3 OAI3 - cmos4: NAND NOR AOI3 OAI3 AOI4 OAI4 - cmos: NAND NOR AOI3 OAI3 AOI4 OAI4 NMUX MUX XOR XNOR - gates: AND NAND OR NOR XOR XNOR ANDNOT ORNOT - aig: AND NAND OR NOR ANDNOT ORNOT - - The alias 'all' represent the full set of all gate types. - - Prefix a gate type with a '-' to remove it from the list. For example - the arguments 'AND,OR,XOR' and 'simple,-MUX' are equivalent. - - The default is 'all,-NMUX,-AOI3,-OAI3,-AOI4,-OAI4'. - - -dff - also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many - clock domains are automatically partitioned in clock domains and each - domain is passed through ABC independently. - - -clk [!][,[!]] - use only the specified clock domain. this is like -dff, but only FF - cells that belong to the specified clock domain are used. - - -keepff - set the "keep" attribute on flip-flop output wires. (and thus preserve - them, for example for equivalence checking.) - - -nocleanup - when this option is used, the temporary files created by this pass - are not removed. this is useful for debugging. - - -showtmp - print the temp dir name in log. usually this is suppressed so that the - command output is identical across runs. - - -markgroups - set a 'abcgroup' attribute on all objects created by ABC. The value of - this attribute is a unique integer for each ABC process started. This - is useful for debugging the partitioning of clock domains. - - -dress - run the 'dress' command after all other ABC commands. This aims to - preserve naming by an equivalence check between the original and post-ABC - netlists (experimental). - -When neither -liberty nor -lut is used, the Yosys standard cell library is -loaded into ABC before the ABC script is executed. - -Note that this is a logic optimization pass within Yosys that is calling ABC -internally. This is not going to "run ABC on your design". It will instead run -ABC on logic snippets extracted from your design. You will not get any useful -output when passing an ABC script that writes a file. Instead write your full -design as BLIF file with write_blif and then load that into ABC externally if -you want to use ABC to convert your design into another format. - -[1] http://www.eecs.berkeley.edu/~alanmi/abc/ -\end{lstlisting} - -\section{abc9 -- use ABC9 for technology mapping} -\label{cmd:abc9} -\begin{lstlisting}[numbers=left,frame=single] - abc9 [options] [selection] - -This script pass performs a sequence of commands to facilitate the use of the ABC -tool [1] for technology mapping of the current design to a target FPGA -architecture. Only fully-selected modules are supported. - - -run : - only run the commands between the labels (see below). an empty - from label is synonymous to 'begin', and empty to label is - synonymous to the end of the command list. - - -exe - use the specified command instead of "/yosys-abc" to execute ABC. - This can e.g. be used to call a specific version of ABC or a wrapper. - - -script - use the specified ABC script file instead of the default script. - - if starts with a plus sign (+), then the rest of the filename - string is interpreted as the command string to be passed to ABC. The - leading plus sign is removed and all commas (,) in the string are - replaced with blanks before the string is passed to ABC. - - if no -script parameter is given, the following scripts are used: - &scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs - - -fast - use different default scripts that are slightly faster (at the cost - of output quality): - &if {C} {W} {D} {R} -v - - -D - set delay target. the string {D} in the default scripts above is - replaced by this option when used, and an empty string otherwise - (indicating best possible delay). - - -lut - generate netlist using luts of (max) the specified width. - - -lut : - generate netlist using luts of (max) the specified width . All - luts with width <= have constant cost. for luts larger than - the area cost doubles with each additional input bit. the delay cost - is still constant for all lut widths. - - -lut - pass this file with lut library to ABC. - - -luts ,,,:,.. - generate netlist using luts. Use the specified costs for luts with 1, - 2, 3, .. inputs. - - -maxlut - when auto-generating the lut library, discard all luts equal to or - greater than this size (applicable when neither -lut nor -luts is - specified). - - -dff - also pass $_ABC9_FF_ cells through to ABC. modules with many clock - domains are marked as such and automatically partitioned by ABC. - - -nocleanup - when this option is used, the temporary files created by this pass - are not removed. this is useful for debugging. - - -showtmp - print the temp dir name in log. usually this is suppressed so that the - command output is identical across runs. - - -box - pass this file with box library to ABC. - -Note that this is a logic optimization pass within Yosys that is calling ABC -internally. This is not going to "run ABC on your design". It will instead run -ABC on logic snippets extracted from your design. You will not get any useful -output when passing an ABC script that writes a file. Instead write your full -design as an XAIGER file with `write_xaiger' and then load that into ABC -externally if you want to use ABC to convert your design into another format. - -[1] http://www.eecs.berkeley.edu/~alanmi/abc/ - - - pre: - abc9_ops -check - scc -set_attr abc9_scc_id {} - abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff] (option for -dff) - abc9_ops -prep_lut (skip if -lut or -luts) - abc9_ops -prep_box [-dff] (skip if -box) - select -set abc9_holes A:abc9_holes - flatten -wb @abc9_holes - techmap @abc9_holes - abc9_ops -prep_dff (only if -dff) - opt -purge @abc9_holes - aigmap - wbflip @abc9_holes - - map: - foreach module in selection - abc9_ops -write_lut /input.lut (skip if '-lut' or '-luts') - abc9_ops -write_box /input.box - write_xaiger -map /input.sym /input.xaig - abc9_exe [options] -cwd [-lut /input.lut] -box /input.box - read_aiger -xaiger -wideports -module_name $abc9 -map /input.sym /output.aig - abc9_ops -reintegrate -\end{lstlisting} - -\section{abc9\_exe -- use ABC9 for technology mapping} -\label{cmd:abc9_exe} -\begin{lstlisting}[numbers=left,frame=single] - abc9_exe [options] - - -This pass uses the ABC tool [1] for technology mapping of the top module -(according to the (* top *) attribute or if only one module is currently selected) -to a target FPGA architecture. - - -exe - use the specified command instead of "/yosys-abc" to execute ABC. - This can e.g. be used to call a specific version of ABC or a wrapper. - - -script - use the specified ABC script file instead of the default script. - - if starts with a plus sign (+), then the rest of the filename - string is interpreted as the command string to be passed to ABC. The - leading plus sign is removed and all commas (,) in the string are - replaced with blanks before the string is passed to ABC. - - if no -script parameter is given, the following scripts are used: - &scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs - - -fast - use different default scripts that are slightly faster (at the cost - of output quality): - &if {C} {W} {D} {R} -v - - -D - set delay target. the string {D} in the default scripts above is - replaced by this option when used, and an empty string otherwise - (indicating best possible delay). - - -lut - generate netlist using luts of (max) the specified width. - - -lut : - generate netlist using luts of (max) the specified width . All - luts with width <= have constant cost. for luts larger than - the area cost doubles with each additional input bit. the delay cost - is still constant for all lut widths. - - -lut - pass this file with lut library to ABC. - - -luts ,,,:,.. - generate netlist using luts. Use the specified costs for luts with 1, - 2, 3, .. inputs. - - -showtmp - print the temp dir name in log. usually this is suppressed so that the - command output is identical across runs. - - -box - pass this file with box library to ABC. - - -cwd - use this as the current working directory, inside which the 'input.xaig' - file is expected. temporary files will be created in this directory, and - the mapped result will be written to 'output.aig'. - -Note that this is a logic optimization pass within Yosys that is calling ABC -internally. This is not going to "run ABC on your design". It will instead run -ABC on logic snippets extracted from your design. You will not get any useful -output when passing an ABC script that writes a file. Instead write your full -design as BLIF file with write_blif and then load that into ABC externally if -you want to use ABC to convert your design into another format. - -[1] http://www.eecs.berkeley.edu/~alanmi/abc/ -\end{lstlisting} - -\section{abc9\_ops -- helper functions for ABC9} -\label{cmd:abc9_ops} -\begin{lstlisting}[numbers=left,frame=single] - abc9_ops [options] [selection] - -This pass contains a set of supporting operations for use during ABC technology -mapping, and is expected to be called in conjunction with other operations from -the `abc9' script pass. Only fully-selected modules are supported. - - -check - check that the design is valid, e.g. (* abc9_box_id *) values are unique, - (* abc9_carry *) is only given for one input/output port, etc. - - -prep_delays - insert `$__ABC9_DELAY' blackbox cells into the design to account for - certain required times. - - -mark_scc - for an arbitrarily chosen cell in each unique SCC of each selected module - (tagged with an (* abc9_scc_id = *) attribute), temporarily mark all - wires driven by this cell's outputs with a (* keep *) attribute in order - to break the SCC. this temporary attribute will be removed on -reintegrate. - - -prep_xaiger - prepare the design for XAIGER output. this includes computing the - topological ordering of ABC9 boxes, as well as preparing the - '$holes' module that contains the logic behaviour of ABC9 - whiteboxes. - - -dff - consider flop cells (those instantiating modules marked with (* abc9_flop *)) - during -prep_{delays,xaiger,box}. - - -prep_dff - compute the clock domain and initial value of each flop in the design. - process the '$holes' module to support clock-enable functionality. - - -prep_lut - pre-compute the lut library by analysing all modules marked with - (* abc9_lut= *). - - -write_lut - write the pre-computed lut library to . - - -prep_box - pre-compute the box library by analysing all modules marked with - (* abc9_box *). - - -write_box - write the pre-computed box library to . - - -reintegrate - for each selected module, re-intergrate the module '$abc9' - by first recovering ABC9 boxes, and then stitching in the remaining primary - inputs and outputs. -\end{lstlisting} - -\section{add -- add objects to the design} -\label{cmd:add} -\begin{lstlisting}[numbers=left,frame=single] - add [selection] - -This command adds objects to the design. It operates on all fully selected -modules. So e.g. 'add -wire foo' will add a wire foo to all selected modules. - - - add {-wire|-input|-inout|-output} [selection] - -Add a wire (input, inout, output port) with the given name and width. The -command will fail if the object exists already and has different properties -than the object to be created. - - - add -global_input [selection] - -Like 'add -input', but also connect the signal between instances of the -selected modules. - - - add {-assert|-assume|-live|-fair|-cover} [-if ] - -Add an $assert, $assume, etc. cell connected to a wire named name1, with its -enable signal optionally connected to a wire named name2 (default: 1'b1). - - - add -mod - -Add module[s] with the specified name[s]. -\end{lstlisting} - -\section{aigmap -- map logic to and-inverter-graph circuit} -\label{cmd:aigmap} -\begin{lstlisting}[numbers=left,frame=single] - aigmap [options] [selection] - -Replace all logic cells with circuits made of only $_AND_ and -$_NOT_ cells. - - -nand - Enable creation of $_NAND_ cells - - -select - Overwrite replaced cells in the current selection with new $_AND_, - $_NOT_, and $_NAND_, cells -\end{lstlisting} - -\section{alumacc -- extract ALU and MACC cells} -\label{cmd:alumacc} -\begin{lstlisting}[numbers=left,frame=single] - alumacc [selection] - -This pass translates arithmetic operations like $add, $mul, $lt, etc. to $alu -and $macc cells. -\end{lstlisting} - -\section{anlogic\_eqn -- Anlogic: Calculate equations for luts} -\label{cmd:anlogic_eqn} -\begin{lstlisting}[numbers=left,frame=single] - anlogic_eqn [selection] - -Calculate equations for luts since bitstream generator depends on it. -\end{lstlisting} - -\section{anlogic\_fixcarry -- Anlogic: fix carry chain} -\label{cmd:anlogic_fixcarry} -\begin{lstlisting}[numbers=left,frame=single] - anlogic_fixcarry [options] [selection] - -Add Anlogic adders to fix carry chain if needed. -\end{lstlisting} - -\section{assertpmux -- adds asserts for parallel muxes} -\label{cmd:assertpmux} -\begin{lstlisting}[numbers=left,frame=single] - assertpmux [options] [selection] - -This command adds asserts to the design that assert that all parallel muxes -($pmux cells) have a maximum of one of their inputs enable at any time. - - -noinit - do not enforce the pmux condition during the init state - - -always - usually the $pmux condition is only checked when the $pmux output - is used by the mux tree it drives. this option will deactivate this - additional constraint and check the $pmux condition always. -\end{lstlisting} - -\section{async2sync -- convert async FF inputs to sync circuits} -\label{cmd:async2sync} -\begin{lstlisting}[numbers=left,frame=single] - async2sync [options] [selection] - -This command replaces async FF inputs with sync circuits emulating the same -behavior for when the async signals are actually synchronized to the clock. - -This pass assumes negative hold time for the async FF inputs. For example when -a reset deasserts with the clock edge, then the FF output will still drive the -reset value in the next cycle regardless of the data-in value at the time of -the clock edge. - -Currently only $adff, $dffsr, and $dlatch cells are supported by this pass. -\end{lstlisting} - -\section{attrmap -- renaming attributes} -\label{cmd:attrmap} -\begin{lstlisting}[numbers=left,frame=single] - attrmap [options] [selection] - -This command renames attributes and/or maps key/value pairs to -other key/value pairs. - - -tocase - Match attribute names case-insensitively and set it to the specified - name. - - -rename - Rename attributes as specified - - -map = = - Map key/value pairs as indicated. - - -imap = = - Like -map, but use case-insensitive match for when - it is a string value. - - -remove = - Remove attributes matching this pattern. - - -modattr - Operate on module attributes instead of attributes on wires and cells. - -For example, mapping Xilinx-style "keep" attributes to Yosys-style: - - attrmap -tocase keep -imap keep="true" keep=1 \ - -imap keep="false" keep=0 -remove keep=0 -\end{lstlisting} - -\section{attrmvcp -- move or copy attributes from wires to driving cells} -\label{cmd:attrmvcp} -\begin{lstlisting}[numbers=left,frame=single] - attrmvcp [options] [selection] - -Move or copy attributes on wires to the cells driving them. - - -copy - By default, attributes are moved. This will only add - the attribute to the cell, without removing it from - the wire. - - -purge - If no selected cell consumes the attribute, then it is - left on the wire by default. This option will cause the - attribute to be removed from the wire, even if no selected - cell takes it. - - -driven - By default, attriburtes are moved to the cell driving the - wire. With this option set it will be moved to the cell - driven by the wire instead. - - -attr - Move or copy this attribute. This option can be used - multiple times. -\end{lstlisting} - -\section{autoname -- automatically assign names to objects} -\label{cmd:autoname} -\begin{lstlisting}[numbers=left,frame=single] - autoname [selection] - -Assign auto-generated public names to objects with private names (the ones -with $-prefix). -\end{lstlisting} - -\section{blackbox -- convert modules into blackbox modules} -\label{cmd:blackbox} -\begin{lstlisting}[numbers=left,frame=single] - blackbox [options] [selection] - -Convert modules into blackbox modules (remove contents and set the blackbox -module attribute). -\end{lstlisting} - -\section{bugpoint -- minimize testcases} -\label{cmd:bugpoint} -\begin{lstlisting}[numbers=left,frame=single] - bugpoint [options] - -This command minimizes testcases that crash Yosys. It removes an arbitrary part -of the design and recursively invokes Yosys with a given script, repeating these -steps while it can find a smaller design that still causes a crash. Once this -command finishes, it replaces the current design with the smallest testcase it -was able to produce. - -It is possible to specify the kinds of design part that will be removed. If none -are specified, all parts of design will be removed. - - -yosys - use this Yosys binary. if not specified, `yosys` is used. - - -script - use this script to crash Yosys. required. - - -grep - only consider crashes that place this string in the log file. - - -fast - run `proc_clean; clean -purge` after each minimization step. converges - faster, but produces larger testcases, and may fail to produce any - testcase at all if the crash is related to dangling wires. - - -clean - run `proc_clean; clean -purge` before checking testcase and after - finishing. produces smaller and more useful testcases, but may fail to - produce any testcase at all if the crash is related to dangling wires. - - -modules - try to remove modules. - - -ports - try to remove module ports. - - -cells - try to remove cells. - - -connections - try to reconnect ports to 'x. - - -assigns - try to remove process assigns from cases. - - -updates - try to remove process updates from syncs. -\end{lstlisting} - -\section{cd -- a shortcut for 'select -module '} -\label{cmd:cd} -\begin{lstlisting}[numbers=left,frame=single] - cd - -This is just a shortcut for 'select -module '. - - - cd - -When no module with the specified name is found, but there is a cell -with the specified name in the current module, then this is equivalent -to 'cd '. - - cd .. - -Remove trailing substrings that start with '.' in current module name until -the name of a module in the current design is generated, then switch to that -module. Otherwise clear the current selection. - - cd - -This is just a shortcut for 'select -clear'. -\end{lstlisting} - -\section{check -- check for obvious problems in the design} -\label{cmd:check} -\begin{lstlisting}[numbers=left,frame=single] - check [options] [selection] - -This pass identifies the following problems in the current design: - - - combinatorial loops - - - two or more conflicting drivers for one wire - - - used wires that do not have a driver - -Options: - - -noinit - Also check for wires which have the 'init' attribute set. - - -initdrv - Also check for wires that have the 'init' attribute set and are not - driven by an FF cell type. - - -mapped - Also check for internal cells that have not been mapped to cells of the - target architecture. - - -allow-tbuf - Modify the -mapped behavior to still allow $_TBUF_ cells. - - -assert - Produce a runtime error if any problems are found in the current design. -\end{lstlisting} - -\section{chformal -- change formal constraints of the design} -\label{cmd:chformal} -\begin{lstlisting}[numbers=left,frame=single] - chformal [types] [mode] [options] [selection] - -Make changes to the formal constraints of the design. The [types] options -the type of constraint to operate on. If none of the following options are given, -the command will operate on all constraint types: - - -assert $assert cells, representing assert(...) constraints - -assume $assume cells, representing assume(...) constraints - -live $live cells, representing assert(s_eventually ...) - -fair $fair cells, representing assume(s_eventually ...) - -cover $cover cells, representing cover() statements - -Exactly one of the following modes must be specified: - - -remove - remove the cells and thus constraints from the design - - -early - bypass FFs that only delay the activation of a constraint - - -delay - delay activation of the constraint by clock cycles - - -skip - ignore activation of the constraint in the first clock cycles - - -assert2assume - -assume2assert - -live2fair - -fair2live - change the roles of cells as indicated. these options can be combined -\end{lstlisting} - -\section{chparam -- re-evaluate modules with new parameters} -\label{cmd:chparam} -\begin{lstlisting}[numbers=left,frame=single] - chparam [ -set name value ]... [selection] - -Re-evaluate the selected modules with new parameters. String values must be -passed in double quotes ("). - - - chparam -list [selection] - -List the available parameters of the selected modules. -\end{lstlisting} - -\section{chtype -- change type of cells in the design} -\label{cmd:chtype} -\begin{lstlisting}[numbers=left,frame=single] - chtype [options] [selection] - -Change the types of cells in the design. - - -set - set the cell type to the given type - - -map - change cells types that match to -\end{lstlisting} - -\section{clean -- remove unused cells and wires} -\label{cmd:clean} -\begin{lstlisting}[numbers=left,frame=single] - clean [options] [selection] - -This is identical to 'opt_clean', but less verbose. - -When commands are separated using the ';;' token, this command will be executed -between the commands. - -When commands are separated using the ';;;' token, this command will be executed -in -purge mode between the commands. -\end{lstlisting} - -\section{clk2fflogic -- convert clocked FFs to generic \$ff cells} -\label{cmd:clk2fflogic} -\begin{lstlisting}[numbers=left,frame=single] - clk2fflogic [options] [selection] - -This command replaces clocked flip-flops with generic $ff cells that use the -implicit global clock. This is useful for formal verification of designs with -multiple clocks. -\end{lstlisting} - -\section{clkbufmap -- insert global buffers on clock networks} -\label{cmd:clkbufmap} -\begin{lstlisting}[numbers=left,frame=single] - clkbufmap [options] [selection] - -Inserts global buffers between nets connected to clock inputs and their drivers. - -In the absence of any selection, all wires without the 'clkbuf_inhibit' -attribute will be considered for global buffer insertion. -Alternatively, to consider all wires without the 'buffer_type' attribute set to -'none' or 'bufr' one would specify: - 'w:* a:buffer_type=none a:buffer_type=bufr %u %d' -as the selection. - - -buf : - Specifies the cell type to use for the global buffers - and its port names. The first port will be connected to - the clock network sinks, and the second will be connected - to the actual clock source. This option is required. - - -inpad : - If specified, a PAD cell of the given type is inserted on - clock nets that are also top module's inputs (in addition - to the global buffer). -\end{lstlisting} - -\section{connect -- create or remove connections} -\label{cmd:connect} -\begin{lstlisting}[numbers=left,frame=single] - connect [-nomap] [-nounset] -set - -Create a connection. This is equivalent to adding the statement 'assign - = ;' to the Verilog input. Per default, all existing -drivers for are unconnected. This can be overwritten by using -the -nounset option. - - - connect [-nomap] -unset - -Unconnect all existing drivers for the specified expression. - - - connect [-nomap] -port - -Connect the specified cell port to the specified cell port. - - -Per default signal alias names are resolved and all signal names are mapped -the the signal name of the primary driver. Using the -nomap option deactivates -this behavior. - -The connect command operates in one module only. Either only one module must -be selected or an active module must be set using the 'cd' command. - -This command does not operate on module with processes. -\end{lstlisting} - -\section{connect\_rpc -- connect to RPC frontend} -\label{cmd:connect_rpc} -\begin{lstlisting}[numbers=left,frame=single] - connect_rpc -exec [args...] - connect_rpc -path - -Load modules using an out-of-process frontend. - - -exec [args...] - run with arguments [args...]. send requests on stdin, read - responses from stdout. - - -path - connect to Unix domain socket at . (Unix) - connect to bidirectional byte-type named pipe at . (Windows) - -A simple JSON-based, newline-delimited protocol is used for communicating with -the frontend. Yosys requests data from the frontend by sending exactly 1 line -of JSON. Frontend responds with data or error message by replying with exactly -1 line of JSON as well. - - -> {"method": "modules"} - <- {"modules": ["", ...]} - <- {"error": ""} - request for the list of modules that can be derived by this frontend. - the 'hierarchy' command will call back into this frontend if a cell - with type is instantiated in the design. - - -> {"method": "derive", "module": ", "parameters": { - "": {"type": "[unsigned|signed|string|real]", - "value": ""}, ...}} - <- {"frontend": "[ilang|verilog|...]","source": ""}} - <- {"error": ""} - request for the module to be derived for a specific set of - parameters. starts with \ for named parameters, and with $ - for unnamed parameters, which are numbered starting at 1. - for integer parameters is always specified as a binary string of unlimited - precision. the returned by the frontend is hygienically parsed - by a built-in Yosys , allowing the RPC frontend to return any - convenient representation of the module. the derived module is cached, - so the response should be the same whenever the same set of parameters - is provided. -\end{lstlisting} - -\section{connwrappers -- match width of input-output port pairs} -\label{cmd:connwrappers} -\begin{lstlisting}[numbers=left,frame=single] - connwrappers [options] [selection] - -Wrappers are used in coarse-grain synthesis to wrap cells with smaller ports -in wrapper cells with a (larger) constant port size. I.e. the upper bits -of the wrapper output are signed/unsigned bit extended. This command uses this -knowledge to rewire the inputs of the driven cells to match the output of -the driving cell. - - -signed - -unsigned - consider the specified signed/unsigned wrapper output - - -port - use the specified parameter to decide if signed or unsigned - -The options -signed, -unsigned, and -port can be specified multiple times. -\end{lstlisting} - -\section{coolrunner2\_fixup -- insert necessary buffer cells for CoolRunner-II architecture} -\label{cmd:coolrunner2_fixup} -\begin{lstlisting}[numbers=left,frame=single] - coolrunner2_fixup [options] [selection] - -Insert necessary buffer cells for CoolRunner-II architecture. -\end{lstlisting} - -\section{coolrunner2\_sop -- break \$sop cells into ANDTERM/ORTERM cells} -\label{cmd:coolrunner2_sop} -\begin{lstlisting}[numbers=left,frame=single] - coolrunner2_sop [options] [selection] - -Break $sop cells into ANDTERM/ORTERM cells. -\end{lstlisting} - -\section{copy -- copy modules in the design} -\label{cmd:copy} -\begin{lstlisting}[numbers=left,frame=single] - copy old_name new_name - -Copy the specified module. Note that selection patterns are not supported -by this command. -\end{lstlisting} - -\section{cover -- print code coverage counters} -\label{cmd:cover} -\begin{lstlisting}[numbers=left,frame=single] - cover [options] [pattern] - -Print the code coverage counters collected using the cover() macro in the Yosys -C++ code. This is useful to figure out what parts of Yosys are utilized by a -test bench. - - -q - Do not print output to the normal destination (console and/or log file) - - -o file - Write output to this file, truncate if exists. - - -a file - Write output to this file, append if exists. - - -d dir - Write output to a newly created file in the specified directory. - -When one or more pattern (shell wildcards) are specified, then only counters -matching at least one pattern are printed. - - -It is also possible to instruct Yosys to print the coverage counters on program -exit to a file using environment variables: - - YOSYS_COVER_DIR="{dir-name}" yosys {args} - - This will create a file (with an auto-generated name) in this - directory and write the coverage counters to it. - - YOSYS_COVER_FILE="{file-name}" yosys {args} - - This will append the coverage counters to the specified file. - - -Hint: Use the following AWK command to consolidate Yosys coverage files: - - gawk '{ p[$3] = $1; c[$3] += $2; } END { for (i in p) - printf "%-60s %10d %s\n", p[i], c[i], i; }' {files} | sort -k3 - - -Coverage counters are only available in Yosys for Linux. -\end{lstlisting} - -\section{cutpoint -- adds formal cut points to the design} -\label{cmd:cutpoint} -\begin{lstlisting}[numbers=left,frame=single] - cutpoint [options] [selection] - -This command adds formal cut points to the design. - - -undef - set cupoint nets to undef (x). the default behavior is to create a - $anyseq cell and drive the cutpoint net from that -\end{lstlisting} - -\section{debug -- run command with debug log messages enabled} -\label{cmd:debug} -\begin{lstlisting}[numbers=left,frame=single] - debug cmd - -Execute the specified command with debug log messages enabled -\end{lstlisting} - -\section{delete -- delete objects in the design} -\label{cmd:delete} -\begin{lstlisting}[numbers=left,frame=single] - delete [selection] - -Deletes the selected objects. This will also remove entire modules, if the -whole module is selected. - - - delete {-input|-output|-port} [selection] - -Does not delete any object but removes the input and/or output flag on the -selected wires, thus 'deleting' module ports. -\end{lstlisting} - -\section{deminout -- demote inout ports to input or output} -\label{cmd:deminout} -\begin{lstlisting}[numbers=left,frame=single] - deminout [options] [selection] - -"Demote" inout ports to input or output ports, if possible. -\end{lstlisting} - -\section{design -- save, restore and reset current design} -\label{cmd:design} -\begin{lstlisting}[numbers=left,frame=single] - design -reset - -Clear the current design. - - - design -save - -Save the current design under the given name. - - - design -stash - -Save the current design under the given name and then clear the current design. - - - design -push - -Push the current design to the stack and then clear the current design. - - - design -push-copy - -Push the current design to the stack without clearing the current design. - - - design -pop - -Reset the current design and pop the last design from the stack. - - - design -load - -Reset the current design and load the design previously saved under the given -name. - - - design -copy-from [-as ] - -Copy modules from the specified design into the current one. The selection is -evaluated in the other design. - - - design -copy-to [-as ] [selection] - -Copy modules from the current design into the specified one. - - - design -import [-as ] [selection] - -Import the specified design into the current design. The source design must -either have a selected top module or the selection must contain exactly one -module that is then used as top module for this command. - - - design -reset-vlog - -The Verilog front-end remembers defined macros and top-level declarations -between calls to 'read_verilog'. This command resets this memory. -\end{lstlisting} - -\section{determine\_init -- Determine the init value of cells} -\label{cmd:determine_init} -\begin{lstlisting}[numbers=left,frame=single] - determine_init [selection] - -Determine the init value of cells that doesn't allow unknown init value. -\end{lstlisting} - -\section{dff2dffe -- transform \$dff cells to \$dffe cells} -\label{cmd:dff2dffe} -\begin{lstlisting}[numbers=left,frame=single] - dff2dffe [options] [selection] - -This pass transforms $dff cells driven by a tree of multiplexers with one or -more feedback paths to $dffe cells. It also works on gate-level cells such as -$_DFF_P_, $_DFF_N_ and $_MUX_. - - -unmap - operate in the opposite direction: replace $dffe cells with combinations - of $dff and $mux cells. the options below are ignored in unmap mode. - - -unmap-mince N - Same as -unmap but only unmap $dffe where the clock enable port - signal is used by less $dffe than the specified number - - -direct - map directly to external gate type. can - be any internal gate-level FF cell (except $_DFFE_??_). the - is the cell type name for a cell with an - identical interface to the , except it - also has an high-active enable port 'E'. - Usually is an intermediate cell type - that is then translated to the final type using 'techmap'. - - -direct-match - like -direct for all DFF cell types matching the expression. - this will use $__DFFE_* as matching the - internal gate type $_DFF_*_, and $__DFFSE_* for those matching - $_DFFS_*_, except for $_DFF_[NP]_, which is converted to - $_DFFE_[NP]_. -\end{lstlisting} - -\section{dff2dffs -- process sync set/reset with SR over CE priority} -\label{cmd:dff2dffs} -\begin{lstlisting}[numbers=left,frame=single] - dff2dffs [options] [selection] - -Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before -dff2dffe for SR over CE priority. - - -match-init - Disallow merging synchronous set/reset that has polarity opposite of the - output wire's init attribute (if any). -\end{lstlisting} - -\section{dffinit -- set INIT param on FF cells} -\label{cmd:dffinit} -\begin{lstlisting}[numbers=left,frame=single] - dffinit [options] [selection] - -This pass sets an FF cell parameter to the the initial value of the net it -drives. (This is primarily used in FPGA flows.) - - -ff - operate on the specified cell type. this option can be used - multiple times. - - -highlow - use the string values "high" and "low" to represent a single-bit - initial value of 1 or 0. (multi-bit values are not supported in this - mode.) - - -strinit - use string values in the command line to represent a single-bit - initial value of 1 or 0. (multi-bit values are not supported in this - mode.) - - -noreinit - fail if the FF cell has already a defined initial value set in other - passes and the initial value of the net it drives is not equal to - the already defined initial value. -\end{lstlisting} - -\section{dfflibmap -- technology mapping of flip-flops} -\label{cmd:dfflibmap} -\begin{lstlisting}[numbers=left,frame=single] - dfflibmap [-prepare] -liberty [selection] - -Map internal flip-flop cells to the flip-flop cells in the technology -library specified in the given liberty file. - -This pass may add inverters as needed. Therefore it is recommended to -first run this pass and then map the logic paths to the target technology. - -When called with -prepare, this command will convert the internal FF cells -to the internal cell types that best match the cells found in the given -liberty file. -\end{lstlisting} - -\section{dump -- print parts of the design in ilang format} -\label{cmd:dump} -\begin{lstlisting}[numbers=left,frame=single] - dump [options] [selection] - -Write the selected parts of the design to the console or specified file in -ilang format. - - -m - also dump the module headers, even if only parts of a single - module is selected - - -n - only dump the module headers if the entire module is selected - - -o - write to the specified file. - - -a - like -outfile but append instead of overwrite -\end{lstlisting} - -\section{echo -- turning echoing back of commands on and off} -\label{cmd:echo} -\begin{lstlisting}[numbers=left,frame=single] - echo on - -Print all commands to log before executing them. - - - echo off - -Do not print all commands to log before executing them. (default) -\end{lstlisting} - -\section{ecp5\_ffinit -- ECP5: handle FF init values} -\label{cmd:ecp5_ffinit} -\begin{lstlisting}[numbers=left,frame=single] - ecp5_ffinit [options] [selection] - -Remove init values for FF output signals when equal to reset value. -If reset is not used, set the reset value to the init value, otherwise -unmap out the reset (if not an async reset). -\end{lstlisting} - -\section{ecp5\_gsr -- ECP5: handle GSR} -\label{cmd:ecp5_gsr} -\begin{lstlisting}[numbers=left,frame=single] - ecp5_gsr [options] [selection] - -Trim active low async resets connected to GSR and resolve GSR parameter, -if a GSR or SGSR primitive is used in the design. - -If any cell has the GSR parameter set to "AUTO", this will be resolved -to "ENABLED" if a GSR primitive is present and the (* nogsr *) attribute -is not set, otherwise it will be resolved to "DISABLED". -\end{lstlisting} - -\section{edgetypes -- list all types of edges in selection} -\label{cmd:edgetypes} -\begin{lstlisting}[numbers=left,frame=single] - edgetypes [options] [selection] - -This command lists all unique types of 'edges' found in the selection. An 'edge' -is a 4-tuple of source and sink cell type and port name. -\end{lstlisting} - -\section{efinix\_fixcarry -- Efinix: fix carry chain} -\label{cmd:efinix_fixcarry} -\begin{lstlisting}[numbers=left,frame=single] - efinix_fixcarry [options] [selection] - -Add Efinix adders to fix carry chain if needed. -\end{lstlisting} - -\section{efinix\_gbuf -- Efinix: insert global clock buffers} -\label{cmd:efinix_gbuf} -\begin{lstlisting}[numbers=left,frame=single] - efinix_gbuf [options] [selection] - -Add Efinix global clock buffers to top module as needed. -\end{lstlisting} - -\section{equiv\_add -- add a \$equiv cell} -\label{cmd:equiv_add} -\begin{lstlisting}[numbers=left,frame=single] - equiv_add [-try] gold_sig gate_sig - -This command adds an $equiv cell for the specified signals. - - - equiv_add [-try] -cell gold_cell gate_cell - -This command adds $equiv cells for the ports of the specified cells. -\end{lstlisting} - -\section{equiv\_induct -- proving \$equiv cells using temporal induction} -\label{cmd:equiv_induct} -\begin{lstlisting}[numbers=left,frame=single] - equiv_induct [options] [selection] - -Uses a version of temporal induction to prove $equiv cells. - -Only selected $equiv cells are proven and only selected cells are used to -perform the proof. - - -undef - enable modelling of undef states - - -seq - the max. number of time steps to be considered (default = 4) - -This command is very effective in proving complex sequential circuits, when -the internal state of the circuit quickly propagates to $equiv cells. - -However, this command uses a weak definition of 'equivalence': This command -proves that the two circuits will not diverge after they produce equal -outputs (observable points via $equiv) for at least cycles (the -specified via -seq). - -Combined with simulation this is very powerful because simulation can give -you confidence that the circuits start out synced for at least cycles -after reset. -\end{lstlisting} - -\section{equiv\_make -- prepare a circuit for equivalence checking} -\label{cmd:equiv_make} -\begin{lstlisting}[numbers=left,frame=single] - equiv_make [options] gold_module gate_module equiv_module - -This creates a module annotated with $equiv cells from two presumably -equivalent modules. Use commands such as 'equiv_simple' and 'equiv_status' -to work with the created equivalent checking module. - - -inames - Also match cells and wires with $... names. - - -blacklist - Do not match cells or signals that match the names in the file. - - -encfile - Match FSM encodings using the description from the file. - See 'help fsm_recode' for details. - -Note: The circuit created by this command is not a miter (with something like -a trigger output), but instead uses $equiv cells to encode the equivalence -checking problem. Use 'miter -equiv' if you want to create a miter circuit. -\end{lstlisting} - -\section{equiv\_mark -- mark equivalence checking regions} -\label{cmd:equiv_mark} -\begin{lstlisting}[numbers=left,frame=single] - equiv_mark [options] [selection] - -This command marks the regions in an equivalence checking module. Region 0 is -the proven part of the circuit. Regions with higher numbers are connected -unproven subcricuits. The integer attribute 'equiv_region' is set on all -wires and cells. -\end{lstlisting} - -\section{equiv\_miter -- extract miter from equiv circuit} -\label{cmd:equiv_miter} -\begin{lstlisting}[numbers=left,frame=single] - equiv_miter [options] miter_module [selection] - -This creates a miter module for further analysis of the selected $equiv cells. - - -trigger - Create a trigger output - - -cmp - Create cmp_* outputs for individual unproven $equiv cells - - -assert - Create a $assert cell for each unproven $equiv cell - - -undef - Create compare logic that handles undefs correctly -\end{lstlisting} - -\section{equiv\_opt -- prove equivalence for optimized circuit} -\label{cmd:equiv_opt} -\begin{lstlisting}[numbers=left,frame=single] - equiv_opt [options] [command] - -This command uses temporal induction to check circuit equivalence before and -after an optimization pass. - - -run : - only run the commands between the labels (see below). an empty - from label is synonymous to the start of the command list, and empty to - label is synonymous to the end of the command list. - - -map - expand the modules in this file before proving equivalence. this is - useful for handling architecture-specific primitives. - - -blacklist - Do not match cells or signals that match the names in the file - (passed to equiv_make). - - -assert - produce an error if the circuits are not equivalent. - - -multiclock - run clk2fflogic before equivalence checking. - - -async2sync - run async2sync before equivalence checking. - - -undef - enable modelling of undef states during equiv_induct. - -The following commands are executed by this verification command: - - run_pass: - hierarchy -auto-top - design -save preopt - [command] - design -stash postopt - - prepare: - design -copy-from preopt -as gold A:top - design -copy-from postopt -as gate A:top - - techmap: (only with -map) - techmap -wb -D EQUIV -autoproc -map ... - - prove: - clk2fflogic (only with -multiclock) - async2sync (only with -async2sync) - equiv_make -blacklist ... gold gate equiv - equiv_induct [-undef] equiv - equiv_status [-assert] equiv - - restore: - design -load preopt -\end{lstlisting} - -\section{equiv\_purge -- purge equivalence checking module} -\label{cmd:equiv_purge} -\begin{lstlisting}[numbers=left,frame=single] - equiv_purge [options] [selection] - -This command removes the proven part of an equivalence checking module, leaving -only the unproven segments in the design. This will also remove and add module -ports as needed. -\end{lstlisting} - -\section{equiv\_remove -- remove \$equiv cells} -\label{cmd:equiv_remove} -\begin{lstlisting}[numbers=left,frame=single] - equiv_remove [options] [selection] - -This command removes the selected $equiv cells. If neither -gold nor -gate is -used then only proven cells are removed. - - -gold - keep gold circuit - - -gate - keep gate circuit -\end{lstlisting} - -\section{equiv\_simple -- try proving simple \$equiv instances} -\label{cmd:equiv_simple} -\begin{lstlisting}[numbers=left,frame=single] - equiv_simple [options] [selection] - -This command tries to prove $equiv cells using a simple direct SAT approach. - - -v - verbose output - - -undef - enable modelling of undef states - - -short - create shorter input cones that stop at shared nodes. This yields - simpler SAT problems but sometimes fails to prove equivalence. - - -nogroup - disabling grouping of $equiv cells by output wire - - -seq - the max. number of time steps to be considered (default = 1) -\end{lstlisting} - -\section{equiv\_status -- print status of equivalent checking module} -\label{cmd:equiv_status} -\begin{lstlisting}[numbers=left,frame=single] - equiv_status [options] [selection] - -This command prints status information for all selected $equiv cells. - - -assert - produce an error if any unproven $equiv cell is found -\end{lstlisting} - -\section{equiv\_struct -- structural equivalence checking} -\label{cmd:equiv_struct} -\begin{lstlisting}[numbers=left,frame=single] - equiv_struct [options] [selection] - -This command adds additional $equiv cells based on the assumption that the -gold and gate circuit are structurally equivalent. Note that this can introduce -bad $equiv cells in cases where the netlists are not structurally equivalent, -for example when analyzing circuits with cells with commutative inputs. This -command will also de-duplicate gates. - - -fwd - by default this command performans forward sweeps until nothing can - be merged by forwards sweeps, then backward sweeps until forward - sweeps are effective again. with this option set only forward sweeps - are performed. - - -fwonly - add the specified cell type to the list of cell types that are only - merged in forward sweeps and never in backward sweeps. $equiv is in - this list automatically. - - -icells - by default, the internal RTL and gate cell types are ignored. add - this option to also process those cell types with this command. - - -maxiter - maximum number of iterations to run before aborting -\end{lstlisting} - -\section{eval -- evaluate the circuit given an input} -\label{cmd:eval} -\begin{lstlisting}[numbers=left,frame=single] - eval [options] [selection] - -This command evaluates the value of a signal given the value of all required -inputs. - - -set - set the specified signal to the specified value. - - -set-undef - set all unspecified source signals to undef (x) - - -table - create a truth table using the specified input signals - - -show - show the value for the specified signal. if no -show option is passed - then all output ports of the current module are used. -\end{lstlisting} - -\section{exec -- execute commands in the operating system shell} -\label{cmd:exec} -\begin{lstlisting}[numbers=left,frame=single] - exec [options] -- [command] - -Execute a command in the operating system shell. All supplied arguments are -concatenated and passed as a command to popen(3). Whitespace is not guaranteed -to be preserved, even if quoted. stdin and stderr are not connected, while stdout is -logged unless the "-q" option is specified. - - - -q - Suppress stdout and stderr from subprocess - - -expect-return - Generate an error if popen() does not return specified value. - May only be specified once; the final specified value is controlling - if specified multiple times. - - -expect-stdout - Generate an error if the specified regex does not match any line - in subprocess's stdout. May be specified multiple times. - - -not-expect-stdout - Generate an error if the specified regex matches any line - in subprocess's stdout. May be specified multiple times. - - - Example: exec -q -expect-return 0 -- echo "bananapie" | grep "nana" -\end{lstlisting} - -\section{expose -- convert internal signals to module ports} -\label{cmd:expose} -\begin{lstlisting}[numbers=left,frame=single] - expose [options] [selection] - -This command exposes all selected internal signals of a module as additional -outputs. - - -dff - only consider wires that are directly driven by register cell. - - -cut - when exposing a wire, create an input/output pair and cut the internal - signal path at that wire. - - -input - when exposing a wire, create an input port and disconnect the internal - driver. - - -shared - only expose those signals that are shared among the selected modules. - this is useful for preparing modules for equivalence checking. - - -evert - also turn connections to instances of other modules to additional - inputs and outputs and remove the module instances. - - -evert-dff - turn flip-flops to sets of inputs and outputs. - - -sep - when creating new wire/port names, the original object name is suffixed - with this separator (default: '.') and the port name or a type - designator for the exposed signal. -\end{lstlisting} - -\section{extract -- find subcircuits and replace them with cells} -\label{cmd:extract} -\begin{lstlisting}[numbers=left,frame=single] - extract -map [options] [selection] - extract -mine [options] [selection] - -This pass looks for subcircuits that are isomorphic to any of the modules -in the given map file and replaces them with instances of this modules. The -map file can be a Verilog source file (*.v) or an ilang file (*.il). - - -map - use the modules in this file as reference. This option can be used - multiple times. - - -map % - use the modules in this in-memory design as reference. This option can - be used multiple times. - - -verbose - print debug output while analyzing - - -constports - also find instances with constant drivers. this may be much - slower than the normal operation. - - -nodefaultswaps - normally builtin port swapping rules for internal cells are used per - default. This turns that off, so e.g. 'a^b' does not match 'b^a' - when this option is used. - - -compat - Per default, the cells in the map file (needle) must have the - type as the cells in the active design (haystack). This option - can be used to register additional pairs of types that should - match. This option can be used multiple times. - - -swap ,[,...] - Register a set of swappable ports for a needle cell type. - This option can be used multiple times. - - -perm ,[,...] ,[,...] - Register a valid permutation of swappable ports for a needle - cell type. This option can be used multiple times. - - -cell_attr - Attributes on cells with the given name must match. - - -wire_attr - Attributes on wires with the given name must match. - - -ignore_parameters - Do not use parameters when matching cells. - - -ignore_param - Do not use this parameter when matching cells. - -This pass does not operate on modules with unprocessed processes in it. -(I.e. the 'proc' pass should be used first to convert processes to netlists.) - -This pass can also be used for mining for frequent subcircuits. In this mode -the following options are to be used instead of the -map option. - - -mine - mine for frequent subcircuits and write them to the given ilang file - - -mine_cells_span - only mine for subcircuits with the specified number of cells - default value: 3 5 - - -mine_min_freq - only mine for subcircuits with at least the specified number of matches - default value: 10 - - -mine_limit_matches_per_module - when calculating the number of matches for a subcircuit, don't count - more than the specified number of matches per module - - -mine_max_fanout - don't consider internal signals with more than connections - -The modules in the map file may have the attribute 'extract_order' set to an -integer value. Then this value is used to determine the order in which the pass -tries to map the modules to the design (ascending, default value is 0). - -See 'help techmap' for a pass that does the opposite thing. -\end{lstlisting} - -\section{extract\_counter -- Extract GreenPak4 counter cells} -\label{cmd:extract_counter} -\begin{lstlisting}[numbers=left,frame=single] - extract_counter [options] [selection] - -This pass converts non-resettable or async resettable down counters to -counter cells. Use a target-specific 'techmap' map file to convert those cells -to the actual target cells. - - -maxwidth N - Only extract counters up to N bits wide (default 64) - - -minwidth N - Only extract counters at least N bits wide (default 2) - - -allow_arst yes|no - Allow counters to have async reset (default yes) - - -dir up|down|both - Look for up-counters, down-counters, or both (default down) - - -pout X,Y,... - Only allow parallel output from the counter to the listed cell types - (if not specified, parallel outputs are not restricted) -\end{lstlisting} - -\section{extract\_fa -- find and extract full/half adders} -\label{cmd:extract_fa} -\begin{lstlisting}[numbers=left,frame=single] - extract_fa [options] [selection] - -This pass extracts full/half adders from a gate-level design. - - -fa, -ha - Enable cell types (fa=full adder, ha=half adder) - All types are enabled if none of this options is used - - -d - Set maximum depth for extracted logic cones (default=20) - - -b - Set maximum breadth for extracted logic cones (default=6) - - -v - Verbose output -\end{lstlisting} - -\section{extract\_reduce -- converts gate chains into \$reduce\_* cells} -\label{cmd:extract_reduce} -\begin{lstlisting}[numbers=left,frame=single] - extract_reduce [options] [selection] - -converts gate chains into $reduce_* cells - -This command finds chains of $_AND_, $_OR_, and $_XOR_ cells and replaces them -with their corresponding $reduce_* cells. Because this command only operates on -these cell types, it is recommended to map the design to only these cell types -using the `abc -g` command. Note that, in some cases, it may be more effective -to map the design to only $_AND_ cells, run extract_reduce, map the remaining -parts of the design to AND/OR/XOR cells, and run extract_reduce a second time. - - -allow-off-chain - Allows matching of cells that have loads outside the chain. These cells - will be replicated and folded into the $reduce_* cell, but the original - cell will remain, driving its original loads. -\end{lstlisting} - -\section{extractinv -- extract explicit inverter cells for invertible cell pins} -\label{cmd:extractinv} -\begin{lstlisting}[numbers=left,frame=single] - extractinv [options] [selection] - -Searches the design for all cells with invertible pins controlled by a cell -parameter (eg. IS_CLK_INVERTED on many Xilinx cells) and removes the parameter. -If the parameter was set to 1, inserts an explicit inverter cell in front of -the pin instead. Normally used for output to ISE, which does not support the -inversion parameters. - -To mark a cell port as invertible, use (* invertible_pin = "param_name" *) -on the wire in the blackbox module. The parameter value should have -the same width as the port, and will be effectively XORed with it. - - -inv : - Specifies the cell type to use for the inverters and its port names. - This option is required. -\end{lstlisting} - -\section{flatten -- flatten design} -\label{cmd:flatten} -\begin{lstlisting}[numbers=left,frame=single] - flatten [options] [selection] - -This pass flattens the design by replacing cells by their implementation. This -pass is very similar to the 'techmap' pass. The only difference is that this -pass is using the current design as mapping library. - -Cells and/or modules with the 'keep_hierarchy' attribute set will not be -flattened by this command. - - -wb - Ignore the 'whitebox' attribute on cell implementations. -\end{lstlisting} - -\section{flowmap -- pack LUTs with FlowMap} -\label{cmd:flowmap} -\begin{lstlisting}[numbers=left,frame=single] - flowmap [options] [selection] - -This pass uses the FlowMap technology mapping algorithm to pack logic gates -into k-LUTs with optimal depth. It allows mapping any circuit elements that can -be evaluated with the `eval` pass, including cells with multiple output ports -and multi-bit input and output ports. - - -maxlut k - perform technology mapping for a k-LUT architecture. if not specified, - defaults to 3. - - -minlut n - only produce n-input or larger LUTs. if not specified, defaults to 1. - - -cells [,,...] - map specified cells. if not specified, maps $_NOT_, $_AND_, $_OR_, - $_XOR_ and $_MUX_, which are the outputs of the `simplemap` pass. - - -relax - perform depth relaxation and area minimization. - - -r-alpha n, -r-beta n, -r-gamma n - parameters of depth relaxation heuristic potential function. - if not specified, alpha=8, beta=2, gamma=1. - - -optarea n - optimize for area by trading off at most n logic levels for fewer LUTs. - n may be zero, to optimize for area without increasing depth. - implies -relax. - - -debug - dump intermediate graphs. - - -debug-relax - explain decisions performed during depth relaxation. -\end{lstlisting} - -\section{fmcombine -- combine two instances of a cell into one} -\label{cmd:fmcombine} -\begin{lstlisting}[numbers=left,frame=single] - fmcombine [options] module_name gold_cell gate_cell - -This pass takes two cells, which are instances of the same module, and replaces -them with one instance of a special 'combined' module, that effectively -contains two copies of the original module, plus some formal properties. - -This is useful for formal test benches that check what differences in behavior -a slight difference in input causes in a module. - - -initeq - Insert assumptions that initially all FFs in both circuits have the - same initial values. - - -anyeq - Do not duplicate $anyseq/$anyconst cells. - - -fwd - Insert forward hint assumptions into the combined module. - - -bwd - Insert backward hint assumptions into the combined module. - (Backward hints are logically equivalend to fordward hits, but - some solvers are faster with bwd hints, or even both -bwd and -fwd.) - - -nop - Don't insert hint assumptions into the combined module. - (This should not provide any speedup over the original design, but - strangely sometimes it does.) - -If none of -fwd, -bwd, and -nop is given, then -fwd is used as default. -\end{lstlisting} - -\section{fminit -- set init values/sequences for formal} -\label{cmd:fminit} -\begin{lstlisting}[numbers=left,frame=single] - fminit [options] - -This pass creates init constraints (for example for reset sequences) in a formal -model. - - -seq - Set sequence using comma-separated list of values, use 'z for - unconstrained bits. The last value is used for the remainder of the - trace. - - -set - Add constant value constraint - - -posedge - -negedge - Set clock for init sequences -\end{lstlisting} - -\section{freduce -- perform functional reduction} -\label{cmd:freduce} -\begin{lstlisting}[numbers=left,frame=single] - freduce [options] [selection] - -This pass performs functional reduction in the circuit. I.e. if two nodes are -equivalent, they are merged to one node and one of the redundant drivers is -disconnected. A subsequent call to 'clean' will remove the redundant drivers. - - -v, -vv - enable verbose or very verbose output - - -inv - enable explicit handling of inverted signals - - -stop - stop after reduction operations. this is mostly used for - debugging the freduce command itself. - - -dump - dump the design to __.il after each reduction - operation. this is mostly used for debugging the freduce command. - -This pass is undef-aware, i.e. it considers don't-care values for detecting -equivalent nodes. - -All selected wires are considered for rewiring. The selected cells cover the -circuit that is analyzed. -\end{lstlisting} - -\section{fsm -- extract and optimize finite state machines} -\label{cmd:fsm} -\begin{lstlisting}[numbers=left,frame=single] - fsm [options] [selection] - -This pass calls all the other fsm_* passes in a useful order. This performs -FSM extraction and optimization. It also calls opt_clean as needed: - - fsm_detect unless got option -nodetect - fsm_extract - - fsm_opt - opt_clean - fsm_opt - - fsm_expand if got option -expand - opt_clean if got option -expand - fsm_opt if got option -expand - - fsm_recode unless got option -norecode - - fsm_info - - fsm_export if got option -export - fsm_map unless got option -nomap - -Options: - - -expand, -norecode, -export, -nomap - enable or disable passes as indicated above - - -fullexpand - call expand with -full option - - -encoding type - -fm_set_fsm_file file - -encfile file - passed through to fsm_recode pass -\end{lstlisting} - -\section{fsm\_detect -- finding FSMs in design} -\label{cmd:fsm_detect} -\begin{lstlisting}[numbers=left,frame=single] - fsm_detect [selection] - -This pass detects finite state machines by identifying the state signal. -The state signal is then marked by setting the attribute 'fsm_encoding' -on the state signal to "auto". - -Existing 'fsm_encoding' attributes are not changed by this pass. - -Signals can be protected from being detected by this pass by setting the -'fsm_encoding' attribute to "none". -\end{lstlisting} - -\section{fsm\_expand -- expand FSM cells by merging logic into it} -\label{cmd:fsm_expand} -\begin{lstlisting}[numbers=left,frame=single] - fsm_expand [-full] [selection] - -The fsm_extract pass is conservative about the cells that belong to a finite -state machine. This pass can be used to merge additional auxiliary gates into -the finite state machine. - -By default, fsm_expand is still a bit conservative regarding merging larger -word-wide cells. Call with -full to consider all cells for merging. -\end{lstlisting} - -\section{fsm\_export -- exporting FSMs to KISS2 files} -\label{cmd:fsm_export} -\begin{lstlisting}[numbers=left,frame=single] - fsm_export [-noauto] [-o filename] [-origenc] [selection] - -This pass creates a KISS2 file for every selected FSM. For FSMs with the -'fsm_export' attribute set, the attribute value is used as filename, otherwise -the module and cell name is used as filename. If the parameter '-o' is given, -the first exported FSM is written to the specified filename. This overwrites -the setting as specified with the 'fsm_export' attribute. All other FSMs are -exported to the default name as mentioned above. - - -noauto - only export FSMs that have the 'fsm_export' attribute set - - -o filename - filename of the first exported FSM - - -origenc - use binary state encoding as state names instead of s0, s1, ... -\end{lstlisting} - -\section{fsm\_extract -- extracting FSMs in design} -\label{cmd:fsm_extract} -\begin{lstlisting}[numbers=left,frame=single] - fsm_extract [selection] - -This pass operates on all signals marked as FSM state signals using the -'fsm_encoding' attribute. It consumes the logic that creates the state signal -and uses the state signal to generate control signal and replaces it with an -FSM cell. - -The generated FSM cell still generates the original state signal with its -original encoding. The 'fsm_opt' pass can be used in combination with the -'opt_clean' pass to eliminate this signal. -\end{lstlisting} - -\section{fsm\_info -- print information on finite state machines} -\label{cmd:fsm_info} -\begin{lstlisting}[numbers=left,frame=single] - fsm_info [selection] - -This pass dumps all internal information on FSM cells. It can be useful for -analyzing the synthesis process and is called automatically by the 'fsm' -pass so that this information is included in the synthesis log file. -\end{lstlisting} - -\section{fsm\_map -- mapping FSMs to basic logic} -\label{cmd:fsm_map} -\begin{lstlisting}[numbers=left,frame=single] - fsm_map [selection] - -This pass translates FSM cells to flip-flops and logic. -\end{lstlisting} - -\section{fsm\_opt -- optimize finite state machines} -\label{cmd:fsm_opt} -\begin{lstlisting}[numbers=left,frame=single] - fsm_opt [selection] - -This pass optimizes FSM cells. It detects which output signals are actually -not used and removes them from the FSM. This pass is usually used in -combination with the 'opt_clean' pass (see also 'help fsm'). -\end{lstlisting} - -\section{fsm\_recode -- recoding finite state machines} -\label{cmd:fsm_recode} -\begin{lstlisting}[numbers=left,frame=single] - fsm_recode [options] [selection] - -This pass reassign the state encodings for FSM cells. At the moment only -one-hot encoding and binary encoding is supported. - -encoding - specify the encoding scheme used for FSMs without the - 'fsm_encoding' attribute or with the attribute set to `auto'. - - -fm_set_fsm_file - generate a file containing the mapping from old to new FSM encoding - in form of Synopsys Formality set_fsm_* commands. - - -encfile - write the mappings from old to new FSM encoding to a file in the - following format: - - .fsm - .map -\end{lstlisting} - -\section{greenpak4\_dffinv -- merge greenpak4 inverters and DFF/latches} -\label{cmd:greenpak4_dffinv} -\begin{lstlisting}[numbers=left,frame=single] - greenpak4_dffinv [options] [selection] - -Merge GP_INV cells with GP_DFF* and GP_DLATCH* cells. -\end{lstlisting} - -\section{help -- display help messages} -\label{cmd:help} -\begin{lstlisting}[numbers=left,frame=single] - help ................ list all commands - help ...... print help message for given command - help -all ........... print complete command reference - - help -cells .......... list all cell types - help ..... print help message for given cell type - help + .... print verilog code for given cell type -\end{lstlisting} - -\section{hierarchy -- check, expand and clean up design hierarchy} -\label{cmd:hierarchy} -\begin{lstlisting}[numbers=left,frame=single] - hierarchy [-check] [-top ] - hierarchy -generate - -In parametric designs, a module might exists in several variations with -different parameter values. This pass looks at all modules in the current -design an re-runs the language frontends for the parametric modules as -needed. It also resolves assignments to wired logic data types (wand/wor), -resolves positional module parameters, unroll array instances, and more. - - -check - also check the design hierarchy. this generates an error when - an unknown module is used as cell type. - - -simcheck - like -check, but also throw an error if blackbox modules are - instantiated, and throw an error if the design has no top module. - - -purge_lib - by default the hierarchy command will not remove library (blackbox) - modules. use this option to also remove unused blackbox modules. - - -libdir - search for files named .v in the specified directory - for unknown modules and automatically run read_verilog for each - unknown module. - - -keep_positionals - per default this pass also converts positional arguments in cells - to arguments using port names. This option disables this behavior. - - -keep_portwidths - per default this pass adjusts the port width on cells that are - module instances when the width does not match the module port. This - option disables this behavior. - - -nodefaults - do not resolve input port default values - - -nokeep_asserts - per default this pass sets the "keep" attribute on all modules - that directly or indirectly contain one or more formal properties. - This option disables this behavior. - - -top - use the specified top module to build the design hierarchy. Modules - outside this tree (unused modules) are removed. - - when the -top option is used, the 'top' attribute will be set on the - specified top module. otherwise a module with the 'top' attribute set - will implicitly be used as top module, if such a module exists. - - -auto-top - automatically determine the top of the design hierarchy and mark it. - - -chparam name value - elaborate the top module using this parameter value. Modules on which - this parameter does not exist may cause a warning message to be output. - This option can be specified multiple times to override multiple - parameters. String values must be passed in double quotes ("). - -In -generate mode this pass generates blackbox modules for the given cell -types (wildcards supported). For this the design is searched for cells that -match the given types and then the given port declarations are used to -determine the direction of the ports. The syntax for a port declaration is: - - {i|o|io}[@]: - -Input ports are specified with the 'i' prefix, output ports with the 'o' -prefix and inout ports with the 'io' prefix. The optional specifies -the position of the port in the parameter list (needed when instantiated -using positional arguments). When is not specified, the can -also contain wildcard characters. - -This pass ignores the current selection and always operates on all modules -in the current design. -\end{lstlisting} - -\section{hilomap -- technology mapping of constant hi- and/or lo-drivers} -\label{cmd:hilomap} -\begin{lstlisting}[numbers=left,frame=single] - hilomap [options] [selection] - -Map constants to 'tielo' and 'tiehi' driver cells. - - -hicell - Replace constant hi bits with this cell. - - -locell - Replace constant lo bits with this cell. - - -singleton - Create only one hi/lo cell and connect all constant bits - to that cell. Per default a separate cell is created for - each constant bit. -\end{lstlisting} - -\section{history -- show last interactive commands} -\label{cmd:history} -\begin{lstlisting}[numbers=left,frame=single] - history - -This command prints all commands in the shell history buffer. This are -all commands executed in an interactive session, but not the commands -from executed scripts. -\end{lstlisting} - -\section{ice40\_braminit -- iCE40: perform SB\_RAM40\_4K initialization from file} -\label{cmd:ice40_braminit} -\begin{lstlisting}[numbers=left,frame=single] - ice40_braminit - -This command processes all SB_RAM40_4K blocks with a non-empty INIT_FILE -parameter and converts it into the required INIT_x attributes -\end{lstlisting} - -\section{ice40\_dsp -- iCE40: map multipliers} -\label{cmd:ice40_dsp} -\begin{lstlisting}[numbers=left,frame=single] - ice40_dsp [options] [selection] - -Map multipliers ($mul/SB_MAC16) and multiply-accumulate ($mul/SB_MAC16 + $add) -cells into iCE40 DSP resources. -Currently, only the 16x16 multiply mode is supported and not the 2 x 8x8 mode. - -Pack input registers (A, B, {C,D}; with optional hold), pipeline registers -({F,J,K,G}, H), output registers (O -- full 32-bits or lower 16-bits only; with -optional hold), and post-adder into into the SB_MAC16 resource. - -Multiply-accumulate operations using the post-adder with feedback on the {C,D} -input will be folded into the DSP. In this scenario only, resetting the -the accumulator to an arbitrary value can be inferred to use the {C,D} input. -\end{lstlisting} - -\section{ice40\_ffinit -- iCE40: handle FF init values} -\label{cmd:ice40_ffinit} -\begin{lstlisting}[numbers=left,frame=single] - ice40_ffinit [options] [selection] - -Remove zero init values for FF output signals. Add inverters to implement -nonzero init values. -\end{lstlisting} - -\section{ice40\_ffssr -- iCE40: merge synchronous set/reset into FF cells} -\label{cmd:ice40_ffssr} -\begin{lstlisting}[numbers=left,frame=single] - ice40_ffssr [options] [selection] - -Merge synchronous set/reset $_MUX_ cells into iCE40 FFs. -\end{lstlisting} - -\section{ice40\_opt -- iCE40: perform simple optimizations} -\label{cmd:ice40_opt} -\begin{lstlisting}[numbers=left,frame=single] - ice40_opt [options] [selection] - -This command executes the following script: - - do - - opt_expr -mux_undef -undriven [-full] - opt_merge - opt_rmdff - opt_clean - while -\end{lstlisting} - -\section{ice40\_wrapcarry -- iCE40: wrap carries} -\label{cmd:ice40_wrapcarry} -\begin{lstlisting}[numbers=left,frame=single] - ice40_wrapcarry [selection] - -Wrap manually instantiated SB_CARRY cells, along with their associated SB_LUT4s, -into an internal $__ICE40_CARRY_WRAPPER cell for preservation across technology -mapping. - -Attributes on both cells will have their names prefixed with 'SB_CARRY.' or -'SB_LUT4.' and attached to the wrapping cell. -A (* keep *) attribute on either cell will be logically OR-ed together. - - -unwrap - unwrap $__ICE40_CARRY_WRAPPER cells back into SB_CARRYs and SB_LUT4s, - including restoring their attributes. -\end{lstlisting} - -\section{insbuf -- insert buffer cells for connected wires} -\label{cmd:insbuf} -\begin{lstlisting}[numbers=left,frame=single] - insbuf [options] [selection] - -Insert buffer cells into the design for directly connected wires. - - -buf - Use the given cell type instead of $_BUF_. (Notice that the next - call to "clean" will remove all $_BUF_ in the design.) -\end{lstlisting} - -\section{iopadmap -- technology mapping of i/o pads (or buffers)} -\label{cmd:iopadmap} -\begin{lstlisting}[numbers=left,frame=single] - iopadmap [options] [selection] - -Map module inputs/outputs to PAD cells from a library. This pass -can only map to very simple PAD cells. Use 'techmap' to further map -the resulting cells to more sophisticated PAD cells. - - -inpad [:] - Map module input ports to the given cell type with the - given output port name. if a 2nd portname is given, the - signal is passed through the pad call, using the 2nd - portname as the port facing the module port. - - -outpad [:] - -inoutpad [:] - Similar to -inpad, but for output and inout ports. - - -toutpad :[:] - Merges $_TBUF_ cells into the output pad cell. This takes precedence - over the other -outpad cell. The first portname is the enable input - of the tristate driver. - - -tinoutpad ::[:] - Merges $_TBUF_ cells into the inout pad cell. This takes precedence - over the other -inoutpad cell. The first portname is the enable input - of the tristate driver and the 2nd portname is the internal output - buffering the external signal. - - -ignore [:]* - Skips mapping inputs/outputs that are already connected to given - ports of the given cell. Can be used multiple times. This is in - addition to the cells specified as mapping targets. - - -widthparam - Use the specified parameter name to set the port width. - - -nameparam - Use the specified parameter to set the port name. - - -bits - create individual bit-wide buffers even for ports that - are wider. (the default behavior is to create word-wide - buffers using -widthparam to set the word size on the cell.) - -Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode. -\end{lstlisting} - -\section{json -- write design in JSON format} -\label{cmd:json} -\begin{lstlisting}[numbers=left,frame=single] - json [options] [selection] - -Write a JSON netlist of all selected objects. - - -o - write to the specified file. - - -aig - also include AIG models for the different gate types - - -compat-int - emit 32-bit or smaller fully-defined parameter values directly - as JSON numbers (for compatibility with old parsers) - -See 'help write_json' for a description of the JSON format used. -\end{lstlisting} - -\section{log -- print text and log files} -\label{cmd:log} -\begin{lstlisting}[numbers=left,frame=single] - log string - -Print the given string to the screen and/or the log file. This is useful for TCL -scripts, because the TCL command "puts" only goes to stdout but not to -logfiles. - - -stdout - Print the output to stdout too. This is useful when all Yosys is executed - with a script and the -q (quiet operation) argument to notify the user. - - -stderr - Print the output to stderr too. - - -nolog - Don't use the internal log() command. Use either -stdout or -stderr, - otherwise no output will be generated at all. - - -n - do not append a newline -\end{lstlisting} - -\section{logger -- set logger properties} -\label{cmd:logger} -\begin{lstlisting}[numbers=left,frame=single] - logger [options] - -This command sets global logger properties, also available using command line -options. - - -[no]time - enable/disable display of timestamp in log output. - - -[no]stderr - enable/disable logging errors to stderr. - - -warn regex - print a warning for all log messages matching the regex. - - -nowarn regex - if a warning message matches the regex, it is printed as regular - message instead. - - -werror regex - if a warning message matches the regex, it is printed as error - message instead and the tool terminates with a nonzero return code. - - -[no]debug - globally enable/disable debug log messages. - - -experimental - do not print warnings for the specified experimental feature - - -expect - expect log,warning or error to appear. In case of error return code is 0. - - -expect-no-warnings - gives error in case there is at least one warning that is not expected. -\end{lstlisting} - -\section{ls -- list modules or objects in modules} -\label{cmd:ls} -\begin{lstlisting}[numbers=left,frame=single] - ls [selection] - -When no active module is selected, this prints a list of modules. - -When an active module is selected, this prints a list of objects in the module. -\end{lstlisting} - -\section{ltp -- print longest topological path} -\label{cmd:ltp} -\begin{lstlisting}[numbers=left,frame=single] - ltp [options] [selection] - -This command prints the longest topological path in the design. (Only considers -paths within a single module, so the design must be flattened.) - - -noff - automatically exclude FF cell types -\end{lstlisting} - -\section{lut2mux -- convert \$lut to \$\_MUX\_} -\label{cmd:lut2mux} -\begin{lstlisting}[numbers=left,frame=single] - lut2mux [options] [selection] - -This pass converts $lut cells to $_MUX_ gates. -\end{lstlisting} - -\section{maccmap -- mapping macc cells} -\label{cmd:maccmap} -\begin{lstlisting}[numbers=left,frame=single] - maccmap [-unmap] [selection] - -This pass maps $macc cells to yosys $fa and $alu cells. When the -unmap option -is used then the $macc cell is mapped to $add, $sub, etc. cells instead. -\end{lstlisting} - -\section{memory -- translate memories to basic cells} -\label{cmd:memory} -\begin{lstlisting}[numbers=left,frame=single] - memory [-nomap] [-nordff] [-memx] [-bram ] [selection] - -This pass calls all the other memory_* passes in a useful order: - - opt_mem - memory_dff [-nordff] (-memx implies -nordff) - opt_clean - memory_share - opt_clean - memory_memx (when called with -memx) - memory_collect - memory_bram -rules (when called with -bram) - memory_map (skipped if called with -nomap) - -This converts memories to word-wide DFFs and address decoders -or multiport memory blocks if called with the -nomap option. -\end{lstlisting} - -\section{memory\_bram -- map memories to block rams} -\label{cmd:memory_bram} -\begin{lstlisting}[numbers=left,frame=single] - memory_bram -rules [selection] - -This pass converts the multi-port $mem memory cells into block ram instances. -The given rules file describes the available resources and how they should be -used. - -The rules file contains configuration options, a set of block ram description -and a sequence of match rules. - -The option 'attr_icase' configures how attribute values are matched. The value 0 -means case-sensitive, 1 means case-insensitive. - -A block ram description looks like this: - - bram RAMB1024X32 # name of BRAM cell - init 1 # set to '1' if BRAM can be initialized - abits 10 # number of address bits - dbits 32 # number of data bits - groups 2 # number of port groups - ports 1 1 # number of ports in each group - wrmode 1 0 # set to '1' if this groups is write ports - enable 4 1 # number of enable bits - transp 0 2 # transparent (for read ports) - clocks 1 2 # clock configuration - clkpol 2 2 # clock polarity configuration - endbram - -For the option 'transp' the value 0 means non-transparent, 1 means transparent -and a value greater than 1 means configurable. All groups with the same -value greater than 1 share the same configuration bit. - -For the option 'clocks' the value 0 means non-clocked, and a value greater -than 0 means clocked. All groups with the same value share the same clock -signal. - -For the option 'clkpol' the value 0 means negative edge, 1 means positive edge -and a value greater than 1 means configurable. All groups with the same value -greater than 1 share the same configuration bit. - -Using the same bram name in different bram blocks will create different variants -of the bram. Verilog configuration parameters for the bram are created as needed. - -It is also possible to create variants by repeating statements in the bram block -and appending '@