pax_global_header00006660000000000000000000000064147754037420014527gustar00rootroot0000000000000052 comment=fee39a3284c90249e1d9684cf6944ffbbcbb8f90 yosys-0.52/000077500000000000000000000000001477540374200126435ustar00rootroot00000000000000yosys-0.52/.clang-format000066400000000000000000000004251477540374200152170ustar00rootroot00000000000000# Default Linux style BasedOnStyle: LLVM IndentWidth: 8 UseTab: Always BreakBeforeBraces: Linux AllowShortIfStatementsOnASingleLine: false IndentCaseLabels: false # From guidelines/CodingStyle TabWidth: 8 ContinuationIndentWidth: 2 ColumnLimit: 150 # BreakBeforeBraces: Linux yosys-0.52/.dockerignore000066400000000000000000000002071477540374200153160ustar00rootroot00000000000000.editorconfig .gitignore .gitmodules .github .git Dockerfile README.md manual guidelines CodeOfConduct .travis .travis.yml yosys-0.52/.editorconfig000066400000000000000000000003601477540374200153170ustar00rootroot00000000000000root = true [*] indent_style = tab indent_size = tab trim_trailing_whitespace = true insert_final_newline = true [abc/**] indent_style = space indent_size = 2 trim_trailing_whitespace = false [*.yml] indent_style = space indent_size = 2 yosys-0.52/.gitattributes000066400000000000000000000000671477540374200155410ustar00rootroot00000000000000*.v linguist-language=Verilog /.gitcommit export-subst yosys-0.52/.gitcommit000066400000000000000000000000511477540374200146340ustar00rootroot00000000000000fee39a3284c90249e1d9684cf6944ffbbcbb8f90 yosys-0.52/.github/000077500000000000000000000000001477540374200142035ustar00rootroot00000000000000yosys-0.52/.github/ISSUE_TEMPLATE/000077500000000000000000000000001477540374200163665ustar00rootroot00000000000000yosys-0.52/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000044011477540374200212600ustar00rootroot00000000000000name: 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 main 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 yosys-0.52/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000006311477540374200203560ustar00rootroot00000000000000contact_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" yosys-0.52/.github/ISSUE_TEMPLATE/docs_report.yml000066400000000000000000000035401477540374200214360ustar00rootroot00000000000000name: Documentation Report description: Report a problem with the Yosys documentation 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 found a bug in Yosys, or in building the documentation, please fill out the Bug Report issue form, this form is for problems with the live documentation on [Read the Docs](https://yosyshq.readthedocs.io/projects/yosys/). Please only report problems that appear on the latest version of the documentation. Please contact [YosysHQ GmbH](https://www.yosyshq.com/) if you need commercial support for Yosys. - type: input id: docs_url attributes: label: Link to page description: "Please provide a link to the page where the problem was found." placeholder: "e.g. https://yosyshq.readthedocs.io/projects/yosys/" validations: required: true - type: input id: build_number attributes: label: Build number description: "If possible, please provide the latest build number from https://readthedocs.org/projects/yosys/builds/." placeholder: "e.g. Build #24078236" validations: required: false - type: textarea id: problem attributes: label: Issue description: "Please describe what is incorrect, invalid, or missing." validations: required: true - type: textarea id: expected attributes: label: Expected description: "If applicable, please describe what should appear instead." validations: required: false yosys-0.52/.github/ISSUE_TEMPLATE/feature_request.yml000066400000000000000000000016321477540374200223160ustar00rootroot00000000000000name: 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 yosys-0.52/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000002401477540374200200000ustar00rootroot00000000000000_What are the reasons/motivation for this change?_ _Explain how this is achieved._ _If applicable, please suggest to reviewers how they can test the change._ yosys-0.52/.github/actions/000077500000000000000000000000001477540374200156435ustar00rootroot00000000000000yosys-0.52/.github/actions/setup-build-env/000077500000000000000000000000001477540374200206665ustar00rootroot00000000000000yosys-0.52/.github/actions/setup-build-env/action.yml000066400000000000000000000025261477540374200226730ustar00rootroot00000000000000name: Build environment setup description: Configure build env for Yosys builds runs: using: composite steps: - name: Install Linux Dependencies if: runner.os == 'Linux' 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 libbz2-dev - name: Install macOS Dependencies if: runner.os == 'macOS' shell: bash run: | HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install bison flex gawk libffi pkg-config bash autoconf llvm lld || true - name: Linux runtime environment if: runner.os == 'Linux' shell: bash run: | echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH echo "procs=$(nproc)" >> $GITHUB_ENV - name: macOS runtime environment if: runner.os == 'macOS' shell: bash run: | echo "${{ github.workspace }}/.local/bin" >> $GITHUB_PATH echo "$(brew --prefix llvm)/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 yosys-0.52/.github/workflows/000077500000000000000000000000001477540374200162405ustar00rootroot00000000000000yosys-0.52/.github/workflows/codeql.yml000066400000000000000000000012031477540374200202260ustar00rootroot00000000000000name: "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 with: submodules: true - 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 yosys-0.52/.github/workflows/extra-builds.yml000066400000000000000000000056371477540374200214010ustar00rootroot00000000000000name: Test extra build flows on: [push, pull_request] jobs: pre_job: runs-on: ubuntu-latest outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]' # cancel previous builds if a new commit is pushed cancel_others: 'true' # only run on push *or* pull_request, not both concurrent_skipping: 'same_content_newer' vs-prep: name: Prepare Visual Studio build runs-on: ubuntu-latest needs: [pre_job] if: needs.pre_job.outputs.should_skip != 'true' steps: - uses: actions/checkout@v4 with: submodules: true - name: Build run: make vcxsrc YOSYS_VER=latest - uses: actions/upload-artifact@v4 with: name: vcxsrc path: yosys-win32-vcxsrc-latest.zip vs-build: name: Visual Studio build runs-on: windows-2019 needs: [vs-prep, pre_job] if: needs.pre_job.outputs.should_skip != 'true' 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@v2 - name: MSBuild working-directory: yosys-win32-vcxsrc-latest run: msbuild YosysVS.sln /p:PlatformToolset=v142 /p:Configuration=Release /p:WindowsTargetPlatformVersion=10.0.17763.0 wasi-build: name: WASI build needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: true - 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 <> $GITHUB_OUTPUT prepare-docs: # docs builds are needed for anything on main, any tagged versions, and any tag # or branch starting with docs-preview needs: check_docs_rebuild if: ${{ needs.check_docs_rebuild.outputs.should_skip != 'true' }} runs-on: [self-hosted, linux, x64, fast] steps: - name: Checkout Yosys uses: actions/checkout@v4 with: persist-credentials: false submodules: true - name: Runtime environment run: | echo "procs=$(nproc)" >> $GITHUB_ENV - name: Build Yosys run: | make config-clang echo "ENABLE_VERIFIC := 1" >> Makefile.conf echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf make -j${{ env.procs }} ENABLE_LTO=1 - name: Prepare docs shell: bash run: make docs/prep -j${{ env.procs }} TARGETS= EXTRA_TARGETS= - name: Upload artifact uses: actions/upload-artifact@v4 with: name: cmd-ref-${{ github.sha }} path: | docs/source/cmd docs/source/generated docs/source/_images docs/source/code_examples - name: Install doc prereqs shell: bash run: | make docs/reqs - name: Test build docs shell: bash run: | make -C docs html -j${{ env.procs }} TARGETS= EXTRA_TARGETS= - name: Trigger RTDs build if: ${{ needs.check_docs_rebuild.outputs.docs_export == 'true' }} uses: dfm/rtds-action@v1.1.0 with: webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }} webhook_token: ${{ secrets.RTDS_WEBHOOK_TOKEN }} commit_ref: ${{ github.ref }} yosys-0.52/.github/workflows/source-vendor.yml000066400000000000000000000017431477540374200215630ustar00rootroot00000000000000name: Create source archive with vendored dependencies on: [push, workflow_dispatch] jobs: vendor-sources: runs-on: ubuntu-latest steps: - name: Checkout repository with submodules uses: actions/checkout@v4 with: submodules: 'recursive' - name: Create clean tarball run: | git archive --format=tar HEAD -o yosys-src-vendored.tar git submodule foreach ' git archive --format=tar --prefix="${sm_path}/" HEAD --output=${toplevel}/vendor-${name}.tar ' # 2008 bug https://lists.gnu.org/archive/html/bug-tar/2008-08/msg00002.html for file in vendor-*.tar; do tar --concatenate --file=yosys-src-vendored.tar "$file" done gzip yosys-src-vendored.tar - name: Store tarball artifact uses: actions/upload-artifact@v4 with: name: vendored-sources path: yosys-src-vendored.tar.gz retention-days: 1 yosys-0.52/.github/workflows/test-build.yml000066400000000000000000000137631477540374200210510ustar00rootroot00000000000000name: Build and run tests on: [push, pull_request] jobs: pre_job: runs-on: ubuntu-latest outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]' # cancel previous builds if a new commit is pushed cancel_others: 'true' # only run on push *or* pull_request, not both concurrent_skipping: 'same_content_newer' pre_docs_job: runs-on: ubuntu-latest outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: paths_ignore: '["**/README.md"]' # cancel previous builds if a new commit is pushed cancel_others: 'true' # only run on push *or* pull_request, not both concurrent_skipping: 'same_content_newer' build-yosys: name: Reusable build runs-on: ${{ matrix.os }} needs: pre_docs_job if: needs.pre_docs_job.outputs.should_skip != 'true' env: CC: clang strategy: matrix: os: [ubuntu-latest, macos-latest] fail-fast: false steps: - name: Checkout Yosys uses: actions/checkout@v4 with: submodules: true - name: Setup environment uses: ./.github/actions/setup-build-env - name: Build shell: bash run: | mkdir build cd build make -f ../Makefile config-$CC echo 'SANITIZER = undefined' >> Makefile.conf make -f ../Makefile -j$procs ENABLE_LTO=1 - name: Log yosys-config output run: | ./yosys-config || true - name: Compress build shell: bash run: | cd build tar -cvf ../build.tar share/ yosys yosys-* - name: Store build artifact uses: actions/upload-artifact@v4 with: name: build-${{ matrix.os }} path: build.tar retention-days: 1 test-yosys: name: Run tests runs-on: ${{ matrix.os }} needs: [build-yosys, pre_job] if: needs.pre_job.outputs.should_skip != 'true' env: CC: clang UBSAN_OPTIONS: halt_on_error=1 strategy: matrix: os: [ubuntu-latest, macos-latest] fail-fast: false steps: - name: Checkout Yosys uses: actions/checkout@v4 - name: Setup environment uses: ./.github/actions/setup-build-env - 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: Get vcd2fst shell: bash run: | git clone https://github.com/mmicko/libwave.git mkdir -p ${{ github.workspace }}/.local/ cd libwave cmake . -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/.local make -j$procs make install - name: Cache iverilog id: cache-iverilog uses: actions/cache@v4 with: path: .local/ key: ${{ matrix.os }}-${{ 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$procs make install - name: Download build artifact uses: actions/download-artifact@v4 with: name: build-${{ matrix.os }} - name: Uncompress build shell: bash run: tar -xvf build.tar - name: Log yosys-config output run: | ./yosys-config || true - name: Run tests shell: bash run: | make -j$procs test TARGETS= EXTRA_TARGETS= CONFIG=$CC - name: Report errors if: ${{ failure() }} shell: bash run: | find tests/**/*.err -print -exec cat {} \; test-docs: name: Run docs tests runs-on: ${{ matrix.os }} needs: [build-yosys, pre_docs_job] if: needs.pre_docs_job.outputs.should_skip != 'true' env: CC: clang strategy: matrix: os: [ubuntu-latest] fail-fast: false steps: - name: Checkout Yosys uses: actions/checkout@v4 - name: Setup environment uses: ./.github/actions/setup-build-env - name: Download build artifact uses: actions/download-artifact@v4 with: name: build-${{ matrix.os }} - name: Uncompress build shell: bash run: tar -xvf build.tar - name: Log yosys-config output run: | ./yosys-config || true - name: Run tests shell: bash run: | make -C docs test -j${{ env.procs }} test-docs-build: name: Try build docs runs-on: [self-hosted, linux, x64, fast] needs: [pre_docs_job] if: needs.pre_docs_job.outputs.should_skip != 'true' strategy: matrix: docs-target: [html, latexpdf] fail-fast: false steps: - name: Checkout Yosys uses: actions/checkout@v4 with: submodules: true - name: Runtime environment run: | echo "procs=$(nproc)" >> $GITHUB_ENV - name: Build Yosys run: | make config-clang echo "ENABLE_CCACHE := 1" >> Makefile.conf make -j${{ env.procs }} - name: Install doc prereqs shell: bash run: | make docs/reqs - name: Build docs shell: bash run: | make docs DOC_TARGET=${{ matrix.docs-target }} -j${{ env.procs }} - name: Store docs build artifact uses: actions/upload-artifact@v4 with: name: docs-build-${{ matrix.docs-target }} path: docs/build/ retention-days: 7 yosys-0.52/.github/workflows/test-compile.yml000066400000000000000000000040521477540374200213710ustar00rootroot00000000000000name: Compiler testing on: [push, pull_request] jobs: pre_job: runs-on: ubuntu-latest outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]' # cancel previous builds if a new commit is pushed cancel_others: 'true' # only run on push *or* pull_request, not both concurrent_skipping: 'same_content_newer' test-compile: runs-on: ${{ matrix.os }} needs: pre_job if: needs.pre_job.outputs.should_skip != 'true' env: CXXFLAGS: ${{ startsWith(matrix.compiler, 'gcc') && '-Wp,-D_GLIBCXX_ASSERTIONS' || ''}} CC_SHORT: ${{ startsWith(matrix.compiler, 'gcc') && 'gcc' || 'clang' }} strategy: matrix: os: - ubuntu-latest compiler: # oldest supported - 'clang-10' - 'gcc-10' # newest, make sure to update maximum standard step to match - 'clang-19' - 'gcc-13' include: # macOS - os: macos-13 compiler: 'clang' fail-fast: false steps: - name: Checkout Yosys uses: actions/checkout@v4 with: submodules: true - name: Setup environment uses: ./.github/actions/setup-build-env - name: Setup Cpp uses: aminya/setup-cpp@v1 with: compiler: ${{ matrix.compiler }} - name: Tool versions shell: bash run: | $CC --version $CXX --version # minimum standard - name: Build C++17 shell: bash run: | make config-$CC_SHORT make -j$procs CXXSTD=c++17 compile-only # maximum standard, only on newest compilers - name: Build C++20 if: ${{ matrix.compiler == 'clang-19' || matrix.compiler == 'gcc-13' }} shell: bash run: | make config-$CC_SHORT make -j$procs CXXSTD=c++20 compile-only yosys-0.52/.github/workflows/test-verific.yml000066400000000000000000000041171477540374200213720ustar00rootroot00000000000000name: Build and run tests with Verific (Linux) on: [push, pull_request] jobs: pre-job: runs-on: ubuntu-latest outputs: should_skip: ${{ steps.skip_check.outputs.should_skip }} steps: - id: skip_check uses: fkirc/skip-duplicate-actions@v5 with: paths_ignore: '["**/README.md", "docs/**", "guidelines/**"]' # cancel previous builds if a new commit is pushed cancel_others: 'true' # only run on push *or* pull_request, not both concurrent_skipping: 'same_content_newer' test-verific: needs: pre-job if: needs.pre-job.outputs.should_skip != 'true' runs-on: [self-hosted, linux, x64, fast] steps: - name: Checkout Yosys uses: actions/checkout@v4 with: persist-credentials: false submodules: true - name: Runtime environment run: | echo "procs=$(nproc)" >> $GITHUB_ENV - name: Build Yosys run: | make config-clang echo "ENABLE_VERIFIC := 1" >> Makefile.conf echo "ENABLE_VERIFIC_EDIF := 1" >> Makefile.conf echo "ENABLE_VERIFIC_LIBERTY := 1" >> Makefile.conf echo "ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 1" >> Makefile.conf echo "ENABLE_CCACHE := 1" >> Makefile.conf echo "ENABLE_FUNCTIONAL_TESTS := 1" >> Makefile.conf make -j${{ env.procs }} ENABLE_LTO=1 - name: Install Yosys run: | make install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX= - name: Checkout SBY uses: actions/checkout@v4 with: repository: 'YosysHQ/sby' path: 'sby' - name: Build SBY run: | make -C sby install DESTDIR=${GITHUB_WORKSPACE}/.local PREFIX= - name: Run Yosys tests run: | make -j${{ env.procs }} test - name: Run Verific specific Yosys tests run: | make -C tests/sva cd tests/svtypes && bash run-test.sh - name: Run SBY tests if: ${{ github.ref == 'refs/heads/main' }} run: | make -C sby run_ci yosys-0.52/.github/workflows/update-flake-lock.yml000066400000000000000000000012521477540374200222530ustar00rootroot00000000000000name: update-flake-lock on: workflow_dispatch: # allows manual triggering schedule: - cron: '0 0 * * 0' # runs weekly on Sunday at 00:00 jobs: lockfile: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install Nix uses: DeterminateSystems/nix-installer-action@main - name: Update flake.lock uses: DeterminateSystems/update-flake-lock@main with: token: ${{CI_CREATE_PR_TOKEN}} pr-title: "Update flake.lock" # Title of PR to be created pr-labels: | # Labels to be set on the PR dependencies automated yosys-0.52/.github/workflows/version.yml000066400000000000000000000023061477540374200204510ustar00rootroot00000000000000name: 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 submodules: true - 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 }} yosys-0.52/.github/workflows/wheels.yml000066400000000000000000000120071477540374200202520ustar00rootroot00000000000000name: Build Wheels for PyPI on: workflow_dispatch: jobs: build_wheels: strategy: fail-fast: false matrix: os: [ { name: "Ubuntu 22.04", family: "linux", runner: "ubuntu-22.04", archs: "x86_64", }, ## Aarch64 is disabled for now: GitHub is committing to EOY ## for free aarch64 runners for open-source projects and ## emulation times out: ## https://github.com/orgs/community/discussions/19197#discussioncomment-10550689 # { # name: "Ubuntu 22.04", # family: "linux", # runner: "ubuntu-22.04", # archs: "aarch64", # }, { name: "macOS 13", family: "macos", runner: "macos-13", archs: "x86_64", }, { name: "macOS 14", family: "macos", runner: "macos-14", archs: "arm64", }, ## Windows is disabled because of an issue with compiling FFI as ## under MinGW in the GitHub Actions environment (SHELL variable has ## whitespace.) # { # name: "Windows Server 2019", # family: "windows", # runner: "windows-2019", # archs: "AMD64", # }, ] name: Build Wheels | ${{ matrix.os.name }} | ${{ matrix.os.archs }} runs-on: ${{ matrix.os.runner }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - if: ${{ matrix.os.family == 'linux' }} name: "[Linux] Set up QEMU" uses: docker/setup-qemu-action@v3 - uses: actions/setup-python@v5 - name: Get Boost Source shell: bash run: | mkdir -p boost curl -L https://github.com/boostorg/boost/releases/download/boost-1.86.0/boost-1.86.0-b2-nodocs.tar.gz | tar --strip-components=1 -xzC boost - name: Get FFI shell: bash run: | mkdir -p ffi curl -L https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz | tar --strip-components=1 -xzC ffi ## Software installed by default in GitHub Action Runner VMs: ## https://github.com/actions/runner-images - if: ${{ matrix.os.family == 'macos' }} name: "[macOS] Flex/Bison" run: | brew install flex bison echo "PATH=$(brew --prefix flex)/bin:$PATH" >> $GITHUB_ENV echo "PATH=$(brew --prefix bison)/bin:$PATH" >> $GITHUB_ENV - if: ${{ matrix.os.family == 'windows' }} name: "[Windows] Flex/Bison" run: | choco install winflexbison3 - if: ${{ matrix.os.family == 'macos' && matrix.os.archs == 'arm64' }} name: "[macOS/arm64] Install Python 3.8 (see: https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64)" uses: actions/setup-python@v5 with: python-version: 3.8 - name: Build wheels uses: pypa/cibuildwheel@v2.21.1 env: # * APIs not supported by PyPy # * Musllinux disabled because it increases build time from 48m to ~3h CIBW_SKIP: > pp* *musllinux* CIBW_ARCHS: ${{ matrix.os.archs }} CIBW_BUILD_VERBOSITY: "1" # manylinux2014 (default) does not have a modern enough C++ compiler for Yosys CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28 CIBW_BEFORE_ALL: bash ./.github/workflows/wheels/cibw_before_all.sh CIBW_ENVIRONMENT: > CXXFLAGS=-I./boost/pfx/include LINKFLAGS=-L./boost/pfx/lib PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a' CIBW_ENVIRONMENT_MACOS: > CXXFLAGS=-I./boost/pfx/include LINKFLAGS=-L./boost/pfx/lib PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig MACOSX_DEPLOYMENT_TARGET=11 makeFlags='BOOST_PYTHON_LIB=./boost/pfx/lib/libboost_python*.a CONFIG=clang' CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh CIBW_TEST_COMMAND: python3 {project}/tests/arch/ecp5/add_sub.py - uses: actions/upload-artifact@v4 with: name: python-wheels-${{ matrix.os.runner }} path: ./wheelhouse/*.whl upload_wheels: name: Upload Wheels runs-on: ubuntu-latest needs: build_wheels steps: - uses: actions/download-artifact@v4 with: path: "." pattern: python-wheels-* merge-multiple: true - run: | ls mkdir -p ./dist mv *.whl ./dist - name: Publish uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_TOKEN }} repository-url: ${{ vars.PYPI_INDEX || 'https://upload.pypi.org/legacy/' }} yosys-0.52/.github/workflows/wheels/000077500000000000000000000000001477540374200175275ustar00rootroot00000000000000yosys-0.52/.github/workflows/wheels/_run_cibw_linux.py000066400000000000000000000027641477540374200233000ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright (C) 2024 Efabless Corporation # # 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 runs the cibuildwheel step from the wheels workflow locally. """ import os import yaml import platform import subprocess __dir__ = os.path.dirname(os.path.abspath(__file__)) workflow = yaml.safe_load(open(os.path.join(os.path.dirname(__dir__), "wheels.yml"))) env = os.environ.copy() steps = workflow["jobs"]["build_wheels"]["steps"] cibw_step = None for step in steps: if (step.get("uses") or "").startswith("pypa/cibuildwheel"): cibw_step = step break for key, value in cibw_step["env"].items(): if key.endswith("WIN") or key.endswith("MAC"): continue env[key] = value env["CIBW_ARCHS"] = os.getenv("CIBW_ARCHS") or platform.machine() subprocess.check_call(["cibuildwheel"], env=env) yosys-0.52/.github/workflows/wheels/cibw_before_all.sh000066400000000000000000000014271477540374200231650ustar00rootroot00000000000000set -e set -x # Build-time dependencies ## Linux Docker Images if command -v yum &> /dev/null; then yum install -y flex bison fi if command -v apk &> /dev/null; then apk add flex bison fi ## macOS/Windows -- installed in GitHub Action itself, not container # Build Static FFI (platform-dependent but not Python version dependent) cd ffi ## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx ## Without this, SHELL has a space in its path which breaks the makefile make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) ## Forces static library to be used in all situations sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc yosys-0.52/.github/workflows/wheels/cibw_before_build.sh000066400000000000000000000020251477540374200235070ustar00rootroot00000000000000set -e set -x # Don't use objects from previous compiles on Windows/macOS make clean # DEBUG: show python3 and python3-config outputs if [ "$(uname)" != "Linux" ]; then # https://github.com/pypa/cibuildwheel/issues/2021 ln -s $(dirname $(readlink -f $(which python3)))/python3-config $(dirname $(which python3))/python3-config fi python3 --version python3-config --includes # Build boost cd ./boost ## Delete the artefacts from previous builds (if any) rm -rf ./pfx ## Bootstrap bjam ./bootstrap.sh --prefix=./pfx ## Build Boost against current version of Python, only for ## static linkage (Boost is statically linked because system boost packages ## wildly vary in versions, including the libboost_python3 version) ./b2\ -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)\ --prefix=./pfx\ --with-filesystem\ --with-system\ --with-python\ cxxflags="$(python3-config --includes) -std=c++17 -fPIC"\ cflags="$(python3-config --includes) -fPIC"\ link=static\ variant=release\ install yosys-0.52/.gitignore000066400000000000000000000013631477540374200146360ustar00rootroot00000000000000*.o *.d *.dwo .*.swp *.gch *.gcda *.gcno *~ __pycache__ /.cache /.cproject /.project /.settings /qtcreator.files /qtcreator.includes /qtcreator.config /qtcreator.creator /qtcreator.creator.user /compile_commands.json /coverage.info /coverage_html /Makefile.conf /viz.js /yosys /yosys.exe /yosys.js /yosys.wasm /yosys-abc /yosys-abc.exe /yosys-config /yosys-smtbmc /yosys-smtbmc.exe /yosys-smtbmc-script.py /yosys-witness /yosys-witness.exe /yosys-witness-script.py /yosys-filterlib /yosys-filterlib.exe /kernel/*.pyh /kernel/python_wrappers.cc /kernel/version_*.cc /share /yosys-win32-mxebin-* /yosys-win32-vcxsrc-* /yosysjs-* /libyosys.so /tests/unit/bintest/ /tests/unit/objtest/ /tests/ystests /result /dist /*.egg-info /build /venv /boost /ffi /*.whl yosys-0.52/.gitmodules000066400000000000000000000003241477540374200150170ustar00rootroot00000000000000[submodule "abc"] path = abc url = https://github.com/YosysHQ/abc # Don't use paths as names to avoid git archive problems [submodule "cxxopts"] path = libs/cxxopts url = https://github.com/jarro2783/cxxopts yosys-0.52/.mailmap000066400000000000000000000006431477540374200142670ustar00rootroot00000000000000Wanda Phinode Wanda Phinode Wanda Phinode Claire Xenia Wolf Claire Xenia Wolf Claire Xenia Wolf Claire Xenia Wolf yosys-0.52/.readthedocs.yaml000066400000000000000000000005101477540374200160660ustar00rootroot00000000000000# .readthedocs.yaml # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details version: 2 build: os: ubuntu-22.04 tools: python: '3.12' formats: - pdf sphinx: configuration: docs/source/conf.py fail_on_warning: true python: install: - requirements: docs/source/requirements.txt yosys-0.52/Brewfile000066400000000000000000000003011477540374200143170ustar00rootroot00000000000000brew "bison" brew "flex" brew "gawk" brew "libffi" brew "git" brew "graphviz" brew "pkg-config" brew "python3" brew "tcl-tk" brew "xdot" brew "bash" brew "boost-python3" brew "llvm" brew "lld" yosys-0.52/CHANGELOG000066400000000000000000001702051477540374200140620ustar00rootroot00000000000000 List of major changes and improvements between releases ======================================================= Yosys 0.51 .. Yosys 0.52 -------------------------- * New commands and options - Added "-pattern-limit" option to "share" pass to limit analysis effort. - Added "libcache" pass to control caching of technology library data parsed from liberty files. - Added "read_verilog_file_list" to parse verilog file list. * Various - Added $macc_v2 cell. - Improve lexer performance and zlib support for "read_liberty". - opt_expr: optimize pow of 2 cells. Yosys 0.50 .. Yosys 0.51 -------------------------- * New commands and options - Added "abstract" pass to allow reducing and never increasing the constraints on a circuit's behavior in a formal verification setting. * Various - "splitcells" pass now splits "aldff" cells. - FunctionalIR documentation * QuickLogic support - Added IOFF inference for qlf_k6n10f * Intel support - Fixed RAM and DSP support. - Overall performance improvement for "synth_intel". Yosys 0.49 .. Yosys 0.50 -------------------------- * Various - "write_verilog" emits "$check" cell names as labels. Yosys 0.48 .. Yosys 0.49 -------------------------- * Various - "$scopeinfo" cells are now part of JSON export by default. - Added option to specify hierarchical separator for "flatten". - Improved "wreduce" to handle more cases of operator size reduction. - Updated hashing interface, see docs/source/yosys_internals/hashing.rst for breaking API changes. * New commands and options - Added "-noscopeinfo" option to "json" and "write_json" pass. Yosys 0.47 .. Yosys 0.48 -------------------------- * Various - Removed "read_ilang" deprecated pass. - Enhanced boxing features in the experimental "abc_new" command. - Added new Tcl methods for design inspection. - Added clock enable inference to "dfflibmap". - Added a Han-Carlson and Sklansky option for $lcu mapping. * New commands and options - Added "-nopeepopt" option to "clk2fflogic" pass. - Added "-liberty" and "-dont_use" options to "clockgate" pass. - Added "-ignore_buses" option to "read_liberty" pass. - Added "-dont_map" option to "techmap" pass. - Added "-selected" option to "write_json" pass. - Added "wrapcell" command for creating wrapper modules around selected cells. - Added "portarcs" command for deriving propagation timing arcs. - Added "setenv" command for setting environment variables. * Gowin support - Added "-family" option to "synth_gowin" pass. - Cell definitions split by family. * Verific support - Improved blackbox support. Yosys 0.46 .. Yosys 0.47 -------------------------- * Various - Added cxxopts library for handling command line arguments. - Added docs generation from cells help output. * New commands and options - Added "-json" option to "synth_xilinx" pass. - Added "-derive_luts" option to "cellmatch" pass. - Added "t:@" syntax to "select" pass. - Added "-list-mod" option to "select" pass. - Removed deprecated "qwp" pass. * Verific support - Initial state handling for VHDL assertions. Yosys 0.45 .. Yosys 0.46 -------------------------- * Various - Added new "functional backend" infrastructure with three example backends (C++, SMTLIB and Rosette). - Added new coarse-grain buffer cell type "$buf" to RTLIL. - Added "-y" command line option to execute a Python script with libyosys available as a built-in module. - Added support for casting to type in Verilog frontend. * New commands and options - Added "clockgate" pass for automatic clock gating cell insertion. - Added "bufnorm" experimental pass to convert design into buffered-normalized form. - Added experimental "aiger2" and "xaiger2" backends, and an experimental "abc_new" command - Added "-force-detailed-loop-check" option to "check" pass. - Added "-unit_delay" option to "read_liberty" pass. * Verific support - Added left and right bound properties to wires when using specific VHDL types. Yosys 0.44 .. Yosys 0.45 -------------------------- * Various - Added cell types help messages. * New back-ends - Added initial NG-Ultra support. ( synth_nanoxplore ) Yosys 0.43 .. Yosys 0.44 -------------------------- * Various - Added ENABLE_LTO compile option to enable link time optimizations. - Build support for Haiku OS. * New commands and options - Added "keep_hierarchy" pass to add attribute with same name to modules based on cost. - Added options "-noopt","-bloat" and "-check_cost" to "test_cell" pass. * New back-ends - Added initial PolarFire support. ( synth_microchip ) Yosys 0.42 .. Yosys 0.43 -------------------------- * Various - C++ compiler with C++17 support is required. - Support for IO liberty files for verification. - Limit padding from shiftadd for "peepopt" pass. * Verific support - Support building Yosys with various Verific library configurations. Can be built now without YosysHQ specific patch and extension library. Yosys 0.41 .. Yosys 0.42 -------------------------- * New commands and options - Added "box_derive" pass to derive box modules. - Added option "assert-mod-count" to "select" pass. - Added option "-header","-push" and "-pop" to "log" pass. * Intel support - Dropped Quartus support in "synth_intel_alm" pass. Yosys 0.40 .. Yosys 0.41 -------------------------- * New commands and options - Added "cellmatch" pass for picking out standard cells automatically. * Various - Extended the experimental incremental JSON API to allow arbitrary smtlib subexpressions. - Added support for using ABCs library merging when providing multiple liberty files. * Verific support - Expose library name as module attribute. Yosys 0.39 .. Yosys 0.40 -------------------------- * New commands and options - Added option "-vhdl2019" to "read" and "verific" pass. * Various - Major documentation overhaul. - Added port statistics to "stat" command. - Added new formatting features to cxxrtl backend. * Verific support - Added better support for VHDL constants import. - Added support for VHDL 2009. 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 -------------------------- * 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 -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" - "synth_xilinx" to now infer wide multiplexers (-widemux to enable) - Renamed labels/options in synth_xilinx (e.g. dram -> map_lutram; -nodram -> -nolutram) - 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) - Added "xilinx_srl" for Xilinx shift register extraction - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl") - Added "xilinx_dsp" for Xilinx DSP packing - "synth_xilinx" to now infer DSP blocks (-nodsp to disable) - 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 "xilinx_dffopt" pass - Added "synth_xilinx -dff" * 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 ---------------------- * Various - Many bugfixes and small improvements - Added support for SystemVerilog interfaces and modports - Added "write_edif -attrprop" - Added "opt_lut" pass - Added "gate2lut.v" techmap rule - Added "rename -src" - Added "equiv_opt" pass - Added "flowmap" LUT mapping pass - Added "rename -wire" to rename cells based on the wires they drive - Added "bugpoint" for creating minimised testcases - Added "write_edif -gndvccy" - "write_verilog" to escape Verilog keywords - Fixed sign handling of real constants - "write_verilog" to write initial statement for initial flop state - Added pmgen pattern matcher generator - Fixed opt_rmdff handling of $_DFFSR_???_ and $_DLATCHSR_???_ - Added "setundef -params" to replace undefined cell parameters - Renamed "yosys -D" to "yosys -U", added "yosys -D" to set Verilog defines - Fixed handling of defparam when default_nettype is none - Fixed "wreduce" flipflop handling - Fixed FIRRTL to Verilog process instance subfield assignment - Added "write_verilog -siminit" - Several fixes and improvements for mem2reg memories - Fixed handling of task output ports in clocked always blocks - Improved handling of and-with-1 and or-with-0 in "opt_expr" - Added "read_aiger" frontend - Added "mutate" pass - Added "hdlname" attribute - Added "rename -output" - Added "read_ilang -lib" - Improved "proc" full_case detection and handling - Added "whitebox" and "lib_whitebox" attributes - Added "read_verilog -nowb", "flatten -wb" and "wbflip" - Added Python bindings and support for Python plug-ins - Added "pmux2shiftx" - Added log_debug framework for reduced default verbosity - Improved "opt_expr" and "opt_clean" handling of (partially) undriven and/or unused wires - Added "peepopt" peephole optimisation pass using pmgen - Added approximate support for SystemVerilog "var" keyword - Added parsing of "specify" blocks into $specrule and $specify[23] - Added support for attributes on parameters and localparams - Added support for parsing attributes on port connections - Added "wreduce -keepdc" - Added support for optimising $dffe and $_DFFE_* cells in "opt_rmdff" - Added Verilog wand/wor wire type support - Added support for elaboration system tasks - Added "muxcover -mux{4,8,16}=" - Added "muxcover -dmux=" - Added "muxcover -nopartial" - Added "muxpack" pass - Added "pmux2shiftx -norange" - Added support for "~" in filename parsing - Added "read_verilog -pwires" feature to turn parameters into wires - Fixed sign extension of unsized constants with 'bx and 'bz MSB - Fixed genvar to be a signed type - Added support for attributes on case rules - Added "upto" and "offset" to JSON frontend and backend - Several liberty file parser improvements - Fixed handling of more complex BRAM patterns - Add "write_aiger -I -O -B" * Formal Verification - Added $changed support to read_verilog - Added "read_verilog -noassert -noassume -assert-assumes" - Added btor ops for $mul, $div, $mod and $concat - Added yosys-smtbmc support for btor witnesses - Added "supercover" pass - Fixed $global_clock handling vs autowire - Added $dffsr support to "async2sync" - Added "fmcombine" pass - Added memory init support in "write_btor" - Added "cutpoint" pass - Changed "ne" to "neq" in btor2 output - Added support for SVA "final" keyword - Added "fmcombine -initeq -anyeq" - Added timescale and generated-by header to yosys-smtbmc vcd output - Improved BTOR2 handling of undriven wires * Verific support - Enabled Verific flags vhdl_support_variable_slice and veri_elaborate_top_level_modules_having_interface_ports - Improved support for asymmetric memories - Added "verific -chparam" - Fixed "verific -extnets" for more complex situations - Added "read -verific" and "read -noverific" - Added "hierarchy -chparam" * New back-ends - Added initial Anlogic support - Added initial SmartFusion2 and IGLOO2 support * ECP5 support - Added "synth_ecp5 -nowidelut" - Added BRAM inference support to "synth_ecp5" - Added support for transforming Diamond IO and flipflop primitives * iCE40 support - Added "ice40_unlut" pass - Added "synth_ice40 -relut" - Added "synth_ice40 -noabc" - Added "synth_ice40 -dffe_min_ce_use" - Added DSP inference support using pmgen - Added support for initialising BRAM primitives from a file - Added iCE40 Ultra RGB LED driver cells * Xilinx support - Use "write_edif -pvector bra" for Xilinx EDIF files - Fixes for VPR place and route support with "synth_xilinx" - Added more cell simulation models - Added "synth_xilinx -family" - Added "stat -tech xilinx" to estimate logic cell usage - Added "synth_xilinx -nocarry" - Added "synth_xilinx -nowidelut" - "synth_xilinx" to now infer hard shift registers (-nosrl to disable) - Added support for mapping RAM32X1D Yosys 0.7 .. Yosys 0.8 ---------------------- * Various - Many bugfixes and small improvements - Strip debug symbols from installed binary - Replace -ignore_redef with -[no]overwrite in front-ends - Added write_verilog hex dump support, add -nohex option - Added "write_verilog -decimal" - Added "scc -set_attr" - Added "verilog_defines" command - Remember defines from one read_verilog to next - Added support for hierarchical defparam - Added FIRRTL back-end - Improved ABC default scripts - Added "design -reset-vlog" - Added "yosys -W regex", "yosys -w regex", and "yosys -e regex" - Added Verilog $rtoi and $itor support - Added "check -initdrv" - Added "read_blif -wideports" - Added support for SystemVerilog "++" and "--" operators - Added support for SystemVerilog unique, unique0, and priority case - Added "write_edif" options for edif "flavors" - Added support for resetall compiler directive - Added simple C beck-end (bitwise combinatorical only atm) - Added $_ANDNOT_ and $_ORNOT_ cell types - Added cell library aliases to "abc -g" - Added "setundef -anyseq" - Added "chtype" command - Added "design -import" - Added "write_table" command - Added "read_json" command - Added "sim" command - Added "extract_fa" and "extract_reduce" commands - Added "extract_counter" command - Added "opt_demorgan" command - Added support for $size and $bits SystemVerilog functions - Added "blackbox" command - Added "ltp" command - Added support for editline as replacement for readline - Added warnings for driver-driver conflicts between FFs (and other cells) and constants - Added "yosys -E" for creating Makefile dependencies files - Added "synth -noshare" - Added "memory_nordff" - Added "setundef -undef -expose -anyconst" - Added "expose -input" - Added specify/specparam parser support (simply ignore them) - Added "write_blif -inames -iattr" - Added "hierarchy -simcheck" - Added an option to statically link abc into yosys - Added protobuf back-end - Added BLIF parsing support for .conn and .cname - Added read_verilog error checking for reg/wire/logic misuse - Added "make coverage" and ENABLE_GCOV build option * Changes in Yosys APIs - Added ConstEval defaultval feature - Added {get,set}_src_attribute() methods on RTLIL::AttrObject - Added SigSpec::is_fully_ones() and Const::is_fully_ones() - Added log_file_warning() and log_file_error() functions * Formal Verification - Added "write_aiger" - Added "yosys-smtbmc --aig" - Added "always " to .smtc format - Added $cover cell type and support for cover properties - Added $fair/$live cell type and support for liveness properties - Added smtbmc support for memory vcd dumping - Added "chformal" command - Added "write_smt2 -stbv" and "write_smt2 -stdt" - Fix equiv_simple, old behavior now available with "equiv_simple -short" - Change to Yices2 as default SMT solver (it is GPL now) - Added "yosys-smtbmc --presat" (now default in SymbiYosys) - Added "yosys-smtbmc --smtc-init --smtc-top --noinit" - Added a brand new "write_btor" command for BTOR2 - Added clk2fflogic memory support and other improvements - Added "async memory write" support to write_smt2 - Simulate clock toggling in yosys-smtbmc VCD output - Added $allseq/$allconst cells for EA-solving - Make -nordff the default in "prep" - Added (* gclk *) attribute - Added "async2sync" pass for single-clock designs with async resets * Verific support - Many improvements in Verific front-end - Added proper handling of concurent SVA properties - Map "const" and "rand const" to $anyseq/$anyconst - Added "verific -import -flatten" and "verific -import -extnets" - Added "verific -vlog-incdir -vlog-define -vlog-libdir" - Remove PSL support (because PSL has been removed in upstream Verific) - Improve integration with "hierarchy" command design elaboration - Added YOSYS_NOVERIFIC for running non-verific test cases with verific bin - Added simpilied "read" command that automatically uses verific if available - Added "verific -set- .." - Added "verific -work " * New back-ends - Added initial Coolrunner-II support - Added initial eASIC support - Added initial ECP5 support * GreenPAK Support - Added support for GP_DLATCH, GP_SPI, GP_DCMx, GP_COUNTx, etc. * iCE40 Support - Add "synth_ice40 -vpr" - Add "synth_ice40 -nodffe" - Add "synth_ice40 -json" - Add Support for UltraPlus cells * MAX10 and Cyclone IV Support - Added initial version of metacommand "synth_intel". - Improved write_verilog command to produce VQM netlist for Quartus Prime. - Added support for MAX10 FPGA family synthesis. - Added support for Cyclone IV family synthesis. - Added example of implementation for DE2i-150 board. - Added example of implementation for MAX10 development kit. - Added LFSR example from Asic World. - Added "dffinit -highlow" for mapping to Intel primitives Yosys 0.6 .. Yosys 0.7 ---------------------- * Various - Added "yosys -D" feature - Added support for installed plugins in $(DATDIR)/plugins/ - Renamed opt_const to opt_expr - Renamed opt_share to opt_merge - Added "prep -flatten" and "synth -flatten" - Added "prep -auto-top" and "synth -auto-top" - Using "mfs" and "lutpack" in ABC lut mapping - Support for abstract modules in chparam - Cleanup abstract modules at end of "hierarchy -top" - Added tristate buffer support to iopadmap - Added opt_expr support for div/mod by power-of-two - Added "select -assert-min -assert-max " - Added "attrmvcp" pass - Added "attrmap" command - Added "tee +INT -INT" - Added "zinit" pass - Added "setparam -type" - Added "shregmap" pass - Added "setundef -init" - Added "nlutmap -assert" - Added $sop cell type and "abc -sop -I -P " - Added "dc2" to default ABC scripts - Added "deminout" - Added "insbuf" command - Added "prep -nomem" - Added "opt_rmdff -keepdc" - Added "prep -nokeepdc" - Added initial version of "synth_gowin" - Added "fsm_expand -full" - Added support for fsm_encoding="user" - Many improvements in GreenPAK4 support - Added black box modules for all Xilinx 7-series lib cells - Added synth_ice40 support for latches via logic loops - Fixed ice40_opt lut unmapping, added "ice40_opt -unlut" * Build System - Added ABCEXTERNAL and ABCURL make variables - Added BINDIR, LIBDIR, and DATDIR make variables - Added PKG_CONFIG make variable - Added SEED make variable (for "make test") - Added YOSYS_VER_STR make variable - Updated min GCC requirement to GCC 4.8 - Updated required Bison version to Bison 3.x * Internal APIs - Added ast.h to exported headers - Added ScriptPass helper class for script-like passes - Added CellEdgesDatabase API * Front-ends and Back-ends - Added filename glob support to all front-ends - Added avail (black-box) module params to ilang format - Added $display %m support - Added support for $stop Verilog system task - Added support for SystemVerilog packages - Fixed procedural assignments to non-unique lvalues, e.g. {y,y} = {a,b} - Added support for "active high" and "active low" latches in read_blif and write_blif - Use init value "2" for all uninitialized FFs in BLIF back-end - Added "read_blif -sop" - Added "write_blif -noalias" - Added various write_blif options for VTR support - write_json: also write module attributes. - Added "write_verilog -nodec -nostr -defparam" - Added "read_verilog -norestrict -assume-asserts" - Added support for bus interfaces to "read_liberty -lib" - Added liberty parser support for types within cell decls - Added "write_verilog -renameprefix -v" - Added "write_edif -nogndvcc" * Formal Verification - Support for hierarchical designs in smt2 back-end - Yosys-smtbmc: Support for hierarchical VCD dumping - Added $initstate cell type and vlog function - Added $anyconst and $anyseq cell types and vlog functions - Added printing of code loc of failed asserts to yosys-smtbmc - Added memory_memx pass, "memory -memx", and "prep -memx" - Added "proc_mux -ifx" - Added "yosys-smtbmc -g" - Deprecated "write_smt2 -regs" (by default on now) - Made "write_smt2 -bv -mem" default, added "write_smt2 -nobv -nomem" - Added support for memories to smtio.py - Added "yosys-smtbmc --dump-vlogtb" - Added "yosys-smtbmc --smtc --dump-smtc" - Added "yosys-smtbmc --dump-all" - Added assertpmux command - Added "yosys-smtbmc --unroll" - Added $past, $stable, $rose, $fell SVA functions - Added "yosys-smtbmc --noinfo and --dummy" - Added "yosys-smtbmc --noincr" - Added "yosys-smtbmc --cex " - Added $ff and $_FF_ cell types - Added $global_clock verilog syntax support for creating $ff cells - Added clk2fflogic Yosys 0.5 .. Yosys 0.6 ---------------------- * Various - Added Contributor Covenant Code of Conduct - Various improvements in dict<> and pool<> - Added hashlib::mfp and refactored SigMap - Improved support for reals as module parameters - Various improvements in SMT2 back-end - Added "keep_hierarchy" attribute - Verilog front-end: define `BLACKBOX in -lib mode - Added API for converting internal cells to AIGs - Added ENABLE_LIBYOSYS Makefile option - Removed "techmap -share_map" (use "-map +/filename" instead) - Switched all Python scripts to Python 3 - Added support for $display()/$write() and $finish() to Verilog front-end - Added "yosys-smtbmc" formal verification flow - Added options for clang sanitizers to Makefile * New commands and options - Added "scc -expect -nofeedback" - Added "proc_dlatch" - Added "check" - Added "select %xe %cie %coe %M %C %R" - Added "sat -dump_json" (WaveJSON format) - Added "sat -tempinduct-baseonly -tempinduct-inductonly" - Added "sat -stepsize" and "sat -tempinduct-step" - Added "sat -show-regs -show-public -show-all" - Added "write_json" (Native Yosys JSON format) - Added "write_blif -attr" - Added "dffinit" - Added "chparam" - Added "muxcover" - Added "pmuxtree" - Added memory_bram "make_outreg" feature - Added "splice -wires" - Added "dff2dffe -direct-match" - Added simplemap $lut support - Added "read_blif" - Added "opt_share -share_all" - Added "aigmap" - Added "write_smt2 -mem -regs -wires" - Added "memory -nordff" - Added "write_smv" - Added "synth -nordff -noalumacc" - Added "rename -top new_name" - Added "opt_const -clkinv" - Added "synth -nofsm" - Added "miter -assert" - Added "read_verilog -noautowire" - Added "read_verilog -nodpi" - Added "tribuf" - Added "lut2mux" - Added "nlutmap" - Added "qwp" - Added "test_cell -noeval" - Added "edgetypes" - Added "equiv_struct" - Added "equiv_purge" - Added "equiv_mark" - Added "equiv_add -try -cell" - Added "singleton" - Added "abc -g -luts" - Added "torder" - Added "write_blif -cname" - Added "submod -copy" - Added "dffsr2dff" - Added "stat -liberty" * Synthesis metacommands - Various improvements in synth_xilinx - Added synth_ice40 and synth_greenpak4 - Added "prep" metacommand for "synthesis lite" * Cell library changes - Added cell types to "help" system - Added $meminit cell type - Added $assume cell type - Added $_MUX4_, $_MUX8_, and $_MUX16_ cells - Added $tribuf and $_TBUF_ cell types - Added read-enable to memory model * YosysJS - Various improvements in emscripten build - Added alternative webworker-based JS API - Added a few example applications Yosys 0.4 .. Yosys 0.5 ---------------------- * API changes - Added log_warning() - Added eval_select_args() and eval_select_op() - Added cell->known(), cell->input(portname), cell->output(portname) - Skip blackbox modules in design->selected_modules() - Replaced std::map<> and std::set<> with dict<> and pool<> - New SigSpec::extend() is what used to be SigSpec::extend_u0() - Added YS_OVERRIDE, YS_FINAL, YS_ATTRIBUTE, YS_NORETURN * Cell library changes - Added flip-flops with enable ($dffe etc.) - Added $equiv cells for equivalence checking framework * Various - Updated ABC to hg rev 61ad5f908c03 - Added clock domain partitioning to ABC pass - Improved plugin building (see "yosys-config --build") - Added ENABLE_NDEBUG Makefile flag for high-performance builds - Added "yosys -d", "yosys -L" and other driver improvements - Added support for multi-bit (array) cell ports to "write_edif" - Now printing most output to stdout, not stderr - Added "onehot" attribute (set by "fsm_map") - Various performance improvements - Vastly improved Xilinx flow - Added "make unsintall" * Equivalence checking - Added equivalence checking commands: equiv_make equiv_simple equiv_status equiv_induct equiv_miter equiv_add equiv_remove * Block RAM support: - Added "memory_bram" command - Added BRAM support to Xilinx flow * Other New Commands and Options - Added "dff2dffe" - Added "fsm -encfile" - Added "dfflibmap -prepare" - Added "write_blid -unbuf -undef -blackbox" - Added "write_smt2" for writing SMT-LIBv2 files - Added "test_cell -w -muxdiv" - Added "select -read" Yosys 0.3.0 .. Yosys 0.4 ------------------------ * Platform Support - Added support for mxe-based cross-builds for win32 - Added sourcecode-export as VisualStudio project - Added experimental EMCC (JavaScript) support * Verilog Frontend - Added -sv option for SystemVerilog (and automatic *.sv file support) - Added support for real-valued constants and constant expressions - Added support for non-standard "via_celltype" attribute on task/func - Added support for non-standard "module mod_name(...);" syntax - Added support for non-standard """ macro bodies - Added support for array with more than one dimension - Added support for $readmemh and $readmemb - Added support for DPI functions * Changes in internal cell library - Added $shift and $shiftx cell types - Added $alu, $lcu, $fa and $macc cell types - Removed $bu0 and $safe_pmux cell types - $mem/$memwr WR_EN input is now a per-data-bit enable signal - Added $_NAND_ $_NOR_ $_XNOR_ $_AOI3_ $_OAI3_ $_AOI4_ $_OAI4_ - Renamed ports of $lut cells (from I->O to A->Y) - Renamed $_INV_ to $_NOT_ * Changes for simple synthesis flows - There is now a "synth" command with a recommended default script - Many improvements in synthesis of arithmetic functions to gates - Multipliers and adders with many operands are using carry-save adder trees - Remaining adders are now implemented using Brent-Kung carry look-ahead adders - Various new high-level optimizations on RTL netlist - Various improvements in FSM optimization - Updated ABC to hg 5b5af75f1dda (from 2014-11-07) * Changes in internal APIs and RTLIL - Added log_id() and log_cell() helper functions - Added function-like cell creation helpers - Added GetSize() function (like .size() but with int) - Major refactoring of RTLIL::Module and related classes - Major refactoring of RTLIL::SigSpec and related classes - Now RTLIL::IdString is essentially an int - Added macros for code coverage counters - Added some Makefile magic for pretty make logs - Added "kernel/yosys.h" with all the core definitions - Changed a lot of code from FILE* to c++ streams - Added RTLIL::Monitor API and "trace" command - Added "Yosys" C++ namespace * Changes relevant to SAT solving - Added ezSAT::keep_cnf() and ezSAT::non_incremental() - Added native ezSAT support for vector shift ops - Updated MiniSAT to git 37dc6c67e2 (from 2013-09-25) * New commands (or large improvements to commands) - Added "synth" command with default script - Added "share" (finally some real resource sharing) - Added "memory_share" (reduce number of ports on memories) - Added "wreduce" and "alumacc" commands - Added "opt -keepdc -fine -full -fast" - Added some "test_*" commands * Various other changes - Added %D and %c select operators - Added support for labels in yosys scripts - Added support for here-documents in yosys scripts - Support "+/" prefix for files from proc_share_dir - Added "autoidx" statement to ilang language - Switched from "yosys-svgviewer" to "xdot" - Renamed "stdcells.v" to "techmap.v" - Various bug fixes and small improvements - Improved welcome and bye messages Yosys 0.2.0 .. Yosys 0.3.0 -------------------------- * Driver program and overall behavior: - Added "design -push" and "design -pop" - Added "tee" command for redirecting log output * Changes in the internal cell library: - Added $dlatchsr and $_DLATCHSR_???_ cell types * Improvements in Verilog frontend: - Improved support for const functions (case, always, repeat) - The generate..endgenerate keywords are now optional - Added support for arrays of module instances - Added support for "`default_nettype" directive - Added support for "`line" directive * Other front- and back-ends: - Various changes to "write_blif" options - Various improvements in EDIF backend - Added "vhdl2verilog" pseudo-front-end - Added "verific" pseudo-front-end * Improvements in technology mapping: - Added support for recursive techmap - Added CONSTMSK and CONSTVAL features to techmap - Added _TECHMAP_CONNMAP_*_ feature to techmap - Added _TECHMAP_REPLACE_ feature to techmap - Added "connwrappers" command for wrap-extract-unwrap method - Added "extract -map %" feature - Added "extract -ignore_param ..." and "extract -ignore_parameters" - Added "techmap -max_iter" option * Improvements to "eval" and "sat" framework: - Now include a copy of Minisat (with build fixes applied) - Switched to Minisat::SimpSolver as SAT back-end - Added "sat -dump_vcd" feature - Added "sat -dump_cnf" feature - Added "sat -initsteps " feature - Added "freduce -stop " feature - Added "freduce -dump " feature * Integration with ABC: - Updated ABC rev to 7600ffb9340c * Improvements in the internal APIs: - Added RTLIL::Module::add... helper methods - Various build fixes for OSX (Darwin) and OpenBSD Yosys 0.1.0 .. Yosys 0.2.0 -------------------------- * Changes to the driver program: - Added "yosys -h" and "yosys -H" - Added support for backslash line continuation in scripts - Added support for #-comments in same line as command - Added "echo" and "log" commands * Improvements in Verilog frontend: - Added support for local registers in named blocks - Added support for "case" in "generate" blocks - Added support for $clog2 system function - Added support for basic SystemVerilog assert statements - Added preprocessor support for macro arguments - Added preprocessor support for `elsif statement - Added "verilog_defaults" command - Added read_verilog -icells option - Added support for constant sizes from parameters - Added "read_verilog -setattr" - Added support for function returning 'integer' - Added limited support for function calls in parameter values - Added "read_verilog -defer" to suppress evaluation of modules with default parameters * Other front- and back-ends: - Added BTOR backend - Added Liberty frontend * Improvements in technology mapping: - The "dfflibmap" command now strongly prefers solutions with no inverters in clock paths - The "dfflibmap" command now prefers cells with smaller area - Added support for multiple -map options to techmap - Added "dfflibmap" support for //-comments in liberty files - Added "memory_unpack" command to revert "memory_collect" - Added standard techmap rule "techmap -share_map pmux2mux.v" - Added "iopadmap -bits" - Added "setundef" command - Added "hilomap" command * Changes in the internal cell library: - Major rewrite of simlib.v for better compatibility with other tools - Added PRIORITY parameter to $memwr cells - Added TRANSPARENT parameter to $memrd cells - Added RD_TRANSPARENT parameter to $mem cells - Added $bu0 cell (always 0-extend, even undef MSB) - Added $assert cell type - Added $slice and $concat cell types * Integration with ABC: - Updated ABC to hg rev 2058c8ccea68 - Tighter integration of ABC build with Yosys build. The make targets 'make abc' and 'make install-abc' are now obsolete. - Added support for passing FFs from one clock domain through ABC - Now always use BLIF as exchange format with ABC - Added support for "abc -script +" - Improved standard ABC recipe - Added support for "keep" attribute to abc command - Added "abc -dff / -clk / -keepff" options * Improvements to "eval" and "sat" framework: - Added support for "0" and "~0" in right-hand side -set expressions - Added "eval -set-undef" and "eval -table" - Added "sat -set-init" and "sat -set-init-*" for sequential problems - Added undef support to SAT solver, incl. various new "sat" options - Added correct support for === and !== for "eval" and "sat" - Added "sat -tempinduct" (default -seq is now non-induction sequential) - Added "sat -prove-asserts" - Complete rewrite of the 'freduce' command - Added "miter" command - Added "sat -show-inputs" and "sat -show-outputs" - Added "sat -ignore_unknown_cells" (now produce an error by default) - Added "sat -falsify" - Now "sat -verify" and "sat -falsify" can also be used without "-prove" - Added "expose" command - Added support for @ to sat and eval signal expressions * Changes in the 'make test' framework and auxiliary test tools: - Added autotest.sh -p and -f options - Replaced autotest.sh ISIM support with XSIM support - Added test cases for SAT framework * Added "abbreviated IDs": - Now $$foo can be abbreviated as $foo. - Usually this last part is a unique id (from RTLIL::autoidx) - This abbreviated IDs are now also used in "show" output * Other changes to selection framework: - Now */ is optional in */: expressions - Added "select -assert-none" and "select -assert-any" - Added support for matching modules by attribute (A:) - Added "select -none" - Added support for r: pattern for matching cell parameters - Added support for !=, <, <=, >=, > for attribute and parameter matching - Added support for %s for selecting sub-modules - Added support for %m for expanding selections to whole modules - Added support for i:*, o:* and x:* pattern for selecting module ports - Added support for s: pattern for matching wire width - Added support for %a operation to select wire aliases * Various other changes to commands and options: - The "ls" command now supports wildcards - Added "show -pause" and "show -format dot" - Added "show -color" support for cells - Added "show -label" and "show -notitle" - Added "dump -m" and "dump -n" - Added "history" command - Added "rename -hide" - Added "connect" command - Added "splitnets -driver" - Added "opt_const -mux_undef" - Added "opt_const -mux_bool" - Added "opt_const -undriven" - Added "opt -mux_undef -mux_bool -undriven -purge" - Added "hierarchy -libdir" - Added "hierarchy -purge_lib" (by default now do not remove lib cells) - Added "delete" command - Added "dump -append" - Added "setattr" and "setparam" commands - Added "design -stash/-copy-from/-copy-to" - Added "copy" command - Added "splice" command yosys-0.52/CODEOWNERS000066400000000000000000000033051477540374200142370ustar00rootroot00000000000000## CODE NOTIFICATIONS # Register yourself here to be notified about modifications # for any files you have an interest in/know your way around. # Each line is a file pattern followed by one or more users. # Both github usernames and email addresses are supported. # Order is important; the last matching pattern takes the most # precedence. Previous matches will not be applied. # PATH (can use glob) USERNAME(S) CODEOWNERS @nakengelhardt passes/cmds/scratchpad.cc @nakengelhardt frontends/rpc/ @whitequark 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 .github/workflows/*.yml @mmicko ## External Contributors # Only users with write permission to the repository get review # requests automatically, but we add information for other # contributors here too, so we know who to ask to take a look. # These still override previous lines, so be careful not to # accidentally disable any of the above rules. frontends/verilog/ @zachjs frontends/ast/ @zachjs techlibs/intel_alm/ @Ravenslofty techlibs/gowin/ @pepijndevos techlibs/gatemate/ @pu-cc # pyosys misc/*.py @btut 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 yosys-0.52/CODE_OF_CONDUCT.md000066400000000000000000000121531477540374200154440ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@yosyshq.com and/or claire@clairexen.net. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. yosys-0.52/CONTRIBUTING.md000066400000000000000000000070101477540374200150720ustar00rootroot00000000000000# Introduction Thanks for thinking about contributing to the Yosys project. If this is your first time contributing to an open source project, please take a look at the following guide: https://opensource.guide/how-to-contribute/#orienting-yourself-to-a-new-project. Information about the Yosys coding style is available on our Read the Docs: https://yosys.readthedocs.io/en/latest/yosys_internals/extending_yosys/contributing.html. # Using the issue tracker The [issue tracker](https://github.com/YosysHQ/yosys/issues) is used for tracking bugs or other problems with Yosys or its documentation. It is also the place to go for requesting new features. When [creating a new issue](https://github.com/YosysHQ/yosys/issues/new/choose), we have a few templates available. Please make use of these! It will make it much easier for someone to respond and help. ### Bug reports Before you submit an issue, please have a search of the existing issues in case one already exists. Making sure that you have a minimal, complete and verifiable example (MVCE) is a great way to quickly check an existing issue against a new one. Stack overflow has a guide on [how to create an MVCE](https://stackoverflow.com/help/minimal-reproducible-example). The [`bugpoint` command](https://yosyshq.readthedocs.io/projects/yosys/en/latest/cmd/bugpoint.html) in Yosys can be helpful for this process. # Using pull requests If you are working on something to add to Yosys, or fix something that isn't working quite right, make a [PR](https://github.com/YosysHQ/yosys/pulls)! An open PR, even as a draft, tells everyone that you're working on it and they don't have to. It can also be a useful way to solicit feedback on in-progress changes. See below to find the best way to [ask us questions](#asking-questions). In general, all changes to the code are done as a PR, with [Continuous Integration (CI)](https://github.com/YosysHQ/yosys/actions) tools that automatically run the full suite of tests compiling and running Yosys. Please make use of this! If you're adding a feature: add a test! Not only does it verify that your feature is working as expected, but it can also be a handy way for people to see how the feature is used. If you're fixing a bug: add a test! If you can, do this first; it's okay if the test starts off failing - you already know there is a bug. CI also helps to make sure that your changes still work under a range of compilers, settings, and targets. ### Labels We use [labels](https://github.com/YosysHQ/yosys/labels) to help categorise issues and PRs. If a label seems relevant to your work, please do add it; this also includes the labels beggining with 'status-'. The 'merge-' labels are used by maintainers for tracking and communicating which PRs are ready and pending merge; please do not use these labels if you are not a maintainer. # Asking questions If you have a question about how to use Yosys, please ask on our [discussions page](https://github.com/YosysHQ/yosys/discussions) or in our [community slack](https://join.slack.com/t/yosyshq/shared_invite/zt-1aopkns2q-EiQ97BeQDt_pwvE41sGSuA). The slack is also a great place to ask questions about developing or contributing to Yosys. We have open dev 'jour fixe' (JF) meetings where developers from YosysHQ and the community come together to discuss open issues and PRs. This is also a good place to talk to us about how to implement larger PRs. Please join the community slack if you would like to join the next meeting, the link is available in the description of the #devel-discuss channel. yosys-0.52/COPYING000066400000000000000000000014111477540374200136730ustar00rootroot00000000000000ISC License Copyright (C) 2012 - 2025 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. yosys-0.52/Dockerfile000066400000000000000000000017171477540374200146430ustar00rootroot00000000000000ARG IMAGE="python:3-slim-buster" #--- FROM $IMAGE AS base RUN apt-get update -qq \ && DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ ca-certificates \ clang \ lld \ curl \ libffi-dev \ libreadline-dev \ tcl-dev \ graphviz \ xdot \ && apt-get autoclean && apt-get clean && apt-get -y autoremove \ && update-ca-certificates \ && rm -rf /var/lib/apt/lists #--- FROM base AS build RUN apt-get update -qq \ && DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ bison \ flex \ gawk \ gcc \ git \ iverilog \ pkg-config \ && apt-get autoclean && apt-get clean && apt-get -y autoremove \ && rm -rf /var/lib/apt/lists COPY . /yosys ENV PREFIX /opt/yosys RUN cd /yosys \ && make \ && make install \ && make test #--- FROM base COPY --from=build /opt/yosys /opt/yosys ENV PATH /opt/yosys/bin:$PATH RUN useradd -m yosys USER yosys CMD ["yosys"] yosys-0.52/Makefile000066400000000000000000001171161477540374200143120ustar00rootroot00000000000000 CONFIG := none # CONFIG := clang # CONFIG := gcc # CONFIG := wasi # CONFIG := msys2-32 # CONFIG := msys2-64 # features (the more the better) ENABLE_TCL := 1 ENABLE_ABC := 1 ENABLE_GLOB := 1 ENABLE_PLUGINS := 1 ENABLE_READLINE := 1 ENABLE_EDITLINE := 0 ENABLE_GHDL := 0 ENABLE_VERIFIC := 0 ENABLE_VERIFIC_SYSTEMVERILOG := 1 ENABLE_VERIFIC_VHDL := 1 ENABLE_VERIFIC_HIER_TREE := 1 ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS := 0 ENABLE_VERIFIC_EDIF := 0 ENABLE_VERIFIC_LIBERTY := 0 ENABLE_COVER := 1 ENABLE_LIBYOSYS := 0 ENABLE_ZLIB := 1 # python wrappers ENABLE_PYOSYS := 0 # other configuration flags ENABLE_GCOV := 0 ENABLE_GPROF := 0 ENABLE_DEBUG := 0 ENABLE_LTO := 0 ENABLE_CCACHE := 0 # sccache is not always a drop-in replacement for ccache in practice ENABLE_SCCACHE := 0 ENABLE_FUNCTIONAL_TESTS := 0 LINK_CURSES := 0 LINK_TERMCAP := 0 LINK_ABC := 0 # Needed for environments that can't run executables (i.e. emscripten, wasm) DISABLE_SPAWN := 0 # Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now) DISABLE_ABC_THREADS := 0 # clang sanitizers SANITIZER = # SANITIZER = address # SANITIZER = memory # SANITIZER = undefined # SANITIZER = cfi # Prefer using ENABLE_DEBUG over setting these OPT_LEVEL := -O3 GCC_LTO := CLANG_LTO := -flto=thin 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 endif ifeq ($(ENABLE_PYOSYS),1) ENABLE_LIBYOSYS := 1 endif BINDIR := $(PREFIX)/bin LIBDIR := $(PREFIX)/lib/$(PROGRAM_PREFIX)yosys DATDIR := $(PREFIX)/share/$(PROGRAM_PREFIX)yosys EXE = OBJS = GENFILES = EXTRA_OBJS = EXTRA_TARGETS = TARGETS = $(PROGRAM_PREFIX)yosys$(EXE) $(PROGRAM_PREFIX)yosys-config PRETTY = 1 SMALL = 0 # Unit test UNITESTPATH := tests/unit all: top-all YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST))) VPATH := $(YOSYS_SRC) CXXSTD ?= c++17 CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include 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 BISON ?= bison STRIP ?= strip AWK ?= awk ifneq ($(shell :; command -v rsync),) RSYNC_CP ?= rsync -rc else RSYNC_CP ?= cp -ru endif ifeq ($(OS), Darwin) PLUGIN_LINKFLAGS += -undefined dynamic_lookup LINKFLAGS += -rdynamic # homebrew search paths ifneq ($(shell :; command -v brew),) BREW_PREFIX := $(shell brew --prefix)/opt $(info $$BREW_PREFIX is [${BREW_PREFIX}]) ifeq ($(ENABLE_PYOSYS),1) CXXFLAGS += -I$(BREW_PREFIX)/boost/include LINKFLAGS += -L$(BREW_PREFIX)/boost/lib -L$(BREW_PREFIX)/boost-python3/lib endif CXXFLAGS += -I$(BREW_PREFIX)/readline/include 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) # macports search paths else ifneq ($(shell :; command -v port),) PORT_PREFIX := $(patsubst %/bin/port,%,$(shell :; command -v port)) CXXFLAGS += -I$(PORT_PREFIX)/include LINKFLAGS += -L$(PORT_PREFIX)/lib PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) export PATH := $(PORT_PREFIX)/bin:$(PATH) endif else LINKFLAGS += -rdynamic ifneq ($(OS), OpenBSD) LIBS += -lrt endif endif ifeq ($(OS), Haiku) # Allow usage of non-posix vasprintf, mkstemps functions CXXFLAGS += -D_DEFAULT_SOURCE endif YOSYS_VER := 0.52 YOSYS_MAJOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f1) YOSYS_MINOR := $(shell echo $(YOSYS_VER) | cut -d'.' -f2 | cut -d'+' -f1) YOSYS_COMMIT := $(shell echo $(YOSYS_VER) | cut -d'+' -f2) CXXFLAGS += -DYOSYS_VER=\\"$(YOSYS_VER)\\" \ -DYOSYS_MAJOR=$(YOSYS_MAJOR) \ -DYOSYS_MINOR=$(YOSYS_MINOR) \ -DYOSYS_COMMIT=$(YOSYS_COMMIT) # 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) ifneq ($(findstring Format:,$(TARBALL_GIT_REV)),) GIT_REV := $(shell GIT_DIR=$(YOSYS_SRC)/.git git rev-parse --short=9 HEAD || echo UNKNOWN) else GIT_REV := $(TARBALL_GIT_REV) endif OBJS = kernel/version_$(GIT_REV).o bumpversion: # sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline c4b5190.. | wc -l`/;" Makefile 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. ABCEXTERNAL ?= define newline endef ifneq ($(wildcard Makefile.conf),) # don't echo Makefile.conf contents when invoked to print source versions ifeq ($(findstring echo-,$(MAKECMDGOALS)),) $(info $(subst $$--$$,$(newline),$(shell sed 's,^,[Makefile.conf] ,; s,$$,$$--$$,;' < Makefile.conf | tr -d '\n' | sed 's,\$$--\$$$$,,'))) endif include Makefile.conf endif PYTHON_EXECUTABLE := $(shell if python3 -c ""; then echo "python3"; else echo "python"; fi) ifeq ($(ENABLE_PYOSYS),1) PYTHON_VERSION_TESTCODE := "import sys;t='{v[0]}.{v[1]}'.format(v=list(sys.version_info[:2]));print(t)" PYTHON_VERSION := $(shell $(PYTHON_EXECUTABLE) -c ""$(PYTHON_VERSION_TESTCODE)"") PYTHON_MAJOR_VERSION := $(shell echo $(PYTHON_VERSION) | cut -f1 -d.) ENABLE_PYTHON_CONFIG_EMBED ?= $(shell $(PYTHON_EXECUTABLE)-config --embed --libs > /dev/null && echo 1) ifeq ($(ENABLE_PYTHON_CONFIG_EMBED),1) PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config --embed else PYTHON_CONFIG := $(PYTHON_EXECUTABLE)-config endif PYTHON_DESTDIR := $(shell $(PYTHON_EXECUTABLE) -c "import site; print(site.getsitepackages()[-1]);") # Reload Makefile.conf to override python specific variables if defined ifneq ($(wildcard Makefile.conf),) include Makefile.conf endif endif ABC_ARCHFLAGS = "" ifeq ($(OS), OpenBSD) ABC_ARCHFLAGS += "-DABC_NO_RLIMIT" endif # This gets overridden later. LTOFLAGS := $(GCC_LTO) ifeq ($(CONFIG),clang) CXX = clang++ CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) ifeq ($(ENABLE_LTO),1) LINKFLAGS += -fuse-ld=lld endif ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" LTOFLAGS := $(CLANG_LTO) ifneq ($(SANITIZER),) $(info [Clang Sanitizer] $(SANITIZER)) CXXFLAGS += -g -O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=$(SANITIZER) LINKFLAGS += -g -fsanitize=$(SANITIZER) ifneq ($(findstring address,$(SANITIZER)),) ENABLE_COVER := 0 endif ifneq ($(findstring memory,$(SANITIZER)),) CXXFLAGS += -fPIE -fsanitize-memory-track-origins LINKFLAGS += -fPIE -fsanitize-memory-track-origins endif ifneq ($(findstring cfi,$(SANITIZER)),) CXXFLAGS += -flto LINKFLAGS += -flto LTOFLAGS = endif endif else ifeq ($(CONFIG),gcc) CXX = g++ CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" else ifeq ($(CONFIG),gcc-static) LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -static LIBS := $(filter-out -lrt,$(LIBS)) CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) 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),wasi) ifeq ($(WASI_SDK),) CXX = clang++ AR = llvm-ar RANLIB = llvm-ranlib WASIFLAGS := -target wasm32-wasi --sysroot $(WASI_SYSROOT) $(WASIFLAGS) else 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=$(CXXSTD) $(OPT_LEVEL) -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) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT" ABCMKARGS += OPTFLAGS="-Os" EXE = .wasm DISABLE_SPAWN := 1 ifeq ($(ENABLE_ABC),1) LINK_ABC := 1 DISABLE_ABC_THREADS := 1 endif else ifeq ($(CONFIG),msys2-32) CXX = i686-w64-mingw32-g++ CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s LIBS := $(filter-out -lrt,$(LIBS)) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DWIN32 -DHAVE_STRUCT_TIMESPEC -fpermissive -w" 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++ CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LINKFLAGS := $(filter-out -rdynamic,$(LINKFLAGS)) -s LIBS := $(filter-out -lrt,$(LIBS)) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DWIN32 -DHAVE_STRUCT_TIMESPEC -fpermissive -w" ABCMKARGS += LIBS="-lpthread -lshlwapi -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)" EXE = .exe else ifeq ($(CONFIG),none) CXXFLAGS += -std=$(CXXSTD) $(OPT_LEVEL) ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H $(ABC_ARCHFLAGS)" LTOFLAGS = else $(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, msys2-32, msys2-64, none) endif ifeq ($(ENABLE_LTO),1) CXXFLAGS += $(LTOFLAGS) LINKFLAGS += $(LTOFLAGS) endif ifeq ($(ENABLE_LIBYOSYS),1) TARGETS += libyosys.so endif ifeq ($(ENABLE_PYOSYS),1) # python-config --ldflags includes -l and -L, but LINKFLAGS is only -L LINKFLAGS += $(filter-out -l%,$(shell $(PYTHON_CONFIG) --ldflags)) LIBS += $(shell $(PYTHON_CONFIG) --libs) CXXFLAGS += $(shell $(PYTHON_CONFIG) --includes) -DWITH_PYTHON EXTRA_TARGETS += wheel # 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 $(LINKFLAGS) $(LIBS) -l$(1) - > /dev/null 2>&1 && echo "-l$(1)") BOOST_PYTHON_LIB ?= $(shell \ $(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 LIBS += $(BOOST_PYTHON_LIB) -lboost_system -lboost_filesystem PY_WRAPPER_FILE = kernel/python_wrappers OBJS += $(PY_WRAPPER_FILE).o PY_GEN_SCRIPT= py_wrap_generator PY_WRAP_INCLUDES := $(shell $(PYTHON_EXECUTABLE) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).print_includes()") endif # ENABLE_PYOSYS ifeq ($(ENABLE_READLINE),1) CXXFLAGS += -DYOSYS_ENABLE_READLINE ifeq ($(OS), $(filter $(OS),FreeBSD OpenBSD NetBSD)) CXXFLAGS += -I/usr/local/include endif LIBS += -lreadline ifeq ($(LINK_CURSES),1) LIBS += -lcurses ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline" endif ifeq ($(LINK_TERMCAP),1) LIBS += -ltermcap ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap" endif else ifeq ($(ENABLE_EDITLINE),1) CXXFLAGS += -DYOSYS_ENABLE_EDITLINE LIBS += -ledit endif ABCMKARGS += "ABC_USE_NO_READLINE=1" endif ifeq ($(DISABLE_ABC_THREADS),1) ABCMKARGS += "ABC_USE_NO_PTHREADS=1" endif ifeq ($(DISABLE_SPAWN),1) CXXFLAGS += -DYOSYS_DISABLE_SPAWN endif ifeq ($(ENABLE_PLUGINS),1) CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS 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 ifeq ($(ENABLE_GLOB),1) CXXFLAGS += -DYOSYS_ENABLE_GLOB endif ifeq ($(ENABLE_ZLIB),1) CXXFLAGS += -DYOSYS_ENABLE_ZLIB LIBS += -lz endif ifeq ($(ENABLE_TCL),1) TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')") 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 CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo $(TCL_LIBS)) ifneq (,$(findstring TCL_WITH_EXTERNAL_TOMMATH,$(CXXFLAGS))) LIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libtommath || echo) endif endif ifeq ($(ENABLE_GCOV),1) CXXFLAGS += --coverage LINKFLAGS += --coverage endif ifeq ($(ENABLE_GPROF),1) CXXFLAGS += -pg LINKFLAGS += -pg endif ifeq ($(ENABLE_DEBUG),1) CXXFLAGS := -Og -DDEBUG $(filter-out $(OPT_LEVEL),$(CXXFLAGS)) endif ifeq ($(ENABLE_ABC),1) CXXFLAGS += -DYOSYS_ENABLE_ABC ifeq ($(LINK_ABC),1) CXXFLAGS += -DYOSYS_LINK_ABC ifeq ($(DISABLE_ABC_THREADS),0) LIBS += -lpthread endif else ifeq ($(ABCEXTERNAL),) TARGETS := $(PROGRAM_PREFIX)yosys-abc$(EXE) $(TARGETS) 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 ?= database util containers ifeq ($(ENABLE_VERIFIC_HIER_TREE),1) VERIFIC_COMPONENTS += hier_tree CXXFLAGS += -DVERIFIC_HIER_TREE_SUPPORT else ifneq ($(wildcard $(VERIFIC_DIR)/hier_tree),) VERIFIC_COMPONENTS += hier_tree endif endif ifeq ($(ENABLE_VERIFIC_SYSTEMVERILOG),1) VERIFIC_COMPONENTS += verilog CXXFLAGS += -DVERIFIC_SYSTEMVERILOG_SUPPORT else ifneq ($(wildcard $(VERIFIC_DIR)/verilog),) VERIFIC_COMPONENTS += verilog endif endif ifeq ($(ENABLE_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 ifeq ($(ENABLE_VERIFIC_YOSYSHQ_EXTENSIONS),1) VERIFIC_COMPONENTS += extensions CXXFLAGS += -DYOSYSHQ_VERIFIC_EXTENSIONS else ifneq ($(wildcard $(VERIFIC_DIR)/extensions),) VERIFIC_COMPONENTS += extensions endif endif CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC ifeq ($(OS), Darwin) LIBS_VERIFIC += $(foreach comp,$(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)),-Wl,-force_load $(comp)) -lz else LIBS_VERIFIC += -Wl,--whole-archive $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -Wl,--no-whole-archive -lz endif endif ifeq ($(ENABLE_COVER),1) CXXFLAGS += -DYOSYS_ENABLE_COVER endif ifeq ($(ENABLE_CCACHE),1) CXX := ccache $(CXX) else ifeq ($(ENABLE_SCCACHE),1) CXX := sccache $(CXX) endif endif define add_share_file EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2))) $(subst //,/,$(1)/$(notdir $(2))): $(2) $$(P) mkdir -p $(1) $$(Q) cp "$(YOSYS_SRC)"/$(2) $(subst //,/,$(1)/$(notdir $(2))) endef define add_gen_share_file EXTRA_TARGETS += $(subst //,/,$(1)/$(notdir $(2))) $(subst //,/,$(1)/$(notdir $(2))): $(2) $$(P) mkdir -p $(1) $$(Q) cp $(2) $(subst //,/,$(1)/$(notdir $(2))) endef define add_include_file $(eval $(call add_share_file,$(dir share/include/$(1)),$(1))) endef define add_extra_objs EXTRA_OBJS += $(1) .SECONDARY: $(1) endef ifeq ($(PRETTY), 1) P_STATUS = 0 P_OFFSET = 0 P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) $(PROGRAM_PREFIX)yosys$(EXE) | $(AWK) 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }')) P_SHOW = [$(shell $(AWK) "BEGIN { N=$(words $(OBJS) $(PROGRAM_PREFIX)yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%] P = @echo "$(if $(findstring $@,$(TARGETS) $(EXTRA_TARGETS)),$(eval P_OFFSET = 10))$(call P_UPDATE)$(call P_SHOW) Building $@"; Q = @ S = -s else P_SHOW = -> P = Q = S = endif $(eval $(call add_include_file,kernel/binding.h)) $(eval $(call add_include_file,kernel/bitpattern.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/cost.h)) $(eval $(call add_include_file,kernel/drivertools.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/gzip.h)) $(eval $(call add_include_file,kernel/hashlib.h)) $(eval $(call add_include_file,kernel/io.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/sexpr.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/yosys_common.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)) OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o kernel/io.o kernel/gzip.o OBJS += kernel/binding.o kernel/tclapi.o OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.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 kernel/sexpr.o OBJS += kernel/drivertools.o kernel/functional.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 OBJS += libs/json11/json11.o OBJS += libs/ezsat/ezsat.o OBJS += libs/ezsat/ezminisat.o OBJS += libs/minisat/Options.o 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 techlibs/%_pm.h: passes/pmgen/pmgen.py techlibs/%.pmg $(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p $(notdir $*) $(filter-out $<,$^) 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 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 OBJS += passes/hierarchy/hierarchy.o OBJS += passes/cmds/select.o OBJS += passes/cmds/show.o OBJS += passes/cmds/stat.o OBJS += passes/cmds/cover.o OBJS += passes/cmds/design.o OBJS += passes/cmds/plugin.o include $(YOSYS_SRC)/passes/proc/Makefile.inc include $(YOSYS_SRC)/passes/opt/Makefile.inc include $(YOSYS_SRC)/passes/techmap/Makefile.inc include $(YOSYS_SRC)/backends/verilog/Makefile.inc include $(YOSYS_SRC)/backends/rtlil/Makefile.inc include $(YOSYS_SRC)/techlibs/common/Makefile.inc endif ifeq ($(LINK_ABC),1) OBJS += $(PROGRAM_PREFIX)yosys-libabc.a endif # prevent the CXXFLAGS set by this Makefile from reaching abc/Makefile, # especially the -MD flag which will break the build when CXX is clang unexport CXXFLAGS top-all: $(TARGETS) $(EXTRA_TARGETS) @echo "" @echo " Build successful." @echo "" .PHONY: compile-only compile-only: $(OBJS) $(GENFILES) $(EXTRA_TARGETS) @echo "" @echo " Compile successful." @echo "" .PHONY: share share: $(EXTRA_TARGETS) @echo "" @echo " Share directory created." @echo "" $(PROGRAM_PREFIX)yosys$(EXE): $(OBJS) $(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) $(CXX) -o libyosys.so -shared -undefined dynamic_lookup -Wl,-install_name,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) else $(P) $(CXX) -o libyosys.so -shared -Wl,-soname,$(LIBDIR)/libyosys.so $(LINKFLAGS) $^ $(LIBS) $(LIBS_VERIFIC) endif %.o: %.cc $(Q) mkdir -p $(dir $@) $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< %.pyh: %.h $(Q) mkdir -p $(dir $@) $(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) $(Q) mkdir -p $(dir $@) $(P) $(PYTHON_EXECUTABLE) -c "from misc import $(PY_GEN_SCRIPT); $(PY_GEN_SCRIPT).gen_wrappers(\"$(PY_WRAPPER_FILE).cc\")" endif %.o: %.cpp $(Q) mkdir -p $(dir $@) $(P) $(CXX) -o $@ -c $(CPPFLAGS) $(CXXFLAGS) $< YOSYS_VER_STR := Yosys $(YOSYS_VER) (git sha1 $(GIT_REV), $(notdir $(CXX)) $(shell \ $(CXX) --version | tr ' ()' '\n' | grep '^[0-9]' | head -n1) $(filter -f% -m% -O% -DNDEBUG,$(CXXFLAGS))) kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile $(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc $(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"$(YOSYS_VER_STR)\"; }" > kernel/version_$(GIT_REV).cc ifeq ($(ENABLE_VERIFIC),1) CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) LIBS_NOVERIFIC = $(foreach v,$(LIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) else CXXFLAGS_NOVERIFIC = $(CXXFLAGS) LIBS_NOVERIFIC = $(LIBS) endif $(PROGRAM_PREFIX)yosys-config: misc/yosys-config.in $(YOSYS_SRC)/Makefile $(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 .PHONY: check-git-abc check-git-abc: @if [ ! -d "$(YOSYS_SRC)/abc" ]; then \ echo "Error: The 'abc' directory does not exist."; \ echo "Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \ exit 1; \ elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^ '; then \ exit 0; \ elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && ! grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ echo "'abc' comes from a tarball. Continuing."; \ exit 0; \ elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^+'; then \ echo "'abc' submodule does not match expected commit."; \ echo "Run 'git submodule update' to check out the correct version."; \ echo "Note: If testing a different version of abc, call 'git commit abc' in the Yosys source directory to update the expected commit."; \ exit 1; \ elif git -C "$(YOSYS_SRC)" submodule status abc 2>/dev/null | grep -q '^U'; then \ echo "'abc' submodule has merge conflicts."; \ echo "Please resolve merge conflicts before continuing."; \ exit 1; \ elif [ -f "$(YOSYS_SRC)/abc/.gitcommit" ] && grep -q '\$$Format:%[hH]\$$' "$(YOSYS_SRC)/abc/.gitcommit"; then \ echo "Error: 'abc' is not configured as a git submodule."; \ echo "To resolve this:"; \ echo "1. Back up your changes: Save any modifications from the 'abc' directory to another location."; \ echo "2. Remove the existing 'abc' directory: Delete the 'abc' directory and all its contents."; \ echo "3. Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \ echo "4. Reapply your changes: Move your saved changes back to the 'abc' directory, if necessary."; \ exit 1; \ else \ echo "Initialize the submodule: Run 'git submodule update --init' to set up 'abc' as a submodule."; \ exit 1; \ fi abc/abc$(EXE) abc/libabc.a: | check-git-abc $(P) $(Q) mkdir -p abc && $(MAKE) -C $(PROGRAM_PREFIX)abc -f "$(realpath $(YOSYS_SRC)/abc/Makefile)" ABCSRC="$(realpath $(YOSYS_SRC)/abc/)" $(S) $(ABCMKARGS) $(if $(filter %.a,$@),PROG="abc",PROG="abc$(EXE)") MSG_PREFIX="$(eval P_OFFSET = 5)$(call P_SHOW)$(eval P_OFFSET = 10) ABC: " $(if $(filter %.a,$@),libabc.a) $(PROGRAM_PREFIX)yosys-abc$(EXE): abc/abc$(EXE) $(P) cp $< $(PROGRAM_PREFIX)yosys-abc$(EXE) $(PROGRAM_PREFIX)yosys-libabc.a: abc/libabc.a $(P) cp $< $(PROGRAM_PREFIX)yosys-libabc.a ifneq ($(SEED),) SEEDOPT="-S $(SEED)" else SEEDOPT="" endif ifneq ($(ABCEXTERNAL),) ABCOPT="-A $(ABCEXTERNAL)" else ABCOPT="" endif # Tests that generate .mk with tests/gen-tests-makefile.sh MK_TEST_DIRS = MK_TEST_DIRS += tests/arch/anlogic MK_TEST_DIRS += tests/arch/ecp5 MK_TEST_DIRS += tests/arch/efinix MK_TEST_DIRS += tests/arch/gatemate MK_TEST_DIRS += tests/arch/gowin MK_TEST_DIRS += tests/arch/ice40 MK_TEST_DIRS += tests/arch/intel_alm MK_TEST_DIRS += tests/arch/machxo2 MK_TEST_DIRS += tests/arch/microchip MK_TEST_DIRS += tests/arch/nanoxplore MK_TEST_DIRS += tests/arch/nexus MK_TEST_DIRS += tests/arch/quicklogic/pp3 MK_TEST_DIRS += tests/arch/quicklogic/qlf_k6n10f MK_TEST_DIRS += tests/arch/xilinx MK_TEST_DIRS += tests/opt MK_TEST_DIRS += tests/sat MK_TEST_DIRS += tests/sim MK_TEST_DIRS += tests/svtypes MK_TEST_DIRS += tests/techmap MK_TEST_DIRS += tests/various ifeq ($(ENABLE_VERIFIC),1) ifneq ($(YOSYS_NOVERIFIC),1) MK_TEST_DIRS += tests/verific endif endif MK_TEST_DIRS += tests/verilog # Tests that don't generate .mk SH_TEST_DIRS = SH_TEST_DIRS += tests/simple SH_TEST_DIRS += tests/simple_abc9 SH_TEST_DIRS += tests/hana SH_TEST_DIRS += tests/asicworld # SH_TEST_DIRS += tests/realmath SH_TEST_DIRS += tests/share SH_TEST_DIRS += tests/opt_share SH_TEST_DIRS += tests/fsm SH_TEST_DIRS += tests/memlib SH_TEST_DIRS += tests/bram SH_TEST_DIRS += tests/svinterfaces SH_TEST_DIRS += tests/xprop SH_TEST_DIRS += tests/select SH_TEST_DIRS += tests/proc SH_TEST_DIRS += tests/blif SH_TEST_DIRS += tests/arch SH_TEST_DIRS += tests/rpc SH_TEST_DIRS += tests/memfile SH_TEST_DIRS += tests/fmt SH_TEST_DIRS += tests/cxxrtl ifeq ($(ENABLE_FUNCTIONAL_TESTS),1) SH_TEST_DIRS += tests/functional endif # Tests that don't generate .mk and need special args SH_ABC_TEST_DIRS = SH_ABC_TEST_DIRS += tests/memories SH_ABC_TEST_DIRS += tests/aiger SH_ABC_TEST_DIRS += tests/alumacc # seed-tests/ is a dummy string, not a directory .PHONY: seed-tests seed-tests: $(SH_TEST_DIRS:%=seed-tests/%) .PHONY: seed-tests/% seed-tests/%: %/run-test.sh $(TARGETS) $(EXTRA_TARGETS) +cd $* && bash run-test.sh $(SEEDOPT) +@echo "...passed tests in $*" # abcopt-tests/ is a dummy string, not a directory .PHONY: abcopt-tests abcopt-tests: $(SH_ABC_TEST_DIRS:%=abcopt-tests/%) abcopt-tests/%: %/run-test.sh $(TARGETS) $(EXTRA_TARGETS) +cd $* && bash run-test.sh $(ABCOPT) $(SEEDOPT) +@echo "...passed tests in $*" # makefile-tests/ is a dummy string, not a directory .PHONY: makefile-tests makefile-tests: $(MK_TEST_DIRS:%=makefile-tests/%) # this target actually emits .mk files %.mk: +cd $(dir $*) && bash run-test.sh # this one spawns submake on each makefile-tests/%: %/run-test.mk $(TARGETS) $(EXTRA_TARGETS) $(MAKE) -C $* -f run-test.mk +@echo "...passed tests in $*" test: makefile-tests abcopt-tests seed-tests @echo "" @echo " Passed \"make test\"." ifeq ($(ENABLE_VERIFIC),1) ifeq ($(YOSYS_NOVERIFIC),1) @echo " Ran tests without verific support due to YOSYS_NOVERIFIC=1." endif endif @echo "" VALGRIND ?= valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all vgtest: $(TARGETS) $(EXTRA_TARGETS) $(VALGRIND) ./yosys -p 'setattr -mod -unset top; synth' $$( ls tests/simple/*.v | grep -v repwhile.v ) @echo "" @echo " Passed \"make vgtest\"." @echo "" vloghtb: $(TARGETS) $(EXTRA_TARGETS) +cd tests/vloghtb && bash run-test.sh @echo "" @echo " Passed \"make vloghtb\"." @echo "" ystests: $(TARGETS) $(EXTRA_TARGETS) rm -rf tests/ystests git clone https://github.com/YosysHQ/yosys-tests.git tests/ystests +$(MAKE) PATH="$$PWD:$$PATH" -C tests/ystests @echo "" @echo " Finished \"make ystests\"." @echo "" # Unit test unit-test: libyosys.so @$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" \ CXXFLAGS="$(CXXFLAGS)" LINKFLAGS="$(LINKFLAGS)" LIBS="$(LIBS)" ROOTPATH="$(CURDIR)" clean-unit-test: @$(MAKE) -C $(UNITESTPATH) clean ifeq ($(ENABLE_PYOSYS),1) wheel: $(TARGETS) $(PYTHON_EXECUTABLE) -m pip wheel . install-wheel: wheel $(PYTHON_EXECUTABLE) -m pip install pyosys-$(YOSYS_MAJOR).$(YOSYS_MINOR).$(YOSYS_COMMIT)-*.whl --force-reinstall else wheel: $(error Pyosys is not enabled. Set ENABLE_PYOSYS=1 to enable it.) install-wheel: $(error Pyosys is not enabled. Set ENABLE_PYOSYS=1 to enable it.) endif install: $(TARGETS) $(EXTRA_TARGETS) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) $(INSTALL_SUDO) cp $(filter-out libyosys.so,$(TARGETS)) $(DESTDIR)$(BINDIR) ifneq ($(filter $(PROGRAM_PREFIX)yosys,$(TARGETS)),) $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys endif ifneq ($(filter $(PROGRAM_PREFIX)yosys-abc,$(TARGETS)),) $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-abc endif ifneq ($(filter $(PROGRAM_PREFIX)yosys-filterlib,$(TARGETS)),) $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/$(PROGRAM_PREFIX)yosys-filterlib endif $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR) $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/. ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(LIBDIR) $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR)/ $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) @$(MAKE) install-wheel 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))) $(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR) ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so ifeq ($(ENABLE_PYOSYS),1) $(INSTALL_SUDO) $(PYTHON_EXECUTABLE) -m pip uninstall -y pyosys endif endif # 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) $(Q) mkdir -p docs/source/cmd $(Q) mkdir -p temp/docs/source/cmd $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' $(Q) $(RSYNC_CP) temp/docs/source/cmd docs/source $(Q) rm -rf temp docs/source/cell/word_add.rst: $(TARGETS) $(EXTRA_TARGETS) $(Q) mkdir -p docs/source/cell $(Q) mkdir -p temp/docs/source/cell $(Q) cd temp && ./../$(PROGRAM_PREFIX)yosys -p 'help -write-rst-cells-manual' $(Q) $(RSYNC_CP) temp/docs/source/cell docs/source $(Q) rm -rf temp docs/source/generated/cells.json: docs/source/generated $(TARGETS) $(EXTRA_TARGETS) $(Q) ./$(PROGRAM_PREFIX)yosys -p 'help -dump-cells-json $@' docs/source/generated/%.cc: backends/%.cc $(Q) mkdir -p $(@D) $(Q) cp $< $@ # diff returns exit code 1 if the files are different, but it's not an error docs/source/generated/functional/rosette.diff: backends/functional/smtlib.cc backends/functional/smtlib_rosette.cc $(Q) mkdir -p $(@D) $(Q) diff -U 20 $^ > $@ || exit 0 PHONY: docs/gen/functional_ir docs/gen/functional_ir: docs/source/generated/functional/smtlib.cc docs/source/generated/functional/rosette.diff PHONY: docs/gen docs/usage docs/reqs docs/gen: $(TARGETS) $(Q) $(MAKE) -C docs gen docs/source/generated: $(Q) mkdir -p docs/source/generated # some commands return an error and print the usage text to stderr define DOC_USAGE_STDERR docs/source/generated/$(1): $(TARGETS) docs/source/generated FORCE -$(Q) ./$(PROGRAM_PREFIX)$(1) --help 2> $$@ endef DOCS_USAGE_STDERR := yosys-filterlib # The in-tree ABC (yosys-abc) is only built when ABCEXTERNAL is not set. ifeq ($(ABCEXTERNAL),) DOCS_USAGE_STDERR += yosys-abc endif $(foreach usage,$(DOCS_USAGE_STDERR),$(eval $(call DOC_USAGE_STDERR,$(usage)))) # others print to stdout define DOC_USAGE_STDOUT docs/source/generated/$(1): $(TARGETS) docs/source/generated $(Q) ./$(PROGRAM_PREFIX)$(1) --help > $$@ || rm $$@ endef DOCS_USAGE_STDOUT := yosys yosys-smtbmc yosys-witness yosys-config $(foreach usage,$(DOCS_USAGE_STDOUT),$(eval $(call DOC_USAGE_STDOUT,$(usage)))) docs/usage: $(addprefix docs/source/generated/,$(DOCS_USAGE_STDOUT) $(DOCS_USAGE_STDERR)) docs/reqs: $(Q) $(MAKE) -C docs reqs .PHONY: docs/prep docs/prep: docs/source/cmd/abc.rst docs/source/generated/cells.json docs/gen docs/usage docs/gen/functional_ir DOC_TARGET ?= html docs: docs/prep $(Q) $(MAKE) -C docs $(DOC_TARGET) clean: rm -rf share rm -rf kernel/*.pyh 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 kernel/python_wrappers.o rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d rm -rf tests/asicworld/*.out tests/asicworld/*.log rm -rf tests/hana/*.out tests/hana/*.log 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 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 rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS)) -$(MAKE) -C docs clean rm -rf docs/source/cmd docs/util/__pycache__ rm -f *.whl rm -f libyosys.so clean-abc: $(MAKE) -C abc DEP= clean rm -f $(PROGRAM_PREFIX)yosys-abc$(EXE) $(PROGRAM_PREFIX)yosys-libabc.a abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a mrproper: clean git clean -xdf coverage: ./$(PROGRAM_PREFIX)yosys -qp 'help; help -all' rm -rf coverage.info coverage_html lcov --capture -d . --no-external -o coverage.info genhtml coverage.info --output-directory coverage_html clean_coverage: find . -name "*.gcda" -type f -delete FUNC_KERNEL := functional.cc functional.h sexpr.cc sexpr.h compute_graph.h FUNC_INCLUDES := $(addprefix --include *,functional/* $(FUNC_KERNEL)) coverage_functional: rm -rf coverage.info coverage_html lcov --capture -d backends/functional -d kernel $(FUNC_INCLUDES) --no-external -o coverage.info genhtml coverage.info --output-directory coverage_html qtcreator: echo "$(CXXFLAGS)" | grep -o '\-D[^ ]*' | tr ' ' '\n' | sed 's/-D/#define /' | sed 's/=/ /'> qtcreator.config { for file in $(basename $(OBJS)); do \ for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \ done; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \); } > qtcreator.files { echo .; find backends frontends kernel libs passes -type f \( -name '*.h' -o -name '*.hh' \) -printf '%h\n' | sort -u; } > qtcreator.includes touch qtcreator.creator vcxsrc: $(GENFILES) $(EXTRA_TARGETS) rm -rf yosys-win32-vcxsrc-$(YOSYS_VER){,.zip} set -e; for f in `ls $(filter %.cc %.cpp,$(GENFILES)) $(addsuffix .cc,$(basename $(OBJS))) $(addsuffix .cpp,$(basename $(OBJS))) 2> /dev/null`; do \ echo "Analyse: $$f" >&2; cpp -std=c++17 -MM -I. -D_YOSYS_ $$f; done | sed 's,.*:,,; s,//*,/,g; s,/[^/]*/\.\./,/,g; y, \\,\n\n,;' | grep '^[^/]' | sort -u | grep -v kernel/version_ > srcfiles.txt echo "libs/fst/fst_win_unistd.h" >> srcfiles.txt bash misc/create_vcxsrc.sh yosys-win32-vcxsrc $(YOSYS_VER) $(GIT_REV) echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"Yosys (Version Information Unavailable)\"; }" > kernel/version.cc zip yosys-win32-vcxsrc-$(YOSYS_VER)/genfiles.zip $(GENFILES) kernel/version.cc zip -r yosys-win32-vcxsrc-$(YOSYS_VER).zip yosys-win32-vcxsrc-$(YOSYS_VER)/ rm -f srcfiles.txt kernel/version.cc config-clean: clean rm -f Makefile.conf config-clang: clean echo 'CONFIG := clang' > Makefile.conf config-gcc: clean echo 'CONFIG := gcc' > Makefile.conf config-gcc-static: clean echo 'CONFIG := gcc-static' > Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf echo 'ENABLE_READLINE := 0' >> Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf config-wasi: clean echo 'CONFIG := wasi' > Makefile.conf echo 'ENABLE_TCL := 0' >> Makefile.conf echo 'ENABLE_ABC := 0' >> Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf echo 'ENABLE_READLINE := 0' >> Makefile.conf echo 'ENABLE_ZLIB := 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 "PREFIX := $(MINGW_PREFIX)" >> Makefile.conf config-gcov: clean echo 'CONFIG := gcc' > Makefile.conf echo 'ENABLE_GCOV := 1' >> Makefile.conf echo 'ENABLE_DEBUG := 1' >> Makefile.conf config-gprof: clean echo 'CONFIG := gcc' > Makefile.conf echo 'ENABLE_GPROF := 1' >> Makefile.conf config-sudo: echo "INSTALL_SUDO := sudo" >> Makefile.conf echo-yosys-ver: @echo "$(YOSYS_VER)" echo-git-rev: @echo "$(GIT_REV)" echo-cxx: @echo "$(CXX)" -include libs/*/*.d -include frontends/*/*.d -include passes/*/*.d -include backends/*/*.d -include kernel/*.d -include techlibs/*/*.d FORCE: .PHONY: all top-all abc test install install-abc docs clean mrproper qtcreator coverage vcxsrc .PHONY: config-clean config-clang config-gcc config-gcc-static config-gprof config-sudo yosys-0.52/README.md000066400000000000000000000210731477540374200141250ustar00rootroot00000000000000yosys – Yosys Open SYnthesis Suite =================================== This is a framework for RTL synthesis tools. It currently has extensive Verilog-2005 support and provides a basic set of synthesis algorithms for various application domains. Yosys can be adapted to perform any synthesis job by combining the existing passes (algorithms) using synthesis scripts and adding additional passes as needed by extending the yosys C++ code base. Yosys is free software licensed under the ISC license (a GPL compatible license that is similar in terms to the MIT license or the 2-clause BSD license). Third-party software distributed alongside this software is licensed under compatible licenses. Please refer to `abc` and `libs` subdirectories for their license terms. Web Site and Other Resources ============================ More information and documentation can be found on the Yosys web site: - https://yosyshq.net/yosys/ Documentation from this repository is automatically built and available on Read the Docs: - https://yosyshq.readthedocs.io/projects/yosys Users interested in formal verification might want to use the formal verification front-end for Yosys, SBY: - https://yosyshq.readthedocs.io/projects/sby/ - https://github.com/YosysHQ/sby 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 ==================== For more details, and instructions for other platforms, check [building from source](https://yosyshq.readthedocs.io/projects/yosys/en/latest/getting_started/installation.html#building-from-source) on Read the Docs. When cloning Yosys, some required libraries are included as git submodules. Make sure to call e.g. $ git clone --recurse-submodules https://github.com/YosysHQ/yosys.git or $ git clone https://github.com/YosysHQ/yosys.git $ cd yosys $ git submodule update --init --recursive You need a C++ compiler with C++17 support (up-to-date CLANG or GCC is recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make. TCL, readline and libffi are optional (see ``ENABLE_*`` settings in Makefile). Xdot (graphviz) is used by the ``show`` command in yosys to display schematics. For example on Ubuntu Linux 16.04 LTS the following commands will install all prerequisites for building yosys: $ sudo apt-get install build-essential clang lld 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 The environment variable `CXX` can be used to control the C++ compiler used, or run one of the following to override it: $ make config-clang $ make config-gcc The Makefile has many variables influencing the build process. These can be adjusted by modifying the Makefile.conf file which is created at the `make config-...` step (see above), or they can be set by passing an option to the make command directly: $ make CXX=$CXX For other compilers and build configurations it might be necessary to make some changes to the config section of the Makefile. It's also an alternative way to set the make variables mentioned above. $ vi Makefile # ..or.. $ vi Makefile.conf To build Yosys simply type 'make' in this directory. $ make $ sudo make install Tests are located in the tests subdirectory and can be executed using the test target. Note that you need gawk as well as a recent version of iverilog (i.e. build from git). Then, execute tests via: $ make test To use a separate (out-of-tree) build directory, provide a path to the Makefile. $ mkdir build; cd build $ make -f ../Makefile Out-of-tree builds require a clean source tree. Getting Started =============== Yosys can be used with the interactive command shell, with synthesis scripts or with command line arguments. Let's perform a simple synthesis job using the interactive command shell: $ ./yosys yosys> the command ``help`` can be used to print a list of all available commands and ``help `` to print details on the specified command: yosys> help help 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 the RTLIL format used by Yosys internally: yosys> write_rtlil convert processes (``always`` blocks) to netlist elements and perform some simple optimizations: yosys> proc; opt display design netlist using ``xdot``: yosys> show the same thing using ``gv`` as postscript viewer: yosys> show -format ps -viewer gv translating netlist to gate logic and perform some simple optimizations: yosys> techmap; opt write design netlist to a new Verilog file: yosys> write_verilog synth.v or using a simple synthesis script: $ cat synth.ys read -sv tests/simple/fiedler-cooley.v hierarchy -top up3down5 proc; opt; techmap; opt write_verilog synth.v $ ./yosys synth.ys If ABC is enabled in the Yosys build configuration and a cell library is given in the liberty file ``mycells.lib``, the following synthesis script will synthesize for the given cell library: # read design read -sv tests/simple/fiedler-cooley.v hierarchy -top up3down5 # the high-level stuff proc; fsm; opt; memory; opt # mapping to internal cell library techmap; opt # mapping flip-flops to mycells.lib dfflibmap -liberty mycells.lib # mapping logic to mycells.lib abc -liberty mycells.lib # cleanup clean If you do not have a liberty file but want to test this synthesis script, you can use the file ``examples/cmos/cmos_cells.lib`` from the yosys sources as simple example. Liberty file downloads for and information about free and open ASIC standard cell libraries can be found here: - http://www.vlsitechnology.org/html/libraries.html - http://www.vlsitechnology.org/synopsys/vsclib013.lib The command ``synth`` provides a good default synthesis script (see ``help synth``): read -sv tests/simple/fiedler-cooley.v synth -top up3down5 # mapping to target cells dfflibmap -liberty mycells.lib abc -liberty mycells.lib clean The command ``prep`` provides a good default word-level synthesis script, as used in SMT-based formal verification. Additional information ====================== The ``read_verilog`` command, used by default when calling ``read`` with Verilog source input, does not perform syntax checking. You should instead lint your source with another tool such as [Verilator](https://www.veripool.org/verilator/) first, e.g. by calling ``verilator --lint-only``. Building the documentation ========================== Note that there is no need to build the manual if you just want to read it. Simply visit https://yosys.readthedocs.io/en/latest/ instead. In addition to those packages listed above for building Yosys from source, the following are used for building the website: $ sudo apt install pdf2svg faketime Or for MacOS, using homebrew: $ brew install pdf2svg libfaketime PDFLaTeX, included with most LaTeX distributions, is also needed during the build process for the website. Or, run the following: $ sudo apt install texlive-latex-base texlive-latex-extra latexmk Or for MacOS, using homebrew: $ brew install basictex $ sudo tlmgr update --self $ sudo tlmgr install collection-latexextra latexmk tex-gyre The Python package, Sphinx, is needed along with those listed in `docs/source/requirements.txt`: $ pip install -U sphinx -r docs/source/requirements.txt 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`. yosys-0.52/abc/000077500000000000000000000000001477540374200133705ustar00rootroot00000000000000yosys-0.52/backends/000077500000000000000000000000001477540374200144155ustar00rootroot00000000000000yosys-0.52/backends/aiger/000077500000000000000000000000001477540374200155045ustar00rootroot00000000000000yosys-0.52/backends/aiger/Makefile.inc000066400000000000000000000001011477540374200177040ustar00rootroot00000000000000 OBJS += backends/aiger/aiger.o OBJS += backends/aiger/xaiger.o yosys-0.52/backends/aiger/aiger.cc000066400000000000000000000667771477540374200171310ustar00rootroot00000000000000/* * 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 "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 void aiger_encode(std::ostream &f, int x) { log_assert(x >= 0); while (x & ~0x7f) { f.put((x & 0x7f) | 0x80); x = x >> 7; } f.put(x); } struct AigerWriter { Module *module; bool zinit_mode; SigMap sigmap; dict init_map; pool input_bits, output_bits; dict not_map, ff_map, alias_map; dict> and_map; vector> asserts, assumes; vector> liveness, fairness; pool initstate_bits; 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; dict aig_map; dict ordered_outputs; dict ordered_latches; 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++; aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); return 2*aig_m; } int bit2aig(SigBit bit) { auto it = aig_map.find(bit); if (it != aig_map.end()) { log_assert(it->second >= 0); 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 int a = -1; if (not_map.count(bit)) { a = bit2aig(not_map.at(bit)) ^ 1; } else if (and_map.count(bit)) { auto args = and_map.at(bit); int a0 = bit2aig(args.first); int a1 = bit2aig(args.second); a = mkgate(a0, a1); } else if (alias_map.count(bit)) { a = bit2aig(alias_map.at(bit)); } else if (initstate_bits.count(bit)) { 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"); log_assert(a >= 0); aig_map[bit] = a; return a; } AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module) { pool undriven_bits; pool unused_bits; // promote public wires for (auto wire : module->wires()) if (wire->name.isPublic()) sigmap.add(wire); // promote output wires for (auto wire : module->wires()) if (wire->port_output) sigmap.add(wire); // promote input wires for (auto wire : module->wires()) if (wire->port_input) sigmap.add(wire); for (auto wire : module->wires()) { if (wire->attributes.count(ID::init)) { SigSpec initsig = sigmap(wire); Const initval = wire->attributes.at(ID::init); for (int i = 0; i < GetSize(wire) && i < GetSize(initval); i++) if (initval[i] == State::S0 || initval[i] == State::S1) init_map[initsig[i]] = initval[i] == State::S1; } for (int i = 0; i < GetSize(wire); i++) { SigBit wirebit(wire, i); SigBit bit = sigmap(wirebit); if (bit.wire == nullptr) { if (wire->port_output) { aig_map[wirebit] = (bit == State::S1) ? 1 : 0; output_bits.insert(wirebit); } continue; } undriven_bits.insert(bit); unused_bits.insert(bit); if (wire->port_input) input_bits.insert(bit); if (wire->port_output) { if (bit != wirebit) alias_map[wirebit] = bit; 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) undriven_bits.erase(bit); for (auto bit : output_bits) unused_bits.erase(bit); for (auto cell : module->cells()) { if (cell->type == ID($_NOT_)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); unused_bits.erase(A); undriven_bits.erase(Y); not_map[Y] = A; continue; } if (cell->type.in(ID($_FF_), ID($_DFF_N_), ID($_DFF_P_))) { SigBit D = sigmap(cell->getPort(ID::D).as_bit()); SigBit Q = sigmap(cell->getPort(ID::Q).as_bit()); 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; } if (cell->type == ID($_AND_)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit B = sigmap(cell->getPort(ID::B).as_bit()); SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); unused_bits.erase(A); unused_bits.erase(B); undriven_bits.erase(Y); and_map[Y] = make_pair(A, B); continue; } if (cell->type == ID($initstate)) { SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); undriven_bits.erase(Y); initstate_bits.insert(Y); continue; } if (cell->type == ID($assert)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit EN = sigmap(cell->getPort(ID::EN).as_bit()); unused_bits.erase(A); unused_bits.erase(EN); asserts.push_back(make_pair(A, EN)); ywmap_asserts.push_back(cell); continue; } if (cell->type == ID($assume)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit EN = sigmap(cell->getPort(ID::EN).as_bit()); unused_bits.erase(A); unused_bits.erase(EN); assumes.push_back(make_pair(A, EN)); ywmap_assumes.push_back(cell); continue; } if (cell->type == ID($live)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit EN = sigmap(cell->getPort(ID::EN).as_bit()); unused_bits.erase(A); unused_bits.erase(EN); liveness.push_back(make_pair(A, EN)); continue; } if (cell->type == ID($fair)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit EN = sigmap(cell->getPort(ID::EN).as_bit()); unused_bits.erase(A); unused_bits.erase(EN); fairness.push_back(make_pair(A, EN)); continue; } if (cell->type == ID($anyconst)) { for (auto bit : sigmap(cell->getPort(ID::Y))) { undriven_bits.erase(bit); ff_map[bit] = bit; } continue; } if (cell->type == ID($anyseq)) { for (auto bit : sigmap(cell->getPort(ID::Y))) { undriven_bits.erase(bit); input_bits.insert(bit); } continue; } if (cell->type == ID($scopeinfo)) continue; log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); } for (auto bit : unused_bits) undriven_bits.erase(bit); if (!undriven_bits.empty()) { undriven_bits.sort(); for (auto bit : undriven_bits) { log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); input_bits.insert(bit); } log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module)); } init_map.sort(); input_bits.sort(); output_bits.sort(); not_map.sort(); ff_map.sort(); and_map.sort(); aig_map[State::S0] = 0; aig_map[State::S1] = 1; for (auto bit : input_bits) { aig_m++, aig_i++; aig_map[bit] = 2*aig_m; } if (imode && input_bits.empty()) { aig_m++, aig_i++; } if (zinit_mode) { for (auto it : ff_map) { if (init_map.count(it.first)) continue; aig_m++, aig_i++; init_inputs[it.first] = 2*aig_m; } } int fair_live_inputs_cnt = GetSize(liveness); int fair_live_inputs_m = aig_m; aig_m += fair_live_inputs_cnt; aig_i += fair_live_inputs_cnt; for (auto it : ff_map) { aig_m++, aig_l++; aig_map[it.first] = 2*aig_m; ordered_latches[it.first] = aig_l-1; if (init_map.count(it.first) == 0) aig_latchinit.push_back(2); else aig_latchinit.push_back(init_map.at(it.first) ? 1 : 0); } if (!initstate_bits.empty() || !init_inputs.empty()) { aig_m++, aig_l++; initstate_ff = 2*aig_m+1; aig_latchinit.push_back(0); } int fair_live_latches_cnt = GetSize(fairness) + 2*GetSize(liveness); int fair_live_latches_m = aig_m; int fair_live_latches_l = aig_l; aig_m += fair_live_latches_cnt; aig_l += fair_live_latches_cnt; for (int i = 0; i < fair_live_latches_cnt; i++) aig_latchinit.push_back(0); if (zinit_mode) { for (auto it : ff_map) { int l = ordered_latches[it.first]; if (aig_latchinit.at(l) == 1) aig_map[it.first] ^= 1; if (aig_latchinit.at(l) == 2) { int gated_ffout = mkgate(aig_map[it.first], initstate_ff^1); int gated_initin = mkgate(init_inputs[it.first], initstate_ff); aig_map[it.first] = mkgate(gated_ffout^1, gated_initin^1)^1; } } } for (auto it : ff_map) { int a = bit2aig(it.second); int l = ordered_latches[it.first]; if (zinit_mode && aig_latchinit.at(l) == 1) aig_latchin.push_back(a ^ 1); else aig_latchin.push_back(a); } if (lmode && aig_l == 0) { aig_m++, aig_l++; aig_latchinit.push_back(0); aig_latchin.push_back(0); } if (!initstate_bits.empty() || !init_inputs.empty()) aig_latchin.push_back(1); for (auto bit : output_bits) { aig_o++; ordered_outputs[bit] = aig_o-1; aig_outputs.push_back(bit2aig(bit)); } if (omode && output_bits.empty()) { aig_o++; aig_outputs.push_back(0); } for (auto it : asserts) { aig_b++; int bit_a = bit2aig(it.first); int bit_en = bit2aig(it.second); aig_outputs.push_back(mkgate(bit_a^1, bit_en)); } if (bmode && asserts.empty()) { aig_b++; aig_outputs.push_back(0); } for (auto it : assumes) { aig_c++; int bit_a = bit2aig(it.first); int bit_en = bit2aig(it.second); aig_outputs.push_back(mkgate(bit_a^1, bit_en)^1); } for (auto it : liveness) { int input_m = ++fair_live_inputs_m; int latch_m1 = ++fair_live_latches_m; int latch_m2 = ++fair_live_latches_m; log_assert(GetSize(aig_latchin) == fair_live_latches_l); fair_live_latches_l += 2; int bit_a = bit2aig(it.first); int bit_en = bit2aig(it.second); int bit_s = 2*input_m; int bit_q1 = 2*latch_m1; int bit_q2 = 2*latch_m2; int bit_d1 = mkgate(mkgate(bit_s, bit_en)^1, bit_q1^1)^1; int bit_d2 = mkgate(mkgate(bit_d1, bit_a)^1, bit_q2^1)^1; aig_j++; aig_latchin.push_back(bit_d1); aig_latchin.push_back(bit_d2); aig_outputs.push_back(mkgate(bit_q1, bit_q2^1)); } for (auto it : fairness) { int latch_m = ++fair_live_latches_m; log_assert(GetSize(aig_latchin) == fair_live_latches_l); fair_live_latches_l += 1; int bit_a = bit2aig(it.first); int bit_en = bit2aig(it.second); int bit_q = 2*latch_m; aig_f++; aig_latchin.push_back(mkgate(mkgate(bit_q^1, bit_en^1)^1, bit_a^1)); aig_outputs.push_back(bit_q^1); } } void write_aiger(std::ostream &f, bool ascii_mode, bool miter_mode, bool symbols_mode) { int aig_obc = aig_o + aig_b + aig_c; int aig_obcj = aig_obc + aig_j; int aig_obcjf = aig_obcj + aig_f; log_assert(aig_m == aig_i + aig_l + aig_a); log_assert(aig_l == GetSize(aig_latchin)); log_assert(aig_l == GetSize(aig_latchinit)); log_assert(aig_obcjf == GetSize(aig_outputs)); if (miter_mode) { if (aig_b || aig_c || aig_j || aig_f) log_error("Running AIGER back-end in -miter mode, but design contains $assert, $assume, $live and/or $fair cells!\n"); f << stringf("%s %d %d %d 0 %d %d\n", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_a, aig_o); } else { f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); if (aig_b || aig_c || aig_j || aig_f) f << stringf(" %d %d %d %d", aig_b, aig_c, aig_j, aig_f); f << stringf("\n"); } if (ascii_mode) { for (int i = 0; i < aig_i; i++) f << stringf("%d\n", 2*i+2); for (int i = 0; i < aig_l; i++) { if (zinit_mode || aig_latchinit.at(i) == 0) f << stringf("%d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i)); else if (aig_latchinit.at(i) == 1) f << stringf("%d %d 1\n", 2*(aig_i+i)+2, aig_latchin.at(i)); else if (aig_latchinit.at(i) == 2) f << stringf("%d %d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i), 2*(aig_i+i)+2); } for (int i = 0; i < aig_obc; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("1\n"); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obcj; i < aig_obcjf; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = 0; i < aig_a; i++) f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); } else { for (int i = 0; i < aig_l; i++) { if (zinit_mode || aig_latchinit.at(i) == 0) f << stringf("%d\n", aig_latchin.at(i)); else if (aig_latchinit.at(i) == 1) f << stringf("%d 1\n", aig_latchin.at(i)); else if (aig_latchinit.at(i) == 2) f << stringf("%d %d\n", aig_latchin.at(i), 2*(aig_i+i)+2); } for (int i = 0; i < aig_obc; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("1\n"); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obcj; i < aig_obcjf; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = 0; i < aig_a; i++) { int lhs = 2*(aig_i+aig_l+i)+2; int rhs0 = aig_gates.at(i).first; int rhs1 = aig_gates.at(i).second; int delta0 = lhs - rhs0; int delta1 = rhs0 - rhs1; aiger_encode(f, delta0); aiger_encode(f, delta1); } } if (symbols_mode) { dict> symbols; for (auto wire : module->wires()) { if (wire->name[0] == '$') continue; SigSpec sig = sigmap(wire); for (int i = 0; i < GetSize(wire); i++) { if (sig[i].wire == nullptr) { if (wire->port_output) sig[i] = SigBit(wire, i); else continue; } if (wire->port_input) { int a = aig_map.at(sig[i]); log_assert((a & 1) == 0); if (GetSize(wire) != 1) symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s[%d]", log_id(wire), i)); else symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s", log_id(wire))); } if (wire->port_output) { int o = ordered_outputs.at(SigSpec(wire, i)); if (GetSize(wire) != 1) symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s[%d]", log_id(wire), i)); else symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s", log_id(wire))); } if (init_inputs.count(sig[i])) { int a = init_inputs.at(sig[i]); log_assert((a & 1) == 0); if (GetSize(wire) != 1) symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s[%d]", log_id(wire), i)); else symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s", log_id(wire))); } if (ordered_latches.count(sig[i])) { int l = ordered_latches.at(sig[i]); const char *p = (zinit_mode && (aig_latchinit.at(l) == 1)) ? "!" : ""; if (GetSize(wire) != 1) symbols[stringf("l%d", l)].push_back(stringf("%s%s[%d]", p, log_id(wire), i)); else symbols[stringf("l%d", l)].push_back(stringf("%s%s", p, log_id(wire))); } } } symbols.sort(); for (auto &sym : symbols) { f << sym.first; std::sort(sym.second.begin(), sym.second.end()); for (auto &s : sym.second) f << " " << s; f << std::endl; } } f << stringf("c\nGenerated by %s\n", yosys_version_str); } void write_map(std::ostream &f, bool verbose_map, bool no_startoffset) { dict input_lines; dict init_lines; dict output_lines; dict latch_lines; dict wire_lines; for (auto wire : module->wires()) { if (!verbose_map && wire->name[0] == '$') continue; SigSpec sig = sigmap(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]); int index = no_startoffset ? i : (wire->start_offset+i); if (verbose_map) 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, 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, 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, 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, index, log_id(wire)); else latch_lines[l] += stringf("latch %d %d %s\n", l, index, log_id(wire)); } } } input_lines.sort(); for (auto &it : input_lines) f << it.second; init_lines.sort(); for (auto &it : init_lines) f << it.second; output_lines.sort(); for (auto &it : output_lines) f << it.second; latch_lines.sort(); 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 { AigerBackend() : Backend("aiger", "write design to AIGER file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_aiger [options] [filename]\n"); log("\n"); log("Write the current design to an AIGER file. The design must be flattened and\n"); log("must not contain any cell types except $_AND_, $_NOT_, simple FF types,\n"); log("$assert and $assume cells, and $initstate cells.\n"); log("\n"); log("$assert and $assume cells are converted to AIGER bad state properties and\n"); log("invariant constraints.\n"); log("\n"); log(" -ascii\n"); log(" write ASCII version of AIGER format\n"); log("\n"); log(" -zinit\n"); log(" convert FFs to zero-initialized FFs, adding additional inputs for\n"); log(" uninitialized FFs.\n"); log("\n"); log(" -miter\n"); log(" design outputs are AIGER bad state properties\n"); log("\n"); log(" -symbols\n"); log(" include a symbol table in the generated AIGER file\n"); log("\n"); log(" -map \n"); log(" write an extra file with port and latch symbols\n"); log("\n"); 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"); log(" AIGER file happy.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool ascii_mode = false; bool zinit_mode = false; bool miter_mode = false; bool symbols_mode = false; bool verbose_map = false; bool imode = false; 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"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-ascii") { ascii_mode = true; continue; } if (args[argidx] == "-zinit") { zinit_mode = true; continue; } if (args[argidx] == "-miter") { miter_mode = true; continue; } if (args[argidx] == "-symbols") { symbols_mode = true; continue; } if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { map_filename = args[++argidx]; continue; } if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) { map_filename = args[++argidx]; 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; } if (args[argidx] == "-O") { omode = true; continue; } if (args[argidx] == "-B") { bmode = true; continue; } if (args[argidx] == "-L") { lmode = true; continue; } break; } 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) log_error("Can't find top module in current design!\n"); if (!design->selected_whole_module(top_module)) log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module)); if (!top_module->processes.empty()) log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", log_id(top_module)); if (!top_module->memories.empty()) log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", log_id(top_module)); AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode); writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); if (!map_filename.empty()) { rewrite_filename(filename); std::ofstream mapf; 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, 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; PRIVATE_NAMESPACE_END yosys-0.52/backends/aiger/xaiger.cc000066400000000000000000000571371477540374200173070ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * 2019 Eddie Hung * * 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/yosys.h" #include "kernel/sigtools.h" #include "kernel/utils.h" #include "kernel/timinginfo.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN void aiger_encode(std::ostream &f, int x) { log_assert(x >= 0); while (x & ~0x7f) { f.put((x & 0x7f) | 0x80); x = x >> 7; } f.put(x); } struct XAigerWriter { Design *design; Module *module; SigMap sigmap; dict init_map; pool input_bits, output_bits; dict not_map, alias_map; dict> and_map; vector ci_bits, co_bits; vector ff_list; dict arrival_times; vector> aig_gates; vector bit2aig_stack; int next_loop_check = 1024; vector aig_outputs; int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; dict aig_map; dict ordered_outputs; vector box_list; int mkgate(int a0, int a1) { aig_m++, aig_a++; aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); return 2*aig_m; } int bit2aig(SigBit bit) { auto it = aig_map.find(bit); if (it != aig_map.end()) { log_assert(it->second >= 0); return it->second; } if (GetSize(bit2aig_stack)== next_loop_check) { for (int i = 0; i < next_loop_check; ++i) { SigBit report_bit = bit2aig_stack[i]; if (report_bit != bit) continue; for (int 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 combinatorial 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 int a = -1; if (not_map.count(bit)) { a = bit2aig(not_map.at(bit)) ^ 1; } else if (and_map.count(bit)) { auto args = and_map.at(bit); int a0 = bit2aig(args.first); int a1 = bit2aig(args.second); a = mkgate(a0, a1); } else if (alias_map.count(bit)) { a = bit2aig(alias_map.at(bit)); } bit2aig_stack.pop_back(); if (bit == State::Sx || bit == State::Sz) { log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n"); a = aig_map.at(State::S0); } log_assert(a >= 0); aig_map[bit] = a; return a; } XAigerWriter(Module *module, bool dff_mode) : design(module->design), module(module), sigmap(module) { pool undriven_bits; pool unused_bits; // promote public wires for (auto wire : module->wires()) if (wire->name.isPublic()) sigmap.add(wire); // promote input wires for (auto wire : module->wires()) if (wire->port_input) sigmap.add(wire); // promote keep wires for (auto wire : module->wires()) if (wire->get_bool_attribute(ID::keep)) sigmap.add(wire); for (auto wire : module->wires()) { auto it = wire->attributes.find(ID::init); for (int i = 0; i < GetSize(wire); i++) { SigBit wirebit(wire, i); SigBit bit = sigmap(wirebit); if (bit.wire == nullptr) { if (wire->port_output) { aig_map[wirebit] = (bit == State::S1) ? 1 : 0; output_bits.insert(wirebit); } continue; } undriven_bits.insert(bit); unused_bits.insert(bit); if (wire->port_input) input_bits.insert(bit); bool keep = wire->get_bool_attribute(ID::keep); if (wire->port_output || keep) { if (bit != wirebit) alias_map[wirebit] = bit; output_bits.insert(wirebit); } if (it != wire->attributes.end()) { auto s = it->second[i]; if (s != State::Sx) { auto r = init_map.insert(std::make_pair(bit, it->second[i])); if (!r.second && r.first->second != it->second[i]) log_error("Bit '%s' has a conflicting (* init *) value.\n", log_signal(bit)); } } } } TimingInfo timing; for (auto cell : module->cells()) { if (!cell->has_keep_attr()) { if (cell->type == ID($_NOT_)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); unused_bits.erase(A); undriven_bits.erase(Y); not_map[Y] = A; continue; } if (cell->type == ID($_AND_)) { SigBit A = sigmap(cell->getPort(ID::A).as_bit()); SigBit B = sigmap(cell->getPort(ID::B).as_bit()); SigBit Y = sigmap(cell->getPort(ID::Y).as_bit()); unused_bits.erase(A); unused_bits.erase(B); undriven_bits.erase(Y); and_map[Y] = make_pair(A, B); continue; } if (dff_mode && cell->type.in(ID($_DFF_N_), ID($_DFF_P_)) && !cell->get_bool_attribute(ID::abc9_keep)) { SigBit D = sigmap(cell->getPort(ID::D).as_bit()); SigBit Q = sigmap(cell->getPort(ID::Q).as_bit()); unused_bits.erase(D); undriven_bits.erase(Q); alias_map[Q] = D; ff_list.emplace_back(cell); continue; } if (cell->type.in(ID($specify2), ID($specify3), ID($specrule))) continue; } RTLIL::Module* inst_module = design->module(cell->type); if (inst_module && inst_module->get_blackbox_attribute()) { bool abc9_flop = false; auto it = cell->attributes.find(ID::abc9_box_seq); if (it != cell->attributes.end()) { log_assert(!cell->has_keep_attr()); log_assert(cell->parameters.empty()); int abc9_box_seq = it->second.as_int(); if (GetSize(box_list) <= abc9_box_seq) box_list.resize(abc9_box_seq+1); box_list[abc9_box_seq] = cell; // Only flop boxes may have arrival times // (all others are combinatorial) log_assert(cell->parameters.empty()); abc9_flop = inst_module->get_bool_attribute(ID::abc9_flop); if (!abc9_flop) continue; } if (!timing.count(inst_module->name)) timing.setup_module(inst_module); for (auto &i : timing.at(inst_module->name).arrival) { if (!cell->hasPort(i.first.name)) 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 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) continue; } bool cell_known = inst_module || cell->known(); for (const auto &c : cell->connections()) { if (c.second.is_fully_const()) continue; auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr; auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first); auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first); if (!is_input && !is_output) log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); if (is_input) for (auto b : c.second) { Wire *w = b.wire; if (!w) continue; // Do not add as PO if bit is already a PI if (input_bits.count(b)) continue; if (!w->port_output || !cell_known) { SigBit I = sigmap(b); if (I != b) alias_map[b] = I; output_bits.insert(b); } } } //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); } dict> box_ports; for (auto cell : box_list) { log_assert(cell); RTLIL::Module* box_module = design->module(cell->type); log_assert(box_module); log_assert(box_module->has_attribute(ID::abc9_box_id)); auto r = box_ports.insert(cell->type); if (r.second) { // Make carry in the last PI, and carry out the last PO // since ABC requires it this way IdString carry_in, carry_out; for (const auto &port_name : box_module->ports) { auto w = box_module->wire(port_name); log_assert(w); if (w->get_bool_attribute(ID::abc9_carry)) { if (w->port_input) { if (carry_in != IdString()) log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module)); carry_in = port_name; } if (w->port_output) { if (carry_out != IdString()) log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module)); carry_out = port_name; } } else r.first->second.push_back(port_name); } if (carry_in != IdString() && carry_out == IdString()) log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module)); if (carry_in == IdString() && carry_out != IdString()) log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module)); if (carry_in != IdString()) { r.first->second.push_back(carry_in); r.first->second.push_back(carry_out); } } for (auto port_name : r.first->second) { auto w = box_module->wire(port_name); log_assert(w); auto rhs = cell->connections_.at(port_name, SigSpec()); rhs.append(Const(State::Sx, GetSize(w)-GetSize(rhs))); if (w->port_input) for (auto b : rhs) { SigBit I = sigmap(b); if (b == RTLIL::Sx) b = State::S0; else if (I != b) { if (I == RTLIL::Sx) alias_map[b] = State::S0; else alias_map[b] = I; } co_bits.emplace_back(b); unused_bits.erase(I); } if (w->port_output) for (const auto &b : rhs) { SigBit O = sigmap(b); if (O != b) alias_map[O] = b; ci_bits.emplace_back(b); undriven_bits.erase(O); } } } for (auto bit : input_bits) undriven_bits.erase(bit); for (auto bit : output_bits) unused_bits.erase(sigmap(bit)); for (auto bit : unused_bits) undriven_bits.erase(bit); // Make all undriven bits a primary input for (auto bit : undriven_bits) { input_bits.insert(bit); undriven_bits.erase(bit); } struct sort_by_port_id { bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const { return a.wire->port_id < b.wire->port_id || (a.wire->port_id == b.wire->port_id && a.offset < b.offset); } }; input_bits.sort(sort_by_port_id()); output_bits.sort(sort_by_port_id()); aig_map[State::S0] = 0; aig_map[State::S1] = 1; for (const auto &bit : input_bits) { aig_m++, aig_i++; log_assert(!aig_map.count(bit)); aig_map[bit] = 2*aig_m; } for (auto cell : ff_list) { const SigBit &q = sigmap(cell->getPort(ID::Q)); aig_m++, aig_i++; log_assert(!aig_map.count(q)); aig_map[q] = 2*aig_m; } for (auto &bit : ci_bits) { aig_m++, aig_i++; // 1'bx may exist here due to a box output // that has been padded to its full width if (bit == State::Sx) continue; 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; } for (auto bit : co_bits) { ordered_outputs[bit] = aig_o++; aig_outputs.push_back(bit2aig(bit)); } for (const auto &bit : output_bits) { ordered_outputs[bit] = aig_o++; int aig; // Unlike bit2aig() which checks aig_map first for // inout/scc bits, since aig_map will point to // the PI, first attempt to find the NOT/AND driver // before resorting to an aig_map lookup (which // could be another PO) if (input_bits.count(bit)) { if (not_map.count(bit)) { aig = bit2aig(not_map.at(bit)) ^ 1; } else if (and_map.count(bit)) { auto args = and_map.at(bit); int a0 = bit2aig(args.first); int a1 = bit2aig(args.second); aig = mkgate(a0, a1); } else aig = aig_map.at(bit); } else aig = bit2aig(bit); aig_outputs.push_back(aig); } for (auto cell : ff_list) { const SigBit &d = sigmap(cell->getPort(ID::D)); aig_o++; aig_outputs.push_back(aig_map.at(d)); } } void write_aiger(std::ostream &f, bool ascii_mode) { int aig_obc = aig_o; int aig_obcj = aig_obc; int aig_obcjf = aig_obcj; log_assert(aig_m == aig_i + aig_l + aig_a); log_assert(aig_obcjf == GetSize(aig_outputs)); f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); f << stringf("\n"); if (ascii_mode) { for (int i = 0; i < aig_i; i++) f << stringf("%d\n", 2*i+2); for (int i = 0; i < aig_obc; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("1\n"); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obcj; i < aig_obcjf; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = 0; i < aig_a; i++) f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); } else { for (int i = 0; i < aig_obc; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("1\n"); for (int i = aig_obc; i < aig_obcj; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = aig_obcj; i < aig_obcjf; i++) f << stringf("%d\n", aig_outputs.at(i)); for (int i = 0; i < aig_a; i++) { int lhs = 2*(aig_i+aig_l+i)+2; int rhs0 = aig_gates.at(i).first; int rhs1 = aig_gates.at(i).second; int delta0 = lhs - rhs0; int delta1 = rhs0 - rhs1; aiger_encode(f, delta0); aiger_encode(f, delta1); } } f << "c"; auto write_buffer = [](std::ostream &buffer, unsigned int u32) { typedef unsigned char uchar; unsigned char u32_be[4] = { (uchar) (u32 >> 24), (uchar) (u32 >> 16), (uchar) (u32 >> 8), (uchar) u32 }; buffer.write((char *) u32_be, sizeof(u32_be)); }; std::stringstream h_buffer; auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); write_h_buffer(1); log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_list) + GetSize(ci_bits)); write_h_buffer(GetSize(input_bits) + GetSize(ff_list) + GetSize(ci_bits)); log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_list) + GetSize(co_bits)); write_h_buffer(GetSize(output_bits) + GetSize(ff_list) + GetSize(co_bits)); log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_list)); write_h_buffer(GetSize(input_bits) + GetSize(ff_list)); log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_list)); write_h_buffer(GetSize(output_bits) + GetSize(ff_list)); log_debug("boxNum = %d\n", GetSize(box_list)); write_h_buffer(GetSize(box_list)); auto write_buffer_float = [](std::stringstream &buffer, float f32) { buffer.write(reinterpret_cast(&f32), sizeof(f32)); }; std::stringstream i_buffer; auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1); for (auto bit : input_bits) write_i_buffer(arrival_times.at(bit, 0)); //std::stringstream o_buffer; //auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1); //for (auto bit : output_bits) // write_o_buffer(0); if (!box_list.empty() || !ff_list.empty()) { dict> cell_cache; int box_count = 0; for (auto cell : box_list) { log_assert(cell); log_assert(cell->parameters.empty()); auto r = cell_cache.insert(cell->type); auto &v = r.first->second; if (r.second) { RTLIL::Module* box_module = design->module(cell->type); log_assert(box_module); int box_inputs = 0, box_outputs = 0; for (auto port_name : box_module->ports) { RTLIL::Wire *w = box_module->wire(port_name); log_assert(w); if (w->port_input) box_inputs += GetSize(w); if (w->port_output) box_outputs += GetSize(w); } std::get<0>(v) = box_inputs; std::get<1>(v) = box_outputs; std::get<2>(v) = box_module->attributes.at(ID::abc9_box_id).as_int(); } write_h_buffer(std::get<0>(v)); write_h_buffer(std::get<1>(v)); write_h_buffer(std::get<2>(v)); write_h_buffer(box_count++); } std::stringstream r_buffer; auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); log_debug("flopNum = %d\n", GetSize(ff_list)); write_r_buffer(ff_list.size()); std::stringstream s_buffer; auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1); write_s_buffer(ff_list.size()); dict clk_to_mergeability; for (const auto cell : ff_list) { const SigBit &d = sigmap(cell->getPort(ID::D)); const SigBit &q = sigmap(cell->getPort(ID::Q)); SigSpec clk_and_pol{sigmap(cell->getPort(ID::C)), cell->type[6] == 'P' ? State::S1 : State::S0}; auto r = clk_to_mergeability.insert(std::make_pair(clk_and_pol, clk_to_mergeability.size()+1)); int mergeability = r.first->second; log_assert(mergeability > 0); write_r_buffer(mergeability); State init = init_map.at(q, State::Sx); log_debug("Cell '%s' (type %s) has (* init *) value '%s'.\n", log_id(cell), log_id(cell->type), log_signal(init)); if (init == State::S1) write_s_buffer(1); else if (init == State::S0) write_s_buffer(0); else { log_assert(init == State::Sx); write_s_buffer(2); } // Use arrival time from output of flop box write_i_buffer(arrival_times.at(d, 0)); //write_o_buffer(0); } f << "r"; std::string buffer_str = r_buffer.str(); write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); f << "s"; buffer_str = s_buffer.str(); write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); RTLIL::Design *holes_design; auto it = saved_designs.find("$abc9_holes"); if (it != saved_designs.end()) holes_design = it->second; else holes_design = nullptr; RTLIL::Module *holes_module = holes_design ? holes_design->module(module->name) : nullptr; if (holes_module) { std::stringstream a_buffer; XAigerWriter writer(holes_module, false /* dff_mode */); writer.write_aiger(a_buffer, false /*ascii_mode*/); f << "a"; std::string buffer_str = a_buffer.str(); write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); } } f << "h"; std::string buffer_str = h_buffer.str(); write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); f << "i"; buffer_str = i_buffer.str(); write_buffer(f, buffer_str.size()); f.write(buffer_str.data(), buffer_str.size()); //f << "o"; //buffer_str = o_buffer.str(); //buffer_size_be = to_big_endian(buffer_str.size()); //f.write(reinterpret_cast(&buffer_size_be), sizeof(buffer_size_be)); //f.write(buffer_str.data(), buffer_str.size()); f << stringf("Generated by %s\n", yosys_version_str); design->scratchpad_set_int("write_xaiger.num_ands", and_map.size()); design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size()); design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size()); design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size()); } void write_map(std::ostream &f) { dict input_lines; dict output_lines; for (auto wire : module->wires()) { for (int i = 0; i < GetSize(wire); i++) { RTLIL::SigBit b(wire, i); if (input_bits.count(b)) { int a = aig_map.at(b); log_assert((a & 1) == 0); input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire)); } if (output_bits.count(b)) { int o = ordered_outputs.at(b); output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), wire->start_offset+i, log_id(wire)); } } } input_lines.sort(); for (auto &it : input_lines) f << it.second; log_assert(input_lines.size() == input_bits.size()); int box_count = 0; for (auto cell : box_list) f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); output_lines.sort(); for (auto &it : output_lines) f << it.second; log_assert(output_lines.size() == output_bits.size()); } }; struct XAigerBackend : public Backend { XAigerBackend() : Backend("xaiger", "write design to XAIGER file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_xaiger [options] [filename]\n"); log("\n"); log("Write the top module (according to the (* top *) attribute or if only one module\n"); log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, (optionally\n"); log("$_DFF_N_, $_DFF_P_), or non (* abc9_box *) cells will be converted into psuedo-\n"); log("inputs and pseudo-outputs. Whitebox contents will be taken from the equivalent\n"); log("module in the '$abc9_holes' design, if it exists.\n"); log("\n"); log(" -ascii\n"); log(" write ASCII version of AIGER format\n"); log("\n"); log(" -map \n"); log(" write an extra file with port and box symbols\n"); log("\n"); log(" -dff\n"); log(" write $_DFF_[NP]_ cells\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool ascii_mode = false, dff_mode = false; std::string map_filename; log_header(design, "Executing XAIGER backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-ascii") { ascii_mode = true; continue; } if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { map_filename = args[++argidx]; continue; } if (args[argidx] == "-dff") { dff_mode = true; continue; } break; } extra_args(f, filename, args, argidx, !ascii_mode); Module *top_module = design->top_module(); if (top_module == nullptr) log_error("Can't find top module in current design!\n"); if (!design->selected_whole_module(top_module)) log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module)); if (!top_module->processes.empty()) log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", log_id(top_module)); if (!top_module->memories.empty()) log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module)); XAigerWriter writer(top_module, dff_mode); writer.write_aiger(*f, ascii_mode); if (!map_filename.empty()) { std::ofstream mapf; 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); } } } XAigerBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/aiger2/000077500000000000000000000000001477540374200155665ustar00rootroot00000000000000yosys-0.52/backends/aiger2/Makefile.inc000066400000000000000000000000401477540374200177700ustar00rootroot00000000000000OBJS += backends/aiger2/aiger.o yosys-0.52/backends/aiger2/aiger.cc000066400000000000000000001136571477540374200172010ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) Martin PoviÅ¡er * * 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. * */ // TODOs: // - gracefully handling inout ports (an error message probably) // - undriven wires // - zero-width operands #include "kernel/register.h" #include "kernel/celltypes.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN #define BITWISE_OPS ID($buf), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), ID($fa), \ ID($bwmux) #define REDUCE_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool) #define LOGIC_OPS ID($logic_and), ID($logic_or), ID($logic_not) #define GATE_OPS ID($_BUF_), ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), \ ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_), \ ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_) #define CMP_OPS ID($eq), ID($ne), ID($lt), ID($le), ID($ge), ID($gt) // TODO //#define ARITH_OPS ID($add), ID($sub), ID($neg) #define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS, \ ID($pmux), ID($bmux) /*, ARITH_OPS*/ template struct Index { struct HierCursor; struct ModuleInfo { Module *module; int len; dict windices; dict suboffsets; pool found_blackboxes; bool indexing = false; bool indexed = false; }; dict modules; int index_wires(ModuleInfo &info, RTLIL::Module *m) { int sum = 0; for (auto w : m->wires()) { info.windices[w] = sum; sum += w->width; } return sum; } bool flatten = false; bool inline_whiteboxes = false; bool allow_blackboxes = false; int index_module(RTLIL::Module *m) { ModuleInfo &info = modules[m]; if (info.indexed) return info.len; if (info.indexing && !info.indexed) log_error("Hierarchy error\n"); info.module = m; int pos = index_wires(info, m); for (auto cell : m->cells()) { if (cell->type.in(KNOWN_OPS) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3))) continue; Module *submodule = m->design->module(cell->type); if (submodule && flatten && !submodule->get_bool_attribute(ID::keep_hierarchy) && !submodule->get_blackbox_attribute(inline_whiteboxes)) { info.suboffsets[cell] = pos; pos += index_module(submodule); } else { if (allow_blackboxes) { info.found_blackboxes.insert(cell); } else { if (!submodule || submodule->get_blackbox_attribute()) log_error("Unsupported cell type: %s (%s in %s)\n", log_id(cell->type), log_id(cell), log_id(m)); } } } info.len = pos; return info.len; } Design *design; Module *top; ModuleInfo *top_minfo; std::vector lits; Index() { } void setup(RTLIL::Module *top) { design = top->design; this->top = top; modules.reserve(top->design->modules().size()); int nlits = index_module(top); log_debug("allocating for %d literals\n", nlits); lits.resize(nlits, Writer::EMPTY_LIT); top_minfo = &modules.at(top); } bool const_folding = true; bool strashing = false; dict, Lit> cache; Lit AND(Lit a, Lit b) { if (const_folding) { if (a == CFALSE || b == CFALSE) return CFALSE; if (a == CTRUE) return b; if (b == CTRUE) return a; } if (!strashing) { return (static_cast(this))->emit_gate(a, b); } else { if (a < b) std::swap(a, b); auto pair = std::make_pair(a, b); if (!cache.count(pair)) { Lit nl = (static_cast(this))->emit_gate(a, b); cache[pair] = nl; return nl; } else { return cache.at(pair); } } } Lit NOT(Lit lit) { return Writer::negate(lit); } Lit OR(Lit a, Lit b) { return NOT(AND(NOT(a), NOT(b))); } Lit MUX(Lit a, Lit b, Lit s) { if (const_folding) { if (a == b) return a; if (s == CFALSE) return a; if (s == CTRUE) return b; } return OR(AND(a, NOT(s)), AND(b, s)); } Lit XOR(Lit a, Lit b) { return OR(AND(a, NOT(b)), AND(NOT(a), b)); } Lit XNOR(Lit a, Lit b) { return NOT(OR(AND(a, NOT(b)), AND(NOT(a), b))); } Lit CARRY(Lit a, Lit b, Lit c) { if (const_folding) { if (c == CTRUE) { return OR(a, b); } else if (c == CFALSE) { return AND(a, b); } } return OR(AND(a, b), AND(c, OR(a, b))); } Lit REDUCE(std::vector lits, bool op_xor=false) { std::vector next; while (lits.size() > 1) { next.clear(); for (int i = 0; i < (int) lits.size(); i += 2) { if (i + 1 >= (int) lits.size()) { next.push_back(lits[i]); } else { Lit a = lits[i], b = lits[i + 1]; next.push_back(op_xor ? XOR(a, b) : AND(a, b)); } } next.swap(lits); } if (lits.empty()) return op_xor ? CFALSE : CTRUE; else return lits.front(); } Lit impl_op(HierCursor &cursor, Cell *cell, IdString oport, int obit) { if (cell->type.in(REDUCE_OPS, LOGIC_OPS, CMP_OPS) && obit != 0) { return CFALSE; } else if (cell->type.in(CMP_OPS)) { SigSpec aport = cell->getPort(ID::A); bool asigned = cell->getParam(ID::A_SIGNED).as_bool(); SigSpec bport = cell->getPort(ID::B); bool bsigned = cell->getParam(ID::B_SIGNED).as_bool(); int width = std::max(aport.size(), bport.size()) + 1; aport.extend_u0(width, asigned); bport.extend_u0(width, bsigned); if (cell->type.in(ID($eq), ID($ne))) { int carry = CTRUE; for (int i = 0; i < width; i++) { Lit a = visit(cursor, aport[i]); Lit b = visit(cursor, bport[i]); carry = AND(carry, XNOR(a, b)); } return (cell->type == ID($eq)) ? carry : /* $ne */ NOT(carry); } else if (cell->type.in(ID($lt), ID($le), ID($gt), ID($ge))) { if (cell->type.in(ID($gt), ID($ge))) std::swap(aport, bport); int carry = cell->type.in(ID($le), ID($ge)) ? CFALSE : CTRUE; Lit a = Writer::EMPTY_LIT; Lit b = Writer::EMPTY_LIT; // TODO: this might not be the most economic structure; revisit at a later date for (int i = 0; i < width; i++) { a = visit(cursor, aport[i]); b = visit(cursor, bport[i]); if (i != width - 1) carry = CARRY(a, NOT(b), carry); } return XOR(carry, XNOR(a, b)); } else { log_abort(); } } else if (cell->type.in(REDUCE_OPS, ID($logic_not))) { SigSpec inport = cell->getPort(ID::A); std::vector lits; for (int i = 0; i < inport.size(); i++) { Lit lit = visit(cursor, inport[i]); if (cell->type.in(ID($reduce_and), ID($reduce_xor), ID($reduce_xnor))) { lits.push_back(lit); } else if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($logic_not))) { lits.push_back(NOT(lit)); } else { log_abort(); } } Lit acc = REDUCE(lits, cell->type.in(ID($reduce_xor), ID($reduce_xnor))); if (!cell->type.in(ID($reduce_xnor), ID($reduce_or), ID($reduce_bool))) return acc; else return NOT(acc); } else if (cell->type.in(ID($logic_and), ID($logic_or))) { SigSpec aport = cell->getPort(ID::A); SigSpec bport = cell->getPort(ID::B); log_assert(aport.size() > 0 && bport.size() > 0); // TODO Lit a = visit(cursor, aport[0]); for (int i = 1; i < aport.size(); i++) { Lit l = visit(cursor, aport[i]); a = OR(a, l); } Lit b = visit(cursor, bport[0]); for (int i = 1; i < bport.size(); i++) { Lit l = visit(cursor, bport[i]); b = OR(b, l); } if (cell->type == ID($logic_and)) return AND(a, b); else if (cell->type == ID($logic_or)) return OR(a, b); else log_abort(); } else if (cell->type.in(BITWISE_OPS, GATE_OPS, ID($pos))) { SigSpec aport = cell->getPort(ID::A); Lit a; if (obit < aport.size()) { a = visit(cursor, aport[obit]); } else { if (cell->getParam(ID::A_SIGNED).as_bool()) a = visit(cursor, aport.msb()); else a = CFALSE; } if (cell->type.in(ID($buf), ID($pos), ID($_BUF_))) { return a; } else if (cell->type.in(ID($not), ID($_NOT_))) { return NOT(a); } else { SigSpec bport = cell->getPort(ID::B); Lit b; if (obit < bport.size()) { b = visit(cursor, bport[obit]); } else { if (cell->getParam(ID::B_SIGNED).as_bool()) b = visit(cursor, bport.msb()); else b = CFALSE; } if (cell->type.in(ID($and), ID($_AND_))) { return AND(a, b); } else if (cell->type.in(ID($_NAND_))) { return NOT(AND(a, b)); } else if (cell->type.in(ID($or), ID($_OR_))) { return OR(a, b); } else if (cell->type.in(ID($_NOR_))) { return NOT(OR(a, b)); } else if (cell->type.in(ID($xor), ID($_XOR_))) { return XOR(a, b); } else if (cell->type.in(ID($xnor), ID($_XNOR_))) { return NOT(XOR(a, b)); } else if (cell->type.in(ID($_ANDNOT_))) { return AND(a, NOT(b)); } else if (cell->type.in(ID($_ORNOT_))) { return OR(a, NOT(b)); } else if (cell->type.in(ID($mux), ID($_MUX_))) { Lit s = visit(cursor, cell->getPort(ID::S)); return MUX(a, b, s); } else if (cell->type.in(ID($bwmux))) { Lit s = visit(cursor, cell->getPort(ID::S)[obit]); return MUX(a, b, s); } else if (cell->type.in(ID($_NMUX_))) { Lit s = visit(cursor, cell->getPort(ID::S)[obit]); return NOT(MUX(a, b, s)); } else if (cell->type.in(ID($fa))) { Lit c = visit(cursor, cell->getPort(ID::C)[obit]); Lit ab = XOR(a, b); if (oport == ID::Y) { return XOR(ab, c); } else /* oport == ID::X */ { return OR(AND(a, b), AND(c, ab)); } } else if (cell->type.in(ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) { Lit c, d; c = visit(cursor, cell->getPort(ID::C)[obit]); if (/* 4 input types */ cell->type.in(ID($_AOI4_), ID($_OAI4_))) d = visit(cursor, cell->getPort(ID::D)[obit]); else d = cell->type == ID($_AOI3_) ? CTRUE : CFALSE; if (/* aoi */ cell->type.in(ID($_AOI3_), ID($_AOI4_))) return NOT(OR(AND(a, b), AND(c, d))); else return NOT(AND(OR(a, b), OR(c, d))); } else { log_abort(); } } } else if (cell->type == ID($pmux)) { SigSpec aport = cell->getPort(ID::A); SigSpec bport = cell->getPort(ID::B); SigSpec sport = cell->getPort(ID::S); int width = aport.size(); Lit a = visit(cursor, aport[obit]); std::vector bar, sels; for (int i = 0; i < sport.size(); i++) { Lit s = visit(cursor, sport[i]); Lit b = visit(cursor, bport[width * i + obit]); bar.push_back(NOT(AND(s, b))); sels.push_back(NOT(s)); } return OR(AND(REDUCE(sels), a), NOT(REDUCE(bar))); } else if (cell->type == ID($bmux)) { SigSpec aport = cell->getPort(ID::A); SigSpec sport = cell->getPort(ID::S); int width = cell->getParam(ID::WIDTH).as_int(); std::vector data; for (int i = obit; i < aport.size(); i += width) data.push_back(visit(cursor, aport[i])); std::vector next; for (int i = 0; i < sport.size(); i++) { Lit s = visit(cursor, sport[i]); next.clear(); for (int j = 0; j < (int) data.size(); j += 2) next.push_back(MUX(data[j], data[j + 1], s)); data.swap(next); } log_assert(data.size() == 1); return data[0]; } else { log_abort(); } } struct HierCursor { typedef std::pair Level; std::vector levels; int instance_offset = 0; HierCursor() { } ModuleInfo &leaf_minfo(Index &index) { if (levels.empty()) return *index.top_minfo; else return levels.back().first; } Module *leaf_module(Index &index) { return leaf_minfo(index).module; } int bitwire_index(Index &index, SigBit bit) { log_assert(bit.wire != nullptr); return instance_offset + leaf_minfo(index).windices[bit.wire] + bit.offset; } Cell *exit(Index &index) { log_assert(!levels.empty()); Cell *instance = levels.back().second; levels.pop_back(); instance_offset -= leaf_minfo(index).suboffsets.at(instance); // return the instance we just exited return instance; } Module *enter(Index &index, Cell *cell) { Design *design = index.design; auto &minfo = leaf_minfo(index); log_assert(minfo.suboffsets.count(cell)); Module *def = design->module(cell->type); log_assert(def); levels.push_back(Level(index.modules.at(def), cell)); instance_offset += minfo.suboffsets.at(cell); // return the module definition we just entered return def; } bool is_top() { return levels.empty(); } std::string path() { std::string ret; bool first = true; for (auto pair : levels) { if (!first) ret += "."; if (!pair.second) ret += RTLIL::unescape_id(pair.first.module->name); else ret += RTLIL::unescape_id(pair.second->name); first = false; } return ret; } int hash() const { int hash = 0; for (auto pair : levels) hash += (uintptr_t) pair.second; return hash; } bool operator==(const HierCursor &other) const { if (levels.size() != other.levels.size()) return false; for (int i = 0; i < levels.size(); i++) if (levels[i].second != other.levels[i].second) return false; return true; } }; bool visit_hook(int, HierCursor&, SigBit) { return false; } Lit visit(HierCursor &cursor, SigBit bit) { if (!bit.wire) { if (bit == State::S1) return CTRUE; else if (bit == State::S0) return CFALSE; else if (bit == State::Sx) return CFALSE; else log_error("Unhandled state %s\n", log_signal(bit)); } int idx = cursor.bitwire_index(*this, bit); if (lits[idx] != Writer::EMPTY_LIT) { // literal already assigned return lits[idx]; } // provide means for the derived class to override // the visit behavior if ((static_cast(this))->visit_hook(idx, cursor, bit)) { return lits[idx]; } Lit ret; if (!bit.wire->port_input) { // an output of a cell Cell *driver = bit.wire->driverCell(); if (driver->type.in(KNOWN_OPS)) { ret = impl_op(cursor, driver, bit.wire->driverPort(), bit.offset); } else { Module *def = cursor.enter(*this, driver); { IdString portname = bit.wire->driverPort(); Wire *w = def->wire(portname); if (!w) log_error("Output port %s on instance %s of %s doesn't exist\n", log_id(portname), log_id(driver), log_id(def)); if (bit.offset >= w->width) log_error("Bit position %d of output port %s on instance %s of %s is out of range (port has width %d)\n", bit.offset, log_id(portname), log_id(driver), log_id(def), w->width); ret = visit(cursor, SigBit(w, bit.offset)); } cursor.exit(*this); } } else { // a module input: we cannot be the top module, otherwise // the branch for pre-existing literals would have been taken log_assert(!cursor.is_top()); // step into the upper module Cell *instance = cursor.exit(*this); { IdString portname = bit.wire->name; if (!instance->hasPort(portname)) log_error("Input port %s on instance %s of %s unconnected\n", log_id(portname), log_id(instance), log_id(instance->type)); auto &port = instance->getPort(portname); if (bit.offset >= port.size()) log_error("Bit %d of input port %s on instance %s of %s unconnected\n", bit.offset, log_id(portname), log_id(instance), log_id(instance->type)); ret = visit(cursor, port[bit.offset]); } cursor.enter(*this, instance); } lits[idx] = ret; return ret; } Lit &pi_literal(SigBit bit, HierCursor *cursor=nullptr) { log_assert(bit.wire); if (!cursor) { log_assert(bit.wire->module == top); log_assert(bit.wire->port_input); return lits[top_minfo->windices[bit.wire] + bit.offset]; } else { log_assert(bit.wire->module == cursor->leaf_module(*this)); return lits[cursor->bitwire_index(*this, bit)]; } } Lit eval_po(SigBit bit, HierCursor *cursor=nullptr) { Lit ret; if (!cursor) { HierCursor cursor_; ret = visit(cursor_, bit); log_assert(cursor_.is_top()); log_assert(cursor_.instance_offset == 0); } else { ret = visit(*cursor, bit); } return ret; } void visit_hierarchy(std::function f, HierCursor &cursor) { f(cursor); ModuleInfo &minfo = cursor.leaf_minfo(*this); for (auto cell : minfo.module->cells()) { if (minfo.suboffsets.count(cell)) { cursor.enter(*this, cell); visit_hierarchy(f, cursor); cursor.exit(*this); } } } void visit_hierarchy(std::function f) { HierCursor cursor; visit_hierarchy(f, cursor); } }; struct AigerWriter : Index { typedef unsigned int Lit; const static constexpr Lit EMPTY_LIT = std::numeric_limits::max(); static Lit negate(Lit lit) { return lit ^ 1; } std::ostream *f; Lit lit_counter; int ninputs, nlatches, noutputs, nands; void encode(int delta) { log_assert(delta >= 0); unsigned int x = delta; while (x & ~0x7f) { f->put((x & 0x7f) | 0x80); x = x >> 7; } f->put(x); } Lit emit_gate(Lit a, Lit b) { Lit out = lit_counter; nands++; lit_counter += 2; if (a < b) std::swap(a, b); encode(out - a); encode(a - b); return out; } void reset_counters() { lit_counter = 2; ninputs = nlatches = noutputs = nands = 0; } void write_header() { log_assert(lit_counter == (Lit) (ninputs + nlatches + nands) * 2 + 2); char buf[128]; snprintf(buf, sizeof(buf) - 1, "aig %08d %08d %08d %08d %08d\n", ninputs + nlatches + nands, ninputs, nlatches, noutputs, nands); f->write(buf, strlen(buf)); } void write(std::ostream *f) { reset_counters(); auto file_start = f->tellp(); // populate inputs std::vector inputs; for (auto id : top->ports) { Wire *w = top->wire(id); log_assert(w); if (w->port_input) for (int i = 0; i < w->width; i++) { pi_literal(SigBit(w, i)) = lit_counter; inputs.push_back(SigBit(w, i)); lit_counter += 2; ninputs++; } } this->f = f; // start with the header write_header(); // insert padding where output literals will go (once known) for (auto id : top->ports) { Wire *w = top->wire(id); log_assert(w); if (w->port_output) { for (auto bit : SigSpec(w)) { (void) bit; char buf[16]; snprintf(buf, sizeof(buf) - 1, "%08d\n", 0); f->write(buf, strlen(buf)); noutputs++; } } } auto data_start = f->tellp(); // now the guts std::vector> outputs; for (auto w : top->wires()) if (w->port_output) { for (auto bit : SigSpec(w)) outputs.push_back({bit, eval_po(bit)}); } auto data_end = f->tellp(); // revisit header and the list of outputs f->seekp(file_start); write_header(); for (auto pair : outputs) { char buf[16]; snprintf(buf, sizeof(buf) - 1, "%08d\n", pair.second); f->write(buf, strlen(buf)); } // double check we arrived at the same offset for the // main data section log_assert(data_start == f->tellp()); f->seekp(data_end); int i = 0; for (auto pair : outputs) { if (SigSpec(pair.first).is_wire()) { char buf[32]; snprintf(buf, sizeof(buf) - 1, "o%d ", i); f->write(buf, strlen(buf)); std::string name = RTLIL::unescape_id(pair.first.wire->name); f->write(name.data(), name.size()); f->put('\n'); } i++; } i = 0; for (auto bit : inputs) { if (SigSpec(bit).is_wire()) { char buf[32]; snprintf(buf, sizeof(buf) - 1, "i%d ", i); f->write(buf, strlen(buf)); std::string name = RTLIL::unescape_id(bit.wire->name); f->write(name.data(), name.size()); f->put('\n'); } i++; } } }; struct XAigerAnalysis : Index { const static constexpr int EMPTY_LIT = -1; XAigerAnalysis() { allow_blackboxes = true; // Disable const folding and strashing as literal values are not unique const_folding = false; strashing = false; } static int negate(int lit) { return lit; } int emit_gate(int a, int b) { return max(a, b) + 1; } pool seen; bool visit_hook(int idx, HierCursor &cursor, SigBit bit) { log_assert(cursor.is_top()); // TOOD: fix analyzer to work with hierarchy if (bit.wire->port_input) return false; Cell *driver = bit.wire->driverCell(); Module *mod = design->module(driver->type); if (!mod || !mod->has_attribute(ID::abc9_box_id)) return false; int max = 1; for (auto wire : mod->wires()) if (wire->port_input) for (int i = 0; i < wire->width; i++) { int ilevel = visit(cursor, driver->getPort(wire->name)[i]); max = std::max(max, ilevel + 1); } lits[idx] = max; if (!seen.count(driver)) seen.insert(driver); return true; } void analyze(Module *top) { setup(top); for (auto id : top->ports) { Wire *w = top->wire(id); log_assert(w); if (w->port_input) for (int i = 0; i < w->width; i++) pi_literal(SigBit(w, i)) = 0; } HierCursor cursor; for (auto box : top_minfo->found_blackboxes) { Module *def = design->module(box->type); if (!(def && def->has_attribute(ID::abc9_box_id))) for (auto &conn : box->connections_) if (box->output(conn.first)) for (auto bit : conn.second) pi_literal(bit, &cursor) = 0; } for (auto w : top->wires()) if (w->port_output) { for (auto bit : SigSpec(w)) (void) eval_po(bit); } for (auto box : top_minfo->found_blackboxes) { Module *def = design->module(box->type); if (!(def && def->has_attribute(ID::abc9_box_id))) for (auto &conn : box->connections_) if (box->input(conn.first)) for (auto bit : conn.second) (void) eval_po(bit); } } }; struct XAigerWriter : AigerWriter { XAigerWriter() { allow_blackboxes = true; } bool mapping_prep = false; pool keep_wires; std::ofstream map_file; typedef std::pair HierBit; std::vector pos; std::vector pis; int proper_pos_counter = 0; pool driven_by_opaque_box; void ensure_pi(SigBit bit, HierCursor cursor={}, bool box_port=false) { Lit &lit = pi_literal(bit, &cursor); if (lit == EMPTY_LIT) { lit = lit_counter; pis.push_back(std::make_pair(bit, cursor)); lit_counter += 2; if (map_file.is_open() && !box_port) { log_assert(cursor.is_top()); // TODO driven_by_opaque_box.insert(bit); map_file << "pi " << pis.size() - 1 << " " << bit.offset << " " << bit.wire->name.c_str() << "\n"; } } else { log_assert(!box_port); } } bool is_pi(SigBit bit, HierCursor cursor={}) { return pi_literal(bit, &cursor) != EMPTY_LIT; } void pad_pi() { pis.push_back(std::make_pair(RTLIL::Sx, HierCursor{})); lit_counter += 2; } void append_box_ports(Cell *box, HierCursor &cursor, bool inputs) { for (auto &conn : box->connections_) { bool is_input = box->input(conn.first); bool is_output = box->output(conn.first); if (!(is_input || is_output) || (is_input && is_output)) log_error("Ambiguous port direction on %s/%s\n", log_id(box->type), log_id(conn.first)); if (is_input && inputs) { int bitp = 0; for (auto bit : conn.second) { if (!bit.wire) { bitp++; continue; } if (map_file.is_open()) { log_assert(cursor.is_top()); map_file << "pseudopo " << proper_pos_counter++ << " " << bitp << " " << box->name.c_str() << " " << conn.first.c_str() << "\n"; } pos.push_back(std::make_pair(bit, cursor)); if (mapping_prep) conn.second[bitp] = RTLIL::Sx; bitp++; } } else if (is_output && !inputs) { for (auto &bit : conn.second) { if (!bit.wire || bit.wire->port_input) log_error("Bad connection"); ensure_pi(bit, cursor); keep_wires.insert(bit.wire); } } } } RTLIL::Module *holes_module; std::stringstream h_buffer; static void write_be32(std::ostream &buffer, uint32_t u32) { typedef unsigned char uchar; unsigned char u32_be[4] = { (uchar) (u32 >> 24), (uchar) (u32 >> 16), (uchar) (u32 >> 8), (uchar) u32 }; buffer.write((char *) u32_be, sizeof(u32_be)); } void prep_boxes(int pending_pos_num) { XAigerAnalysis analysis; log_debug("preforming analysis on '%s'\n", log_id(top)); analysis.analyze(top); log_debug("analysis on '%s' done\n", log_id(top)); // boxes which have timing data, maybe a whitebox model std::vector> nonopaque_boxes; // boxes which are fully opaque std::vector> opaque_boxes; log_debug("found boxes:\n"); visit_hierarchy([&](HierCursor &cursor) { auto &minfo = cursor.leaf_minfo(*this); for (auto box : minfo.found_blackboxes) { log_debug(" - %s.%s (type %s): ", cursor.path().c_str(), RTLIL::unescape_id(box->name).c_str(), log_id(box->type)); Module *box_module = design->module(box->type), *box_derived; if (box_module && !box->parameters.empty()) { // TODO: This is potentially costly even if a cached derivation exists box_derived = design->module(box_module->derive(design, box->parameters)); log_assert(box_derived); } else { box_derived = box_module; } if (box_derived && box_derived->has_attribute(ID::abc9_box_id)) { // This is an ABC9 box, we have timing data, maybe even a whitebox model // These need to go last in the AIGER port list. nonopaque_boxes.push_back(std::make_tuple(cursor, box, box_derived)); log_debug("non-opaque\n"); } else { opaque_boxes.push_back(std::make_tuple(cursor, box, box_derived)); log_debug("opaque\n"); } } }); for (auto [cursor, box, def] : opaque_boxes) append_box_ports(box, cursor, false); holes_module = design->addModule(NEW_ID); std::vector holes_pis; int boxes_ci_num = 0, boxes_co_num = 0; int box_seq = 0; std::vector boxes_order(analysis.seen.begin(), analysis.seen.end()); std::reverse(boxes_order.begin(), boxes_order.end()); nonopaque_boxes.clear(); for (auto box : boxes_order) { HierCursor cursor; Module *def = design->module(box->type); nonopaque_boxes.push_back(std::make_tuple(cursor, box, def)); } for (auto [cursor, box, def] : nonopaque_boxes) { // use `def->name` not `box->type` as we want the derived type Cell *holes_wb = holes_module->addCell(NEW_ID, def->name); int holes_pi_idx = 0; if (map_file.is_open()) { log_assert(cursor.is_top()); map_file << "box " << box_seq << " " << box->name.c_str() << "\n"; } box_seq++; for (auto port_id : def->ports) { Wire *port = def->wire(port_id); log_assert(port); SigSpec conn = box->hasPort(port_id) ? box->getPort(port_id) : SigSpec{}; if (port->port_input && !port->port_output) { // primary for (int i = 0; i < port->width; i++) { SigBit bit; if (i < conn.size()) { bit = conn[i]; } else { // FIXME: hierarchical path log_warning("connection on port %s[%d] of instance %s (type %s) missing, using 1'bx\n", log_id(port_id), i, log_id(box), log_id(box->type)); bit = RTLIL::Sx; } pos.push_back(std::make_pair(bit, cursor)); } boxes_co_num += port->width; if (mapping_prep && !conn.empty()) box->setPort(port_id, SigSpec(RTLIL::Sx, conn.size())); // holes SigSpec in_conn; for (int i = 0; i < port->width; i++) { while (holes_pi_idx >= (int) holes_pis.size()) { Wire *w = holes_module->addWire(NEW_ID, 1); w->port_input = true; holes_module->ports.push_back(w->name); holes_pis.push_back(w); } in_conn.append(holes_pis[holes_pi_idx]); holes_pi_idx++; } holes_wb->setPort(port_id, in_conn); } else if (port->port_output && !port->port_input) { // primary for (int i = 0; i < port->width; i++) { SigBit bit; if (i < conn.size() && conn[i].is_wire()) { bit = conn[i]; } else { // FIXME: hierarchical path log_warning("connection on port %s[%d] of instance %s (type %s) missing\n", log_id(port_id), i, log_id(box), log_id(box->type)); pad_pi(); continue; } ensure_pi(bit, cursor, true); keep_wires.insert(bit.wire); } boxes_ci_num += port->width; // holes Wire *w = holes_module->addWire(NEW_ID, port->width); w->port_output = true; holes_module->ports.push_back(w->name); holes_wb->setPort(port_id, w); } else { log_error("Ambiguous port direction on %s/%s\n", log_id(box->type), log_id(port_id)); } } } for (auto [cursor, box, def] : opaque_boxes) append_box_ports(box, cursor, true); write_be32(h_buffer, 1); write_be32(h_buffer, pis.size()); log_debug("ciNum = %zu\n", pis.size()); write_be32(h_buffer, pending_pos_num + pos.size()); log_debug("coNum = %zu\n", pending_pos_num + pos.size()); write_be32(h_buffer, pis.size() - boxes_ci_num); log_debug("piNum = %zu\n", pis.size() - boxes_ci_num); write_be32(h_buffer, pending_pos_num + pos.size() - boxes_co_num); log_debug("poNum = %zu\n", pending_pos_num + pos.size() - boxes_co_num); write_be32(h_buffer, nonopaque_boxes.size()); box_seq = 0; for (auto [cursor, box, def] : nonopaque_boxes) { int box_ci_num = 0, box_co_num = 0; for (auto port_id : def->ports) { Wire *port = def->wire(port_id); log_assert(port); if (port->port_input && !port->port_output) { box_co_num += port->width; } else if (port->port_output && !port->port_input) { box_ci_num += port->width; } else { log_abort(); } } write_be32(h_buffer, box_co_num); write_be32(h_buffer, box_ci_num); write_be32(h_buffer, def->attributes.at(ID::abc9_box_id).as_int()); write_be32(h_buffer, box_seq++); } } void clear_boxes() { design->remove(holes_module); } void write(std::ostream *f) { reset_counters(); for (auto w : top->wires()) if (w->port_input) for (int i = 0; i < w->width; i++) ensure_pi(SigBit(w, i)); int proper_po_num = 0; for (auto w : top->wires()) if (w->port_output) proper_po_num += w->width; prep_boxes(proper_po_num); for (auto w : top->wires()) if (w->port_output) for (int i = 0; i < w->width; i++) { if (map_file.is_open() && !driven_by_opaque_box.count(SigBit(w, i))) { map_file << "po " << proper_pos_counter++ << " " << i << " " << w->name.c_str() << "\n"; } pos.push_back(std::make_pair(SigBit(w, i), HierCursor{})); } this->f = f; // start with the header ninputs = pis.size(); noutputs = pos.size(); write_header(); // insert padding where output literals will go (once known) for (auto _ : pos) { char buf[16]; snprintf(buf, sizeof(buf) - 1, "%08d\n", 0); f->write(buf, strlen(buf)); } auto data_start = f->tellp(); // now the guts std::vector outlits; for (auto &pair : pos) outlits.push_back(eval_po(pair.first, &pair.second)); // revisit header and the list of outputs f->seekp(0); ninputs = pis.size(); noutputs = pos.size(); write_header(); for (auto lit : outlits) { char buf[16]; snprintf(buf, sizeof(buf) - 1, "%08d\n", lit); f->write(buf, strlen(buf)); } // double check we arrived at the same offset for the // main data section log_assert(data_start == f->tellp()); // extensions f->seekp(0, std::ios::end); f->put('c'); // insert empty 'r' and 's' sections (abc crashes if we provide 'a' without those) f->put('r'); write_be32(*f, 4); write_be32(*f, 0); f->put('s'); write_be32(*f, 4); write_be32(*f, 0); f->put('h'); // TODO: get rid of std::string copy std::string h_buffer_str = h_buffer.str(); write_be32(*f, h_buffer_str.size()); f->write(h_buffer_str.data(), h_buffer_str.size()); #if 1 f->put('a'); write_be32(*f, 0); // size to be filled later auto holes_aiger_start = f->tellp(); { AigerWriter holes_writer; holes_writer.flatten = true; holes_writer.inline_whiteboxes = true; holes_writer.setup(holes_module); holes_writer.write(f); } auto holes_aiger_size = f->tellp() - holes_aiger_start; f->seekp(holes_aiger_start, std::ios::beg); f->seekp(-4, std::ios::cur); write_be32(*f, holes_aiger_size); #endif f->seekp(0, std::ios::end); if (mapping_prep) { std::vector to_remove_cells; for (auto cell : top->cells()) if (!top_minfo->found_blackboxes.count(cell)) to_remove_cells.push_back(cell); for (auto cell : to_remove_cells) top->remove(cell); pool to_remove; for (auto wire : top->wires()) if (!wire->port_input && !wire->port_output && !keep_wires.count(wire)) to_remove.insert(wire); top->remove(to_remove); } clear_boxes(); } }; struct Aiger2Backend : Backend { Aiger2Backend() : Backend("aiger2", "(experimental) write design to AIGER file") { experimental(); } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_aiger2 [options] [filename]\n"); log("\n"); log("Write the selected module to an AIGER file.\n"); log("\n"); log(" -strash\n"); log(" perform structural hashing while writing\n"); log("\n"); log(" -flatten\n"); log(" allow descending into submodules and write a flattened view of the design\n"); log(" hierarchy starting at the selected top\n"); log("\n"); log("This command is able to ingest all combinational cells except for:\n"); log("\n"); pool supported = {KNOWN_OPS}; CellTypes ct; ct.setup_internals_eval(); log(" "); int col = 0; for (auto pair : ct.cell_types) if (!supported.count(pair.first)) { if (col + pair.first.size() + 2 > 72) { log("\n "); col = 0; } col += pair.first.size() + 2; log("%s, ", log_id(pair.first)); } log("\n"); log("\n"); log("And all combinational gates except for:\n"); log("\n"); CellTypes ct2; ct2.setup_stdcells(); log(" "); col = 0; for (auto pair : ct2.cell_types) if (!supported.count(pair.first)) { if (col + pair.first.size() + 2 > 72) { log("\n "); col = 0; } col += pair.first.size() + 2; log("%s, ", log_id(pair.first)); } log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, Design *design) override { log_header(design, "Executing AIGER2 backend.\n"); size_t argidx; AigerWriter writer; writer.const_folding = true; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-strash") writer.strashing = true; else if (args[argidx] == "-flatten") writer.flatten = true; else break; } extra_args(f, filename, args, argidx); Module *top = design->top_module(); if (!top || !design->selected_whole_module(top)) log_cmd_error("No top module selected\n"); design->bufNormalize(true); writer.setup(top); writer.write(f); // we are leaving the sacred land, un-bufnormalize // (if not, this will lead to bugs: the buf-normalized // flag must not be kept on past the code that can work // with it) design->bufNormalize(false); } } Aiger2Backend; struct XAiger2Backend : Backend { XAiger2Backend() : Backend("xaiger2", "(experimental) write module to XAIGER file") { experimental(); } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_xaiger2 [options] [filename]\n"); log("\n"); log("Write the selected module to a XAIGER file including the 'h' and 'a' extensions\n"); log("with box information for ABC.\n"); log("\n"); log(" -strash\n"); log(" perform structural hashing while writing\n"); log("\n"); log(" -flatten\n"); log(" allow descending into submodules and write a flattened view of the design\n"); log(" hierarchy starting at the selected top\n"); log("\n"); log(" -mapping_prep\n"); log(" after the file is written, prepare the module for reintegration of\n"); log(" a mapping in a subsequent command. all cells which are not blackboxed nor\n"); log(" whiteboxed are removed from the design as well as all wires which only\n"); log(" connect to removed cells\n"); log(" (conflicts with -flatten)\n"); log("\n"); log(" -map2 \n"); log(" write a map2 file which 'read_xaiger2 -sc_mapping' can read to\n"); log(" reintegrate a mapping\n"); log(" (conflicts with -flatten)\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, Design *design) override { log_header(design, "Executing XAIGER2 backend.\n"); size_t argidx; XAigerWriter writer; std::string map_filename; writer.const_folding = true; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-strash") writer.strashing = true; else if (args[argidx] == "-flatten") writer.flatten = true; else if (args[argidx] == "-mapping_prep") writer.mapping_prep = true; else if (args[argidx] == "-map2" && argidx + 1 < args.size()) map_filename = args[++argidx]; else break; } extra_args(f, filename, args, argidx); Module *top = design->top_module(); if (!top || !design->selected_whole_module(top)) log_cmd_error("No top module selected\n"); if (!map_filename.empty()) { writer.map_file.open(map_filename); if (!writer.map_file) log_cmd_error("Failed to open '%s' for writing\n", map_filename.c_str()); } design->bufNormalize(true); writer.setup(top); writer.write(f); // we are leaving the sacred land, un-bufnormalize // (if not, this will lead to bugs: the buf-normalized // flag must not be kept on past the code that can work // with it) design->bufNormalize(false); } } XAiger2Backend; PRIVATE_NAMESPACE_END yosys-0.52/backends/blif/000077500000000000000000000000001477540374200153315ustar00rootroot00000000000000yosys-0.52/backends/blif/Makefile.inc000066400000000000000000000000371477540374200175410ustar00rootroot00000000000000 OBJS += backends/blif/blif.o yosys-0.52/backends/blif/blif.cc000066400000000000000000000566061477540374200165710ustar00rootroot00000000000000/* * 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. * */ // [[CITE]] Berkeley Logic Interchange Format (BLIF) // University of California. Berkeley. July 28, 1992 // http://www.ece.cmu.edu/~ee760/760docs/blif.pdf #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct BlifDumperConfig { bool icells_mode; bool conn_mode; bool impltf_mode; bool gates_mode; bool cname_mode; bool iname_mode; bool param_mode; bool attr_mode; bool iattr_mode; bool blackbox_mode; bool noalias_mode; std::string buf_type, buf_in, buf_out; std::map> unbuf_types; std::string true_type, true_out, false_type, false_out, undef_type, undef_out; BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false), cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false), blackbox_mode(false), noalias_mode(false) { } }; struct BlifDumper { std::ostream &f; RTLIL::Module *module; RTLIL::Design *design; BlifDumperConfig *config; CellTypes ct; SigMap sigmap; dict init_bits; BlifDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig *config) : f(f), module(module), design(design), config(config), ct(design), sigmap(module) { for (Wire *wire : module->wires()) if (wire->attributes.count(ID::init)) { SigSpec initsig = sigmap(wire); Const initval = wire->attributes.at(ID::init); for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) switch (initval[i]) { case State::S0: init_bits[initsig[i]] = 0; break; case State::S1: init_bits[initsig[i]] = 1; break; default: break; } } } pool cstr_bits_seen; const std::string str(RTLIL::IdString id) { std::string str = RTLIL::unescape_id(id); for (size_t i = 0; i < str.size(); i++) if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>') str[i] = '?'; return str; } const std::string str(RTLIL::SigBit sig) { cstr_bits_seen.insert(sig); if (sig.wire == NULL) { if (sig == RTLIL::State::S0) return config->false_type == "-" || config->false_type == "+" ? config->false_out.c_str() : "$false"; if (sig == RTLIL::State::S1) return config->true_type == "-" || config->true_type == "+" ? config->true_out.c_str() : "$true"; return config->undef_type == "-" || config->undef_type == "+" ? config->undef_out.c_str() : "$undef"; } std::string str = RTLIL::unescape_id(sig.wire->name); for (size_t i = 0; i < str.size(); i++) if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>') str[i] = '?'; if (sig.wire->width != 1) str += stringf("[%d]", sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset); return str; } const std::string str_init(RTLIL::SigBit sig) { sigmap.apply(sig); if (init_bits.count(sig) == 0) return " 2"; string str = stringf(" %d", init_bits.at(sig)); return str; } const char *subckt_or_gate(std::string cell_type) { if (!config->gates_mode) return "subckt"; if (design->module(RTLIL::escape_id(cell_type)) == nullptr) return "gate"; if (design->module(RTLIL::escape_id(cell_type))->get_blackbox_attribute()) return "gate"; return "subckt"; } void dump_params(const char *command, dict ¶ms) { for (auto ¶m : params) { f << stringf("%s %s ", command, log_id(param.first)); if (param.second.flags & RTLIL::CONST_FLAG_STRING) { std::string str = param.second.decode_string(); f << stringf("\""); for (char ch : str) if (ch == '"' || ch == '\\') f << stringf("\\%c", ch); else if (ch < 32 || ch >= 127) f << stringf("\\%03o", ch); else f << stringf("%c", ch); f << stringf("\"\n"); } else f << stringf("%s\n", param.second.as_string().c_str()); } } void dump() { f << stringf("\n"); f << stringf(".model %s\n", str(module->name).c_str()); std::map inputs, outputs; for (auto wire : module->wires()) { if (wire->port_input) inputs[wire->port_id] = wire; if (wire->port_output) outputs[wire->port_id] = wire; } f << stringf(".inputs"); for (auto &it : inputs) { RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str()); } f << stringf("\n"); f << stringf(".outputs"); for (auto &it : outputs) { RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str()); } f << stringf("\n"); if (module->get_blackbox_attribute()) { f << stringf(".blackbox\n"); f << stringf(".end\n"); return; } if (!config->impltf_mode) { if (!config->false_type.empty()) { if (config->false_type == "+") f << stringf(".names %s\n", config->false_out.c_str()); else if (config->false_type != "-") f << stringf(".%s %s %s=$false\n", subckt_or_gate(config->false_type), config->false_type.c_str(), config->false_out.c_str()); } else f << stringf(".names $false\n"); if (!config->true_type.empty()) { if (config->true_type == "+") f << stringf(".names %s\n1\n", config->true_out.c_str()); else if (config->true_type != "-") f << stringf(".%s %s %s=$true\n", subckt_or_gate(config->true_type), config->true_type.c_str(), config->true_out.c_str()); } else f << stringf(".names $true\n1\n"); if (!config->undef_type.empty()) { if (config->undef_type == "+") f << stringf(".names %s\n", config->undef_out.c_str()); else if (config->undef_type != "-") f << stringf(".%s %s %s=$undef\n", subckt_or_gate(config->undef_type), config->undef_type.c_str(), config->undef_out.c_str()); } else f << stringf(".names $undef\n"); } 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", str(cell->getPort(portnames.first)).c_str(), str(cell->getPort(portnames.second)).c_str()); continue; } if (!config->icells_mode && cell->type == ID($_NOT_)) { f << stringf(".names %s %s\n0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AND_)) { f << stringf(".names %s %s %s\n11 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OR_)) { f << stringf(".names %s %s %s\n1- 1\n-1 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_XOR_)) { f << stringf(".names %s %s %s\n10 1\n01 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NAND_)) { f << stringf(".names %s %s %s\n0- 1\n-0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NOR_)) { f << stringf(".names %s %s %s\n00 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_XNOR_)) { f << stringf(".names %s %s %s\n11 1\n00 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_ANDNOT_)) { f << stringf(".names %s %s %s\n10 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_ORNOT_)) { f << stringf(".names %s %s %s\n1- 1\n-0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AOI3_)) { f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OAI3_)) { f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_AOI4_)) { f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_OAI4_)) { f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_MUX_)) { f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::S)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_NMUX_)) { f << stringf(".names %s %s %s %s\n0-0 1\n-01 1\n", str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::S)).c_str(), str(cell->getPort(ID::Y)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_FF_)) { f << stringf(".latch %s %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DFF_N_)) { f << stringf(".latch %s %s fe %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DFF_P_)) { f << stringf(".latch %s %s re %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DLATCH_N_)) { f << stringf(".latch %s %s al %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($_DLATCH_P_)) { f << stringf(".latch %s %s ah %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(), str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str()); goto internal_cell; } if (!config->icells_mode && cell->type == ID($lut)) { f << stringf(".names"); auto &inputs = cell->getPort(ID::A); auto width = cell->parameters.at(ID::WIDTH).as_int(); log_assert(inputs.size() == width); for (int i = width-1; i >= 0; i--) f << stringf(" %s", str(inputs.extract(i, 1)).c_str()); auto &output = cell->getPort(ID::Y); log_assert(output.size() == 1); f << stringf(" %s", str(output).c_str()); f << stringf("\n"); RTLIL::SigSpec mask = cell->parameters.at(ID::LUT); for (int i = 0; i < (1 << width); i++) if (mask[i] == State::S1) { for (int j = width-1; j >= 0; j--) { f << ((i>>j)&1 ? '1' : '0'); } f << " 1\n"; } goto internal_cell; } if (!config->icells_mode && cell->type == ID($sop)) { f << stringf(".names"); auto &inputs = cell->getPort(ID::A); auto width = cell->parameters.at(ID::WIDTH).as_int(); auto depth = cell->parameters.at(ID::DEPTH).as_int(); vector table = cell->parameters.at(ID::TABLE).to_bits(); while (GetSize(table) < 2*width*depth) table.push_back(State::S0); log_assert(inputs.size() == width); for (int i = 0; i < width; i++) f << stringf(" %s", str(inputs.extract(i, 1)).c_str()); auto &output = cell->getPort(ID::Y); log_assert(output.size() == 1); f << stringf(" %s", str(output).c_str()); f << stringf("\n"); for (int i = 0; i < depth; i++) { for (int j = 0; j < width; j++) { bool pat0 = table.at(2*width*i + 2*j + 0) == State::S1; bool pat1 = table.at(2*width*i + 2*j + 1) == State::S1; if (pat0 && !pat1) f << "0"; else if (!pat0 && pat1) f << "1"; else f << "-"; } f << " 1\n"; } goto internal_cell; } f << stringf(".%s %s", subckt_or_gate(cell->type.str()), str(cell->type).c_str()); for (auto &conn : cell->connections()) { if (conn.second.size() == 1) { f << stringf(" %s=%s", str(conn.first).c_str(), str(conn.second[0]).c_str()); continue; } Module *m = design->module(cell->type); Wire *w = m ? m->wire(conn.first) : nullptr; if (w == nullptr) { for (int i = 0; i < GetSize(conn.second); i++) f << stringf(" %s[%d]=%s", str(conn.first).c_str(), i, str(conn.second[i]).c_str()); } else { for (int i = 0; i < std::min(GetSize(conn.second), GetSize(w)); i++) { SigBit sig(w, i); f << stringf(" %s[%d]=%s", str(conn.first).c_str(), sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset, str(conn.second[i]).c_str()); } } } f << stringf("\n"); if (config->cname_mode) f << stringf(".cname %s\n", str(cell->name).c_str()); if (config->attr_mode) dump_params(".attr", cell->attributes); if (config->param_mode) dump_params(".param", cell->parameters); if (0) { internal_cell: if (config->iname_mode) f << stringf(".cname %s\n", str(cell->name).c_str()); if (config->iattr_mode) dump_params(".attr", cell->attributes); } } for (auto &conn : module->connections()) for (int i = 0; i < conn.first.size(); i++) { SigBit lhs_bit = conn.first[i]; SigBit rhs_bit = conn.second[i]; if (config->noalias_mode && cstr_bits_seen.count(lhs_bit) == 0) continue; if (config->conn_mode) f << stringf(".conn %s %s\n", str(rhs_bit).c_str(), str(lhs_bit).c_str()); else if (!config->buf_type.empty()) f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(), config->buf_in.c_str(), str(rhs_bit).c_str(), config->buf_out.c_str(), str(lhs_bit).c_str()); else f << stringf(".names %s %s\n1 1\n", str(rhs_bit).c_str(), str(lhs_bit).c_str()); } f << stringf(".end\n"); } static void dump(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BlifDumperConfig &config) { BlifDumper dumper(f, module, design, &config); dumper.dump(); } }; struct BlifBackend : public Backend { BlifBackend() : Backend("blif", "write design to BLIF file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_blif [options] [filename]\n"); log("\n"); log("Write the current design to an BLIF file.\n"); log("\n"); log(" -top top_module\n"); log(" set the specified module as design top module\n"); log("\n"); log(" -buf \n"); log(" use cells of type with the specified port names for buffers\n"); log("\n"); log(" -unbuf \n"); log(" replace buffer cells with the specified name and port names with\n"); log(" a .names statement that models a buffer\n"); log("\n"); log(" -true \n"); log(" -false \n"); log(" -undef \n"); log(" use the specified cell types to drive nets that are constant 1, 0, or\n"); log(" undefined. when '-' is used as , then specifies\n"); log(" the wire name to be used for the constant signal and no cell driving\n"); log(" that wire is generated. when '+' is used as , then \n"); log(" specifies the wire name to be used for the constant signal and a .names\n"); log(" statement is generated to drive the wire.\n"); log("\n"); log(" -noalias\n"); log(" if a net name is aliasing another net name, then by default a net\n"); log(" without fanout is created that is driven by the other net. This option\n"); 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 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"); log(" functions. Instead create .subckt or .gate lines for all cells.\n"); log("\n"); log(" -gates\n"); log(" print .gate instead of .subckt lines for all cells that are not\n"); log(" instantiations of other modules from this design.\n"); log("\n"); log(" -conn\n"); log(" do not generate buffers for connected wires. instead use the\n"); log(" non-standard .conn statement.\n"); log("\n"); log(" -attr\n"); log(" use the non-standard .attr statement to write cell attributes\n"); log("\n"); log(" -param\n"); log(" use the non-standard .param statement to write cell parameters\n"); log("\n"); log(" -cname\n"); log(" use the non-standard .cname statement to write cell names\n"); log("\n"); log(" -iname, -iattr\n"); log(" enable -cname and -attr functionality for .names statements\n"); log(" (the .cname and .attr statements will be included in the BLIF\n"); log(" output after the truth table for the .names statement)\n"); log("\n"); log(" -blackbox\n"); log(" write blackbox cells with .blackbox statement.\n"); log("\n"); log(" -impltf\n"); log(" do not write definitions for the $true, $false and $undef wires.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { std::string top_module_name; std::string buf_type, buf_in, buf_out; std::string true_type, true_out; std::string false_type, false_out; BlifDumperConfig config; log_header(design, "Executing BLIF backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-top" && argidx+1 < args.size()) { top_module_name = args[++argidx]; continue; } if (args[argidx] == "-buf" && argidx+3 < args.size()) { config.buf_type = args[++argidx]; config.buf_in = args[++argidx]; config.buf_out = args[++argidx]; continue; } if (args[argidx] == "-unbuf" && argidx+3 < args.size()) { RTLIL::IdString unbuf_type = RTLIL::escape_id(args[++argidx]); RTLIL::IdString unbuf_in = RTLIL::escape_id(args[++argidx]); RTLIL::IdString unbuf_out = RTLIL::escape_id(args[++argidx]); config.unbuf_types[unbuf_type] = std::pair(unbuf_in, unbuf_out); continue; } if (args[argidx] == "-true" && argidx+2 < args.size()) { config.true_type = args[++argidx]; config.true_out = args[++argidx]; continue; } if (args[argidx] == "-false" && argidx+2 < args.size()) { config.false_type = args[++argidx]; config.false_out = args[++argidx]; continue; } if (args[argidx] == "-undef" && argidx+2 < args.size()) { config.undef_type = args[++argidx]; config.undef_out = args[++argidx]; continue; } if (args[argidx] == "-icells") { config.icells_mode = true; continue; } if (args[argidx] == "-gates") { config.gates_mode = true; continue; } if (args[argidx] == "-conn") { config.conn_mode = true; continue; } if (args[argidx] == "-cname") { config.cname_mode = true; continue; } if (args[argidx] == "-param") { config.param_mode = true; continue; } if (args[argidx] == "-attr") { config.attr_mode = true; continue; } if (args[argidx] == "-iname") { config.iname_mode = true; continue; } if (args[argidx] == "-iattr") { config.iattr_mode = true; continue; } if (args[argidx] == "-blackbox") { config.blackbox_mode = true; continue; } if (args[argidx] == "-impltf") { config.impltf_mode = true; continue; } if (args[argidx] == "-noalias") { config.noalias_mode = true; continue; } break; } extra_args(f, filename, args, argidx); if (top_module_name.empty()) for (auto module : design->modules()) if (module->get_bool_attribute(ID::top)) top_module_name = module->name.str(); *f << stringf("# Generated by %s\n", yosys_version_str); std::vector mod_list; design->sort(); for (auto module : design->modules()) { if (module->get_blackbox_attribute() && !config.blackbox_mode) continue; if (module->processes.size() != 0) log_error("Found unmapped processes in module %s: unmapped processes are not supported in BLIF backend!\n", log_id(module->name)); if (module->memories.size() != 0) log_error("Found unmapped memories in module %s: unmapped memories are not supported in BLIF backend!\n", log_id(module->name)); if (module->name == RTLIL::escape_id(top_module_name)) { BlifDumper::dump(*f, module, design, config); top_module_name.clear(); continue; } mod_list.push_back(module); } if (!top_module_name.empty()) log_error("Can't find top module `%s'!\n", top_module_name.c_str()); for (auto module : mod_list) BlifDumper::dump(*f, module, design, config); } } BlifBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/btor/000077500000000000000000000000001477540374200153635ustar00rootroot00000000000000yosys-0.52/backends/btor/.gitignore000066400000000000000000000000211477540374200173440ustar00rootroot00000000000000/test_cells.tmp/ yosys-0.52/backends/btor/Makefile.inc000066400000000000000000000000371477540374200175730ustar00rootroot00000000000000 OBJS += backends/btor/btor.o yosys-0.52/backends/btor/btor.cc000066400000000000000000001307351477540374200166510ustar00rootroot00000000000000/* * 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. * */ // [[CITE]] Btor2 , BtorMC and Boolector 3.0 // 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/ #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" #include "kernel/json.h" #include "kernel/yw.h" #include "kernel/utils.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct BtorWorker { std::ostream &f; SigMap sigmap; RTLIL::Module *module; bool verbose; bool single_bad; bool cover_mode; bool print_internal_names; int next_nid = 1; int initstate_nid = -1; // => dict sorts_bv; // (, ) => dict, int> sorts_mem; // SigBit => (, ) dict> bit_nid; // => dict nid_width; // SigSpec => dict sig_nid; // bit to driving cell dict bit_cell; // nids for constants dict consts; // ff inputs that need to be evaluated (, ) vector> ff_todo; vector> mem_todo; pool cell_recursion_guard; vector bad_properties; dict initbits; pool statewires; pool srcsymbols; vector memories; dict mem_cells; string indent, info_filename; 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; va_start(ap, fmt); f << indent << vstringf(fmt, ap); va_end(ap); } void infof(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3)) { va_list ap; va_start(ap, fmt); info_lines.push_back(vstringf(fmt, ap)); va_end(ap); } template string getinfo(T *obj, bool srcsym = false) { string infostr = log_id(obj); if (!srcsym && !print_internal_names && infostr[0] == '$') return ""; if (obj->attributes.count(ID::src)) { string src = obj->attributes.at(ID::src).decode_string().c_str(); if (srcsym && infostr[0] == '$') { std::replace(src.begin(), src.end(), ' ', '_'); if (srcsymbols.count(src) || module->count_id("\\" + src)) { for (int i = 1;; i++) { string s = stringf("%s-%d", src.c_str(), i); if (!srcsymbols.count(s) && !module->count_id("\\" + s)) { src = s; break; } } } srcsymbols.insert(src); infostr = src; } else { infostr += " ; " + src; } } 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) { f << indent << stringf(" ; begin %s\n", id.c_str()); indent += " "; } } void btorf_pop(const string &id) { if (verbose) { indent = indent.substr(4); f << indent << stringf(" ; end %s\n", id.c_str()); } } int get_bv_sid(int width) { if (sorts_bv.count(width) == 0) { int nid = next_nid++; btorf("%d sort bitvec %d\n", nid, width); sorts_bv[width] = nid; } return sorts_bv.at(width); } int get_mem_sid(int abits, int dbits) { pair key(abits, dbits); if (sorts_mem.count(key) == 0) { int addr_sid = get_bv_sid(abits); int data_sid = get_bv_sid(dbits); int nid = next_nid++; btorf("%d sort array %d %d\n", nid, addr_sid, data_sid); sorts_mem[key] = nid; } return sorts_mem.at(key); } void add_nid_sig(int nid, const SigSpec &sig) { if (verbose) f << indent << stringf("; %d %s\n", nid, log_signal(sig)); for (int i = 0; i < GetSize(sig); i++) bit_nid[sig[i]] = make_pair(nid, i); sig_nid[sig] = nid; nid_width[nid] = GetSize(sig); } void export_cell(Cell *cell) { if (cell_recursion_guard.count(cell)) { string cell_list; for (auto c : cell_recursion_guard) cell_list += stringf("\n %s", log_id(c)); log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list.c_str()); } cell_recursion_guard.insert(cell); btorf_push(log_id(cell)); if (cell->type.in(ID($add), ID($sub), ID($mul), ID($and), ID($or), ID($xor), ID($xnor), ID($shl), ID($sshl), ID($shr), ID($sshr), ID($shift), ID($shiftx), ID($concat), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_))) { string btor_op; if (cell->type == ID($add)) btor_op = "add"; if (cell->type == ID($sub)) btor_op = "sub"; if (cell->type == ID($mul)) btor_op = "mul"; if (cell->type.in(ID($shl), ID($sshl))) btor_op = "sll"; if (cell->type == ID($shr)) btor_op = "srl"; if (cell->type == ID($sshr)) btor_op = "sra"; if (cell->type.in(ID($shift), ID($shiftx))) btor_op = "shift"; if (cell->type.in(ID($and), ID($_AND_))) btor_op = "and"; if (cell->type.in(ID($or), ID($_OR_))) btor_op = "or"; if (cell->type.in(ID($xor), ID($_XOR_))) btor_op = "xor"; if (cell->type == ID($concat)) btor_op = "concat"; if (cell->type == ID($_NAND_)) btor_op = "nand"; if (cell->type == ID($_NOR_)) btor_op = "nor"; if (cell->type.in(ID($xnor), ID($_XNOR_))) btor_op = "xnor"; log_assert(!btor_op.empty()); int width_ay = std::max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::Y))); int width = std::max(width_ay, GetSize(cell->getPort(ID::B))); bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; if (btor_op == "shift" && !b_signed) btor_op = "srl"; if (cell->type.in(ID($shl), ID($sshl), ID($shr), ID($sshr))) b_signed = false; if (cell->type == ID($sshr) && !a_signed) btor_op = "srl"; int sid = get_bv_sid(width); int nid; int nid_a; if (cell->type.in(ID($shl), ID($shr), ID($shift), ID($shiftx)) && a_signed && width_ay < width) { // sign-extend A up to the width of Y int nid_a_padded = get_sig_nid(cell->getPort(ID::A), width_ay, a_signed); // zero-extend the rest int zeroes = get_sig_nid(Const(0, width-width_ay)); nid_a = next_nid++; btorf("%d concat %d %d %d\n", nid_a, sid, zeroes, nid_a_padded); } else { nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); } int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); if (btor_op == "shift") { int nid_r = next_nid++; btorf("%d srl %d %d %d\n", nid_r, sid, nid_a, nid_b); int nid_b_neg = next_nid++; btorf("%d neg %d %d\n", nid_b_neg, sid, nid_b); int nid_l = next_nid++; btorf("%d sll %d %d %d\n", nid_l, sid, nid_a, nid_b_neg); int sid_bit = get_bv_sid(1); int nid_zero = get_sig_nid(Const(0, width)); int nid_b_ltz = next_nid++; btorf("%d slt %d %d %d\n", nid_b_ltz, sid_bit, nid_b, nid_zero); nid = next_nid++; btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell).c_str()); } else { nid = next_nid++; btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); if (GetSize(sig) < width) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($div), ID($mod), ID($modfloor))) { bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; string btor_op; if (cell->type == ID($div)) btor_op = "div"; // "rem" = truncating modulo if (cell->type == ID($mod)) btor_op = "rem"; // "mod" = flooring modulo if (cell->type == ID($modfloor)) { // "umod" doesn't exist because it's the same as "urem" btor_op = a_signed || b_signed ? "mod" : "rem"; } log_assert(!btor_op.empty()); int width = GetSize(cell->getPort(ID::Y)); width = std::max(width, GetSize(cell->getPort(ID::A))); width = std::max(width, GetSize(cell->getPort(ID::B))); int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); int sid = get_bv_sid(width); int nid = next_nid++; btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); SigSpec sig = sigmap(cell->getPort(ID::Y)); if (GetSize(sig) < width) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($_ANDNOT_), ID($_ORNOT_))) { int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_b = get_sig_nid(cell->getPort(ID::B)); int nid1 = next_nid++; int nid2 = next_nid++; if (cell->type == ID($_ANDNOT_)) { btorf("%d not %d %d\n", nid1, sid, nid_b); btorf("%d and %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str()); } if (cell->type == ID($_ORNOT_)) { btorf("%d not %d %d\n", nid1, sid, nid_b); btorf("%d or %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); add_nid_sig(nid2, sig); goto okay; } if (cell->type.in(ID($_OAI3_), ID($_AOI3_))) { int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_b = get_sig_nid(cell->getPort(ID::B)); int nid_c = get_sig_nid(cell->getPort(ID::C)); int nid1 = next_nid++; int nid2 = next_nid++; int nid3 = next_nid++; if (cell->type == ID($_OAI3_)) { btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d and %d %d %d\n", nid2, sid, nid1, nid_c); btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str()); } if (cell->type == ID($_AOI3_)) { btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d or %d %d %d\n", nid2, sid, nid1, nid_c); btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); add_nid_sig(nid3, sig); goto okay; } if (cell->type.in(ID($_OAI4_), ID($_AOI4_))) { int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_b = get_sig_nid(cell->getPort(ID::B)); int nid_c = get_sig_nid(cell->getPort(ID::C)); int nid_d = get_sig_nid(cell->getPort(ID::D)); int nid1 = next_nid++; int nid2 = next_nid++; int nid3 = next_nid++; int nid4 = next_nid++; if (cell->type == ID($_OAI4_)) { btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d or %d %d %d\n", nid2, sid, nid_c, nid_d); btorf("%d and %d %d %d\n", nid3, sid, nid1, nid2); btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str()); } if (cell->type == ID($_AOI4_)) { btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d and %d %d %d\n", nid2, sid, nid_c, nid_d); btorf("%d or %d %d %d\n", nid3, sid, nid1, nid2); btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); add_nid_sig(nid4, sig); goto okay; } if (cell->type.in(ID($lt), ID($le), ID($eq), ID($eqx), ID($ne), ID($nex), ID($ge), ID($gt))) { string btor_op; if (cell->type == ID($lt)) btor_op = "lt"; if (cell->type == ID($le)) btor_op = "lte"; if (cell->type.in(ID($eq), ID($eqx))) btor_op = "eq"; if (cell->type.in(ID($ne), ID($nex))) btor_op = "neq"; if (cell->type == ID($ge)) btor_op = "gte"; if (cell->type == ID($gt)) btor_op = "gt"; log_assert(!btor_op.empty()); int width = 1; width = std::max(width, GetSize(cell->getPort(ID::A))); width = std::max(width, GetSize(cell->getPort(ID::B))); bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); int nid = next_nid++; if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt))) { btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } else { btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); if (GetSize(sig) > 1) { int sid = get_bv_sid(GetSize(sig)); int nid2 = next_nid++; btorf("%d uext %d %d %d\n", nid2, sid, nid, GetSize(sig) - 1); nid = nid2; } add_nid_sig(nid, sig); goto okay; } 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"; 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 nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); 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++; btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($logic_and), ID($logic_or), ID($logic_not))) { string btor_op; if (cell->type == ID($logic_and)) btor_op = "and"; if (cell->type == ID($logic_or)) btor_op = "or"; if (cell->type == ID($logic_not)) btor_op = "not"; log_assert(!btor_op.empty()); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_b = btor_op != "not" ? get_sig_nid(cell->getPort(ID::B)) : 0; if (GetSize(cell->getPort(ID::A)) > 1) { int nid_red_a = next_nid++; btorf("%d redor %d %d\n", nid_red_a, sid, nid_a); nid_a = nid_red_a; } if (btor_op != "not" && GetSize(cell->getPort(ID::B)) > 1) { int nid_red_b = next_nid++; btorf("%d redor %d %d\n", nid_red_b, sid, nid_b); nid_b = nid_red_b; } int nid = next_nid++; if (btor_op != "not") btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); else 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)); if (GetSize(sig) > 1) { int sid = get_bv_sid(GetSize(sig)); int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1)); int nid2 = next_nid++; btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool), ID($reduce_xor), ID($reduce_xnor))) { string btor_op; if (cell->type == ID($reduce_and)) btor_op = "redand"; if (cell->type.in(ID($reduce_or), ID($reduce_bool))) btor_op = "redor"; if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) btor_op = "redxor"; log_assert(!btor_op.empty()); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid = next_nid++; if (cell->type == ID($reduce_xnor)) { int nid2 = next_nid++; btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); btorf("%d not %d %d\n", nid2, sid, nid); nid = nid2; } else { 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)); if (GetSize(sig) > 1) { int sid = get_bv_sid(GetSize(sig)); int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1)); int nid2 = next_nid++; btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid); nid = nid2; } add_nid_sig(nid, sig); goto okay; } if (cell->type.in(ID($mux), ID($_MUX_), ID($_NMUX_))) { SigSpec sig_a = sigmap(cell->getPort(ID::A)); SigSpec sig_b = sigmap(cell->getPort(ID::B)); SigSpec sig_s = sigmap(cell->getPort(ID::S)); SigSpec sig_y = sigmap(cell->getPort(ID::Y)); int nid_a = get_sig_nid(sig_a); int nid_b = get_sig_nid(sig_b); int nid_s = get_sig_nid(sig_s); int sid = get_bv_sid(GetSize(sig_y)); int nid = next_nid++; if (cell->type == ID($_NMUX_)) { int tmp = nid; nid = next_nid++; btorf("%d ite %d %d %d %d\n", tmp, sid, nid_s, nid_b, nid_a); btorf("%d not %d %d%s\n", nid, sid, tmp, getinfo(cell).c_str()); } else { btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell).c_str()); } add_nid_sig(nid, sig_y); goto okay; } if (cell->type == ID($pmux)) { SigSpec sig_a = sigmap(cell->getPort(ID::A)); SigSpec sig_b = sigmap(cell->getPort(ID::B)); SigSpec sig_s = sigmap(cell->getPort(ID::S)); SigSpec sig_y = sigmap(cell->getPort(ID::Y)); int width = GetSize(sig_a); int sid = get_bv_sid(width); int nid = get_sig_nid(sig_a); for (int i = 0; i < GetSize(sig_s); i++) { int nid_b = get_sig_nid(sig_b.extract(i*width, width)); int nid_s = get_sig_nid(sig_s.extract(i)); int nid2 = next_nid++; if (i == GetSize(sig_s)-1) btorf("%d ite %d %d %d %d%s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell).c_str()); else btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid); nid = nid2; } add_nid_sig(nid, sig_y); goto okay; } 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() || 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); bool negedge = false; if (cell->type == ID($_DFF_N_)) negedge = true; if (cell->type == ID($dff) && !cell->getParam(ID::CLK_POLARITY).as_bool()) negedge = true; if (!info_filename.empty()) info_clocks[nid] |= negedge ? 2 : 1; if (ywmap_json.active()) ywmap_clock_bits[sig_c] |= negedge ? 2 : 1; } IdString symbol; if (sig_q.is_wire()) { Wire *w = sig_q.as_wire(); if (w->port_id == 0) { statewires.insert(w); symbol = w->name; } } Const initval; for (int i = 0; i < GetSize(sig_q); i++) if (initbits.count(sig_q[i])) initval.bits().push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0); else initval.bits().push_back(State::Sx); int nid_init_val = -1; if (!initval.is_fully_undef()) nid_init_val = get_sig_nid(initval, -1, false, true); int sid = get_bv_sid(GetSize(sig_q)); int nid = next_nid++; if (symbol.empty() || (!print_internal_names && symbol[0] == '$')) btorf("%d state %d\n", nid, sid); 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) btorf("; initval = %s\n", log_signal(initval)); btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val); } ff_todo.push_back(make_pair(nid, cell)); add_nid_sig(nid, sig_q); goto okay; } if (cell->type.in(ID($anyconst), ID($anyseq))) { SigSpec sig_y = sigmap(cell->getPort(ID::Y)); int sid = get_bv_sid(GetSize(sig_y)); int nid = next_nid++; 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++; btorf("%d next %d %d %d\n", nid2, sid, nid, nid); } add_nid_sig(nid, sig_y); goto okay; } if (cell->type == ID($initstate)) { SigSpec sig_y = sigmap(cell->getPort(ID::Y)); if (initstate_nid < 0) { int sid = get_bv_sid(1); int one_nid = get_sig_nid(State::S1); int zero_nid = get_sig_nid(State::S0); initstate_nid = next_nid++; 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->is_mem_cell()) { Mem *mem = mem_cells[cell]; int abits = ceil_log2(mem->size); bool asyncwr = false; bool syncwr = false; for (auto &port : mem->wr_ports) { if (port.clk_enable) syncwr = true; else asyncwr = true; } if (asyncwr && syncwr) 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) { if (port.clk_enable) 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); int sid = get_mem_sid(abits, mem->width); int nid_init_val = -1; if (!mem->inits.empty()) { Const initdata = mem->get_init_data(); bool constword = true; Const firstword = initdata.extract(0, mem->width); for (int i = 1; i < mem->size; i++) { Const thisword = initdata.extract(i*mem->width, mem->width); if (thisword != firstword) { constword = false; break; } } // If not fully defined, undef bits should be able to take a // different value for each address so we can't initialise from // one value (and btor2parser doesn't like it) if (constword && firstword.is_fully_def()) { if (verbose) btorf("; initval = %s\n", log_signal(firstword)); nid_init_val = get_sig_nid(firstword, -1, false, true); } else { 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()) continue; Const thisaddr(i, abits); int nid_thisword = get_sig_nid(thisword, -1, false, true); int nid_thisaddr = get_sig_nid(thisaddr, -1, false, true); int last_nid_init_val = nid_init_val; nid_init_val = next_nid++; if (verbose) btorf("; initval[%d] = %s\n", i, log_signal(thisword)); btorf("%d write %d %d %d %d\n", nid_init_val, sid, last_nid_init_val, nid_thisaddr, nid_thisword); } } } int nid = next_nid++; int nid_head = nid; if (mem->memid[0] == '$') btorf("%d state %d\n", nid, sid); 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++; btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val); } if (asyncwr) { for (auto &port : mem->wr_ports) { SigSpec wa = port.addr; wa.extend_u0(abits); int wa_nid = get_sig_nid(wa); int wd_nid = get_sig_nid(port.data); int we_nid = get_sig_nid(port.en); int nid2 = next_nid++; btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid); int nid3 = next_nid++; btorf("%d not %d %d\n", nid3, data_sid, we_nid); int nid4 = next_nid++; btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3); int nid5 = next_nid++; btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid); int nid6 = next_nid++; btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4); int nid7 = next_nid++; btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6); int nid8 = next_nid++; btorf("%d redor %d %d\n", nid8, bool_sid, we_nid); int nid9 = next_nid++; btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head); nid_head = nid9; } } for (auto &port : mem->rd_ports) { SigSpec ra = port.addr; ra.extend_u0(abits); int ra_nid = get_sig_nid(ra); int rd_nid = next_nid++; btorf("%d read %d %d %d\n", rd_nid, data_sid, nid_head, ra_nid); add_nid_sig(rd_nid, port.data); } if (!asyncwr) { mem_todo.push_back(make_pair(nid, mem)); } else { int nid2 = next_nid++; btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head); } goto okay; } 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)); cell_recursion_guard.erase(cell); } int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false, bool is_init = false) { int nid = -1; sigmap.apply(sig); for (auto bit : sig) if (bit == State::Sx) goto has_undef_bits; if (0) { has_undef_bits: SigSpec sig_mask_undef, sig_noundef; int first_undef = -1; for (int i = 0; i < GetSize(sig); i++) if (sig[i] == State::Sx) { if (first_undef < 0) first_undef = i; sig_mask_undef.append(State::S1); sig_noundef.append(State::S0); } else { sig_mask_undef.append(State::S0); sig_noundef.append(sig[i]); } if (to_width < 0 || first_undef < to_width) { int sid = get_bv_sid(GetSize(sig)); int nid_input = next_nid++; if (is_init) { btorf("%d state %d\n", nid_input, sid); 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()) { nid_masked_input = nid_input; } else { int nid_mask_undef = get_sig_nid(sig_mask_undef); nid_masked_input = next_nid++; btorf("%d and %d %d %d\n", nid_masked_input, sid, nid_input, nid_mask_undef); } if (sig_noundef.is_fully_zero()) { nid = nid_masked_input; } else { int nid_noundef = get_sig_nid(sig_noundef); nid = next_nid++; btorf("%d or %d %d %d\n", nid, sid, nid_masked_input, nid_noundef); } goto extend_or_trim; } sig = sig_noundef; } if (sig_nid.count(sig) == 0) { // , vector> nidbits; // collect all bits for (int i = 0; i < GetSize(sig); i++) { SigBit bit = sig[i]; if (bit_nid.count(bit) == 0) { if (bit.wire == nullptr) { Const c(bit.data); while (i+GetSize(c) < GetSize(sig) && sig[i+GetSize(c)].wire == nullptr) c.bits().push_back(sig[i+GetSize(c)].data); if (consts.count(c) == 0) { int sid = get_bv_sid(GetSize(c)); int nid = next_nid++; btorf("%d const %d %s\n", nid, sid, c.as_string().c_str()); consts[c] = nid; nid_width[nid] = GetSize(c); } int nid = consts.at(c); for (int j = 0; j < GetSize(c); j++) nidbits.push_back(make_pair(nid, j)); i += GetSize(c)-1; continue; } else { if (bit_cell.count(bit) == 0) { SigSpec s = bit; while (i+GetSize(s) < GetSize(sig) && sig[i+GetSize(s)].wire != nullptr && bit_cell.count(sig[i+GetSize(s)]) == 0) s.append(sig[i+GetSize(s)]); log_warning("No driver for signal %s.\n", log_signal(s)); 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); add_nid_sig(nid, s); for (int j = 0; j < GetSize(s); j++) nidbits.push_back(make_pair(nid, j)); i += GetSize(s)-1; continue; } else { export_cell(bit_cell.at(bit)); log_assert(bit_nid.count(bit)); } } } nidbits.push_back(bit_nid.at(bit)); } int width = 0; int nid = -1; // group bits and emit slice-concat chain for (int i = 0; i < GetSize(nidbits); i++) { int nid2 = nidbits[i].first; int lower = nidbits[i].second; int upper = lower; while (i+1 < GetSize(nidbits) && nidbits[i+1].first == nidbits[i].first && nidbits[i+1].second == nidbits[i].second+1) upper++, i++; int nid3 = nid2; if (lower != 0 || upper+1 != nid_width.at(nid2)) { int sid = get_bv_sid(upper-lower+1); nid3 = next_nid++; btorf("%d slice %d %d %d %d\n", nid3, sid, nid2, upper, lower); } int nid4 = nid3; if (nid >= 0) { int sid = get_bv_sid(width+upper-lower+1); nid4 = next_nid++; btorf("%d concat %d %d %d\n", nid4, sid, nid3, nid); } width += upper-lower+1; nid = nid4; } sig_nid[sig] = nid; nid_width[nid] = width; } nid = sig_nid.at(sig); extend_or_trim: if (to_width >= 0 && to_width != GetSize(sig)) { if (to_width < GetSize(sig)) { int sid = get_bv_sid(to_width); int nid2 = next_nid++; btorf("%d slice %d %d %d 0\n", nid2, sid, nid, to_width-1); nid = nid2; } else { int sid = get_bv_sid(to_width); int nid2 = next_nid++; btorf("%d %s %d %d %d\n", nid2, is_signed ? "sext" : "uext", sid, nid, to_width - GetSize(sig)); nid = nid2; } } return nid; } 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) { mem.narrow(); mem_dict[mem.memid] = &mem; } for (auto cell : module->cells()) 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)) { Const attrval = wire->attributes.at(ID::init); for (int i = 0; i < GetSize(wire) && i < GetSize(attrval); i++) if (attrval[i] == State::S0 || attrval[i] == State::S1) initbits[sigmap(SigBit(wire, i))] = (attrval[i] == State::S1); } if (!wire->port_id || !wire->port_input) continue; SigSpec sig = sigmap(wire); int sid = get_bv_sid(GetSize(sig)); 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"); for (auto cell : module->cells()) for (auto &conn : cell->connections()) { if (!cell->output(conn.first)) continue; for (auto bit : sigmap(conn.second)) bit_cell[bit] = cell; } for (auto wire : module->wires()) { if (!wire->port_id || !wire->port_output) continue; btorf_push(stringf("output %s", log_id(wire))); int nid = get_sig_nid(wire); btorf("%d output %d%s\n", next_nid++, nid, getinfo(wire).c_str()); btorf_pop(stringf("output %s", log_id(wire))); } for (auto cell : module->cells()) { if (cell->type == ID($assume)) { btorf_push(log_id(cell)); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_en = get_sig_nid(cell->getPort(ID::EN)); int nid_not_en = next_nid++; int nid_a_or_not_en = next_nid++; int nid = next_nid++; btorf("%d not %d %d\n", nid_not_en, sid, nid_en); btorf("%d or %d %d %d\n", nid_a_or_not_en, sid, nid_a, nid_not_en); btorf("%d constraint %d\n", nid, nid_a_or_not_en); btorf_pop(log_id(cell)); } if (cell->type == ID($assert)) { btorf_push(log_id(cell)); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_en = get_sig_nid(cell->getPort(ID::EN)); int nid_not_a = next_nid++; int nid_en_and_not_a = next_nid++; btorf("%d not %d %d\n", nid_not_a, sid, nid_a); btorf("%d and %d %d %d\n", nid_en_and_not_a, sid, nid_en, nid_not_a); if (single_bad && !cover_mode) { bad_properties.push_back(nid_en_and_not_a); } else { if (cover_mode) { infof("bad %d%s\n", nid_en_and_not_a, getinfo(cell, true).c_str()); } else { int nid = next_nid++; btorf("%d bad %d%s\n", nid, nid_en_and_not_a, getinfo(cell, true).c_str()); } } btorf_pop(log_id(cell)); } if (cell->type == ID($cover) && cover_mode) { btorf_push(log_id(cell)); int sid = get_bv_sid(1); int nid_a = get_sig_nid(cell->getPort(ID::A)); int nid_en = get_sig_nid(cell->getPort(ID::EN)); int nid_en_and_a = next_nid++; btorf("%d and %d %d %d\n", nid_en_and_a, sid, nid_en, nid_a); if (single_bad) { bad_properties.push_back(nid_en_and_a); } else { int nid = next_nid++; btorf("%d bad %d%s\n", nid, nid_en_and_a, getinfo(cell, true).c_str()); } btorf_pop(log_id(cell)); } } for (auto wire : module->wires()) { if (wire->port_id || wire->name[0] == '$') continue; btorf_push(stringf("wire %s", log_id(wire))); int sid = get_bv_sid(GetSize(wire)); int nid = get_sig_nid(sigmap(wire)); if (statewires.count(wire)) continue; 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; } while (!ff_todo.empty() || !mem_todo.empty()) { vector> todo; todo.swap(ff_todo); for (auto &it : todo) { int nid = it.first; Cell *cell = it.second; btorf_push(stringf("next %s", log_id(cell))); SigSpec sig = sigmap(cell->getPort(ID::D)); int nid_q = get_sig_nid(sig); int sid = get_bv_sid(GetSize(sig)); btorf("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell).c_str()); btorf_pop(stringf("next %s", log_id(cell))); } vector> mtodo; mtodo.swap(mem_todo); for (auto &it : mtodo) { int nid = it.first; Mem *mem = it.second; btorf_push(stringf("next %s", log_id(mem->memid))); int abits = ceil_log2(mem->size); int data_sid = get_bv_sid(mem->width); int bool_sid = get_bv_sid(1); int sid = get_mem_sid(abits, mem->width); int nid_head = nid; for (auto &port : mem->wr_ports) { SigSpec wa = port.addr; wa.extend_u0(abits); int wa_nid = get_sig_nid(wa); int wd_nid = get_sig_nid(port.data); int we_nid = get_sig_nid(port.en); int nid2 = next_nid++; btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid); int nid3 = next_nid++; btorf("%d not %d %d\n", nid3, data_sid, we_nid); int nid4 = next_nid++; btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3); int nid5 = next_nid++; btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid); int nid6 = next_nid++; btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4); int nid7 = next_nid++; btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6); int nid8 = next_nid++; btorf("%d redor %d %d\n", nid8, bool_sid, we_nid); int nid9 = next_nid++; btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head); nid_head = nid9; } int nid2 = next_nid++; btorf("%d next %d %d %d%s\n", nid2, sid, nid, nid_head, (mem->cell ? getinfo(mem->cell) : getinfo(mem->mem)).c_str()); btorf_pop(stringf("next %s", log_id(mem->memid))); } } while (!bad_properties.empty()) { vector todo; bad_properties.swap(todo); int sid = get_bv_sid(1); int cursor = 0; while (cursor+1 < GetSize(todo)) { int nid_a = todo[cursor++]; int nid_b = todo[cursor++]; int nid = next_nid++; bad_properties.push_back(nid); btorf("%d or %d %d %d\n", nid, sid, nid_a, nid_b); } if (!bad_properties.empty()) { if (cursor < GetSize(todo)) bad_properties.push_back(todo[cursor++]); log_assert(cursor == GetSize(todo)); } else { int nid = next_nid++; log_assert(cursor == 0); log_assert(GetSize(todo) == 1); btorf("%d bad %d\n", nid, todo[cursor]); } } if (!info_filename.empty()) { for (auto &it : info_clocks) { switch (it.second) { case 1: infof("posedge %d\n", it.first); break; case 2: infof("negedge %d\n", it.first); break; case 3: infof("event %d\n", it.first); break; default: log_abort(); } } std::ofstream f; f.open(info_filename.c_str(), std::ofstream::trunc); if (f.fail()) log_error("Can't open file `%s' for writing: %s\n", info_filename.c_str(), strerror(errno)); for (auto &it : info_lines) 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(); } } }; struct BtorBackend : public Backend { BtorBackend() : Backend("btor", "write design to BTOR file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_btor [options] [filename]\n"); log("\n"); log("Write a BTOR description of the current design.\n"); log("\n"); log(" -v\n"); log(" Add comments and indentation to BTOR output file\n"); log("\n"); log(" -s\n"); log(" Output only a single bad property for all asserts\n"); log("\n"); log(" -c\n"); log(" Output cover properties using 'bad' statements instead of asserts\n"); log("\n"); log(" -i \n"); log(" Create additional info file with auxiliary information\n"); log("\n"); 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++) { if (args[argidx] == "-v") { verbose = true; continue; } if (args[argidx] == "-s") { single_bad = true; continue; } if (args[argidx] == "-c") { cover_mode = true; continue; } if (args[argidx] == "-i" && argidx+1 < args.size()) { info_filename = args[++argidx]; continue; } if (args[argidx] == "-x") { 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); RTLIL::Module *topmod = design->top_module(); if (topmod == nullptr) log_cmd_error("No top module found.\n"); *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, ywmap_filename); *f << stringf("; end of yosys output\n"); } } BtorBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/btor/test_cells.sh000077500000000000000000000011631477540374200200640ustar00rootroot00000000000000#!/usr/bin/env bash set -ex rm -rf test_cells.tmp mkdir -p test_cells.tmp cd test_cells.tmp ../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor /$shiftx' for fn in test_*.il; do ../../../yosys -p " read_rtlil $fn rename gold gate synth read_rtlil $fn miter -equiv -make_assert -flatten gold gate main hierarchy -top main write_btor ${fn%.il}.btor " btormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out if grep " SATISFIABLE" ${fn%.il}.out; then echo "Check failed for ${fn%.il}." exit 1 fi done echo "OK." yosys-0.52/backends/cxxrtl/000077500000000000000000000000001477540374200157415ustar00rootroot00000000000000yosys-0.52/backends/cxxrtl/Makefile.inc000066400000000000000000000012651477540374200201550ustar00rootroot00000000000000 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)) yosys-0.52/backends/cxxrtl/cxxrtl_backend.cc000066400000000000000000004136171477540374200212570ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2019-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 "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/utils.h" #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 // [[CITE]] // Peter Eades; Xuemin Lin; W. F. Smyth, "A Fast Effective Heuristic For The Feedback Arc Set Problem" // Information Processing Letters, Vol. 47, pp 319-323, 1993 // https://pdfs.semanticscholar.org/c7ed/d9acce96ca357876540e19664eb9d976637f.pdf // A topological sort (on a cell/wire graph) is always possible in a fully flattened RTLIL design without // processes or logic loops where every wire has a single driver. Logic loops are illegal in RTLIL and wires // with multiple drivers can be split by the `splitnets` pass; however, interdependencies between processes // or module instances can create strongly connected components without introducing evaluation nondeterminism. // We wish to support designs with such benign SCCs (as well as designs with multiple drivers per wire), so // we sort the graph in a way that minimizes feedback arcs. If there are no feedback arcs in the sorted graph, // then a more efficient evaluation method is possible, since eval() will always immediately converge. template struct Scheduler { struct Vertex { T *data; Vertex *prev, *next; pool preds, succs; Vertex() : data(NULL), prev(this), next(this) {} Vertex(T *data) : data(data), prev(NULL), next(NULL) {} bool empty() const { log_assert(data == NULL); if (next == this) { log_assert(prev == next); return true; } return false; } void link(Vertex *list) { log_assert(prev == NULL && next == NULL); next = list; prev = list->prev; list->prev->next = this; list->prev = this; } void unlink() { log_assert(prev->next == this && next->prev == this); prev->next = next; next->prev = prev; next = prev = NULL; } int delta() const { return succs.size() - preds.size(); } }; std::vector vertices; Vertex *sources = new Vertex; Vertex *sinks = new Vertex; dict bins; ~Scheduler() { delete sources; delete sinks; for (auto bin : bins) delete bin.second; for (auto vertex : vertices) delete vertex; } Vertex *add(T *data) { Vertex *vertex = new Vertex(data); vertices.push_back(vertex); return vertex; } void relink(Vertex *vertex) { if (vertex->succs.empty()) vertex->link(sinks); else if (vertex->preds.empty()) vertex->link(sources); else { int delta = vertex->delta(); if (!bins.count(delta)) bins[delta] = new Vertex; vertex->link(bins[delta]); } } Vertex *remove(Vertex *vertex) { vertex->unlink(); for (auto pred : vertex->preds) { if (pred == vertex) continue; log_assert(pred->succs[vertex]); pred->unlink(); pred->succs.erase(vertex); relink(pred); } for (auto succ : vertex->succs) { if (succ == vertex) continue; log_assert(succ->preds[vertex]); succ->unlink(); succ->preds.erase(vertex); relink(succ); } vertex->preds.clear(); vertex->succs.clear(); return vertex; } std::vector schedule() { std::vector s1, s2r; for (auto vertex : vertices) relink(vertex); bool bins_empty = false; while (!(sinks->empty() && sources->empty() && bins_empty)) { while (!sinks->empty()) s2r.push_back(remove(sinks->next)); while (!sources->empty()) s1.push_back(remove(sources->next)); // Choosing u in this implementation isn't O(1), but the paper handwaves which data structure they suggest // using to get O(1) relinking *and* find-max-key ("it is clear"... no it isn't), so this code uses a very // naive implementation of find-max-key. bins_empty = true; bins.template sort>(); for (auto bin : bins) { if (!bin.second->empty()) { bins_empty = false; s1.push_back(remove(bin.second->next)); break; } } } s1.insert(s1.end(), s2r.rbegin(), s2r.rend()); return s1; } }; bool is_unary_cell(RTLIL::IdString type) { return type.in( ID($not), ID($logic_not), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($pos), ID($neg)); } bool is_binary_cell(RTLIL::IdString type) { return type.in( 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($modfloor), ID($divfloor)); } bool is_extending_cell(RTLIL::IdString type) { return !type.in( ID($logic_not), ID($logic_and), ID($logic_or), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool)); } 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($bmux), ID($demux)); } 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.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) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); log_assert(cell_module != nullptr); 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(RTLIL::Module *module, RTLIL::IdString port) { 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(module), log_signal(output_wire)); else if (is_comb) return CxxrtlPortType::COMB; else if (is_sync) return CxxrtlPortType::SYNC; 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; } bool is_cxxrtl_sync_port(const RTLIL::Cell *cell, RTLIL::IdString port) { return cxxrtl_port_type(cell, port) == CxxrtlPortType::SYNC; } struct FlowGraph { struct Node { enum class Type { CONNECT, CELL_SYNC, CELL_EVAL, EFFECT_SYNC, PROCESS_SYNC, PROCESS_CASE, MEM_RDPORT, MEM_WRPORTS, }; Type type; RTLIL::SigSig connect = {}; 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> node_comb_defs, node_sync_defs, node_uses; dict wire_def_inlinable; dict> wire_use_inlinable; dict bit_has_state; ~FlowGraph() { for (auto node : nodes) delete node; } void add_defs(Node *node, const RTLIL::SigSpec &sig, bool is_ff, bool inlinable) { for (auto chunk : sig.chunks()) if (chunk.wire) { if (is_ff) { // 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) bit_has_state[bit] |= is_ff; // 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) { for (auto chunk : sig.chunks()) if (chunk.wire) { wire_uses[chunk.wire].insert(node); 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_inlinable[chunk.wire][node] = false; } } 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 { // 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, /*inlinable=*/true); add_uses(node, conn.second); } Node *add_node(const RTLIL::SigSig &conn) { Node *node = new Node; node->type = Node::Type::CONNECT; node->connect = conn; nodes.push_back(node); add_connect_defs_uses(node, conn); return node; } // Cells void add_cell_sync_defs(Node *node, const RTLIL::Cell *cell) { // To understand why this node type is necessary and why it produces comb defs, consider a cell // with input \i and sync output \o, used in a design such that \i is connected to \o. This does // not result in a feedback arc because the output is synchronous. However, a naive implementation // of code generation for cells that assigns to inputs, evaluates cells, assigns from outputs // would not be able to immediately converge... // // wire<1> i_tmp; // cell->p_i = i_tmp.curr; // cell->eval(); // i_tmp.next = cell->p_o.curr; // // ... since the wire connecting the input and output ports would not be localizable. To solve // this, the cell is split into two scheduling nodes; one exclusively for sync outputs, and // another for inputs and all non-sync outputs. This way the generated code can be rearranged... // // value<1> i_tmp; // i_tmp = cell->p_o.curr; // cell->p_i = i_tmp; // cell->eval(); // // eliminating the unnecessary delta cycle. Conceptually, the CELL_SYNC node type is a series of // connections of the form `connect \lhs \cell.\sync_output`; the right-hand side of these is not // expressible as a wire in RTLIL. If it was expressible, then `\cell.\sync_output` would have // a sync def, and this node would be an ordinary CONNECT node, with `\lhs` having a comb def. // Because it isn't, a special node type is used, the right-hand side does not appear anywhere, // and the left-hand side has a comb def. for (auto conn : cell->connections()) if (cell->output(conn.first)) if (is_cxxrtl_sync_port(cell, conn.first)) { // See note regarding inlinability below. add_defs(node, conn.second, /*is_ff=*/false, /*inlinable=*/false); } } void add_cell_eval_defs_uses(Node *node, const RTLIL::Cell *cell) { for (auto conn : cell->connections()) { if (cell->output(conn.first)) { 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, /*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 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 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)) add_uses(node, conn.second); } } Node *add_node(const RTLIL::Cell *cell) { log_assert(cell->known()); bool has_fully_sync_outputs = false; for (auto conn : cell->connections()) if (cell->output(conn.first) && is_cxxrtl_sync_port(cell, conn.first)) { has_fully_sync_outputs = true; break; } if (has_fully_sync_outputs) { Node *node = new Node; node->type = Node::Type::CELL_SYNC; node->cell = cell; nodes.push_back(node); add_cell_sync_defs(node, cell); } Node *node = new Node; node->type = Node::Type::CELL_EVAL; node->cell = cell; nodes.push_back(node); add_cell_eval_defs_uses(node, cell); 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_rule_defs_uses(Node *node, const RTLIL::CaseRule *case_) { for (auto &action : case_->actions) { add_defs(node, action.first, /*is_ff=*/false, /*inlinable=*/false); add_uses(node, action.second); } for (auto sub_switch : case_->switches) { add_uses(node, sub_switch->signal); for (auto sub_case : sub_switch->cases) { for (auto &compare : sub_case->compare) add_uses(node, compare); add_case_rule_defs_uses(node, sub_case); } } } void add_sync_rules_defs_uses(Node *node, const RTLIL::Process *process) { 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, /*inlinable=*/false); else 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_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_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) { std::vector result; size_t prev = 0; while (true) { size_t curr = str.find_first_of(sep, prev); if (curr == std::string::npos) { std::string part = str.substr(prev); if (!part.empty()) result.push_back(part); break; } else { std::string part = str.substr(prev, curr - prev); if (!part.empty()) result.push_back(part); prev = curr + 1; } } return result; } std::string escape_c_string(const std::string &input) { std::string output; output.push_back('"'); for (auto c : input) { if (::isprint(c)) { if (c == '\\' || c == '"') output.push_back('\\'); output.push_back(c); } else { char l = c & 0x7, m = (c >> 3) & 0x7, h = (c >> 6) & 0x3; output.append("\\"); output.push_back('0' + h); output.push_back('0' + m); output.push_back('0' + l); } } output.push_back('"'); return output; } std::string escape_cxx_string(const std::string &input) { std::string output = escape_c_string(input); if (output.find('\0') != std::string::npos) { output.insert(0, "std::string {"); output.append(stringf(", %zu}", input.size())); } 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) { if (object->has_attribute(ID::hdlname)) return object->get_string_attribute(ID::hdlname); else 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; bool unbuffer_internal = false; bool unbuffer_public = false; bool localize_internal = false; bool localize_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; dict> schedule, debug_schedule; dict wire_types, debug_wire_types; dict bit_has_state; dict> blackbox_specializations; dict eval_converges; void inc_indent() { indent += "\t"; } void dec_indent() { indent.resize(indent.size() - 1); } // RTLIL allows any characters in names other than whitespace. This presents an issue for generating C++ code // because C++ identifiers may be only alphanumeric, cannot clash with C++ keywords, and cannot clash with cxxrtl // identifiers. This issue can be solved with a name mangling scheme. We choose a name mangling scheme that results // in readable identifiers, does not depend on an up-to-date list of C++ keywords, and is easy to apply. Its rules: // 1. All generated identifiers start with `_`. // 1a. Generated identifiers for public names (beginning with `\`) start with `p_`. // 1b. Generated identifiers for internal names (beginning with `$`) start with `i_`. // 2. An underscore is escaped with another underscore, i.e. `__`. // 3. Any other non-alnum character is escaped with underscores around its lowercase hex code, e.g. `@` as `_40_`. std::string mangle_name(const RTLIL::IdString &name) { std::string mangled; bool first = true; for (char c : name.str()) { if (first) { first = false; if (c == '\\') mangled += "p_"; else if (c == '$') mangled += "i_"; else log_assert(false); } else { if (isalnum(c)) { mangled += c; } else if (c == '_') { mangled += "__"; } else { char l = c & 0xf, h = (c >> 4) & 0xf; mangled += '_'; mangled += (h < 10 ? '0' + h : 'a' + h - 10); mangled += (l < 10 ? '0' + l : 'a' + l - 10); mangled += '_'; } } } return mangled; } std::string mangle_module_name(const RTLIL::IdString &name, bool is_blackbox = false) { // Class namespace. if (is_blackbox) return "bb_" + mangle_name(name); return mangle_name(name); } std::string mangle_memory_name(const RTLIL::IdString &name) { // Class member namespace. return "memory_" + mangle_name(name); } std::string mangle_cell_name(const RTLIL::IdString &name) { // Class member namespace. return "cell_" + mangle_name(name); } std::string mangle_wire_name(const RTLIL::IdString &name) { // Class member namespace. return mangle_name(name); } std::string mangle(const RTLIL::Module *module) { 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); } std::string mangle(const RTLIL::Cell *cell) { return mangle_cell_name(cell->name); } std::string mangle(const RTLIL::Wire *wire) { return mangle_wire_name(wire->name); } std::string mangle(RTLIL::SigBit sigbit) { log_assert(sigbit.wire != NULL); if (sigbit.wire->width == 1) return mangle(sigbit.wire); return mangle(sigbit.wire) + "_" + std::to_string(sigbit.offset); } std::vector template_param_names(const RTLIL::Module *module) { if (!module->has_attribute(ID(cxxrtl_template))) return {}; if (!(module->attributes.at(ID(cxxrtl_template)).flags & RTLIL::CONST_FLAG_STRING)) log_cmd_error("Attribute `cxxrtl_template' of module `%s' is not a string.\n", log_id(module)); std::vector param_names = split_by(module->get_string_attribute(ID(cxxrtl_template)), " \t"); for (const auto ¶m_name : param_names) { // Various lowercase prefixes (p_, i_, cell_, ...) are used for member variables, so require // parameters to start with an uppercase letter to avoid name conflicts. (This is the convention // in both Verilog and C++, anyway.) if (!isupper(param_name[0])) log_cmd_error("Attribute `cxxrtl_template' of module `%s' includes a parameter `%s', " "which does not start with an uppercase letter.\n", log_id(module), param_name.c_str()); } return param_names; } std::string template_params(const RTLIL::Module *module, bool is_decl) { std::vector param_names = template_param_names(module); if (param_names.empty()) return ""; std::string params = "<"; bool first = true; for (const auto ¶m_name : param_names) { if (!first) params += ", "; first = false; if (is_decl) params += "size_t "; params += param_name; } params += ">"; return params; } std::string template_args(const RTLIL::Cell *cell) { RTLIL::Module *cell_module = cell->module->design->module(cell->type); log_assert(cell_module != nullptr); if (!cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) return ""; std::vector param_names = template_param_names(cell_module); if (param_names.empty()) return ""; std::string params = "<"; bool first = true; for (const auto ¶m_name : param_names) { if (!first) params += ", "; first = false; params += "/*" + param_name + "=*/"; RTLIL::IdString id_param_name = '\\' + param_name; if (!cell->hasParam(id_param_name)) log_cmd_error("Cell `%s.%s' does not have a parameter `%s', which is required by the templated module `%s'.\n", log_id(cell->module), log_id(cell), param_name.c_str(), log_id(cell_module)); RTLIL::Const param_value = cell->getParam(id_param_name); if (((param_value.flags & ~RTLIL::CONST_FLAG_SIGNED) != 0) || param_value.as_int() < 0) log_cmd_error("Parameter `%s' of cell `%s.%s', which is required by the templated module `%s', " "is not a positive integer.\n", param_name.c_str(), log_id(cell->module), log_id(cell), log_id(cell_module)); params += std::to_string(cell->getParam(id_param_name).as_int()); } params += ">"; return params; } std::string fresh_temporary() { return stringf("tmp_%d", temporary++); } void dump_attrs(const RTLIL::AttrObject *object) { for (auto attr : object->attributes) { f << indent << "// " << attr.first.str() << ": "; if (attr.second.flags & RTLIL::CONST_FLAG_STRING) { f << attr.second.decode_string(); } else { f << attr.second.as_int(/*is_signed=*/attr.second.flags & RTLIL::CONST_FLAG_SIGNED); } f << "\n"; } } void dump_const_init(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false) { const int CHUNK_SIZE = 32; f << "{"; while (width > 0) { int chunk_width = min(width, CHUNK_SIZE); uint32_t chunk = data.extract(offset, chunk_width).as_int(); if (fixed_width) f << stringf("0x%.*xu", (3 + chunk_width) / 4, chunk); else f << stringf("%#xu", chunk); if (width > CHUNK_SIZE) f << ','; offset += CHUNK_SIZE; width -= CHUNK_SIZE; } f << "}"; } void dump_const(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false) { f << "value<" << width << ">"; dump_const_init(data, width, offset, fixed_width); } void dump_const(const RTLIL::Const &data) { dump_const(data, data.size()); } 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 { 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; } 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; else if (chunk.width == 1) f << ".slice<" << chunk.offset << ">()"; else f << ".slice<" << chunk.offset+chunk.width-1 << "," << chunk.offset << ">()"; return true; } } 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, for_debug); } else { 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, bool for_debug = false) { dump_sigspec(sig, /*is_lhs=*/true, for_debug); } 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, for_debug); if (is_complex) f << ".val()"; } 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) continue; 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: break; } } } void dump_connect_expr(const RTLIL::SigSig &conn, bool for_debug = false) { dump_sigspec_rhs(conn.second, for_debug); } void dump_connect(const RTLIL::SigSig &conn, bool for_debug = false) { std::vector inlined_cells; collect_sigspec_rhs(conn.second, for_debug, inlined_cells); dump_inlined_cells(inlined_cells); f << indent; dump_sigspec_lhs(conn.first, for_debug); f << " = "; dump_connect_expr(conn, for_debug); f << ";\n"; } 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"; for (auto conn : cell->connections()) if (cell->output(conn.first)) if (is_cxxrtl_sync_port(cell, conn.first) && !conn.second.empty()) { f << indent; dump_sigspec_lhs(conn.second, for_debug); f << " = " << mangle(cell) << access << mangle_wire_name(conn.first) << ".curr;\n"; } } void dump_cell_expr(const RTLIL::Cell *cell, bool for_debug = false) { // Unary cells if (is_unary_cell(cell->type)) { f << cell->type.substr(1); 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), for_debug); f << ")"; // Binary cells } else if (is_binary_cell(cell->type)) { f << cell->type.substr(1); if (is_extending_cell(cell->type)) 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), for_debug); f << ", "; 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), for_debug); f << " ? "; dump_sigspec_rhs(cell->getPort(ID::B), for_debug); f << " : "; dump_sigspec_rhs(cell->getPort(ID::A), for_debug); f << ")"; // Parallel (one-hot) muxes } else if (cell->type == ID($pmux)) { int width = cell->getParam(ID::WIDTH).as_int(); 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), for_debug); f << " ? "; dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width), for_debug); f << " : "; } 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), for_debug); f << ".concat("; 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), for_debug); f << ".slice<"; f << cell->getParam(ID::OFFSET).as_int() + cell->getParam(ID::Y_WIDTH).as_int() - 1; f << ","; f << cell->getParam(ID::OFFSET).as_int(); f << ">().val()"; } else { log_assert(false); } } void dump_print(const RTLIL::Cell *cell) { 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 dump_effect(const RTLIL::Cell *cell) { Fmt fmt; fmt.parse_rtlil(cell); 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, bool for_debug = false) { std::vector inlined_cells; collect_cell_eval(cell, for_debug, inlined_cells); dump_inlined_cells(inlined_cells); // Elidable cells if (is_inlinable_cell(cell->type)) { f << indent; dump_sigspec_lhs(cell->getPort(ID::Y), for_debug); f << " = "; 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)) { 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); 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 ("; dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n"; inc_indent(); } f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_sigspec_rhs(cell->getPort(ID::D)); f << ";\n"; if (cell->hasPort(ID::EN) && cell->type != ID($sdffce)) { dec_indent(); f << indent << "}\n"; } if (cell->hasPort(ID::SRST)) { f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::SRST)); f << " == value<1> {" << cell->getParam(ID::SRST_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_const(cell->getParam(ID::SRST_VALUE)); f << ";\n"; dec_indent(); f << indent << "}\n"; } if (cell->hasPort(ID::EN) && cell->type == ID($sdffce)) { dec_indent(); f << indent << "}\n"; } dec_indent(); f << indent << "}\n"; } else if (cell->hasPort(ID::EN)) { // Level-sensitive logic f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::EN)); f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_sigspec_rhs(cell->getPort(ID::D)); f << ";\n"; dec_indent(); f << indent << "}\n"; } if (cell->hasPort(ID::ARST)) { // Asynchronous reset (entire coarse cell at once) f << indent << "if ("; dump_sigspec_rhs(cell->getPort(ID::ARST)); f << " == value<1> {" << cell->getParam(ID::ARST_POLARITY).as_bool() << "u}) {\n"; inc_indent(); f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; dump_const(cell->getParam(ID::ARST_VALUE)); f << ";\n"; 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_rhs(cell->getPort(ID::Q)); f << ".update("; dump_const(RTLIL::Const(RTLIL::S1, cell->getParam(ID::WIDTH).as_int())); f << ", "; dump_sigspec_rhs(cell->getPort(ID::SET)); f << (cell->getParam(ID::SET_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n"; } if (cell->hasPort(ID::CLR)) { // Asynchronous clear (for individual bits; priority over set) f << indent; dump_sigspec_lhs(cell->getPort(ID::Q)); f << " = "; 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"; } // 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)) { 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") && 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: // top.p_clk = value<1>{0u}; top.step(); // top.p_clk = value<1>{1u}; top.step(); // 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. 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"; } } } auto assign_from_outputs = [&](bool cell_converged) { for (auto conn : cell->connections()) { if (cell->output(conn.first)) { if (conn.second.empty()) continue; // ignore disconnected ports if (is_cxxrtl_sync_port(cell, conn.first)) continue; // fully sync ports are handled in CELL_SYNC nodes f << indent; dump_sigspec_lhs(conn.second); f << " = " << mangle(cell) << access << mangle_wire_name(conn.first); // Similarly to how there is no purpose to buffering cell inputs, there is also no purpose to buffering // combinatorial cell outputs in case the cell converges within one cycle. (To convince yourself that // this optimization is valid, consider that, since the cell converged within one cycle, it would not // 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.) // // 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 f << ".curr;\n"; } } }; 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); } 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 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, for_debug); f << " = "; dump_sigspec_rhs(sigsig.second, for_debug); f << ";\n"; } void dump_case_rule(const RTLIL::CaseRule *rule, bool for_debug = false) { for (auto action : rule->actions) dump_assign(action, for_debug); for (auto switch_ : rule->switches) dump_switch_rule(switch_, for_debug); } 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, for_debug); f << ";\n"; bool first = true; for (auto case_ : rule->cases) { // The case attributes (for nested cases) are printed before the if/else if/else statement. dump_attrs(rule); f << indent; if (!first) f << "} else "; first = false; if (!case_->compare.empty()) { f << "if ("; bool first = true; for (auto &compare : case_->compare) { if (!first) f << " || "; first = false; if (compare.is_fully_def()) { f << signal_temp << " == "; 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()) { switch (bit) { case RTLIL::S0: case RTLIL::S1: compare_mask.bits().push_back(RTLIL::S1); compare_value.bits().push_back(bit); break; case RTLIL::Sx: case RTLIL::Sz: case RTLIL::Sa: compare_mask.bits().push_back(RTLIL::S0); compare_value.bits().push_back(RTLIL::S0); break; default: log_assert(false); } } f << "and_uu<" << compare.size() << ">(" << signal_temp << ", "; dump_const(compare_mask); f << ") == "; dump_const(compare_value); } else { log_assert(false); } } f << ") "; } f << "{\n"; inc_indent(); dump_case_rule(case_, for_debug); dec_indent(); } f << indent << "}\n"; } void dump_process_case(const RTLIL::Process *proc, bool for_debug = false) { dump_attrs(proc); 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, 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; switch (sync->type) { case RTLIL::STp: log_assert(sync_bit.wire != nullptr); events.insert("posedge_" + mangle(sync_bit)); break; case RTLIL::STn: log_assert(sync_bit.wire != nullptr); events.insert("negedge_" + mangle(sync_bit)); break; case RTLIL::STe: log_assert(sync_bit.wire != nullptr); events.insert("posedge_" + mangle(sync_bit)); events.insert("negedge_" + mangle(sync_bit)); break; case RTLIL::STa: events.insert("true"); break; case RTLIL::ST0: case RTLIL::ST1: case RTLIL::STg: case RTLIL::STi: log_assert(false); } if (!events.empty()) { f << indent << "if ("; bool first = true; for (auto &event : events) { if (!first) f << " || "; first = false; f << event; } f << ") {\n"; inc_indent(); 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_cell_effect_sync(std::vector &cells) { log_assert(!cells.empty()); const auto &trg = cells[0]->getPort(ID::TRG); const auto &trg_polarity = cells[0]->getParam(ID::TRG_POLARITY); 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(); 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"; } 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 (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_mem_wrports(const Mem *mem, bool for_debug = false) { 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"; } } } 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(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 << "<" << 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 (int n = 0; n < words; n++) { if (n % 4 == 0) f << "\n" << indent; else f << " "; dump_const(init.data, mem.width, n * mem.width, /*fixed_width=*/true); f << ","; } dec_indent(); 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"; } } 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) { inc_indent(); f << indent << "bool converged = " << (eval_converges.at(module) ? "true" : "false") << ";\n"; if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { for (auto wire : module->wires()) { if (edge_wires[wire]) { for (auto edge_type : edge_types) { if (edge_type.first.wire == wire) { if (edge_type.second != RTLIL::STn) { f << indent << "bool posedge_" << mangle(edge_type.first) << " = "; f << "this->posedge_" << mangle(edge_type.first) << "();\n"; } if (edge_type.second != RTLIL::STp) { f << indent << "bool negedge_" << mangle(edge_type.first) << " = "; f << "this->negedge_" << mangle(edge_type.first) << "();\n"; } } } } } for (auto wire : module->wires()) dump_wire(wire, /*is_local=*/true); for (auto node : schedule[module]) { switch (node.type) { case FlowGraph::Node::Type::CONNECT: dump_connect(node.connect); break; case FlowGraph::Node::Type::CELL_SYNC: dump_cell_sync(node.cell); break; case FlowGraph::Node::Type::CELL_EVAL: dump_cell_eval(node.cell); break; 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; } } } f << indent << "return converged;\n"; 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()) { 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 &mem : mod_memories[module]) { if (!writable_memories.count({module, mem.memid})) continue; 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 << "if (" << mangle(cell) << access << "commit(observer)) changed = true;\n"; } } f << indent << "return changed;\n"; dec_indent(); } void dump_serialized_metadata(const dict &metadata_map) { // Creating thousands metadata_map objects using initializer lists in a single function results in one of: // 1. Megabytes of stack usage (with __attribute__((optnone))). // 2. Minutes of compile time (without __attribute__((optnone))). // So, don't create them. std::string data; auto put_u64 = [&](uint64_t value) { for (size_t count = 0; count < 8; count++) { data += (char)(value >> 56); value <<= 8; } }; 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; } data += metadata_item.first.str().substr(1) + '\0'; // In Yosys, a real is a type of string. if (metadata_item.second.flags & RTLIL::CONST_FLAG_REAL) { double dvalue = std::stod(metadata_item.second.decode_string()); uint64_t uvalue; static_assert(sizeof(dvalue) == sizeof(uvalue), "double must be 64 bits in size"); memcpy(&uvalue, &dvalue, sizeof(uvalue)); data += 'd'; put_u64(uvalue); } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) { data += 's'; data += metadata_item.second.decode_string(); data += '\0'; } else if (metadata_item.second.flags & RTLIL::CONST_FLAG_SIGNED) { data += 'i'; put_u64((uint64_t)metadata_item.second.as_int(/*is_signed=*/true)); } else { data += 'u'; put_u64(metadata_item.second.as_int(/*is_signed=*/false)); } } f << escape_c_string(data); } void dump_metadata_map(const dict &metadata_map) { if (metadata_map.empty()) { f << "metadata_map()"; } else { 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, bool serialize = true) { 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); if (serialize) { dump_serialized_metadata(attributes); } else { 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_member_wires = 0; size_t count_undriven = 0; size_t count_driven_sync = 0; size_t count_driven_comb = 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"; 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, /*serialize=*/false); 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_serialized_metadata(module_attrs); f << ", "; dump_serialized_metadata(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)) << ", "; dump_debug_attrs(wire); f << ", " << mangle(wire); if (wire->start_offset != 0 || !flags.empty()) { f << ", " << wire->start_offset; bool first = true; for (auto flag : flags) { if (first) { first = false; f << ", "; } else { f << "|"; } f << "debug_item::" << flag; } } 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)) << ", "; dump_debug_attrs(wire); f << ", "; // 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); if (wire->start_offset != 0) f << ", " << wire->start_offset; 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)) << ", "; dump_debug_attrs(wire); f << ", const_" << mangle(wire); if (wire->start_offset != 0) f << ", " << wire->start_offset; 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)) << ", "; dump_debug_attrs(wire); f << ", debug_eval_outline, " << mangle(wire); if (wire->start_offset != 0) f << ", " << wire->start_offset; 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)) << ", "; if (mem.packed) { dump_debug_attrs(mem.cell); } else { dump_debug_attrs(mem.mem); } f << ", " << mangle(&mem) << ", "; f << mem.start_offset << ");\n"; } } dec_indent(); f << indent << "}\n"; if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) { 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; f << "debug_info(items, scopes, path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ", "; dump_debug_attrs(cell, /*serialize=*/false); 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(" 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(" Mixed driver: %zu\n", count_mixed_driver); 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)" : ""); } } void dump_module_intf(RTLIL::Module *module) { dump_attrs(module); if (module->get_bool_attribute(ID(cxxrtl_blackbox))) { if (module->has_attribute(ID(cxxrtl_template))) f << indent << "template" << template_params(module, /*is_decl=*/true) << "\n"; f << indent << "struct " << mangle(module) << " : public module {\n"; inc_indent(); for (auto wire : module->wires()) { if (wire->port_id != 0) dump_wire(wire, /*is_local=*/false); } f << "\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 << "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 << "\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 << 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"; dec_indent(); f << indent << "}; // struct " << mangle(module) << "\n"; f << "\n"; if (blackbox_specializations.count(module)) { // If templated black boxes are used, the constructor of any module which includes the black box cell // (which calls the declared but not defined in the generated code `create` function) may only be used // if (a) the create function is defined in the same translation unit, or (b) the create function has // a forward-declared explicit specialization. // // Option (b) makes it possible to have the generated code and the black box implementation in different // translation units, which is convenient. Of course, its downside is that black boxes must predefine // a specialization for every combination of parameters the generated code may use; but since the main // purpose of templated black boxes is abstracting over datapath width, it is expected that there would // be very few such combinations anyway. for (auto specialization : blackbox_specializations[module]) { f << indent << "template<>\n"; f << indent << "std::unique_ptr<" << mangle(module) << specialization << "> "; f << mangle(module) << specialization << "::"; f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n"; f << "\n"; } } } else { f << indent << "struct " << mangle(module) << " : public module {\n"; inc_indent(); for (auto wire : module->wires()) dump_wire(wire, /*is_local=*/false); for (auto wire : module->wires()) dump_debug_wire(wire, /*is_local=*/false); bool has_memories = false; 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); RTLIL::Module *cell_module = module->design->module(cell->type); log_assert(cell_module != nullptr); if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) { f << indent << "std::unique_ptr<" << mangle(cell_module) << template_args(cell) << "> "; f << mangle(cell) << " = " << mangle(cell_module) << template_args(cell); f << "::create(" << escape_cxx_string(get_hdl_name(cell)) << ", "; dump_metadata_map(cell->parameters); f << ", "; dump_metadata_map(cell->attributes); f << ");\n"; } else { f << indent << mangle(cell_module) << " " << mangle(cell) << " {interior()};\n"; } has_cells = true; } if (has_cells) f << "\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"; } } void dump_module_impl(RTLIL::Module *module) { if (module->get_bool_attribute(ID(cxxrtl_blackbox))) return; f << indent << "void " << mangle(module) << "::reset() {\n"; dump_reset_method(module); f << indent << "}\n"; f << "\n"; f << indent << "bool " << mangle(module) << "::eval(performer *performer) {\n"; dump_eval_method(module); f << indent << "}\n"; if (debug_info) { 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"; } void dump_design(RTLIL::Design *design) { RTLIL::Module *top_module = nullptr; std::vector modules; TopoSort topo_design; for (auto module : design->modules()) { if (!design->selected_module(module)) continue; if (module->get_bool_attribute(ID(cxxrtl_blackbox))) modules.push_back(module); // cxxrtl blackboxes first if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl_blackbox))) continue; if (module->get_bool_attribute(ID::top)) top_module = module; topo_design.node(module); for (auto cell : module->cells()) { if (is_internal_cell(cell->type) || is_cxxrtl_blackbox_cell(cell)) continue; RTLIL::Module *cell_module = design->module(cell->type); log_assert(cell_module != nullptr); topo_design.edge(cell_module, module); } } bool no_loops = topo_design.sort(); log_assert(no_loops); modules.insert(modules.end(), topo_design.sorted.begin(), topo_design.sorted.end()); if (split_intf) { // The only thing more depraved than include guards, is mangling filenames to turn them into include guards. std::string include_guard = design_ns + "_header"; std::transform(include_guard.begin(), include_guard.end(), include_guard.begin(), ::toupper); f << "#ifndef " << include_guard << "\n"; f << "#define " << include_guard << "\n"; f << "\n"; if (top_module != nullptr && debug_info) { f << "#include \n"; f << "\n"; f << "#ifdef __cplusplus\n"; f << "extern \"C\" {\n"; f << "#endif\n"; f << "\n"; f << "cxxrtl_toplevel " << design_ns << "_create();\n"; f << "\n"; f << "#ifdef __cplusplus\n"; f << "}\n"; f << "#endif\n"; f << "\n"; } else { f << "// The CXXRTL C API is not available because the design is built without debug information.\n"; f << "\n"; } f << "#ifdef __cplusplus\n"; f << "\n"; f << "#include \n"; f << "\n"; f << "using namespace cxxrtl;\n"; f << "\n"; f << "namespace " << design_ns << " {\n"; f << "\n"; for (auto module : modules) dump_module_intf(module); f << "} // namespace " << design_ns << "\n"; f << "\n"; f << "#endif // __cplusplus\n"; f << "\n"; f << "#endif\n"; *intf_f << f.str(); f.str(""); } if (split_intf) f << "#include \"" << basename(intf_filename) << "\"\n"; else 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 << "#endif\n"; f << "\n"; f << "#if defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n"; f << "#include \n"; f << "#endif\n"; f << "\n"; f << "using namespace cxxrtl_yosys;\n"; f << "\n"; f << "namespace " << design_ns << " {\n"; f << "\n"; for (auto module : modules) { if (!split_intf) dump_module_intf(module); dump_module_impl(module); } f << "} // namespace " << design_ns << "\n"; f << "\n"; if (top_module != nullptr && debug_info) { f << "extern \"C\"\n"; f << "cxxrtl_toplevel " << design_ns << "_create() {\n"; inc_indent(); std::string top_type = design_ns + "::" + mangle(top_module); f << indent << "return new _cxxrtl_toplevel { "; f << "std::unique_ptr<" << top_type << ">(new " + top_type + ")"; f << " };\n"; dec_indent(); f << "}\n"; } *impl_f << f.str(); f.str(""); } // Edge-type sync rules require us to emit edge detectors, which require coordination between // eval and commit phases. To do this we need to collect them upfront. // // Note that the simulator commit phase operates at wire granularity but edge-type sync rules // operate at wire bit granularity; it is possible to have code similar to: // wire [3:0] clocks; // always @(posedge clocks[0]) ... // To handle this we track edge sensitivity both for wires and wire bits. void register_edge_signal(SigMap &sigmap, RTLIL::SigSpec signal, RTLIL::SyncType type) { signal = sigmap(signal); 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]; if (!edge_types.count(sigbit)) edge_types[sigbit] = type; else if (edge_types[sigbit] != type) edge_types[sigbit] = RTLIL::STe; // 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) { bool has_feedback_arcs = false; bool has_buffered_comb_wires = false; for (auto module : design->modules()) { if (!design->selected_module(module)) continue; 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) { 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)) log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' is not a string with one character per bit.\n", log_id(module), log_signal(wire)); std::string edges = wire->get_string_attribute(ID(cxxrtl_edge)); for (int i = 0; i < GetSize(wire); i++) { RTLIL::SigSpec wire_sig = wire; switch (edges[i]) { case '-': break; case 'p': register_edge_signal(sigmap, wire_sig[i], RTLIL::STp); break; case 'n': register_edge_signal(sigmap, wire_sig[i], RTLIL::STn); break; case 'a': register_edge_signal(sigmap, wire_sig[i], RTLIL::STe); break; default: log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' contains specifiers " "other than '-', 'p', 'n', or 'a'.\n", log_id(module), log_signal(wire)); } } } } // Black boxes converge by default, since their implementations are quite unlikely to require // internal propagation of comb signals. eval_converges[module] = true; 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); 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() && !cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", log_id(cell->type)); if (cell_module && cell_module->get_bool_attribute(ID(cxxrtl_blackbox)) && cell_module->get_bool_attribute(ID(cxxrtl_template))) blackbox_specializations[cell_module].insert(template_args(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($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); } // 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); } } } 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.bits()[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) { switch (sync->type) { // Edge-type sync rules require pre-registration. case RTLIL::STp: case RTLIL::STn: case RTLIL::STe: register_edge_signal(sigmap, sync->signal, sync->type); break; // Level-type sync rules require no special handling. case RTLIL::ST0: case RTLIL::ST1: case RTLIL::STa: break; case RTLIL::STg: log_cmd_error("Global clock is not supported.\n"); // Handling of init-type sync rules is delegated to the `proc_init` pass, so we can use the wire // attribute regardless of input. case RTLIL::STi: log_assert(false); } for (auto &memwr : sync->mem_write_actions) { writable_memories.insert({module, memwr.memid}); } } } // 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*> node_vertex_map; for (auto node : flow.nodes) 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_vertex_map[succ_node]; vertex->succs.insert(succ_vertex); succ_vertex->preds.insert(vertex); } } // Find out whether the order includes any feedback arcs. std::vector node_order; pool evaluated_nodes; pool feedback_wires; for (auto vertex : scheduler.schedule()) { auto node = vertex->data; 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 unbuffered. evaluated_nodes.insert(node); for (auto wire : flow.node_comb_defs[node]) for (auto succ_node : flow.wire_uses[wire]) if (evaluated_nodes[succ_node]) feedback_wires.insert(wire); } if (!feedback_wires.empty()) { has_feedback_arcs = true; log("Module `%s' contains feedback arcs through wires:\n", log_id(module)); for (auto wire : feedback_wires) 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.isPublic() && !unbuffer_internal) continue; if (wire->name.isPublic() && !unbuffer_public) continue; if (flow.wire_sync_defs.count(wire) > 0) continue; 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.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 // wires, since using a singly buffered wire for any kind of state introduces a race condition) should contain // no wires attached to combinatorial outputs. Feedback wires, by definition, make that impossible. However, // it is possible that a design with no feedback arcs would end up with doubly buffered wires in such cases // 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 (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)); for (auto wire : buffered_comb_wires) 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(); if (debug_info) { // 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()) { 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; if (wire->port_input || wire->port_output) continue; // preserve input/output metadata in flags 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 { 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) { // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated // by optimizing the design, if after `proc; flatten` there are any feedback wires remaining, it is very // likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message. const char *why_pessimistic = nullptr; if (has_feedback_arcs) why_pessimistic = "feedback wires"; else if (has_buffered_comb_wires) why_pessimistic = "buffered combinatorial wires"; log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic); if (!run_flatten) log("Flattening may eliminate %s from the design.\n", why_pessimistic); if (!run_proc) log("Converting processes to netlists may eliminate %s from the design.\n", why_pessimistic); } } void check_design(RTLIL::Design *design, bool &has_sync_init) { has_sync_init = false; for (auto module : design->modules()) { if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox))) continue; if (!design->selected_whole_module(module)) if (design->selected_module(module)) log_cmd_error("Can't handle partially selected module `%s'!\n", id2cstr(module->name)); if (!design->selected_module(module)) continue; for (auto proc : module->processes) for (auto sync : proc.second->syncs) if (sync->type == RTLIL::STi) has_sync_init = true; } } void prepare_design(RTLIL::Design *design) { bool did_anything = false; bool has_sync_init; log_push(); check_design(design, has_sync_init); if (run_hierarchy) { Pass::call(design, "hierarchy -auto-top"); did_anything = true; } if (run_flatten) { Pass::call(design, "flatten"); did_anything = true; } if (run_proc) { Pass::call(design, "proc"); did_anything = true; } else if (has_sync_init) { // We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those // in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.) Pass::call(design, "proc_prune"); Pass::call(design, "proc_clean"); Pass::call(design, "proc_init"); did_anything = true; } // Recheck the design if it was modified. if (did_anything) check_design(design, has_sync_init); log_assert(!has_sync_init); log_pop(); if (did_anything) log_spacer(); analyze_design(design); } }; struct CxxrtlBackend : public Backend { static const int DEFAULT_OPT_LEVEL = 6; static const int DEFAULT_DEBUG_LEVEL = 4; CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_cxxrtl [options] [filename]\n"); log("\n"); log("Write C++ code that simulates the design. The generated code requires a driver\n"); log("that instantiates the design, toggles its clock, and interacts with its ports.\n"); log("\n"); log("The following driver may be used as an example for a design with a single clock\n"); log("driving rising edge triggered flip-flops:\n"); log("\n"); log(" #include \"top.cc\"\n"); log("\n"); log(" int main() {\n"); log(" cxxrtl_design::p_top top;\n"); log(" top.step();\n"); log(" while (1) {\n"); log(" /* user logic */\n"); log(" top.p_clk.set(false);\n"); log(" top.step();\n"); log(" top.p_clk.set(true);\n"); log(" top.step();\n"); log(" }\n"); log(" }\n"); log("\n"); log("Note that CXXRTL simulations, just like the hardware they are simulating, are\n"); log("subject to race conditions. If, in the example above, the user logic would run\n"); log("simultaneously with the rising edge of the clock, the design would malfunction.\n"); log("\n"); log("This backend supports replacing parts of the design with black boxes implemented\n"); log("in C++. If a module marked as a CXXRTL black box, its implementation is ignored,\n"); log("and the generated code consists only of an interface and a factory function.\n"); log("The driver must implement the factory function that creates an implementation of\n"); log("the black box, taking into account the parameters it is instantiated with.\n"); log("\n"); log("For example, the following Verilog code defines a CXXRTL black box interface for\n"); log("a synchronous debug sink:\n"); log("\n"); log(" (* cxxrtl_blackbox *)\n"); log(" module debug(...);\n"); log(" (* cxxrtl_edge = \"p\" *) input clk;\n"); log(" input en;\n"); log(" input [7:0] i_data;\n"); log(" (* cxxrtl_sync *) output [7:0] o_data;\n"); log(" endmodule\n"); log("\n"); log("For this HDL interface, this backend will generate the following C++ interface:\n"); log("\n"); log(" struct bb_p_debug : public module {\n"); log(" value<1> p_clk;\n"); log(" bool posedge_p_clk() const { /* ... */ }\n"); log(" value<1> p_en;\n"); log(" value<8> p_i_data;\n"); log(" wire<8> p_o_data;\n"); log("\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"); log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n"); log(" };\n"); log("\n"); log("The `create' function must be implemented by the driver. For example, it could\n"); log("always provide an implementation logging the values to standard error stream:\n"); log("\n"); log(" namespace cxxrtl_design {\n"); log("\n"); log(" struct stderr_debug : public bb_p_debug {\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(performer);\n"); log(" }\n"); log(" };\n"); log("\n"); log(" std::unique_ptr\n"); log(" bb_p_debug::create(std::string name, cxxrtl::metadata_map parameters,\n"); log(" cxxrtl::metadata_map attributes) {\n"); log(" return std::make_unique();\n"); log(" }\n"); log("\n"); log(" }\n"); log("\n"); log("For complex applications of black boxes, it is possible to parameterize their\n"); log("port widths. For example, the following Verilog code defines a CXXRTL black box\n"); log("interface for a configurable width debug sink:\n"); log("\n"); log(" (* cxxrtl_blackbox, cxxrtl_template = \"WIDTH\" *)\n"); log(" module debug(...);\n"); log(" parameter WIDTH = 8;\n"); log(" (* cxxrtl_edge = \"p\" *) input clk;\n"); log(" input en;\n"); log(" (* cxxrtl_width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n"); log(" (* cxxrtl_width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n"); log(" endmodule\n"); log("\n"); log("For this parametric HDL interface, this backend will generate the following C++\n"); log("interface (only the differences are shown):\n"); log("\n"); log(" template\n"); log(" struct bb_p_debug : public module {\n"); log(" // ...\n"); log(" value p_i_data;\n"); log(" wire p_o_data;\n"); log(" // ...\n"); log(" static std::unique_ptr>\n"); log(" create(std::string name, metadata_map parameters, metadata_map attributes);\n"); log(" };\n"); log("\n"); log("The `create' function must be implemented by the driver, specialized for every\n"); log("possible combination of template parameters. (Specialization is necessary to\n"); log("enable separate compilation of generated code and black box implementations.)\n"); log("\n"); log(" template\n"); log(" struct stderr_debug : public bb_p_debug {\n"); log(" // ...\n"); log(" };\n"); log("\n"); log(" template<>\n"); log(" std::unique_ptr>\n"); log(" bb_p_debug<8>::create(std::string name, cxxrtl::metadata_map parameters,\n"); log(" cxxrtl::metadata_map attributes) {\n"); log(" return std::make_unique>();\n"); log(" }\n"); log("\n"); log("The following attributes are recognized by this backend:\n"); log("\n"); log(" cxxrtl_blackbox\n"); log(" only valid on modules. if specified, the module contents are ignored,\n"); log(" and the generated code includes only the module interface and a factory\n"); log(" function, which will be called to instantiate the module.\n"); log("\n"); log(" cxxrtl_edge\n"); log(" only valid on inputs of black boxes. must be one of \"p\", \"n\", \"a\".\n"); log(" if specified on signal `clk`, the generated code includes edge detectors\n"); log(" `posedge_p_clk()` (if \"p\"), `negedge_p_clk()` (if \"n\"), or both (if\n"); log(" \"a\"), simplifying implementation of clocked black boxes.\n"); log("\n"); log(" cxxrtl_template\n"); log(" only valid on black boxes. must contain a space separated sequence of\n"); log(" identifiers that have a corresponding black box parameters. for each\n"); log(" of them, the generated code includes a `size_t` template parameter.\n"); log("\n"); log(" cxxrtl_width\n"); log(" only valid on ports of black boxes. must be a constant expression, which\n"); log(" is directly inserted into generated code.\n"); log("\n"); log(" cxxrtl_comb, cxxrtl_sync\n"); log(" only valid on outputs of black boxes. if specified, indicates that every\n"); log(" bit of the output port is driven, correspondingly, by combinatorial or\n"); log(" synchronous logic. this knowledge is used for scheduling optimizations.\n"); log(" if neither is specified, the output will be pessimistically treated as\n"); log(" driven by both combinatorial and synchronous logic.\n"); 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"); log(" of the interface is derived from filename of the implementation.\n"); log(" otherwise, interface and implementation are generated together.\n"); log("\n"); log(" -namespace \n"); 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"); log(" for improved performance; it will be determined automatically if absent.\n"); log("\n"); log(" -noflatten\n"); log(" don't flatten the design. fully flattened designs can evaluate within\n"); log(" one delta cycle if they have no combinatorial feedback.\n"); log(" note that the debug interface and waveform dumps use full hierarchical\n"); log(" names for all wires even in flattened designs.\n"); log("\n"); log(" -noproc\n"); log(" don't convert processes to netlists. in most designs, converting\n"); log(" processes significantly improves evaluation performance at the cost of\n"); log(" slight increase in compilation time.\n"); log("\n"); log(" -O \n"); log(" set the optimization level. the default is -O%d. higher optimization\n", DEFAULT_OPT_LEVEL); log(" levels dramatically decrease compile and run time, and highest level\n"); log(" possible for a design should be used.\n"); log("\n"); log(" -O0\n"); log(" no optimization.\n"); log("\n"); log(" -O1\n"); log(" unbuffer internal wires if possible.\n"); log("\n"); log(" -O2\n"); log(" like -O1, and localize internal wires if possible.\n"); log("\n"); log(" -O3\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"); log("\n"); log(" -O5\n"); log(" like -O4, and localize public wires not marked (*keep*) if possible.\n"); log("\n"); log(" -O6\n"); 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. the C API is disabled.\n"); log("\n"); log(" -g1\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; int opt_level = DEFAULT_OPT_LEVEL; int debug_level = DEFAULT_DEBUG_LEVEL; CxxrtlWorker worker; log_header(design, "Executing CXXRTL backend.\n"); 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; } if (args[argidx] == "-noflatten") { noflatten = true; continue; } if (args[argidx] == "-noproc") { noproc = true; continue; } if (args[argidx] == "-Og") { 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++; 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()) { opt_level = std::stoi(args[++argidx]); continue; } if (args[argidx].substr(0, 2) == "-O" && args[argidx].size() == 3 && isdigit(args[argidx][2])) { opt_level = std::stoi(args[argidx].substr(2)); continue; } if (args[argidx] == "-g" && argidx+1 < args.size()) { debug_level = std::stoi(args[++argidx]); continue; } if (args[argidx].substr(0, 2) == "-g" && args[argidx].size() == 3 && isdigit(args[argidx][2])) { debug_level = std::stoi(args[argidx].substr(2)); continue; } if (args[argidx] == "-header") { worker.split_intf = true; continue; } if (args[argidx] == "-namespace" && argidx+1 < args.size()) { 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.inline_public = true; YS_FALLTHROUGH case 5: worker.localize_public = true; YS_FALLTHROUGH case 4: worker.unbuffer_public = true; YS_FALLTHROUGH case 3: worker.inline_internal = true; YS_FALLTHROUGH case 2: worker.localize_internal = true; YS_FALLTHROUGH case 1: worker.unbuffer_internal = true; YS_FALLTHROUGH case 0: break; default: log_cmd_error("Invalid optimization level %d.\n", opt_level); } 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 case 0: break; default: log_cmd_error("Invalid debug information level %d.\n", debug_level); } std::ofstream intf_f; if (worker.split_intf) { if (filename == "") log_cmd_error("Option -header must be used with a filename.\n"); worker.intf_filename = filename.substr(0, filename.rfind('.')) + ".h"; intf_f.open(worker.intf_filename, std::ofstream::trunc); if (intf_f.fail()) log_cmd_error("Can't open file `%s' for writing: %s\n", worker.intf_filename.c_str(), strerror(errno)); worker.intf_f = &intf_f; } worker.impl_f = f; worker.prepare_design(design); worker.dump_design(design); } } CxxrtlBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/cxxrtl/runtime/000077500000000000000000000000001477540374200174245ustar00rootroot00000000000000yosys-0.52/backends/cxxrtl/runtime/README.txt000066400000000000000000000022521477540374200211230ustar00rootroot00000000000000This 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.yosys-0.52/backends/cxxrtl/runtime/cxxrtl/000077500000000000000000000000001477540374200207505ustar00rootroot00000000000000yosys-0.52/backends/cxxrtl/runtime/cxxrtl/capi/000077500000000000000000000000001477540374200216645ustar00rootroot00000000000000yosys-0.52/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc000066400000000000000000000107171477540374200245210ustar00rootroot00000000000000/* * 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, nullptr, 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(); } yosys-0.52/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h000066400000000000000000000402111477540374200243530ustar00rootroot00000000000000/* * 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. * */ #ifndef CXXRTL_CAPI_H #define CXXRTL_CAPI_H // This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_capi.cc`. // // The CXXRTL C API makes it possible to drive CXXRTL designs using C or any other language that // supports the C ABI, for example, Python. It does not provide a way to implement black boxes. #include #include #include #ifdef __cplusplus extern "C" { #endif // Opaque reference to a design toplevel. // // A design toplevel can only be used to create a design handle. typedef struct _cxxrtl_toplevel *cxxrtl_toplevel; // The constructor for a design toplevel is provided as a part of generated code for that design. // Its prototype matches: // // cxxrtl_toplevel _create(); // Opaque reference to a design handle. // // A design handle is required by all operations in the C API. typedef struct _cxxrtl_handle *cxxrtl_handle; // Create a design handle from a design toplevel. // // 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. // // Returns 1 if the design is known to immediately converge, 0 otherwise. int cxxrtl_eval(cxxrtl_handle handle); // Commit the design, replacing the `curr` value of internal state and output wires with the `next` // value. // // Return 1 if any of the `curr` values were updated, 0 otherwise. int cxxrtl_commit(cxxrtl_handle handle); // Simulate the design to a fixed point. // // Returns the number of delta cycles. size_t cxxrtl_step(cxxrtl_handle handle); // Type of a simulated object. // // The type of a simulated object indicates the way it is stored and the operations that are legal // to perform on it (i.e. won't crash the simulation). It says very little about object semantics, // which is specified through flags. enum cxxrtl_type { // Values correspond to singly buffered netlist nodes, i.e. nodes driven exclusively by // combinatorial cells, or toplevel input nodes. // // Values can be inspected via the `curr` pointer. If the `next` pointer is NULL, the value is // driven by a constant and can never be modified. Otherwise, the value can be modified through // the `next` pointer (which is equal to `curr` if not NULL). Note that changes to the bits // driven by combinatorial cells will be ignored. // // Values always have depth 1. CXXRTL_VALUE = 0, // Wires correspond to doubly buffered netlist nodes, i.e. nodes driven, at least in part, by // storage cells, or by combinatorial cells that are a part of a feedback path. They are also // present in non-optimized builds. // // Wires can be inspected via the `curr` pointer and modified via the `next` pointer (which are // distinct for wires). Note that changes to the bits driven by combinatorial cells will be // ignored. // // Wires always have depth 1. CXXRTL_WIRE = 1, // Memories correspond to memory cells. // // Memories can be inspected and modified via the `curr` pointer. Due to a limitation of this // API, memories cannot yet be modified in a guaranteed race-free way, and the `next` pointer is // always NULL. CXXRTL_MEMORY = 2, // Aliases correspond to netlist nodes driven by another node such that their value is always // exactly equal. // // Aliases can be inspected via the `curr` pointer. They cannot be modified, and the `next` // 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. }; // Flags of a simulated object. // // The flags of a simulated object indicate its role in the netlist: // * The flags `CXXRTL_INPUT` and `CXXRTL_OUTPUT` designate module ports. // * The flags `CXXRTL_DRIVEN_SYNC`, `CXXRTL_DRIVEN_COMB`, and `CXXRTL_UNDRIVEN` specify // the semantics of node state. An object with several of these flags set has different bits // follow different semantics. enum cxxrtl_flag { // Node is a module input port. // // This flag can be set on objects of type `CXXRTL_VALUE` and `CXXRTL_WIRE`. It may be combined // with `CXXRTL_OUTPUT`, as well as other flags. CXXRTL_INPUT = 1 << 0, // Node is a module output port. // // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with `CXXRTL_INPUT`, // as well as other flags. CXXRTL_OUTPUT = 1 << 1, // Node is a module inout port. // // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with other flags. CXXRTL_INOUT = (CXXRTL_INPUT|CXXRTL_OUTPUT), // Node has bits that are driven by a storage cell. // // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with // `CXXRTL_DRIVEN_COMB` and `CXXRTL_UNDRIVEN`, as well as other flags. // // This flag is set on wires that have bits connected directly to the output of a flip-flop or // a latch, and hold its state. Many `CXXRTL_WIRE` objects may not have the `CXXRTL_DRIVEN_SYNC` // flag set; for example, output ports and feedback wires generally won't. Writing to the `next` // pointer of these wires updates stored state, and for designs without combinatorial loops, // capturing the value from every of these wires through the `curr` pointer creates a complete // snapshot of the design state. CXXRTL_DRIVEN_SYNC = 1 << 2, // Node has bits that are driven by a combinatorial cell or another node. // // 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 // through the `next` pointer (if it is not NULL) has no effect. CXXRTL_DRIVEN_COMB = 1 << 3, // Node has bits that are not driven. // // This flag can be set on objects of type `CXXRTL_VALUE` and `CXXRTL_WIRE`. It may be combined // with `CXXRTL_DRIVEN_SYNC` and `CXXRTL_DRIVEN_COMB`, as well as other flags. // // This flag is set on objects that have bits not driven by an output of any cell or by another // node, such as inputs and dangling wires. CXXRTL_UNDRIVEN = 1 << 4, // More object flags may be added in the future, but the existing ones will never change. }; // Description of a simulated 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. // // All objects have the same memory layout determined by `width` and `depth`, but the type // determines all other properties of the object. uint32_t type; // actually `enum cxxrtl_type` // Flags of the object. uint32_t flags; // actually bit mask of `enum cxxrtl_flags` // Width of the object in bits. size_t width; // Index of the least significant bit. size_t lsb_at; // Depth of the object. Only meaningful for memories; for other objects, always 1. size_t depth; // Index of the first word. Only meaningful for memories; for other objects, always 0; size_t zero_at; // Bits stored in the object, as 32-bit chunks, least significant bits first. // // The width is rounded up to a multiple of 32; the padding bits are always set to 0 by // the simulation code, and must be always written as 0 when modified by user code. // In memories, every element is stored contiguously. Therefore, the total number of chunks // in any object is `((width + 31) / 32) * depth`. // // To allow the simulation to be partitioned into multiple independent units communicating // 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. }; // Retrieve description of a simulated object. // // The `name` is the full hierarchical name of the object in the Yosys notation, where public names // have a `\` prefix and hierarchy levels are separated by single spaces. For example, if // the top-level module instantiates a module `foo`, which in turn contains a wire `bar`, the full // hierarchical name is `\foo \bar`. // // The storage of a single abstract object may be split (usually with the `splitnets` pass) into // many physical parts, all of which correspond to the same hierarchical name. To handle such cases, // this function returns an array and writes its length to `parts`. The array is sorted by `lsb_at`. // // Returns the object parts if it was found, NULL otherwise. The returned parts are valid until // the design is destroyed. struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts); // Retrieve description of a single part simulated object. // // 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. 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); if (object == NULL || parts == 1) return object; return NULL; } // Enumerate simulated objects. // // For every object in the simulation, `callback` is called with the provided `data`, the full // hierarchical name of the object (see `cxxrtl_get` for details), and the object parts. // The provided `name` and `object` values are valid until the design is destroyed. 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 #endif yosys-0.52/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc000066400000000000000000000053771477540374200253630ustar00rootroot00000000000000/* * 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_vcd.h`. #include #include extern const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle); struct _cxxrtl_vcd { cxxrtl::vcd_writer writer; bool flush = false; }; cxxrtl_vcd cxxrtl_vcd_create() { return new _cxxrtl_vcd; } void cxxrtl_vcd_destroy(cxxrtl_vcd vcd) { delete vcd; } void cxxrtl_vcd_timescale(cxxrtl_vcd vcd, int number, const char *unit) { vcd->writer.timescale(number, unit); } void cxxrtl_vcd_add(cxxrtl_vcd vcd, const char *name, cxxrtl_object *object) { // Note the copy. We don't know whether `object` came from a design (in which case it is // an instance of `debug_item`), or from user code (in which case it is an instance of // `cxxrtl_object`), so casting the pointer wouldn't be safe. vcd->writer.add(name, cxxrtl::debug_item(*object)); } void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle) { vcd->writer.add(cxxrtl_debug_items_from_handle(handle)); } void cxxrtl_vcd_add_from_if(cxxrtl_vcd vcd, cxxrtl_handle handle, void *data, int (*filter)(void *data, const char *name, const cxxrtl_object *object)) { vcd->writer.add(cxxrtl_debug_items_from_handle(handle), [=](const std::string &name, const cxxrtl::debug_item &item) { return filter(data, name.c_str(), static_cast(&item)); }); } void cxxrtl_vcd_add_from_without_memories(cxxrtl_vcd vcd, cxxrtl_handle handle) { vcd->writer.add_without_memories(cxxrtl_debug_items_from_handle(handle)); } void cxxrtl_vcd_sample(cxxrtl_vcd vcd, uint64_t time) { if (vcd->flush) { vcd->writer.buffer.clear(); vcd->flush = false; } vcd->writer.sample(time); } void cxxrtl_vcd_read(cxxrtl_vcd vcd, const char **data, size_t *size) { if (vcd->flush) { vcd->writer.buffer.clear(); vcd->flush = false; } *data = vcd->writer.buffer.c_str(); *size = vcd->writer.buffer.size(); vcd->flush = true; } yosys-0.52/backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h000066400000000000000000000102761477540374200252170ustar00rootroot00000000000000/* * 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. * */ #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`. // // The CXXRTL C API for VCD writing makes it possible to insert virtual probes into designs and // dump waveforms to Value Change Dump files. #include #include #include #ifdef __cplusplus extern "C" { #endif // Opaque reference to a VCD writer. typedef struct _cxxrtl_vcd *cxxrtl_vcd; // Create a VCD writer. cxxrtl_vcd cxxrtl_vcd_create(); // Release all resources used by a VCD writer. void cxxrtl_vcd_destroy(cxxrtl_vcd vcd); // Set VCD timescale. // // The `number` must be 1, 10, or 100, and the `unit` must be one of `"s"`, `"ms"`, `"us"`, `"ns"`, // `"ps"`, or `"fs"`. // // Timescale can only be set before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_timescale(cxxrtl_vcd vcd, int number, const char *unit); // Schedule a specific CXXRTL object to be sampled. // // The `name` is a full hierarchical name as described for `cxxrtl_get`; it does not need to match // the original name of `object`, if any. The `object` must outlive the VCD writer, but there are // no other requirements; if desired, it can be provided by user code, rather than come from // a design. // // Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_add(cxxrtl_vcd vcd, const char *name, struct cxxrtl_object *object); // Schedule all CXXRTL objects in a simulation. // // The design `handle` must outlive the VCD writer. // // Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle); // Schedule CXXRTL objects in a simulation that match a given predicate. // // For every object in the simulation, `filter` is called with the provided `data`, the full // hierarchical name of the object (see `cxxrtl_get` for details), and the object description. // The object will be sampled if the predicate returns a non-zero value. // // Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_add_from_if(cxxrtl_vcd vcd, cxxrtl_handle handle, void *data, int (*filter)(void *data, const char *name, const struct cxxrtl_object *object)); // Schedule all CXXRTL objects in a simulation except for memories. // // The design `handle` must outlive the VCD writer. // // Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`. void cxxrtl_vcd_add_from_without_memories(cxxrtl_vcd vcd, cxxrtl_handle handle); // Sample all scheduled objects. // // First, `time` is written to the internal buffer. Second, the values of every signal changed since // the previous call to `cxxrtl_vcd_sample` (all values if this is the first call) are written to // the internal buffer. The contents of the buffer can be retrieved with `cxxrtl_vcd_read`. void cxxrtl_vcd_sample(cxxrtl_vcd vcd, uint64_t time); // Retrieve buffered VCD data. // // The pointer to the start of the next chunk of VCD data is assigned to `*data`, and the length // of that chunk is assigned to `*size`. The pointer to the data is valid until the next call to // `cxxrtl_vcd_sample` or `cxxrtl_vcd_read`. Once all of the buffered data has been retrieved, // this function will always return zero sized chunks. void cxxrtl_vcd_read(cxxrtl_vcd vcd, const char **data, size_t *size); #ifdef __cplusplus } #endif #endif yosys-0.52/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h000066400000000000000000002151751477540374200224600ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2019-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 included by the designs generated with `write_cxxrtl`. It is not used in Yosys itself. // // The CXXRTL support library implements compile time specialized arbitrary width arithmetics, as well as provides // composite lvalues made out of bit slices and concatenations of lvalues. This allows the `write_cxxrtl` pass // to perform a straightforward translation of RTLIL structures to readable C++, relying on the C++ compiler // to unwrap the abstraction and generate efficient code. #ifndef CXXRTL_H #define CXXRTL_H #include #include #include #include #include #include #include #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. #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 { // All arbitrary-width values in CXXRTL are backed by arrays of unsigned integers called chunks. The chunk size // is the same regardless of the value width to simplify manipulating values via FFI interfaces, e.g. driving // and introspecting the simulation in Python. // // It is practical to use chunk sizes between 32 bits and platform register size because when arithmetics on // narrower integer types is legalized by the C++ compiler, it inserts code to clear the high bits of the register. // However, (a) most of our operations do not change those bits in the first place because of invariants that are // invisible to the compiler, (b) we often operate on non-power-of-2 values and have to clear the high bits anyway. // Therefore, using relatively wide chunks and clearing the high bits explicitly and only when we know they may be // clobbered results in simpler generated code. typedef uint32_t chunk_t; typedef uint64_t wide_chunk_t; template struct chunk_traits { static_assert(std::is_integral::value && std::is_unsigned::value, "chunk type must be an unsigned integral type"); using type = T; static constexpr size_t bits = std::numeric_limits::digits; static constexpr T mask = std::numeric_limits::max(); }; template struct expr_base; template struct value : public expr_base> { static constexpr size_t bits = Bits; using chunk = chunk_traits; static constexpr chunk::type msb_mask = (Bits % chunk::bits == 0) ? chunk::mask : chunk::mask >> (chunk::bits - (Bits % chunk::bits)); static constexpr size_t chunks = (Bits + chunk::bits - 1) / chunk::bits; chunk::type data[chunks] = {}; value() = default; template explicit constexpr value(Init ...init) : data{init...} {} value(const 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 { return *this; } std::string str() const { std::stringstream ss; ss << *this; return ss.str(); } // Conversion operations. // // 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::value, int>::type = 0> CXXRTL_ALWAYS_INLINE IntegerT get() const { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "get() requires T to be an unsigned integral type"); static_assert(std::numeric_limits::digits >= Bits, "get() requires T to be at least as wide as the value is"); IntegerT result = 0; for (size_t n = 0; n < chunks; n++) result |= IntegerT(data[n]) << (n * chunk::bits); return result; } template::value, int>::type = 0> CXXRTL_ALWAYS_INLINE 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] = (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. // // These operations are used to implement slicing, concatenation, and blitting. // The trunc, zext and sext operations add or remove most significant bits (i.e. on the left); // the rtrunc and rzext operations add or remove least significant bits (i.e. on the right). template CXXRTL_ALWAYS_INLINE value trunc() const { static_assert(NewBits <= Bits, "trunc() may not increase width"); value result; for (size_t n = 0; n < result.chunks; n++) result.data[n] = data[n]; result.data[result.chunks - 1] &= result.msb_mask; return result; } template CXXRTL_ALWAYS_INLINE value zext() const { static_assert(NewBits >= Bits, "zext() may not decrease width"); value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n]; return result; } template CXXRTL_ALWAYS_INLINE value sext() const { static_assert(NewBits >= Bits, "sext() may not decrease width"); value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n]; if (is_neg()) { result.data[chunks - 1] |= ~msb_mask; for (size_t n = chunks; n < result.chunks; n++) result.data[n] = chunk::mask; result.data[result.chunks - 1] &= result.msb_mask; } return result; } template CXXRTL_ALWAYS_INLINE value rtrunc() const { static_assert(NewBits <= Bits, "rtrunc() may not increase width"); value result; constexpr size_t shift_chunks = (Bits - NewBits) / chunk::bits; constexpr size_t shift_bits = (Bits - NewBits) % chunk::bits; chunk::type carry = 0; if (shift_chunks + result.chunks < chunks) { carry = (shift_bits == 0) ? 0 : data[shift_chunks + result.chunks] << (chunk::bits - shift_bits); } for (size_t n = result.chunks; n > 0; n--) { result.data[n - 1] = carry | (data[shift_chunks + n - 1] >> shift_bits); carry = (shift_bits == 0) ? 0 : data[shift_chunks + n - 1] << (chunk::bits - shift_bits); } return result; } template CXXRTL_ALWAYS_INLINE value rzext() const { static_assert(NewBits >= Bits, "rzext() may not decrease width"); value result; constexpr size_t shift_chunks = (NewBits - Bits) / chunk::bits; constexpr size_t shift_bits = (NewBits - Bits) % chunk::bits; 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 (shift_chunks + chunks < result.chunks) result.data[shift_chunks + chunks] = carry; return result; } // Bit blit operation, i.e. a partial read-modify-write. template CXXRTL_ALWAYS_INLINE value blit(const value &source) const { static_assert(Stop >= Start, "blit() may not reverse bit order"); constexpr chunk::type start_mask = ~(chunk::mask << (Start % chunk::bits)); constexpr chunk::type stop_mask = (Stop % chunk::bits + 1 == chunk::bits) ? 0 : (chunk::mask << (Stop % chunk::bits + 1)); value masked = *this; if (Start / chunk::bits == Stop / chunk::bits) { masked.data[Start / chunk::bits] &= stop_mask | start_mask; } else { masked.data[Start / chunk::bits] &= start_mask; for (size_t n = Start / chunk::bits + 1; n < Stop / chunk::bits; n++) masked.data[n] = 0; masked.data[Stop / chunk::bits] &= stop_mask; } value shifted = source .template rzext() .template zext(); return masked.bit_or(shifted); } // Helpers for selecting extending or truncating operation depending on whether the result is wider or narrower // than the operand. In C++17 these can be replaced with `if constexpr`. template struct zext_cast { CXXRTL_ALWAYS_INLINE value operator()(const value &val) { return val.template zext(); } }; template struct zext_cast::type> { CXXRTL_ALWAYS_INLINE value operator()(const value &val) { return val.template trunc(); } }; template struct sext_cast { CXXRTL_ALWAYS_INLINE value operator()(const value &val) { return val.template sext(); } }; template struct sext_cast::type> { CXXRTL_ALWAYS_INLINE value operator()(const value &val) { return val.template trunc(); } }; template CXXRTL_ALWAYS_INLINE value zcast() const { return zext_cast()(*this); } template CXXRTL_ALWAYS_INLINE value scast() const { 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. bool bit(size_t offset) const { return data[offset / chunk::bits] & (1 << (offset % chunk::bits)); } void set_bit(size_t offset, bool value = true) { size_t offset_chunks = offset / chunk::bits; size_t offset_bits = offset % chunk::bits; data[offset_chunks] &= ~(1 << offset_bits); data[offset_chunks] |= value ? 1 << offset_bits : 0; } explicit operator bool() const { return !is_zero(); } bool is_zero() const { for (size_t n = 0; n < chunks; n++) if (data[n] != 0) return false; return true; } bool is_neg() const { return data[chunks - 1] & (1 << ((Bits - 1) % chunk::bits)); } bool operator ==(const value &other) const { for (size_t n = 0; n < chunks; n++) if (data[n] != other.data[n]) return false; return true; } bool operator !=(const value &other) const { return !(*this == other); } value bit_not() const { value result; for (size_t n = 0; n < chunks; n++) result.data[n] = ~data[n]; result.data[chunks - 1] &= msb_mask; return result; } value bit_and(const value &other) const { value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n] & other.data[n]; return result; } value bit_or(const value &other) const { value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n] | other.data[n]; return result; } value bit_xor(const value &other) const { value result; for (size_t n = 0; n < chunks; n++) result.data[n] = data[n] ^ other.data[n]; return result; } value update(const value &val, const value &mask) const { return bit_and(mask.bit_not()).bit_or(val.bit_and(mask)); } template value shl(const value &amount) const { // Ensure our early return is correct by prohibiting values larger than 4 Gbit. static_assert(Bits <= chunk::mask, "shl() of unreasonably large values is not supported"); // Detect shifts definitely large than Bits early. for (size_t n = 1; n < amount.chunks; n++) if (amount.data[n] != 0) return {}; // 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 {}; value result; chunk::type carry = 0; for (size_t n = 0; n < chunks - shift_chunks; n++) { result.data[shift_chunks + n] = (data[n] << shift_bits) | carry; carry = (shift_bits == 0) ? 0 : data[n] >> (chunk::bits - shift_bits); } result.data[result.chunks - 1] &= result.msb_mask; return result; } template value shr(const value &amount) const { // Ensure our early return is correct by prohibiting values larger than 4 Gbit. static_assert(Bits <= chunk::mask, "shr() of unreasonably large values is not supported"); // Detect shifts definitely large than Bits early. for (size_t n = 1; n < amount.chunks; n++) if (amount.data[n] != 0) 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 (Signed && is_neg()) ? value().bit_not() : value(); value result; chunk::type carry = 0; for (size_t n = 0; n < chunks - shift_chunks; n++) { result.data[chunks - shift_chunks - 1 - n] = carry | (data[chunks - 1 - n] >> shift_bits); carry = (shift_bits == 0) ? 0 : data[chunks - 1 - n] << (chunk::bits - shift_bits); } if (Signed && is_neg()) { 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 (amount.data[0] != 0) result.data[top_chunk_idx] |= chunk::mask << top_chunk_bits; result.data[result.chunks - 1] &= result.msb_mask; } return result; } template value sshr(const value &amount) const { 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++) { // This loop implements the population count idiom as recognized by LLVM and GCC. for (chunk::type x = data[n]; x != 0; count++) x = x & (x - 1); } return count; } size_t ctlz() const { size_t count = 0; for (size_t n = 0; n < chunks; n++) { chunk::type x = data[chunks - 1 - n]; // 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; } template std::pair, bool /*CarryOut*/> alu(const value &other) const { value result; bool carry = CarryIn; for (size_t n = 0; n < result.chunks; n++) { result.data[n] = data[n] + (Invert ? ~other.data[n] : other.data[n]) + carry; if (result.chunks - 1 == n) result.data[result.chunks - 1] &= result.msb_mask; carry = (result.data[n] < data[n]) || (result.data[n] == data[n] && carry); } return {result, carry}; } value add(const value &other) const { return alu(other).first; } value sub(const value &other) const { return alu(other).first; } value neg() const { return value().sub(*this); } bool ucmp(const value &other) const { bool carry; std::tie(std::ignore, carry) = alu(other); return !carry; // a.ucmp(b) ≡ a u< b } bool scmp(const value &other) const { value result; bool carry; std::tie(result, carry) = alu(other); bool overflow = (is_neg() == !other.is_neg()) && (is_neg() != result.is_neg()); return result.is_neg() ^ overflow; // a.scmp(b) ≡ a s< b } template value mul(const value &other) const { value result; wide_chunk_t wide_result[result.chunks + 1] = {}; for (size_t n = 0; n < chunks; n++) { for (size_t m = 0; m < chunks && n + m < result.chunks; m++) { wide_result[n + m] += wide_chunk_t(data[n]) * wide_chunk_t(other.data[m]); wide_result[n + m + 1] += wide_result[n + m] >> chunk::bits; wide_result[n + m] &= chunk::mask; } } for (size_t n = 0; n < result.chunks; n++) { result.data[n] = wide_result[n]; } 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 (is_neg()) dividend = dividend.neg(); if (other.is_neg()) divisor = divisor.neg(); std::tie(quotient, remainder) = dividend.udivmod(divisor); if (is_neg() != other.is_neg()) quotient = quotient.neg(); if (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. template struct slice_expr : public expr_base> { static_assert(Stop >= Start, "slice_expr() may not reverse bit order"); static_assert(Start < T::bits && Stop < T::bits, "slice_expr() must be within bounds"); static constexpr size_t bits = Stop - Start + 1; T &expr; slice_expr(T &expr) : expr(expr) {} slice_expr(const slice_expr &) = delete; CXXRTL_ALWAYS_INLINE operator value() const { return static_cast &>(expr) .template rtrunc() .template trunc(); } CXXRTL_ALWAYS_INLINE slice_expr &operator=(const value &rhs) { // Generic partial assignment implemented using a read-modify-write operation on the sliced expression. expr = static_cast &>(expr) .template blit(rhs); return *this; } // A helper that forces the cast to value<>, which allows deduction to work. CXXRTL_ALWAYS_INLINE value val() const { return static_cast &>(*this); } }; // Expression template for a concatenation, usable as lvalue or rvalue, and composable with other expression templates here. template struct concat_expr : public expr_base> { static constexpr size_t bits = T::bits + U::bits; T &ms_expr; U &ls_expr; concat_expr(T &ms_expr, U &ls_expr) : ms_expr(ms_expr), ls_expr(ls_expr) {} concat_expr(const concat_expr &) = delete; CXXRTL_ALWAYS_INLINE operator value() const { value ms_shifted = static_cast &>(ms_expr) .template rzext(); value ls_extended = static_cast &>(ls_expr) .template zext(); return ms_shifted.bit_or(ls_extended); } CXXRTL_ALWAYS_INLINE concat_expr &operator=(const value &rhs) { ms_expr = rhs.template rtrunc(); ls_expr = rhs.template trunc(); return *this; } // A helper that forces the cast to value<>, which allows deduction to work. CXXRTL_ALWAYS_INLINE value val() const { return static_cast &>(*this); } }; // Base class for expression templates, providing helper methods for operations that are valid on both rvalues and lvalues. // // Note that expression objects (slices and concatenations) constructed in this way should NEVER be captured because // they refer to temporaries that will, in general, only live until the end of the statement. For example, both of // these snippets perform use-after-free: // // const auto &a = val.slice<7,0>().slice<1>(); // value<1> b = a; // // auto &&c = val.slice<7,0>().slice<1>(); // c = value<1>{1u}; // // An easy way to write code using slices and concatenations safely is to follow two simple rules: // * Never explicitly name any type except `value` or `const value &`. // * Never use a `const auto &` or `auto &&` in any such expression. // Then, any code that compiles will be well-defined. template struct expr_base { template CXXRTL_ALWAYS_INLINE slice_expr slice() const { return {*static_cast(this)}; } template CXXRTL_ALWAYS_INLINE slice_expr slice() { return {*static_cast(this)}; } template CXXRTL_ALWAYS_INLINE concat_expr::type> concat(const U &other) const { return {*static_cast(this), other}; } template CXXRTL_ALWAYS_INLINE concat_expr::type> concat(U &&other) { return {*static_cast(this), other}; } }; template std::ostream &operator<<(std::ostream &os, const value &val) { auto old_flags = os.flags(std::ios::right); auto old_width = os.width(0); auto old_fill = os.fill('0'); os << val.bits << '\'' << std::hex; for (size_t n = val.chunks - 1; n != (size_t)-1; n--) { if (n == val.chunks - 1 && Bits % value::chunk::bits != 0) os.width((Bits % value::chunk::bits + 3) / 4); else os.width((value::chunk::bits + 3) / 4); os << val.data[n]; } os.fill(old_fill); os.width(old_width); os.flags(old_flags); return os; } template struct wire { static constexpr size_t bits = Bits; value curr; value next; wire() = default; 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 &operator=(const wire &) = delete; wire(wire &&) = default; wire &operator=(wire &&) = default; template CXXRTL_ALWAYS_INLINE IntegerT get() const { return curr.template get(); } template CXXRTL_ALWAYS_INLINE void set(IntegerT other) { next.template set(other); } // 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; } return false; } }; template std::ostream &operator<<(std::ostream &os, const wire &val) { os << val.curr; return os; } template struct memory { const size_t depth; std::unique_ptr[]> data; explicit memory(size_t depth) : depth(depth), data(new value[depth]) {} memory(const memory &) = delete; memory &operator=(const memory &) = delete; 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 < 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 < depth); return data[index]; } // A simple way to make a writable memory would be to use an array of wires instead of an array of values. // However, there are two significant downsides to this approach: first, it has large overhead (2× space // overhead, and O(depth) time overhead during commit); second, it does not simplify handling write port // priorities. Although in principle write ports could be ordered or conditionally enabled in generated // code based on their priorities and selected addresses, the feedback arc set problem is computationally // expensive, and the heuristic based algorithms are not easily modified to guarantee (rather than prefer) // a particular write port evaluation order. // // The approach used here instead is to queue writes into a buffer during the eval phase, then perform // the writes during the commit phase in the priority order. This approach has low overhead, with both space // and time proportional to the amount of write ports. Because virtually every memory in a practical design // has at most two write ports, linear search is used on every write, being the fastest and simplest approach. struct write { size_t index; value val; value mask; int priority; }; std::vector write_queue; void update(size_t index, const value &val, const value &mask, int priority = 0) { 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, [](const int a, const write& b) { return a < b.priority; }), write { index, val, mask, priority }); } // 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); 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(); return changed; } }; struct metadata { const enum { MISSING = 0, UINT = 1, SINT = 2, STRING = 3, DOUBLE = 4, } value_type; // 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 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(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) {} metadata(const metadata &) = default; metadata &operator=(const metadata &) = delete; uint64_t as_uint() const { assert(value_type == UINT); return uint_value; } int64_t as_sint() const { assert(value_type == SINT); return sint_value; } const std::string &as_string() const { assert(value_type == STRING); return string_value; } double as_double() const { assert(value_type == DOUBLE); return double_value; } // Internal CXXRTL use only. static std::map deserialize(const char *ptr) { std::map result; std::string name; // Grammar: // string ::= [^\0]+ \0 // metadata ::= [uid] .{8} | s // map ::= ( )* \0 for (;;) { if (*ptr) { name += *ptr++; } else if (!name.empty()) { ptr++; auto get_u64 = [&]() { uint64_t result = 0; for (size_t count = 0; count < 8; count++) result = (result << 8) | *ptr++; return result; }; char type = *ptr++; if (type == 'u') { uint64_t value = get_u64(); result.emplace(name, value); } else if (type == 'i') { int64_t value = (int64_t)get_u64(); result.emplace(name, value); } else if (type == 'd') { double dvalue; uint64_t uvalue = get_u64(); static_assert(sizeof(dvalue) == sizeof(uvalue), "double must be 64 bits in size"); memcpy(&dvalue, &uvalue, sizeof(dvalue)); result.emplace(name, dvalue); } else if (type == 's') { std::string value; while (*ptr) value += *ptr++; ptr++; result.emplace(name, value); } else { assert(false && "Unknown type specifier"); return result; } name.clear(); } else { return result; } } } }; typedef std::map metadata_map; 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 { LITERAL = 0, INTEGER = 1, STRING = 2, UNICHAR = 3, VLOG_TIME = 4, } type; // LITERAL type std::string str; // INTEGER/STRING/UNICHAR types // + value val; // INTEGER/STRING/VLOG_TIME types enum { RIGHT = 0, LEFT = 1, NUMERIC = 2, } justify; // = RIGHT; char padding; // = '\0'; size_t width; // = 0; // INTEGER type unsigned base; // = 10; bool signed_; // = false; enum { MINUS = 0, PLUS_MINUS = 1, SPACE_MINUS = 2, } sign; // = MINUS; bool hex_upper; // = false; bool show_base; // = false; bool group; // = 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; std::string prefix; switch (type) { case LITERAL: return str; case STRING: { 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 UNICHAR: { uint32_t codepoint = val.template zcast<32>().template get(); if (codepoint >= 0x10000) buf += (char)(0xf0 | (codepoint >> 18)); else if (codepoint >= 0x800) buf += (char)(0xe0 | (codepoint >> 12)); else if (codepoint >= 0x80) buf += (char)(0xc0 | (codepoint >> 6)); else buf += (char)codepoint; if (codepoint >= 0x10000) buf += (char)(0x80 | ((codepoint >> 12) & 0x3f)); if (codepoint >= 0x800) buf += (char)(0x80 | ((codepoint >> 6) & 0x3f)); if (codepoint >= 0x80) buf += (char)(0x80 | ((codepoint >> 0) & 0x3f)); break; } case INTEGER: { bool negative = signed_ && val.is_neg(); if (negative) { prefix = "-"; val = val.neg(); } else { switch (sign) { case MINUS: break; case PLUS_MINUS: prefix = "+"; break; case SPACE_MINUS: prefix = " "; break; } } size_t val_width = Bits; if (base != 10) { val_width = 1; for (size_t index = 0; index < Bits; index++) if (val.bit(index)) val_width = index + 1; } if (base == 2) { if (show_base) prefix += "0b"; for (size_t index = 0; index < val_width; index++) { if (group && index > 0 && index % 4 == 0) buf += '_'; buf += (val.bit(index) ? '1' : '0'); } } else if (base == 8 || base == 16) { if (show_base) prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o"; size_t step = (base == 16) ? 4 : 3; for (size_t index = 0; index < val_width; index += step) { if (group && index > 0 && index % (4 * step) == 0) buf += '_'; 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 += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value]; } } else if (base == 10) { if (show_base) prefix += "0d"; if (val.is_zero()) buf += '0'; value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); size_t index = 0; while (!xval.is_zero()) { if (group && index > 0 && index % 3 == 0) buf += '_'; 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; index++; } } else assert(false && "Unsupported base for fmt_part"); if (justify == NUMERIC && group && padding == '0') { int group_size = base == 10 ? 3 : 4; while (prefix.size() + buf.size() < width) { if (buf.size() % (group_size + 1) == group_size) buf += '_'; buf += '0'; } } std::reverse(buf.begin(), buf.end()); 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 (prefix.size() + buf.size() < width) { size_t pad_width = width - prefix.size() - buf.size(); switch (justify) { case LEFT: str += prefix; str += buf; str += std::string(pad_width, padding); break; case RIGHT: str += std::string(pad_width, padding); str += prefix; str += buf; break; case NUMERIC: str += prefix; str += std::string(pad_width, padding); str += buf; break; } } else { str += prefix; str += buf; } 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, OUTLINE = CXXRTL_OUTLINE, }; // Object flags. enum : uint32_t { INPUT = CXXRTL_INPUT, OUTPUT = CXXRTL_OUTPUT, INOUT = CXXRTL_INOUT, DRIVEN_SYNC = CXXRTL_DRIVEN_SYNC, DRIVEN_COMB = CXXRTL_DRIVEN_COMB, UNDRIVEN = CXXRTL_UNDRIVEN, }; debug_item(const ::cxxrtl_object &object) : cxxrtl_object(object) {} template debug_item(value &item, size_t lsb_offset = 0, uint32_t flags_ = 0) { static_assert(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = VALUE; flags = flags_; width = Bits; lsb_at = lsb_offset; depth = 1; 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(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = VALUE; flags = DRIVEN_COMB; width = Bits; lsb_at = lsb_offset; depth = 1; 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(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_; width = Bits; lsb_at = lsb_offset; depth = 1; 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(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.depth; zero_at = zero_offset; 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(Bits == 0 || sizeof(item) == value::chunks * sizeof(chunk_t), "value is not compatible with C layout"); type = ALIAS; flags = DRIVEN_COMB; width = Bits; lsb_at = lsb_offset; depth = 1; 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(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; width = Bits; lsb_at = lsb_offset; depth = 1; 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; 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) { return a.lsb_at < b.lsb_at; }); } // This overload exists to reduce excessive stack slot allocation in `CXXRTL_EXTREMELY_COLD void debug_info()`. template void add(const std::string &base_path, const char *path, const char *serialized_item_attrs, T&&... args) { add(base_path + path, debug_item(std::forward(args)...), metadata::deserialize(serialized_item_attrs)); } size_t count(const std::string &path) const { if (table.count(path) == 0) return 0; return table.at(path).size(); } const std::vector &at(const std::string &path) const { return table.at(path); } // 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); } 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 }); } // This overload exists to reduce excessive stack slot allocation in `CXXRTL_EXTREMELY_COLD void debug_info()`. void add(const std::string &base_path, const char *path, const char *module_name, const char *serialized_module_attrs, const char *serialized_cell_attrs) { add(base_path + path, module_name, metadata::deserialize(serialized_module_attrs), metadata::deserialize(serialized_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; 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(performer *performer = nullptr) { size_t deltas = 0; bool converged = false; do { converged = eval(performer); deltas++; } while (commit() && !converged); return deltas; } 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(&items, /*scopes=*/nullptr, path);` instead."))) #endif void debug_info(debug_items &items, std::string path) { debug_info(&items, /*scopes=*/nullptr, path); } }; } // namespace cxxrtl // 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. // // The `write_cxxrtl` pass translates internal cells (cells with names that start with `$`) to calls of these // functions. All of Yosys arithmetic and logical cells perform sign or zero extension on their operands, // whereas basic operations on arbitrary width values require operands to be of the same width. These functions // bridge the gap by performing the necessary casts. They are named similar to `cell_A[B]`, where A and B are `u` // if the corresponding operand is unsigned, and `s` if it is signed. namespace cxxrtl_yosys { using namespace cxxrtl; // std::max isn't constexpr until C++14 for no particular reason (it's an oversight), so we define our own. template CXXRTL_ALWAYS_INLINE constexpr T max(const T &a, const T &b) { return a > b ? a : b; } // Logic operations template CXXRTL_ALWAYS_INLINE value logic_not(const value &a) { return value { a ? 0u : 1u }; } template CXXRTL_ALWAYS_INLINE value logic_and(const value &a, const value &b) { return value { (bool(a) && bool(b)) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value logic_or(const value &a, const value &b) { return value { (bool(a) || bool(b)) ? 1u : 0u }; } // Reduction operations template CXXRTL_ALWAYS_INLINE value reduce_and(const value &a) { return value { a.bit_not().is_zero() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value reduce_or(const value &a) { return value { a ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value reduce_xor(const value &a) { return value { (a.ctpop() % 2) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value reduce_xnor(const value &a) { return value { (a.ctpop() % 2) ? 0u : 1u }; } template CXXRTL_ALWAYS_INLINE value reduce_bool(const value &a) { return value { a ? 1u : 0u }; } // Bitwise operations template CXXRTL_ALWAYS_INLINE value not_u(const value &a) { return a.template zcast().bit_not(); } template CXXRTL_ALWAYS_INLINE value not_s(const value &a) { return a.template scast().bit_not(); } template CXXRTL_ALWAYS_INLINE value and_uu(const value &a, const value &b) { return a.template zcast().bit_and(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value and_ss(const value &a, const value &b) { return a.template scast().bit_and(b.template scast()); } template CXXRTL_ALWAYS_INLINE value or_uu(const value &a, const value &b) { return a.template zcast().bit_or(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value or_ss(const value &a, const value &b) { return a.template scast().bit_or(b.template scast()); } template CXXRTL_ALWAYS_INLINE value xor_uu(const value &a, const value &b) { return a.template zcast().bit_xor(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value xor_ss(const value &a, const value &b) { return a.template scast().bit_xor(b.template scast()); } template CXXRTL_ALWAYS_INLINE value xnor_uu(const value &a, const value &b) { return a.template zcast().bit_xor(b.template zcast()).bit_not(); } template CXXRTL_ALWAYS_INLINE value xnor_ss(const value &a, const value &b) { return a.template scast().bit_xor(b.template scast()).bit_not(); } template CXXRTL_ALWAYS_INLINE value shl_uu(const value &a, const value &b) { return a.template zcast().shl(b); } template CXXRTL_ALWAYS_INLINE value shl_su(const value &a, const value &b) { return a.template scast().shl(b); } template CXXRTL_ALWAYS_INLINE value sshl_uu(const value &a, const value &b) { return a.template zcast().shl(b); } template CXXRTL_ALWAYS_INLINE value sshl_su(const value &a, const value &b) { return a.template scast().shl(b); } template CXXRTL_ALWAYS_INLINE value shr_uu(const value &a, const value &b) { return a.shr(b).template zcast(); } template CXXRTL_ALWAYS_INLINE value shr_su(const value &a, const value &b) { return a.shr(b).template scast(); } template CXXRTL_ALWAYS_INLINE value sshr_uu(const value &a, const value &b) { return a.shr(b).template zcast(); } template CXXRTL_ALWAYS_INLINE value sshr_su(const value &a, const value &b) { return a.sshr(b).template scast(); } template CXXRTL_ALWAYS_INLINE value shift_uu(const value &a, const value &b) { return shr_uu(a, b); } template CXXRTL_ALWAYS_INLINE value shift_su(const value &a, const value &b) { return shr_su(a, b); } template CXXRTL_ALWAYS_INLINE value shift_us(const value &a, const value &b) { return b.is_neg() ? shl_uu(a, b.template sext().neg()) : shr_uu(a, b); } template CXXRTL_ALWAYS_INLINE value shift_ss(const value &a, const value &b) { return b.is_neg() ? shl_su(a, b.template sext().neg()) : shr_su(a, b); } template CXXRTL_ALWAYS_INLINE value shiftx_uu(const value &a, const value &b) { return shift_uu(a, b); } template CXXRTL_ALWAYS_INLINE value shiftx_su(const value &a, const value &b) { return shift_su(a, b); } template CXXRTL_ALWAYS_INLINE value shiftx_us(const value &a, const value &b) { return shift_us(a, b); } template CXXRTL_ALWAYS_INLINE value shiftx_ss(const value &a, const value &b) { return shift_ss(a, b); } // Comparison operations template CXXRTL_ALWAYS_INLINE value eq_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value{ a.template zext() == b.template zext() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value eq_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value{ a.template sext() == b.template sext() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value ne_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value{ a.template zext() != b.template zext() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value ne_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value{ a.template sext() != b.template sext() ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value eqx_uu(const value &a, const value &b) { return eq_uu(a, b); } template CXXRTL_ALWAYS_INLINE value eqx_ss(const value &a, const value &b) { return eq_ss(a, b); } template CXXRTL_ALWAYS_INLINE value nex_uu(const value &a, const value &b) { return ne_uu(a, b); } template CXXRTL_ALWAYS_INLINE value nex_ss(const value &a, const value &b) { return ne_ss(a, b); } template CXXRTL_ALWAYS_INLINE value gt_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { b.template zext().ucmp(a.template zext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value gt_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { b.template sext().scmp(a.template sext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value ge_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { !a.template zext().ucmp(b.template zext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value ge_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { !a.template sext().scmp(b.template sext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value lt_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { a.template zext().ucmp(b.template zext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value lt_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { a.template sext().scmp(b.template sext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value le_uu(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { !b.template zext().ucmp(a.template zext()) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value le_ss(const value &a, const value &b) { constexpr size_t BitsExt = max(BitsA, BitsB); return value { !b.template sext().scmp(a.template sext()) ? 1u : 0u }; } // Arithmetic operations template CXXRTL_ALWAYS_INLINE value pos_u(const value &a) { return a.template zcast(); } template CXXRTL_ALWAYS_INLINE value pos_s(const value &a) { return a.template scast(); } template CXXRTL_ALWAYS_INLINE value neg_u(const value &a) { return a.template zcast().neg(); } template CXXRTL_ALWAYS_INLINE value neg_s(const value &a) { return a.template scast().neg(); } template CXXRTL_ALWAYS_INLINE value add_uu(const value &a, const value &b) { return a.template zcast().add(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value add_ss(const value &a, const value &b) { return a.template scast().add(b.template scast()); } template CXXRTL_ALWAYS_INLINE value sub_uu(const value &a, const value &b) { return a.template zcast().sub(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value sub_ss(const value &a, const value &b) { return a.template scast().sub(b.template scast()); } template CXXRTL_ALWAYS_INLINE value mul_uu(const value &a, const value &b) { constexpr size_t BitsM = BitsA >= BitsB ? BitsA : BitsB; return a.template zcast().template mul(b.template zcast()); } template CXXRTL_ALWAYS_INLINE value mul_ss(const value &a, const value &b) { return a.template scast().template mul(b.template scast()); } template 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(); 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) { 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 CXXRTL_ALWAYS_INLINE value div_uu(const value &a, const value &b) { return divmod_uu(a, b).first; } template CXXRTL_ALWAYS_INLINE value div_ss(const value &a, const value &b) { return divmod_ss(a, b).first; } template CXXRTL_ALWAYS_INLINE value mod_uu(const value &a, const value &b) { return divmod_uu(a, b).second; } template CXXRTL_ALWAYS_INLINE 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; size_t index; template memory_index(const value &addr, size_t offset, size_t depth) { static_assert(value::chunks <= 1, "memory address is too wide"); size_t offset_index = addr.data[0]; valid = (offset_index >= offset && offset_index < offset + depth); index = offset_index - offset; } }; } // namespace cxxrtl_yosys #endif yosys-0.52/backends/cxxrtl/runtime/cxxrtl/cxxrtl_replay.h000066400000000000000000000724701477540374200240330ustar00rootroot00000000000000/* * 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() { int fd; if ((fd = writefd.exchange(-1)) != -1) close(fd); if ((fd = readfd.exchange(-1)) != -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 yosys-0.52/backends/cxxrtl/runtime/cxxrtl/cxxrtl_time.h000066400000000000000000000140361477540374200234670ustar00rootroot00000000000000/* * 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 yosys-0.52/backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h000066400000000000000000000206751477540374200233130ustar00rootroot00000000000000/* * 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. * */ #ifndef CXXRTL_VCD_H #define CXXRTL_VCD_H #include namespace cxxrtl { class vcd_writer { struct variable { size_t ident; size_t width; chunk_t *curr; 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; bool streaming = false; void emit_timescale(unsigned number, const std::string &unit) { assert(!streaming); assert(number == 1 || number == 10 || number == 100); assert(unit == "s" || unit == "ms" || unit == "us" || unit == "ns" || unit == "ps" || unit == "fs"); buffer += "$timescale " + std::to_string(number) + " " + unit + " $end\n"; } void emit_scope(const std::vector &scope) { assert(!streaming); size_t same_scope_count = 0; while ((same_scope_count < current_scope.size()) && (same_scope_count < scope.size()) && (current_scope[same_scope_count] == scope[same_scope_count])) { same_scope_count++; } while (current_scope.size() > same_scope_count) { buffer += "$upscope $end\n"; current_scope.pop_back(); } while (current_scope.size() < scope.size()) { buffer += "$scope module " + scope[current_scope.size()] + " $end\n"; current_scope.push_back(scope[current_scope.size()]); } } void emit_ident(size_t ident) { do { buffer += '!' + ident % 94; // "base94" ident /= 94; } 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 += " "; emit_name(name); if (multipart || name.back() == ']' || lsb_at != 0) { if (var.width == 1) buffer += " [" + std::to_string(lsb_at) + "]"; else buffer += " [" + std::to_string(lsb_at + var.width - 1) + ":" + std::to_string(lsb_at) + "]"; } buffer += " $end\n"; } void emit_enddefinitions() { assert(!streaming); buffer += "$enddefinitions $end\n"; streaming = true; } void emit_time(uint64_t timestamp) { assert(streaming); buffer += "#" + std::to_string(timestamp) + "\n"; } void emit_scalar(const variable &var) { assert(streaming); assert(var.width == 1); buffer += (*var.curr ? '1' : '0'); emit_ident(var.ident); buffer += '\n'; } void emit_vector(const variable &var) { assert(streaming); buffer += 'b'; for (size_t bit = var.width - 1; bit != (size_t)-1; bit--) { bool bit_curr = var.curr[bit / (8 * sizeof(chunk_t))] & (1 << (bit % (8 * sizeof(chunk_t)))); buffer += (bit_curr ? '1' : '0'); } if (var.width == 0) buffer += '0'; buffer += ' '; emit_ident(var.ident); buffer += '\n'; } 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, outline_it->first, &outline_it->second }); } else { 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(); } } bool test_variable(const variable &var) { 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.cache_offset])) { return false; } else { std::copy(&var.curr[0], &var.curr[chunks], &cache[var.cache_offset]); return true; } } static std::vector split_hierarchy(const std::string &hier_name) { std::vector hierarchy; size_t prev = 0; while (true) { size_t curr = hier_name.find_first_of(' ', prev); if (curr == std::string::npos) { hierarchy.push_back(hier_name.substr(prev)); break; } else { hierarchy.push_back(hier_name.substr(prev, curr - prev)); prev = curr + 1; } } return hierarchy; } public: std::string buffer; void timescale(unsigned number, const std::string &unit) { emit_timescale(number, unit); } void add(const std::string &hier_name, const debug_item &item, bool multipart = false) { std::vector scope = split_hierarchy(hier_name); std::string name = scope.back(); scope.pop_back(); emit_scope(scope); switch (item.type) { // Not the best naming but oh well... case debug_item::VALUE: emit_var(register_variable(item.width, item.curr, /*constant=*/item.next == nullptr), "wire", name, item.lsb_at, multipart); break; case debug_item::WIRE: emit_var(register_variable(item.width, item.curr), "reg", name, item.lsb_at, multipart); break; case debug_item::MEMORY: { const size_t stride = (item.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8); for (size_t index = 0; index < item.depth; index++) { chunk_t *nth_curr = &item.curr[stride * index]; std::string nth_name = name + '[' + std::to_string(index) + ']'; emit_var(register_variable(item.width, nth_curr), "reg", nth_name, item.lsb_at, multipart); } break; } case debug_item::ALIAS: // Like VALUE, but, even though `item.next == nullptr` always holds, the underlying value // can actually change, and must be tracked. In most cases the VCD identifier will be // unified with the aliased reg, but we should handle the case where only the alias is // added to the VCD writer, too. 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; } } template void add(const debug_items &items, const Filter &filter) { // `debug_items` is a map, so the items are already sorted in an order optimal for emitting // VCD scope sections. for (auto &it : items.table) for (auto &part : it.second) if (filter(it.first, part)) add(it.first, part, it.second.size() > 1); } void add(const debug_items &items) { this->add(items, [](const std::string &, const debug_item &) { return true; }); } void add_without_memories(const debug_items &items) { this->add(items, [](const std::string &, const debug_item &item) { return item.type != debug_item::MEMORY; }); } void sample(uint64_t timestamp) { bool first_sample = !streaming; if (first_sample) { emit_scope({}); emit_enddefinitions(); } reset_outlines(); emit_time(timestamp); for (auto var : variables) if (test_variable(var) || first_sample) { if (var.width == 1) emit_scalar(var); else emit_vector(var); } } }; } #endif yosys-0.52/backends/edif/000077500000000000000000000000001477540374200153245ustar00rootroot00000000000000yosys-0.52/backends/edif/Makefile.inc000066400000000000000000000000371477540374200175340ustar00rootroot00000000000000 OBJS += backends/edif/edif.o yosys-0.52/backends/edif/edif.cc000066400000000000000000000501271477540374200165470ustar00rootroot00000000000000/* * 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. * */ // [[CITE]] EDIF Version 2 0 0 Grammar // http://web.archive.org/web/20050730021644/http://www.edif.org/documentation/BNF_GRAMMAR/index.html #include "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN #define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str() #define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(RTLIL::unescape_id(_id), true, _ren, _bl, _br).c_str() #define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str() struct EdifNames { int counter; char delim_left, delim_right; std::set generated_names, used_names; std::map name_map; EdifNames() : counter(1), delim_left('['), delim_right(']') { } std::string operator()(std::string id, bool define, bool port_rename = false, int range_left = 0, int range_right = 0) { if (define) { std::string new_id = operator()(id, false); if (port_rename) return stringf("(rename %s \"%s%c%d:%d%c\")", new_id.c_str(), id.c_str(), delim_left, range_left, range_right, delim_right); return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id; } if (name_map.count(id) > 0) return name_map.at(id); if (generated_names.count(id) > 0) goto do_rename; if (id == "GND" || id == "VCC") goto do_rename; for (size_t i = 0; i < id.size(); i++) { if ('A' <= id[i] && id[i] <= 'Z') continue; if ('a' <= id[i] && id[i] <= 'z') continue; if ('0' <= id[i] && id[i] <= '9' && i > 0) continue; if (id[i] == '_' && i > 0 && i != id.size()-1) continue; goto do_rename; } used_names.insert(id); return id; do_rename:; std::string gen_name; while (1) { gen_name = stringf("id%05d", counter++); if (generated_names.count(gen_name) == 0 && used_names.count(gen_name) == 0) break; } generated_names.insert(gen_name); name_map[id] = gen_name; return gen_name; } }; struct EdifBackend : public Backend { EdifBackend() : Backend("edif", "write design to EDIF netlist file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_edif [options] [filename]\n"); log("\n"); log("Write the current design to an EDIF netlist file.\n"); log("\n"); log(" -top top_module\n"); log(" set the specified module as design top module\n"); log("\n"); log(" -nogndvcc\n"); log(" do not create \"GND\" and \"VCC\" cells. (this will produce an error\n"); log(" if the design contains constant nets. use \"hilomap\" to map to custom\n"); log(" constant drivers first)\n"); log("\n"); log(" -gndvccy\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"); log("\n"); log(" -keep\n"); log(" create extra KEEP nets by allowing a cell to drive multiple nets.\n"); log("\n"); log(" -pvector {par|bra|ang}\n"); 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"); log("is targeted.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { log_header(design, "Executing EDIF backend.\n"); 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); EdifNames edif_names; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-top" && argidx+1 < args.size()) { top_module_name = args[++argidx]; continue; } if (args[argidx] == "-nogndvcc") { nogndvcc = true; continue; } if (args[argidx] == "-gndvccy") { gndvccy = true; continue; } if (args[argidx] == "-attrprop") { attr_properties = true; continue; } if (args[argidx] == "-keep") { keepmode = true; continue; } if (args[argidx] == "-pvector" && argidx+1 < args.size()) { std::string parray; port_rename = true; parray = args[++argidx]; if (parray == "par") { edif_names.delim_left = '(';edif_names.delim_right = ')'; } else if (parray == "ang") { edif_names.delim_left = '<';edif_names.delim_right = '>'; } else { edif_names.delim_left = '[';edif_names.delim_right = ']'; } continue; } if (args[argidx] == "-lsbidx") { lsbidx = true; continue; } break; } extra_args(f, filename, args, argidx); if (top_module_name.empty()) for (auto module : design->modules()) if (module->get_bool_attribute(ID::top)) top_module_name = module->name.str(); 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; if (top_module_name.empty()) top_module_name = module->name.str(); if (module->processes.size() != 0) log_error("Found unmapped processes in module %s: unmapped processes are not supported in EDIF backend!\n", log_id(module->name)); if (module->memories.size() != 0) log_error("Found unmapped memories in module %s: unmapped memories are not supported in EDIF backend!\n", log_id(module->name)); 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] = std::max(lib_cell_ports[cell->type][p.first], GetSize(p.second)); } } } if (top_module_name.empty()) log_error("No module found in design!\n"); *f << stringf("(edif %s\n", EDIF_DEF(top_module_name)); *f << stringf(" (edifVersion 2 0 0)\n"); *f << stringf(" (edifLevel 0)\n"); *f << stringf(" (keywordMap (keywordLevel 0))\n"); *f << stringf(" (comment \"Generated by %s\")\n", yosys_version_str); *f << stringf(" (external LIB\n"); *f << stringf(" (edifLevel 0)\n"); *f << stringf(" (technology (numberDefinition))\n"); if (!nogndvcc) { *f << stringf(" (cell GND\n"); *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'G'); *f << stringf(" )\n"); *f << stringf(" )\n"); *f << stringf(" (cell VCC\n"); *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'P'); *f << stringf(" )\n"); *f << stringf(" )\n"); } for (auto &cell_it : lib_cell_ports) { *f << stringf(" (cell %s\n", EDIF_DEF(cell_it.first)); *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); *f << stringf(" (interface\n"); for (auto &port_it : cell_it.second) { const char *dir = "INOUT"; if (ct.cell_known(cell_it.first)) { if (!ct.cell_output(cell_it.first, port_it.first)) dir = "INPUT"; else if (!ct.cell_input(cell_it.first, port_it.first)) dir = "OUTPUT"; } int width = port_it.second; int start = 0; bool upto = false; auto m = design->module(cell_it.first); if (m) { auto w = m->wire(port_it.first); if (w) { width = GetSize(w); start = w->start_offset; upto = w->upto; } } if (width == 1) *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir); else { int b[2]; b[upto ? 0 : 1] = start; b[upto ? 1 : 0] = start+width-1; *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), width, dir); } } *f << stringf(" )\n"); *f << stringf(" )\n"); *f << stringf(" )\n"); } *f << stringf(" )\n"); std::vector sorted_modules; // extract module dependencies std::map> module_deps; for (auto module : design->modules()) { module_deps[module] = std::set(); for (auto cell : module->cells()) if (design->module(cell->type) != nullptr) module_deps[module].insert(design->module(cell->type)); } // simple good-enough topological sort // (O(n*m) on n elements and depth m) while (module_deps.size() > 0) { size_t sorted_modules_idx = sorted_modules.size(); for (auto &it : module_deps) { for (auto &dep : it.second) if (module_deps.count(dep) > 0) goto not_ready_yet; // log("Next in topological sort: %s\n", log_id(it.first->name)); sorted_modules.push_back(it.first); not_ready_yet:; } if (sorted_modules_idx == sorted_modules.size()) log_error("Cyclic dependency between modules found! Cycle includes module %s.\n", log_id(module_deps.begin()->first->name)); while (sorted_modules_idx < sorted_modules.size()) module_deps.erase(sorted_modules.at(sorted_modules_idx++)); } *f << stringf(" (library DESIGN\n"); *f << stringf(" (edifLevel 0)\n"); *f << stringf(" (technology (numberDefinition))\n"); auto add_prop = [&](IdString name, Const val) { if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0) *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str()); else if (val.size() <= 32 && RTLIL::SigSpec(val).is_fully_def()) *f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int()); else { std::string hex_string = ""; for (auto i = 0; i < val.size(); i += 4) { int digit_value = 0; if (i+0 < val.size() && val.at(i+0) == RTLIL::State::S1) digit_value |= 1; if (i+1 < val.size() && val.at(i+1) == RTLIL::State::S1) digit_value |= 2; if (i+2 < val.size() && val.at(i+2) == RTLIL::State::S1) digit_value |= 4; if (i+3 < val.size() && val.at(i+3) == RTLIL::State::S1) digit_value |= 8; char digit_str[2] = { "0123456789abcdef"[digit_value], 0 }; hex_string = std::string(digit_str) + hex_string; } *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val), hex_string.c_str()); } }; for (auto module : sorted_modules) { if (module->get_blackbox_attribute()) continue; SigMap sigmap(module); std::map>> net_join_db; *f << stringf(" (cell %s\n", EDIF_DEF(module->name)); *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); *f << stringf(" (interface\n"); for (auto cell : module->cells()) { for (auto &conn : cell->connections()) if (cell->output(conn.first)) sigmap.add(conn.second); } for (auto wire : module->wires()) for (auto b1 : SigSpec(wire)) { auto b2 = sigmap(b1); if (b1 == b2 || !b2.wire) continue; log_assert(b1.wire != nullptr); Wire *w1 = b1.wire; Wire *w2 = b2.wire; { int c1 = w1->get_bool_attribute(ID::keep); int c2 = w2->get_bool_attribute(ID::keep); if (c1 > c2) goto promote; if (c1 < c2) goto nopromote; } { int c1 = w1->name.isPublic(); int c2 = w2->name.isPublic(); if (c1 > c2) goto promote; if (c1 < c2) goto nopromote; } { auto count_nontrivial_attr = [](Wire *w) { int count = w->attributes.size(); count -= w->attributes.count(ID::src); count -= w->attributes.count(ID::unused_bits); return count; }; int c1 = count_nontrivial_attr(w1); int c2 = count_nontrivial_attr(w2); if (c1 > c2) goto promote; if (c1 < c2) goto nopromote; } { int c1 = w1->port_id ? INT_MAX - w1->port_id : 0; int c2 = w2->port_id ? INT_MAX - w2->port_id : 0; if (c1 > c2) goto promote; if (c1 < c2) goto nopromote; } nopromote: if (0) promote: sigmap.add(b1); } for (auto wire : module->wires()) { if (wire->port_id == 0) continue; const char *dir = "INOUT"; if (!wire->port_output) dir = "INPUT"; else if (!wire->port_input) dir = "OUTPUT"; if (wire->width == 1) { *f << stringf(" (port %s (direction %s)", EDIF_DEF(wire->name), dir); if (attr_properties) for (auto &p : wire->attributes) add_prop(p.first, p.second); *f << ")\n"; RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire)); net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input)); } else { int b[2]; b[wire->upto ? 0 : 1] = wire->start_offset; b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1; *f << stringf(" (port (array %s %d) (direction %s)", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir); if (attr_properties) for (auto &p : wire->attributes) add_prop(p.first, p.second); *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), lsbidx ? i : GetSize(wire)-i-1), wire->port_input)); } } } *f << stringf(" )\n"); *f << stringf(" (contents\n"); if (!nogndvcc) { *f << stringf(" (instance GND (viewRef VIEW_NETLIST (cellRef GND (libraryRef LIB))))\n"); *f << stringf(" (instance VCC (viewRef VIEW_NETLIST (cellRef VCC (libraryRef LIB))))\n"); } for (auto cell : module->cells()) { *f << stringf(" (instance %s\n", EDIF_DEF(cell->name)); *f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type), lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : ""); for (auto &p : cell->parameters) add_prop(p.first, p.second); if (attr_properties) for (auto &p : cell->attributes) add_prop(p.first, p.second); *f << stringf(")\n"); for (auto &p : cell->connections()) { RTLIL::SigSpec sig = sigmap(p.second); for (int i = 0; i < GetSize(sig); i++) if (sig[i].wire == NULL && sig[i] != RTLIL::State::S0 && sig[i] != RTLIL::State::S1) 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 = 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 = lsbidx ? i : GetSize(w)-i-1; width = GetSize(w); } } if (width == 1) net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first))); else { net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))", EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first))); } } } } for (auto &it : net_join_db) { RTLIL::SigBit sig = it.first; if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) { if (sig == RTLIL::State::Sx) { for (auto &ref : it.second) log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str()); sig = RTLIL::State::S0; } else if (sig == RTLIL::State::Sz) { continue; } else { for (auto &ref : it.second) log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str()); log_abort(); } } std::string netname; if (sig == RTLIL::State::S0) netname = "GND_NET"; else if (sig == RTLIL::State::S1) netname = "VCC_NET"; else { netname = log_signal(sig); for (size_t i = 0; i < netname.size(); i++) if (netname[i] == ' ' || netname[i] == '\\') netname.erase(netname.begin() + i--); } *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); for (auto &ref : it.second) *f << stringf(" %s\n", ref.first.c_str()); if (sig.wire == NULL) { if (nogndvcc) log_error("Design contains constant nodes (map with \"hilomap\" first).\n"); if (sig == RTLIL::State::S0) *f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G'); if (sig == RTLIL::State::S1) *f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P'); } *f << stringf(" )"); if (attr_properties && sig.wire != NULL) for (auto &p : sig.wire->attributes) add_prop(p.first, p.second); *f << stringf("\n )\n"); } for (auto wire : module->wires()) { if (!wire->get_bool_attribute(ID::keep)) continue; for(int i = 0; i < wire->width; i++) { SigBit raw_sig = RTLIL::SigSpec(wire, i); SigBit mapped_sig = sigmap(raw_sig); if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0) continue; std::string netname = log_signal(raw_sig); for (size_t i = 0; i < netname.size(); i++) if (netname[i] == ' ' || netname[i] == '\\') netname.erase(netname.begin() + i--); if (keepmode) { *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); auto &refs = net_join_db.at(mapped_sig); for (auto &ref : refs) if (ref.second) *f << stringf(" %s\n", ref.first.c_str()); *f << stringf(" )"); if (attr_properties && raw_sig.wire != NULL) for (auto &p : raw_sig.wire->attributes) add_prop(p.first, p.second); *f << stringf("\n )\n"); } else { log_warning("Ignoring conflicting 'keep' property on net %s. Use -keep to generate the extra net nevertheless.\n", EDIF_DEF(netname)); } } } *f << stringf(" )\n"); *f << stringf(" )\n"); *f << stringf(" )\n"); } *f << stringf(" )\n"); *f << stringf(" (design %s\n", EDIF_DEF(top_module_name)); *f << stringf(" (cellRef %s (libraryRef DESIGN))\n", EDIF_REF(top_module_name)); *f << stringf(" )\n"); *f << stringf(")\n"); } } EdifBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/edif/runtest.py000066400000000000000000000103551477540374200174060ustar00rootroot00000000000000#!/usr/bin/env python3 import os import numpy as np enable_upto = True enable_offset = True enable_hierarchy = True enable_logic = True def make_module(f, modname, width, subs): print("module %s (A, B, C, X, Y, Z);" % modname, file=f) inbits = list() outbits = list() for p in "ABC": offset = np.random.randint(10) if enable_offset else 0 if enable_upto and np.random.randint(2): print(" input [%d:%d] %s;" % (offset, offset+width-1, p), file=f) else: print(" input [%d:%d] %s;" % (offset+width-1, offset, p), file=f) for i in range(offset, offset+width): inbits.append("%s[%d]" % (p, i)) for p in "XYZ": offset = np.random.randint(10) if enable_offset else 0 if enable_upto and np.random.randint(2): print(" output [%d:%d] %s;" % (offset, offset+width-1, p), file=f) else: print(" output [%d:%d] %s;" % (offset+width-1, offset, p), file=f) for i in range(offset, offset+width): outbits.append("%s[%d]" % (p, i)) instidx = 0 subcandidates = list(subs.keys()) while len(outbits) > 0: submod = None if len(subcandidates): submod = np.random.choice(subcandidates) subcandidates.remove(submod) if submod is None or 3*subs[submod] >= len(outbits): for bit in outbits: if enable_logic: print(" assign %s = %s & ~%s;" % (bit, np.random.choice(inbits), np.random.choice(inbits)), file=f) else: print(" assign %s = %s;" % (bit, np.random.choice(inbits)), file=f) break instidx += 1 print(" %s inst%d (" % (submod, instidx), file=f) for p in "ABC": print(" .%s({%s})," % (p, ",".join(np.random.choice(inbits, subs[submod]))), file=f) for p in "XYZ": bits = list(np.random.choice(outbits, subs[submod], False)) for bit in bits: outbits.remove(bit) print(" .%s({%s})%s" % (p, ",".join(bits), "," if p != "Z" else ""), file=f) print(" );", file=f); print("endmodule", file=f) with open("test_top.v", "w") as f: if enable_hierarchy: make_module(f, "sub1", 2, {}) make_module(f, "sub2", 3, {}) make_module(f, "sub3", 4, {}) make_module(f, "sub4", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) make_module(f, "sub5", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) make_module(f, "sub6", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) make_module(f, "top", 32, {"sub4": 8, "sub5": 8, "sub6": 8}) else: make_module(f, "top", 32, {}) os.system("set -x; ../../yosys -p 'synth_xilinx -top top; write_edif -pvector par test_syn.edif' test_top.v") with open("test_syn.tcl", "w") as f: print("read_edif test_syn.edif", file=f) print("link_design", file=f) print("write_verilog -force test_syn.v", file=f) os.system("set -x; vivado -nojournal -nolog -mode batch -source test_syn.tcl") with open("test_tb.v", "w") as f: print("module tb;", file=f) print(" reg [31:0] A, B, C;", file=f) print(" wire [31:0] X, Y, Z;", file=f) print("", file=f) print(" top uut (", file=f) print(" .A(A),", file=f) print(" .B(B),", file=f) print(" .C(C),", file=f) print(" .X(X),", file=f) print(" .Y(Y),", file=f) print(" .Z(Z)", file=f) print(" );", file=f) print("", file=f) print(" initial begin", file=f) for i in range(100): print(" A = 32'h%08x;" % np.random.randint(2**32), file=f) print(" B = 32'h%08x;" % np.random.randint(2**32), file=f) print(" C = 32'h%08x;" % np.random.randint(2**32), file=f) print(" #10;", file=f) print(" $display(\"%x %x %x\", X, Y, Z);", file=f) print(" #10;", file=f) print(" $finish;", file=f) print(" end", file=f) print("endmodule", file=f) os.system("set -x; iverilog -o test_gold test_tb.v test_top.v") os.system("set -x; iverilog -o test_gate test_tb.v test_syn.v ../../techlibs/xilinx/cells_sim.v") os.system("set -x; ./test_gold > test_gold.out") os.system("set -x; ./test_gate > test_gate.out") os.system("set -x; md5sum test_gold.out test_gate.out") yosys-0.52/backends/firrtl/000077500000000000000000000000001477540374200157175ustar00rootroot00000000000000yosys-0.52/backends/firrtl/.gitignore000066400000000000000000000000241477540374200177030ustar00rootroot00000000000000test.fir test_out.v yosys-0.52/backends/firrtl/Makefile.inc000066400000000000000000000000431477540374200201240ustar00rootroot00000000000000 OBJS += backends/firrtl/firrtl.o yosys-0.52/backends/firrtl/firrtl.cc000066400000000000000000001233121477540374200175320ustar00rootroot00000000000000/* * 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 "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" #include #include #include #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN pool used_names; dict namecache; int autoid_counter; typedef unsigned FDirection; static const FDirection FD_NODIRECTION = 0x0; static const FDirection FD_IN = 0x1; static const FDirection FD_OUT = 0x2; static const FDirection FD_INOUT = 0x3; static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width std::string getFileinfo(const RTLIL::AttrObject *design_entity) { std::string src(design_entity->get_src_attribute()); std::string fileinfo_str = src.empty() ? "" : "@[" + src + "]"; return fileinfo_str; } // Get a port direction with respect to a specific module. FDirection getPortFDirection(IdString id, Module *module) { Wire *wire = module->wires_.at(id); FDirection direction = FD_NODIRECTION; if (wire && wire->port_id) { if (wire->port_input) direction |= FD_IN; if (wire->port_output) direction |= FD_OUT; } return direction; } string next_id() { string new_id; while (1) { new_id = stringf("_%d", autoid_counter++); if (used_names.count(new_id) == 0) break; } used_names.insert(new_id); return new_id; } const char *make_id(IdString id) { if (namecache.count(id) != 0) return namecache.at(id).c_str(); string new_id = log_id(id); for (int i = 0; i < GetSize(new_id); i++) { char &ch = new_id[i]; if ('a' <= ch && ch <= 'z') continue; if ('A' <= ch && ch <= 'Z') continue; if ('0' <= ch && ch <= '9' && i != 0) continue; if ('_' == ch) continue; ch = '_'; } while (used_names.count(new_id) != 0) new_id += '_'; namecache[id] = new_id; used_names.insert(new_id); return namecache.at(id).c_str(); } std::string dump_const_string(const RTLIL::Const &data) { std::string res_str; 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]; } return res_str; } std::string dump_const(const RTLIL::Const &data) { std::string res_str; // // 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.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[i]) { case State::S0: break; case State::S1: int_val |= (1 << i); break; default: log_error("Unexpected int value\n"); break; } } res_str += stringf("%d", int_val); } 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[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 += "\""; } } 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)); } 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() ); 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) { 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++) reverse_wire_map[sig[i]] = make_pair(id, i); } FirrtlWorker(Module *module, std::ostream &f, RTLIL::Design *theDesign) : module(module), f(f), design(theDesign), indent(" ") { } static string make_expr(const SigSpec &sig) { string expr; for (auto chunk : sig.chunks()) { string new_expr; if (chunk.wire == nullptr) { std::vector bits = chunk.data; new_expr = stringf("UInt<%d>(\"h", GetSize(bits)); while (GetSize(bits) % 4 != 0) bits.push_back(State::S0); for (int i = GetSize(bits)-4; i >= 0; i -= 4) { int val = 0; if (bits[i+0] == State::S1) val += 1; if (bits[i+1] == State::S1) val += 2; if (bits[i+2] == State::S1) val += 4; if (bits[i+3] == State::S1) val += 8; new_expr.push_back(val < 10 ? '0' + val : 'a' + val - 10); } new_expr += "\")"; } else if (chunk.offset == 0 && chunk.width == chunk.wire->width) { new_expr = make_id(chunk.wire->name); } else { string wire_id = make_id(chunk.wire->name); new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset); } if (expr.empty()) expr = new_expr; else expr = "cat(" + new_expr + ", " + expr + ")"; } return expr; } std::string fid(RTLIL::IdString internal_id) { return make_id(internal_id); } std::string cellname(RTLIL::Cell *cell) { return fid(cell->name).c_str(); } void process_instance(RTLIL::Cell *cell, vector &wire_exprs) { std::string cell_type = fid(cell->type); std::string instanceOf; // If this is a parameterized module, its parent module is encoded in the cell type if (cell->type.begins_with("$paramod")) { log_assert(cell->has_attribute(ID::hdlname)); instanceOf = cell->get_string_attribute(ID::hdlname); } else { instanceOf = cell_type; } std::string cell_name = cellname(cell); std::string cell_name_comment; if (cell_name != fid(cell->name)) cell_name_comment = " /* " + fid(cell->name) + " */ "; else cell_name_comment = ""; // Find the module corresponding to this instance. auto instModule = design->module(cell->type); // If there is no instance for this, just return. if (instModule == NULL) { 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(), instanceName.c_str(), cellFileinfo.c_str())); for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { if (it->second.size() > 0) { const SigSpec &secondSig = it->second; const std::string firstName = cell_name + "." + make_id(it->first); const std::string secondExpr = make_expr(secondSig); // Find the direction for this port. FDirection dir = getPortFDirection(it->first, instModule); std::string sourceExpr, sinkExpr; const SigSpec *sinkSig = nullptr; switch (dir) { case FD_INOUT: log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second)); YS_FALLTHROUGH case FD_OUT: sourceExpr = firstName; sinkExpr = secondExpr; sinkSig = &secondSig; break; case FD_NODIRECTION: log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second)); YS_FALLTHROUGH case FD_IN: sourceExpr = secondExpr; sinkExpr = firstName; break; default: log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir); break; } // Check for subfield assignment. std::string bitsString = "bits("; if (sinkExpr.compare(0, bitsString.length(), bitsString) == 0) { if (sinkSig == nullptr) log_error("Unknown subfield %s.%s\n", cell_type.c_str(), sinkExpr.c_str()); // Don't generate the assignment here. // Add the source and sink to the "reverse_wire_map" and we'll output the assignment // as part of the coalesced subfield assignments for this wire. register_reverse_wire_map(sourceExpr, *sinkSig); } else { wire_exprs.push_back(stringf("\n%s%s <= %s %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str(), cellFileinfo.c_str())); } } } wire_exprs.push_back(stringf("\n")); } // Given an expression for a shift amount, and a maximum width, // generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics. std::string gen_dshl(const string b_expr, const int b_width) { string result = b_expr; if (b_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) { int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1; string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<name), moduleFileinfo.c_str()); 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()) { const auto wireName = make_id(wire->name); std::string wireFileinfo = getFileinfo(wire); // If a wire has initial data, issue a warning since FIRRTL doesn't currently support it. if (wire->attributes.count(ID::init)) { log_warning("Initial value (%s) for (%s.%s) not supported\n", wire->attributes.at(ID::init).as_string().c_str(), log_id(module), log_id(wire)); } if (wire->port_id) { 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 %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("%swire %s: UInt<%d> %s\n", indent.c_str(), wireName, wire->width, wireFileinfo.c_str())); } } for (auto cell : module->cells()) { Const ndef(0, 0); // Is this cell is a module instance? 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" int b_width = cell->parameters.at(ID::B_WIDTH, ndef).as_int(); // The width of "A" const int y_width = cell->parameters.at(ID::Y_WIDTH, ndef).as_int(); // The width of the result const bool a_signed = cell->parameters.at(ID::A_SIGNED, ndef).as_bool(); const bool b_signed = cell->parameters.at(ID::B_SIGNED, ndef).as_bool(); bool firrtl_is_signed = a_signed; // The result is signed (subsequent code may change this). int firrtl_width = 0; string primop; bool always_uint = false; string y_id = make_id(cell->name); std::string cellFileinfo = getFileinfo(cell); 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("%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 + ")"; } // Don't use the results of logical operations (a single bit) to control padding if (!(cell->type.in(ID($eq), ID($eqx), ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($reduce_bool), ID($logic_not)) && y_width == 1) ) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); } // Assume the FIRRTL width is a single bit. firrtl_width = 1; if (cell->type.in(ID($not), ID($_NOT_))) primop = "not"; else if (cell->type == ID($neg)) { primop = "neg"; firrtl_is_signed = true; // Result of "neg" is signed (an SInt). firrtl_width = a_width; } else if (cell->type == ID($logic_not)) { primop = "eq"; a_expr = stringf("%s, UInt(0)", a_expr.c_str()); } else if (cell->type == ID($reduce_and)) primop = "andr"; else if (cell->type == ID($reduce_or)) primop = "orr"; else if (cell->type == ID($reduce_xor)) primop = "xorr"; else if (cell->type == ID($reduce_xnor)) { primop = "not"; a_expr = stringf("xorr(%s)", a_expr.c_str()); } else if (cell->type == ID($reduce_bool)) { primop = "neq"; // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand. a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width); } string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str()); if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.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($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_), ID($eq), ID($eqx), ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($shr), ID($sshr), ID($sshl), ID($shl), ID($logic_and), ID($logic_or), ID($pow))) { 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("%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 + ")"; // Expand the "A" operand to the result width if (a_width < y_width) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); a_width = y_width; } } // Shift amount is always unsigned, and needn't be padded to result width, // otherwise, we need to cast the b_expr appropriately if (b_signed && !cell->type.in(ID($shr), ID($sshr), ID($shl), ID($sshl), ID($pow))) { b_expr = "asSInt(" + b_expr + ")"; // Expand the "B" operand to the result width if (b_width < y_width) { b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); b_width = y_width; } } // For the arithmetic ops, expand operand widths to result widths befor performing the operation. // This corresponds (according to iverilog) to what verilog compilers implement. if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_))) { if (a_width < y_width) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); a_width = y_width; } if (b_width < y_width) { b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); b_width = y_width; } } // Assume the FIRRTL width is the width of "A" firrtl_width = a_width; auto a_sig = cell->getPort(ID::A); if (cell->type == ID($add)) { primop = "add"; firrtl_is_signed = a_signed | b_signed; firrtl_width = max(a_width, b_width); } else if (cell->type == ID($sub)) { primop = "sub"; firrtl_is_signed = true; int a_widthInc = (!a_signed && b_signed) ? 2 : (a_signed && !b_signed) ? 1 : 0; int b_widthInc = (a_signed && !b_signed) ? 2 : (!a_signed && b_signed) ? 1 : 0; firrtl_width = max(a_width + a_widthInc, b_width + b_widthInc); } else if (cell->type == ID($mul)) { primop = "mul"; firrtl_is_signed = a_signed | b_signed; firrtl_width = a_width + b_width; } else if (cell->type == ID($div)) { primop = "div"; firrtl_is_signed = a_signed | b_signed; firrtl_width = a_width; } else if (cell->type == ID($mod)) { // "rem" = truncating modulo primop = "rem"; firrtl_width = min(a_width, b_width); } else if (cell->type.in(ID($and), ID($_AND_))) { primop = "and"; always_uint = true; firrtl_width = max(a_width, b_width); } else if (cell->type.in(ID($or), ID($_OR_))) { primop = "or"; always_uint = true; firrtl_width = max(a_width, b_width); } else if (cell->type.in(ID($xor), ID($_XOR_))) { primop = "xor"; always_uint = true; firrtl_width = max(a_width, b_width); } else if (cell->type == ID($xnor)) { primop = "xnor"; always_uint = true; firrtl_width = max(a_width, b_width); } 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))) { primop = "neq"; always_uint = true; firrtl_width = 1; } else if (cell->type == ID($gt)) { primop = "gt"; always_uint = true; firrtl_width = 1; } else if (cell->type == ID($ge)) { primop = "geq"; always_uint = true; firrtl_width = 1; } else if (cell->type == ID($lt)) { primop = "lt"; always_uint = true; firrtl_width = 1; } else if (cell->type == ID($le)) { primop = "leq"; always_uint = true; firrtl_width = 1; } 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; // Is the shift amount constant? auto b_sig = cell->getPort(ID::B); if (b_sig.is_fully_const()) { primop = "shl"; int shift_amount = b_sig.as_int(); b_expr = std::to_string(shift_amount); firrtl_width = a_width + shift_amount; } else { primop = "dshl"; // Convert from FIRRTL left shift semantics. b_expr = gen_dshl(b_expr, b_width); firrtl_width = a_width + (1 << b_width) - 1; } } 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? auto b_sig = cell->getPort(ID::B); if (b_sig.is_fully_const()) { primop = "shr"; int shift_amount = b_sig.as_int(); b_expr = std::to_string(shift_amount); firrtl_width = max(1, a_width - shift_amount); } else { primop = "dshr"; firrtl_width = a_width; } // We'll need to do some special fixups if the source (and thus result) is signed. if (firrtl_is_signed) { // If this is a "logical" shift right, pretend the source is unsigned. if (cell->type == ID($shr)) { a_expr = "asUInt(" + a_expr + ")"; } } } else if ((cell->type == ID($logic_and))) { primop = "and"; a_expr = "neq(" + a_expr + ", UInt(0))"; b_expr = "neq(" + b_expr + ", UInt(0))"; always_uint = true; firrtl_width = 1; } else if ((cell->type == ID($logic_or))) { primop = "or"; a_expr = "neq(" + a_expr + ", UInt(0))"; b_expr = "neq(" + b_expr + ", UInt(0))"; always_uint = true; firrtl_width = 1; } else if ((cell->type == ID($pow))) { if (a_sig.is_fully_const() && a_sig.as_int() == 2) { // We'll convert this to a shift. To simplify things, change the a_expr to "1" // so we can use b_expr directly as a shift amount. // Only support 2 ** N (i.e., shift left) // 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. a_expr = firrtl_is_signed ? "SInt(1)" : "UInt(1)"; extract_y_bits = true; // Is the shift amount constant? auto b_sig = cell->getPort(ID::B); if (b_sig.is_fully_const()) { primop = "shl"; int shiftAmount = b_sig.as_int(); if (shiftAmount < 0) { log_error("Negative power exponent - %d: %s.%s\n", shiftAmount, log_id(module), log_id(cell)); } b_expr = std::to_string(shiftAmount); firrtl_width = a_width + shiftAmount; } else { primop = "dshl"; // Convert from FIRRTL left shift semantics. b_expr = gen_dshl(b_expr, b_width); firrtl_width = a_width + (1 << b_width) - 1; } } else { log_error("Non power 2: %s.%s\n", log_id(module), log_id(cell)); } } auto it = cell->parameters.find(ID::B_SIGNED); if (it == cell->parameters.end() || !it->second.as_bool()) { b_expr = "asUInt(" + b_expr + ")"; } string expr; // Deal with $xnor == ~^ (not xor) if (primop == "xnor") { expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str()); } else { expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str()); } // Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result. // If the operation is signed, the FIRRTL width will be 1 one bit larger. if (extract_y_bits) { expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1); } else if (firrtl_is_signed && (firrtl_width + 1) < y_width) { expr = stringf("pad(%s, %d)", expr.c_str(), y_width); } if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.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($mux), ID($_MUX_))) { auto it = cell->parameters.find(ID::WIDTH); int width = it == cell->parameters.end()? 1 : it->second.as_int(); 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("%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 %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->is_mem_cell()) { // Will be handled below, as part of a Mem. continue; } if (cell->type.in(ID($dff))) { bool clkpol = cell->parameters.at(ID::CLK_POLARITY).as_bool(); if (clkpol == false) log_error("Negative edge clock on FF %s.%s.\n", log_id(module), log_id(cell)); int width = cell->parameters.at(ID::WIDTH).as_int(); string expr = make_expr(cell->getPort(ID::D)); string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")"; 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 %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; } if (cell->type == ID($shiftx)) { // assign y = a[b +: y_width]; // We'll extract the correct bits as part of the primop. 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("%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) auto b_string = b_expr.c_str(); int b_sign = cell->parameters.at(ID::B_WIDTH).as_int() - 1; b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string); } string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_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; } if (cell->type == ID($shift)) { // assign y = a >> b; // where b may be negative string a_expr = make_expr(cell->getPort(ID::A)); string b_expr = make_expr(cell->getPort(ID::B)); auto b_string = b_expr.c_str(); string expr; 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. std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_width).c_str(), y_width); std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string); expr = stringf("mux(%s < 0, %s, %s)", b_string, dshl.c_str(), dshr.c_str() ); } else { expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string); } 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; } if (cell->type == ID($pos)) { // assign y = a; // printCell(cell); string a_expr = make_expr(cell->getPort(ID::A)); // Verilog appears to treat the result as signed, so if the result is wider than "A", // we need to pad. if (a_width < y_width) { a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); } 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("%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); } for (auto wire : module->wires()) { string expr; std::string wireFileinfo = getFileinfo(wire); if (wire->port_input) continue; int cursor = 0; bool is_valid = false; bool make_unconn_id = false; while (cursor < wire->width) { int chunk_width = 1; string new_expr; SigBit start_bit(wire, cursor); if (reverse_wire_map.count(start_bit)) { pair start_map = reverse_wire_map.at(start_bit); while (cursor+chunk_width < wire->width) { SigBit stop_bit(wire, cursor+chunk_width); if (reverse_wire_map.count(stop_bit) == 0) break; pair stop_map = reverse_wire_map.at(stop_bit); stop_map.second -= chunk_width; if (start_map != stop_map) break; chunk_width++; } new_expr = stringf("bits(%s, %d, %d)", start_map.first.c_str(), start_map.second + chunk_width - 1, start_map.second); is_valid = true; } else { if (unconn_id.empty()) { unconn_id = next_id(); make_unconn_id = true; } new_expr = unconn_id; } if (expr.empty()) expr = new_expr; else expr = "cat(" + new_expr + ", " + expr + ")"; cursor += chunk_width; } if (is_valid) { if (make_unconn_id) { 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%s is invalid\n", indent.c_str(), unconn_id.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(); } // `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%s is invalid\n", indent.c_str(), make_id(wire->name))); } } for (auto str : port_decls) f << str; f << stringf("\n"); for (auto str : wire_decls) f << str; f << stringf("\n"); for (auto str : mem_exprs) f << str; f << stringf("\n"); for (auto str : cell_exprs) f << str; f << stringf("\n"); for (auto str : wire_exprs) f << str; f << stringf("\n"); } void run() { emit_module(); } }; struct FirrtlBackend : public Backend { FirrtlBackend() : Backend("firrtl", "write design to a FIRRTL file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_firrtl [options] [filename]\n"); log("\n"); 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 { size_t argidx = args.size(); // We aren't expecting any arguments. // If we weren't explicitly passed a filename, use the last argument (if it isn't a flag). if (filename == "") { if (argidx > 0 && args[argidx - 1][0] != '-') { // extra_args and friends need to see this argument. argidx -= 1; filename = args[argidx]; } } extra_args(f, filename, args, argidx); if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing FIRRTL backend.\n"); log_push(); Pass::call(design, "pmuxtree"); Pass::call(design, "bmuxmap"); Pass::call(design, "demuxmap"); Pass::call(design, "bwmuxmap"); namecache.clear(); autoid_counter = 0; // Get the top module, or a reasonable facsimile - we need something for the circuit name. Module *top = design->top_module(); Module *last = nullptr; // Generate module and wire names. for (auto module : design->modules()) { make_id(module->name); last = module; if (top == nullptr && module->get_bool_attribute(ID::top)) { top = module; } for (auto wire : module->wires()) if (wire->port_id) make_id(wire->name); } 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()) { if (!module->get_blackbox_attribute()) { FirrtlWorker worker(module, *f, design); worker.run(); } } namecache.clear(); autoid_counter = 0; } } FirrtlBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/firrtl/test.sh000066400000000000000000000006231477540374200172330ustar00rootroot00000000000000#!/usr/bin/env bash set -ex cd ../../ make cd backends/firrtl ../../yosys -q -p 'prep -nordff; write_firrtl test.fir' $1 firrtl -i test.fir -o test_out.v -ll Info ../../yosys -p " read_verilog $1 rename Top gold read_verilog test_out.v rename Top gate prep memory_map miter -equiv -flatten gold gate miter hierarchy -top miter sat -verify -prove trigger 0 -set-init-zero -seq 10 miter " yosys-0.52/backends/firrtl/test.v000066400000000000000000000034021477540374200170640ustar00rootroot00000000000000module test( input clk, wen, input [7:0] uns, input signed [7:0] a, b, input signed [23:0] c, input signed [2:0] sel, output [15:0] s, d, y, z, u, q, p, mul, div, mod, mux, And, Or, Xor, eq, neq, gt, lt, geq, leq, eqx, shr, sshr, shl, sshl, Land, Lor, Lnot, Not, Neg, pos, Andr, Orr, Xorr, Xnorr, Reduce_bool, output [7:0] PMux ); //initial begin //$display("shr = %b", shr); //end assign s = a+{b[6:2], 2'b1}; assign d = a-b; assign y = x; assign z[7:0] = s+d; assign z[15:8] = s-d; assign p = a & b | x; assign mul = a * b; assign div = a / b; assign mod = a % b; assign mux = x[0] ? a : b; assign And = a & b; assign Or = a | b; assign Xor = a ^ b; assign Not = ~a; assign Neg = -a; assign eq = a == b; assign neq = a != b; assign gt = a > b; assign lt = a < b; assign geq = a >= b; assign leq = a <= b; assign eqx = a === b; assign shr = a >> b; //0111111111000000 assign sshr = a >>> b; assign shl = a << b; assign sshl = a <<< b; assign Land = a && b; assign Lor = a || b; assign Lnot = !a; assign pos = $signed(uns); assign Andr = &a; assign Orr = |a; assign Xorr = ^a; assign Xnorr = ~^a; always @* if(!a) begin Reduce_bool = a; end else begin Reduce_bool = b; end //always @(sel or c or a) // begin // case (sel) // 3'b000: PMux = a; // 3'b001: PMux = c[7:0]; // 3'b010: PMux = c[15:8]; // 3'b100: PMux = c[23:16]; // endcase // end endmodule yosys-0.52/backends/functional/000077500000000000000000000000001477540374200165575ustar00rootroot00000000000000yosys-0.52/backends/functional/Makefile.inc000066400000000000000000000002371477540374200207710ustar00rootroot00000000000000OBJS += backends/functional/cxx.o OBJS += backends/functional/smtlib.o OBJS += backends/functional/smtlib_rosette.o OBJS += backends/functional/test_generic.o yosys-0.52/backends/functional/cxx.cc000066400000000000000000000262151477540374200176760ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2024 Emily Schmidt * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC * * 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/yosys.h" #include "kernel/functional.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN const char *reserved_keywords[] = { "alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit", "atomic_noexcept","auto","bitand","bitor","bool","break","case", "catch","char","char16_t","char32_t","char8_t","class","co_await", "co_return","co_yield","compl","concept","const","const_cast","consteval", "constexpr","constinit","continue","decltype","default","delete", "do","double","dynamic_cast","else","enum","explicit","export", "extern","false","float","for","friend","goto","if","inline", "int","long","mutable","namespace","new","noexcept","not","not_eq", "nullptr","operator","or","or_eq","private","protected","public", "reflexpr","register","reinterpret_cast","requires","return","short", "signed","sizeof","static","static_log_assert","static_cast","struct", "switch","synchronized","template","this","thread_local","throw", "true","try","typedef","typeid","typename","union","unsigned", "using","virtual","void","volatile","wchar_t","while","xor","xor_eq", nullptr }; template struct CxxScope : public Functional::Scope { CxxScope() { for(const char **p = reserved_keywords; *p != nullptr; p++) this->reserve(*p); } bool is_character_legal(char c, int index) override { return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || c == '_' || c == '$'); } }; struct CxxType { Functional::Sort sort; CxxType(Functional::Sort sort) : sort(sort) {} std::string to_string() const { if(sort.is_memory()) { return stringf("Memory<%d, %d>", sort.addr_width(), sort.data_width()); } else if(sort.is_signal()) { return stringf("Signal<%d>", sort.width()); } else { log_error("unknown sort"); } } }; using CxxWriter = Functional::Writer; struct CxxStruct { std::string name; dict types; CxxScope scope; CxxStruct(std::string name) : name(name) { scope.reserve("fn"); scope.reserve("visit"); } void insert(IdString name, CxxType type) { scope(name, name); types.insert({name, type}); } void print(CxxWriter &f) { f.print("\tstruct {} {{\n", name); for (auto p : types) { f.print("\t\t{} {};\n", p.second.to_string(), scope(p.first, p.first)); } f.print("\n\t\ttemplate void visit(T &&fn) {{\n"); for (auto p : types) { f.print("\t\t\tfn(\"{}\", {});\n", RTLIL::unescape_id(p.first), scope(p.first, p.first)); } f.print("\t\t}}\n"); f.print("\t}};\n\n"); }; std::string operator[](IdString field) { return scope(field, field); } }; std::string cxx_const(RTLIL::Const const &value) { std::stringstream ss; ss << "Signal<" << value.size() << ">(" << std::hex << std::showbase; if(value.size() > 32) ss << "{"; for(int i = 0; i < value.size(); i += 32) { if(i > 0) ss << ", "; ss << value.extract(i, 32).as_int(); } if(value.size() > 32) ss << "}"; ss << ")"; return ss.str(); } template struct CxxPrintVisitor : public Functional::AbstractVisitor { using Node = Functional::Node; CxxWriter &f; NodePrinter np; CxxStruct &input_struct; CxxStruct &state_struct; CxxPrintVisitor(CxxWriter &f, NodePrinter np, CxxStruct &input_struct, CxxStruct &state_struct) : f(f), np(np), input_struct(input_struct), state_struct(state_struct) { } template void print(const char *fmt, Args&&... args) { f.print_with(np, fmt, std::forward(args)...); } void buf(Node, Node n) override { print("{}", n); } void slice(Node, Node a, int offset, int out_width) override { print("{0}.slice<{2}>({1})", a, offset, out_width); } void zero_extend(Node, Node a, int out_width) override { print("{}.zero_extend<{}>()", a, out_width); } void sign_extend(Node, Node a, int out_width) override { print("{}.sign_extend<{}>()", a, out_width); } void concat(Node, Node a, Node b) override { print("{}.concat({})", a, b); } void add(Node, Node a, Node b) override { print("{} + {}", a, b); } void sub(Node, Node a, Node b) override { print("{} - {}", a, b); } void mul(Node, Node a, Node b) override { print("{} * {}", a, b); } void unsigned_div(Node, Node a, Node b) override { print("{} / {}", a, b); } void unsigned_mod(Node, Node a, Node b) override { print("{} % {}", a, b); } void bitwise_and(Node, Node a, Node b) override { print("{} & {}", a, b); } void bitwise_or(Node, Node a, Node b) override { print("{} | {}", a, b); } void bitwise_xor(Node, Node a, Node b) override { print("{} ^ {}", a, b); } void bitwise_not(Node, Node a) override { print("~{}", a); } void unary_minus(Node, Node a) override { print("-{}", a); } void reduce_and(Node, Node a) override { print("{}.all()", a); } void reduce_or(Node, Node a) override { print("{}.any()", a); } void reduce_xor(Node, Node a) override { print("{}.parity()", a); } void equal(Node, Node a, Node b) override { print("{} == {}", a, b); } void not_equal(Node, Node a, Node b) override { print("{} != {}", a, b); } void signed_greater_than(Node, Node a, Node b) override { print("{}.signed_greater_than({})", a, b); } void signed_greater_equal(Node, Node a, Node b) override { print("{}.signed_greater_equal({})", a, b); } void unsigned_greater_than(Node, Node a, Node b) override { print("{} > {}", a, b); } void unsigned_greater_equal(Node, Node a, Node b) override { print("{} >= {}", a, b); } void logical_shift_left(Node, Node a, Node b) override { print("{} << {}", a, b); } void logical_shift_right(Node, Node a, Node b) override { print("{} >> {}", a, b); } void arithmetic_shift_right(Node, Node a, Node b) override { print("{}.arithmetic_shift_right({})", a, b); } void mux(Node, Node a, Node b, Node s) override { print("{2}.any() ? {1} : {0}", a, b, s); } void constant(Node, RTLIL::Const const & value) override { print("{}", cxx_const(value)); } void input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); print("input.{}", input_struct[name]); } void state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); print("current_state.{}", state_struct[name]); } void memory_read(Node, Node mem, Node addr) override { print("{}.read({})", mem, addr); } void memory_write(Node, Node mem, Node addr, Node data) override { print("{}.write({}, {})", mem, addr, data); } }; bool equal_def(RTLIL::Const const &a, RTLIL::Const const &b) { if(a.size() != b.size()) return false; for(int i = 0; i < a.size(); i++) if((a[i] == State::S1) != (b[i] == State::S1)) return false; return true; } struct CxxModule { Functional::IR ir; CxxStruct input_struct, output_struct, state_struct; std::string module_name; explicit CxxModule(Module *module) : ir(Functional::IR::from_module(module)), input_struct("Inputs"), output_struct("Outputs"), state_struct("State") { for (auto input : ir.inputs()) input_struct.insert(input->name, input->sort); for (auto output : ir.outputs()) output_struct.insert(output->name, output->sort); for (auto state : ir.states()) state_struct.insert(state->name, state->sort); module_name = CxxScope().unique_name(module->name); } void write_header(CxxWriter &f) { f.print("#include \"sim.h\"\n\n"); } void write_struct_def(CxxWriter &f) { f.print("struct {} {{\n", module_name); input_struct.print(f); output_struct.print(f); state_struct.print(f); f.print("\tstatic void eval(Inputs const &, Outputs &, State const &, State &);\n"); f.print("\tstatic void initialize(State &);\n"); f.print("}};\n\n"); } void write_initial_def(CxxWriter &f) { f.print("void {0}::initialize({0}::State &state)\n{{\n", module_name); for (auto state : ir.states()) { if (state->sort.is_signal()) f.print("\tstate.{} = {};\n", state_struct[state->name], cxx_const(state->initial_value_signal())); else if (state->sort.is_memory()) { f.print("\t{{\n"); f.print("\t\tstd::array, {}> mem;\n", state->sort.data_width(), 1<sort.addr_width()); const auto &contents = state->initial_value_memory(); f.print("\t\tmem.fill({});\n", cxx_const(contents.default_value())); for(auto range : contents) for(auto addr = range.base(); addr < range.limit(); addr++) if(!equal_def(range[addr], contents.default_value())) f.print("\t\tmem[{}] = {};\n", addr, cxx_const(range[addr])); f.print("\t\tstate.{} = mem;\n", state_struct[state->name]); f.print("\t}}\n"); } } f.print("}}\n\n"); } void write_eval_def(CxxWriter &f) { f.print("void {0}::eval({0}::Inputs const &input, {0}::Outputs &output, {0}::State const ¤t_state, {0}::State &next_state)\n{{\n", module_name); CxxScope locals; locals.reserve("input"); locals.reserve("output"); locals.reserve("current_state"); locals.reserve("next_state"); auto node_name = [&](Functional::Node n) { return locals(n.id(), n.name()); }; CxxPrintVisitor printVisitor(f, node_name, input_struct, state_struct); for (auto node : ir) { f.print("\t{} {} = ", CxxType(node.sort()).to_string(), node_name(node)); node.visit(printVisitor); f.print(";\n"); } for (auto state : ir.states()) f.print("\tnext_state.{} = {};\n", state_struct[state->name], node_name(state->next_value())); for (auto output : ir.outputs()) f.print("\toutput.{} = {};\n", output_struct[output->name], node_name(output->value())); f.print("}}\n\n"); } }; struct FunctionalCxxBackend : public Backend { FunctionalCxxBackend() : Backend("functional_cxx", "convert design to C++ using the functional backend") {} void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log("TODO: add help message\n"); log("\n"); } void printCxx(std::ostream &stream, std::string, Module *module) { CxxWriter f(stream); CxxModule mod(module); mod.write_header(f); mod.write_struct_def(f); mod.write_eval_def(f); mod.write_initial_def(f); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { log_header(design, "Executing Functional C++ backend.\n"); size_t argidx = 1; extra_args(f, filename, args, argidx, design); for (auto module : design->selected_modules()) { log("Dumping module `%s'.\n", module->name.c_str()); printCxx(*f, filename, module); } } } FunctionalCxxBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/functional/cxx_runtime/000077500000000000000000000000001477540374200211245ustar00rootroot00000000000000yosys-0.52/backends/functional/cxx_runtime/sim.h000066400000000000000000000254471477540374200221010ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2024 Emily Schmidt * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC * * 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 SIM_H #define SIM_H #include #include #include #include #include template class Signal { template friend class Signal; std::array _bits; public: Signal() { } Signal(uint32_t val) { for(size_t i = 0; i < n; i++) if(i < 32) _bits[i] = val & (1< vals) { size_t k, i; k = 0; for (auto val : vals) { for(i = 0; i < 32; i++) if(i + k < n) _bits[i + k] = val & (1< static Signal from_array(T vals) { size_t k, i; Signal ret; k = 0; for (auto val : vals) { for(i = 0; i < 32; i++) if(i + k < n) ret._bits[i + k] = val & (1< ret; for(size_t i = 0; i < n; i++) if(i < 32) ret._bits[i] = val & (1< ret; for(size_t i = 0; i < n; i++) ret._bits[i] = b; return ret; } int size() const { return n; } bool operator[](int i) const { assert(n >= 0 && i < n); return _bits[i]; } template Signal slice(size_t offset) const { Signal ret; assert(offset + m <= n); std::copy(_bits.begin() + offset, _bits.begin() + offset + m, ret._bits.begin()); return ret; } bool any() const { for(int i = 0; i < n; i++) if(_bits[i]) return true; return false; } bool all() const { for(int i = 0; i < n; i++) if(!_bits[i]) return false; return true; } bool parity() const { bool result = false; for(int i = 0; i < n; i++) result ^= _bits[i]; return result; } bool sign() const { return _bits[n-1]; } template T as_numeric() const { T ret = 0; for(size_t i = 0; i < std::min(sizeof(T) * 8, n); i++) if(_bits[i]) ret |= ((T)1)< T as_numeric_clamped() const { for(size_t i = sizeof(T) * 8; i < n; i++) if(_bits[i]) return ~((T)0); return as_numeric(); } uint32_t as_int() const { return as_numeric(); } private: std::string as_string_p2(int b) const { std::string ret; for(int i = (n - 1) - (n - 1) % b; i >= 0; i -= b) ret += "0123456789abcdef"[(*this >> Signal<32>(i)).as_int() & ((1< t = *this; Signal b = 10; do{ ret += (char)('0' + (t % b).as_int()); t = t / b; }while(t.any()); std::reverse(ret.begin(), ret.end()); return ret; } public: std::string as_string(int base = 16, bool showbase = true) const { std::string ret; if(showbase) { ret += std::to_string(n); switch(base) { case 2: ret += "'b"; break; case 8: ret += "'o"; break; case 10: ret += "'d"; break; case 16: ret += "'h"; break; default: assert(0); } } switch(base) { case 2: return ret + as_string_p2(1); case 8: return ret + as_string_p2(3); case 10: return ret + as_string_b10(); case 16: return ret + as_string_p2(4); default: assert(0); } } friend std::ostream &operator << (std::ostream &os, Signal const &s) { return os << s.as_string(); } Signal operator ~() const { Signal ret; for(size_t i = 0; i < n; i++) ret._bits[i] = !_bits[i]; return ret; } Signal operator -() const { Signal ret; int x = 1; for(size_t i = 0; i < n; i++) { x += (int)!_bits[i]; ret._bits[i] = (x & 1) != 0; x >>= 1; } return ret; } Signal operator +(Signal const &b) const { Signal ret; int x = 0; for(size_t i = 0; i < n; i++){ x += (int)_bits[i] + (int)b._bits[i]; ret._bits[i] = x & 1; x >>= 1; } return ret; } Signal operator -(Signal const &b) const { Signal ret; int x = 1; for(size_t i = 0; i < n; i++){ x += (int)_bits[i] + (int)!b._bits[i]; ret._bits[i] = x & 1; x >>= 1; } return ret; } Signal operator *(Signal const &b) const { Signal ret; int x = 0; for(size_t i = 0; i < n; i++){ for(size_t j = 0; j <= i; j++) x += (int)_bits[j] & (int)b._bits[i-j]; ret._bits[i] = x & 1; x >>= 1; } return ret; } private: Signal divmod(Signal const &b, bool modulo) const { if(!b.any()) return 0; Signal q = 0; Signal r = 0; for(size_t i = n; i-- != 0; ){ r = r << Signal<1>(1); r._bits[0] = _bits[i]; if(r >= b){ r = r - b; q._bits[i] = true; } } return modulo ? r : q; } public: Signal operator /(Signal const &b) const { return divmod(b, false); } Signal operator %(Signal const &b) const { return divmod(b, true); } bool operator ==(Signal const &b) const { for(size_t i = 0; i < n; i++) if(_bits[i] != b._bits[i]) return false; return true; } bool operator >=(Signal const &b) const { for(size_t i = n; i-- != 0; ) if(_bits[i] != b._bits[i]) return _bits[i]; return true; } bool operator >(Signal const &b) const { for(size_t i = n; i-- != 0; ) if(_bits[i] != b._bits[i]) return _bits[i]; return false; } bool operator !=(Signal const &b) const { return !(*this == b); } bool operator <=(Signal const &b) const { return b <= *this; } bool operator <(Signal const &b) const { return b < *this; } bool signed_greater_than(Signal const &b) const { if(_bits[n-1] != b._bits[n-1]) return b._bits[n-1]; return *this > b; } bool signed_greater_equal(Signal const &b) const { if(_bits[n-1] != b._bits[n-1]) return b._bits[n-1]; return *this >= b; } Signal operator &(Signal const &b) const { Signal ret; for(size_t i = 0; i < n; i++) ret._bits[i] = _bits[i] && b._bits[i]; return ret; } Signal operator |(Signal const &b) const { Signal ret; for(size_t i = 0; i < n; i++) ret._bits[i] = _bits[i] || b._bits[i]; return ret; } Signal operator ^(Signal const &b) const { Signal ret; for(size_t i = 0; i < n; i++) ret._bits[i] = _bits[i] != b._bits[i]; return ret; } template Signal operator <<(Signal const &b) const { Signal ret = 0; size_t amount = b.template as_numeric_clamped(); if(amount < n) std::copy(_bits.begin(), _bits.begin() + (n - amount), ret._bits.begin() + amount); return ret; } template Signal operator >>(Signal const &b) const { Signal ret = 0; size_t amount = b.template as_numeric_clamped(); if(amount < n) std::copy(_bits.begin() + amount, _bits.end(), ret._bits.begin()); return ret; } template Signal arithmetic_shift_right(Signal const &b) const { Signal ret = Signal::repeat(sign()); size_t amount = b.template as_numeric_clamped(); if(amount < n) std::copy(_bits.begin() + amount, _bits.end(), ret._bits.begin()); return ret; } template Signal concat(Signal const& b) const { Signal ret; std::copy(_bits.begin(), _bits.end(), ret._bits.begin()); std::copy(b._bits.begin(), b._bits.end(), ret._bits.begin() + n); return ret; } template Signal zero_extend() const { assert(m >= n); Signal ret = 0; std::copy(_bits.begin(), _bits.end(), ret._bits.begin()); return ret; } template Signal sign_extend() const { assert(m >= n); Signal ret = Signal::repeat(sign()); std::copy(_bits.begin(), _bits.end(), ret._bits.begin()); return ret; } }; template class Memory { std::array, 1< _contents; public: Memory() {} Memory(std::array, 1< const &contents) : _contents(contents) {} Signal read(Signal addr) const { return _contents[addr.template as_numeric()]; } Memory write(Signal addr, Signal data) const { Memory ret = *this; ret._contents[addr.template as_numeric()] = data; return ret; } }; #endif yosys-0.52/backends/functional/smtlib.cc000066400000000000000000000266731477540374200203760ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2024 Emily Schmidt * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC * * 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/functional.h" #include "kernel/yosys.h" #include "kernel/sexpr.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN using SExprUtil::list; const char *reserved_keywords[] = { // reserved keywords from the smtlib spec "BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "match", "par", "assert", "check-sat", "check-sat-assuming", "declare-const", "declare-datatype", "declare-datatypes", "declare-fun", "declare-sort", "define-fun", "define-fun-rec", "define-funs-rec", "define-sort", "exit", "get-assertions", "symbol", "sort", "get-assignment", "get-info", "get-model", "get-option", "get-proof", "get-unsat-assumptions", "get-unsat-core", "get-value", "pop", "push", "reset", "reset-assertions", "set-info", "set-logic", "set-option", // reserved for our own purposes "pair", "Pair", "first", "second", "inputs", "state", nullptr }; struct SmtScope : public Functional::Scope { SmtScope() { for(const char **p = reserved_keywords; *p != nullptr; p++) reserve(*p); } bool is_character_legal(char c, int index) override { return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("~!@$%^&*_-+=<>.?/", c)); } }; struct SmtSort { Functional::Sort sort; SmtSort(Functional::Sort sort) : sort(sort) {} SExpr to_sexpr() const { if(sort.is_memory()) { return list("Array", list("_", "BitVec", sort.addr_width()), list("_", "BitVec", sort.data_width())); } else if(sort.is_signal()) { return list("_", "BitVec", sort.width()); } else { log_error("unknown sort"); } } }; class SmtStruct { struct Field { SmtSort sort; std::string accessor; }; idict field_names; vector fields; SmtScope &scope; public: std::string name; SmtStruct(std::string name, SmtScope &scope) : scope(scope), name(name) {} void insert(IdString field_name, SmtSort sort) { field_names(field_name); auto accessor = scope.unique_name("\\" + name + "_" + RTLIL::unescape_id(field_name)); fields.emplace_back(Field{sort, accessor}); } void write_definition(SExprWriter &w) { w.open(list("declare-datatype", name)); w.open(list()); w.open(list(name)); for(const auto &field : fields) w << list(field.accessor, field.sort.to_sexpr()); w.close(3); } template void write_value(SExprWriter &w, Fn fn) { if(field_names.empty()) { // Zero-argument constructors in SMTLIB must not be called as functions. w << name; } else { w.open(list(name)); for(auto field_name : field_names) { w << fn(field_name); w.comment(RTLIL::unescape_id(field_name), true); } w.close(); } } SExpr access(SExpr record, IdString name) { size_t i = field_names.at(name); return list(fields[i].accessor, std::move(record)); } }; std::string smt_const(RTLIL::Const const &c) { std::string s = "#b"; for(int i = c.size(); i-- > 0; ) s += c[i] == State::S1 ? '1' : '0'; return s; } struct SmtPrintVisitor : public Functional::AbstractVisitor { using Node = Functional::Node; std::function n; SmtStruct &input_struct; SmtStruct &state_struct; SmtPrintVisitor(SmtStruct &input_struct, SmtStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} SExpr from_bool(SExpr &&arg) { return list("ite", std::move(arg), "#b1", "#b0"); } SExpr to_bool(SExpr &&arg) { return list("=", std::move(arg), "#b1"); } SExpr extract(SExpr &&arg, int offset, int out_width = 1) { return list(list("_", "extract", offset + out_width - 1, offset), std::move(arg)); } SExpr buf(Node, Node a) override { return n(a); } SExpr slice(Node, Node a, int offset, int out_width) override { return extract(n(a), offset, out_width); } SExpr zero_extend(Node, Node a, int out_width) override { return list(list("_", "zero_extend", out_width - a.width()), n(a)); } SExpr sign_extend(Node, Node a, int out_width) override { return list(list("_", "sign_extend", out_width - a.width()), n(a)); } SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); } SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); } SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); } SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); } SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); } SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } SExpr reduce_and(Node, Node a) override { return from_bool(list("=", n(a), smt_const(RTLIL::Const(State::S1, a.width())))); } SExpr reduce_or(Node, Node a) override { return from_bool(list("distinct", n(a), smt_const(RTLIL::Const(State::S0, a.width())))); } SExpr reduce_xor(Node, Node a) override { vector s { "bvxor" }; for(int i = 0; i < a.width(); i++) s.push_back(extract(n(a), i)); return s; } SExpr equal(Node, Node a, Node b) override { return from_bool(list("=", n(a), n(b))); } SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("distinct", n(a), n(b))); } SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); } SExpr extend(SExpr &&a, int in_width, int out_width) { if(in_width < out_width) return list(list("_", "zero_extend", out_width - in_width), std::move(a)); else return std::move(a); } SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } SExpr mux(Node, Node a, Node b, Node s) override { return list("ite", to_bool(n(s)), n(b), n(a)); } SExpr constant(Node, RTLIL::Const const &value) override { return smt_const(value); } SExpr memory_read(Node, Node mem, Node addr) override { return list("select", n(mem), n(addr)); } SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("store", n(mem), n(addr), n(data)); } SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } }; struct SmtModule { Functional::IR ir; SmtScope scope; std::string name; SmtStruct input_struct; SmtStruct output_struct; SmtStruct state_struct; SmtModule(Module *module) : ir(Functional::IR::from_module(module)) , scope() , name(scope.unique_name(module->name)) , input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope) , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) , state_struct(scope.unique_name(module->name.str() + "_State"), scope) { scope.reserve(name + "-initial"); for (auto input : ir.inputs()) input_struct.insert(input->name, input->sort); for (auto output : ir.outputs()) output_struct.insert(output->name, output->sort); for (auto state : ir.states()) state_struct.insert(state->name, state->sort); } void write_eval(SExprWriter &w) { w.push(); w.open(list("define-fun", name, list(list("inputs", input_struct.name), list("state", state_struct.name)), list("Pair", output_struct.name, state_struct.name))); auto inlined = [&](Functional::Node n) { return n.fn() == Functional::Fn::constant; }; SmtPrintVisitor visitor(input_struct, state_struct); auto node_to_sexpr = [&](Functional::Node n) -> SExpr { if(inlined(n)) return n.visit(visitor); else return scope(n.id(), n.name()); }; visitor.n = node_to_sexpr; for(auto n : ir) if(!inlined(n)) { w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); w.comment(SmtSort(n.sort()).to_sexpr().to_string(), true); } w.open(list("pair")); output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); w.pop(); } void write_initial(SExprWriter &w) { std::string initial = name + "-initial"; w << list("declare-const", initial, state_struct.name); for (auto state : ir.states()) { if(state->sort.is_signal()) w << list("assert", list("=", state_struct.access(initial, state->name), smt_const(state->initial_value_signal()))); else if(state->sort.is_memory()) { const auto &contents = state->initial_value_memory(); for(int i = 0; i < 1<sort.addr_width(); i++) { auto addr = smt_const(RTLIL::Const(i, state->sort.addr_width())); w << list("assert", list("=", list("select", state_struct.access(initial, state->name), addr), smt_const(contents[i]))); } } } } void write(std::ostream &out) { SExprWriter w(out); input_struct.write_definition(w); output_struct.write_definition(w); state_struct.write_definition(w); w << list("declare-datatypes", list(list("Pair", 2)), list(list("par", list("X", "Y"), list(list("pair", list("first", "X"), list("second", "Y")))))); write_eval(w); write_initial(w); } }; struct FunctionalSmtBackend : public Backend { FunctionalSmtBackend() : Backend("functional_smt2", "Generate SMT-LIB from Functional IR") {} void help() override { log("\nFunctional SMT Backend.\n\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { log_header(design, "Executing Functional SMT Backend.\n"); size_t argidx = 1; extra_args(f, filename, args, argidx, design); for (auto module : design->selected_modules()) { log("Processing module `%s`.\n", module->name.c_str()); SmtModule smt(module); smt.write(*f); } } } FunctionalSmtBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/functional/smtlib_rosette.cc000066400000000000000000000274121477540374200221330ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2024 Emily Schmidt * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC * * 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/functional.h" #include "kernel/yosys.h" #include "kernel/sexpr.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN using SExprUtil::list; const char *reserved_keywords[] = { // reserved keywords from the racket spec "struct", "lambda", "values", "extract", "concat", "bv", "let", "define", "cons", "list", "read", "write", "stream", "error", "raise", "exit", "for", "begin", "when", "unless", "module", "require", "provide", "apply", "if", "cond", "even", "odd", "any", "and", "or", "match", "command-line", "ffi-lib", "thread", "kill", "sync", "future", "touch", "subprocess", "make-custodian", "custodian-shutdown-all", "current-custodian", "make", "tcp", "connect", "prepare", "malloc", "free", "_fun", "_cprocedure", "build", "path", "file", "peek", "bytes", "flush", "with", "lexer", "parser", "syntax", "interface", "send", "make-object", "new", "instantiate", "define-generics", "set", // reserved for our own purposes "inputs", "state", "name", nullptr }; struct SmtrScope : public Functional::Scope { SmtrScope() { for(const char **p = reserved_keywords; *p != nullptr; p++) reserve(*p); } bool is_character_legal(char c, int index) override { return isascii(c) && (isalpha(c) || (isdigit(c) && index > 0) || strchr("@$%^&_+=.", c)); } }; struct SmtrSort { Functional::Sort sort; SmtrSort(Functional::Sort sort) : sort(sort) {} SExpr to_sexpr() const { if(sort.is_memory()) { return list("list", list("bitvector", sort.addr_width()), list("bitvector", sort.data_width())); } else if(sort.is_signal()) { return list("bitvector", sort.width()); } else { log_error("unknown sort"); } } }; class SmtrStruct { struct Field { SmtrSort sort; std::string accessor; std::string name; }; idict field_names; vector fields; SmtrScope &global_scope; SmtrScope local_scope; public: std::string name; SmtrStruct(std::string name, SmtrScope &scope) : global_scope(scope), local_scope(), name(name) {} void insert(IdString field_name, SmtrSort sort) { field_names(field_name); auto base_name = local_scope.unique_name(field_name); auto accessor = name + "-" + base_name; global_scope.reserve(accessor); fields.emplace_back(Field{sort, accessor, base_name}); } void write_definition(SExprWriter &w) { vector field_list; for(const auto &field : fields) { field_list.emplace_back(field.name); } w.push(); w.open(list("struct", name, field_list, "#:transparent")); if (field_names.size()) { for (const auto &field : fields) { auto bv_type = field.sort.to_sexpr(); w.comment(field.name + " " + bv_type.to_string()); } } w.pop(); } template void write_value(SExprWriter &w, Fn fn) { w.open(list(name)); for(auto field_name : field_names) { w << fn(field_name); w.comment(RTLIL::unescape_id(field_name), true); } w.close(); } SExpr access(SExpr record, IdString name) { size_t i = field_names.at(name); return list(fields[i].accessor, std::move(record)); } }; std::string smt_const(RTLIL::Const const &c) { std::string s = "#b"; for(int i = c.size(); i-- > 0; ) s += c[i] == State::S1 ? '1' : '0'; return s; } struct SmtrPrintVisitor : public Functional::AbstractVisitor { using Node = Functional::Node; std::function n; SmtrStruct &input_struct; SmtrStruct &state_struct; SmtrPrintVisitor(SmtrStruct &input_struct, SmtrStruct &state_struct) : input_struct(input_struct), state_struct(state_struct) {} SExpr from_bool(SExpr &&arg) { return list("bool->bitvector", std::move(arg)); } SExpr to_bool(SExpr &&arg) { return list("bitvector->bool", std::move(arg)); } SExpr to_list(SExpr &&arg) { return list("bitvector->bits", std::move(arg)); } SExpr buf(Node, Node a) override { return n(a); } SExpr slice(Node, Node a, int offset, int out_width) override { return list("extract", offset + out_width - 1, offset, n(a)); } SExpr zero_extend(Node, Node a, int out_width) override { return list("zero-extend", n(a), list("bitvector", out_width)); } SExpr sign_extend(Node, Node a, int out_width) override { return list("sign-extend", n(a), list("bitvector", out_width)); } SExpr concat(Node, Node a, Node b) override { return list("concat", n(b), n(a)); } SExpr add(Node, Node a, Node b) override { return list("bvadd", n(a), n(b)); } SExpr sub(Node, Node a, Node b) override { return list("bvsub", n(a), n(b)); } SExpr mul(Node, Node a, Node b) override { return list("bvmul", n(a), n(b)); } SExpr unsigned_div(Node, Node a, Node b) override { return list("bvudiv", n(a), n(b)); } SExpr unsigned_mod(Node, Node a, Node b) override { return list("bvurem", n(a), n(b)); } SExpr bitwise_and(Node, Node a, Node b) override { return list("bvand", n(a), n(b)); } SExpr bitwise_or(Node, Node a, Node b) override { return list("bvor", n(a), n(b)); } SExpr bitwise_xor(Node, Node a, Node b) override { return list("bvxor", n(a), n(b)); } SExpr bitwise_not(Node, Node a) override { return list("bvnot", n(a)); } SExpr unary_minus(Node, Node a) override { return list("bvneg", n(a)); } SExpr reduce_and(Node, Node a) override { return list("apply", "bvand", to_list(n(a))); } SExpr reduce_or(Node, Node a) override { return list("apply", "bvor", to_list(n(a))); } SExpr reduce_xor(Node, Node a) override { return list("apply", "bvxor", to_list(n(a))); } SExpr equal(Node, Node a, Node b) override { return from_bool(list("bveq", n(a), n(b))); } SExpr not_equal(Node, Node a, Node b) override { return from_bool(list("not", list("bveq", n(a), n(b)))); } SExpr signed_greater_than(Node, Node a, Node b) override { return from_bool(list("bvsgt", n(a), n(b))); } SExpr signed_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvsge", n(a), n(b))); } SExpr unsigned_greater_than(Node, Node a, Node b) override { return from_bool(list("bvugt", n(a), n(b))); } SExpr unsigned_greater_equal(Node, Node a, Node b) override { return from_bool(list("bvuge", n(a), n(b))); } SExpr extend(SExpr &&a, int in_width, int out_width) { if(in_width < out_width) return list("zero-extend", std::move(a), list("bitvector", out_width)); else return std::move(a); } SExpr logical_shift_left(Node, Node a, Node b) override { return list("bvshl", n(a), extend(n(b), b.width(), a.width())); } SExpr logical_shift_right(Node, Node a, Node b) override { return list("bvlshr", n(a), extend(n(b), b.width(), a.width())); } SExpr arithmetic_shift_right(Node, Node a, Node b) override { return list("bvashr", n(a), extend(n(b), b.width(), a.width())); } SExpr mux(Node, Node a, Node b, Node s) override { return list("if", to_bool(n(s)), n(b), n(a)); } SExpr constant(Node, RTLIL::Const const& value) override { return list("bv", smt_const(value), value.size()); } SExpr memory_read(Node, Node mem, Node addr) override { return list("list-ref-bv", n(mem), n(addr)); } SExpr memory_write(Node, Node mem, Node addr, Node data) override { return list("list-set-bv", n(mem), n(addr), n(data)); } SExpr input(Node, IdString name, IdString kind) override { log_assert(kind == ID($input)); return input_struct.access("inputs", name); } SExpr state(Node, IdString name, IdString kind) override { log_assert(kind == ID($state)); return state_struct.access("state", name); } }; struct SmtrModule { Functional::IR ir; SmtrScope scope; std::string name; SmtrStruct input_struct; SmtrStruct output_struct; SmtrStruct state_struct; SmtrModule(Module *module) : ir(Functional::IR::from_module(module)) , scope() , name(scope.unique_name(module->name)) , input_struct(scope.unique_name(module->name.str() + "_Inputs"), scope) , output_struct(scope.unique_name(module->name.str() + "_Outputs"), scope) , state_struct(scope.unique_name(module->name.str() + "_State"), scope) { scope.reserve(name + "_initial"); for (auto input : ir.inputs()) input_struct.insert(input->name, input->sort); for (auto output : ir.outputs()) output_struct.insert(output->name, output->sort); for (auto state : ir.states()) state_struct.insert(state->name, state->sort); } void write_eval(SExprWriter &w) { w.push(); w.open(list("define", list(name, "inputs", "state"))); auto inlined = [&](Functional::Node n) { return n.fn() == Functional::Fn::constant; }; SmtrPrintVisitor visitor(input_struct, state_struct); auto node_to_sexpr = [&](Functional::Node n) -> SExpr { if(inlined(n)) return n.visit(visitor); else return scope(n.id(), n.name()); }; visitor.n = node_to_sexpr; for(auto n : ir) if(!inlined(n)) { w.open(list("let", list(list(node_to_sexpr(n), n.visit(visitor)))), false); w.comment(SmtrSort(n.sort()).to_sexpr().to_string(), true); } w.open(list("cons")); output_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.output(name).value()); }); state_struct.write_value(w, [&](IdString name) { return node_to_sexpr(ir.state(name).next_value()); }); w.pop(); } void write_initial(SExprWriter &w) { w.push(); auto initial = name + "_initial"; w.open(list("define", initial)); w.open(list(state_struct.name)); for (auto state : ir.states()) { if (state->sort.is_signal()) w << list("bv", smt_const(state->initial_value_signal()), state->sort.width()); else if (state->sort.is_memory()) { const auto &contents = state->initial_value_memory(); w.open(list("list")); for(int i = 0; i < 1<sort.addr_width(); i++) { w << list("bv", smt_const(contents[i]), state->sort.data_width()); } w.close(); } } w.pop(); } void write(std::ostream &out) { SExprWriter w(out); input_struct.write_definition(w); output_struct.write_definition(w); state_struct.write_definition(w); write_eval(w); write_initial(w); } }; struct FunctionalSmtrBackend : public Backend { FunctionalSmtrBackend() : Backend("functional_rosette", "Generate Rosette compatible Racket from Functional IR") {} void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_functional_rosette [options] [filename]\n"); log("\n"); log("Functional Rosette Backend.\n"); log("\n"); log(" -provides\n"); log(" include 'provide' statement(s) for loading output as a module\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { auto provides = false; log_header(design, "Executing Functional Rosette Backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-provides") provides = true; else break; } extra_args(f, filename, args, argidx); *f << "#lang rosette/safe\n"; if (provides) { *f << "(provide (all-defined-out))\n"; } for (auto module : design->selected_modules()) { log("Processing module `%s`.\n", module->name.c_str()); SmtrModule smtr(module); smtr.write(*f); } } } FunctionalSmtrBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/functional/test_generic.cc000066400000000000000000000131631477540374200215450ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2024 Emily Schmidt * Copyright (C) 2024 National Technology and Engineering Solutions of Sandia, LLC * * 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/yosys.h" #include "kernel/functional.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct MemContentsTest { int addr_width, data_width; MemContents state; using addr_t = MemContents::addr_t; std::map reference; MemContentsTest(int addr_width, int data_width) : addr_width(addr_width), data_width(data_width), state(addr_width, data_width, RTLIL::Const(State::S0, data_width)) {} void check() { state.check(); for(auto addr = 0; addr < (1<second != state[addr]) goto error; } else { if(state.count_range(addr, addr + 1) != 0) goto error; } } return; error: printf("FAIL\n"); int digits = (data_width + 3) / 4; for(auto addr = 0; addr < (1<second : state.default_value(); std::string ref_string = stringf("%.*x", digits, ref_value.as_int()); bool sta_def = state.count_range(addr, addr + 1) == 1; RTLIL::Const sta_value = state[addr]; std::string sta_string = stringf("%.*x", digits, sta_value.as_int()); if(ref_def && sta_def) { if(ref_value == sta_value) printf("%s%s", ref_string.c_str(), string(digits, ' ').c_str()); else printf("%s%s", ref_string.c_str(), sta_string.c_str()); } else if(ref_def) { printf("%s%s", ref_string.c_str(), string(digits, 'M').c_str()); } else if(sta_def) { printf("%s%s", sta_string.c_str(), string(digits, 'X').c_str()); } else { printf("%s", string(2*digits, ' ').c_str()); } printf(" "); if(addr % 8 == 7) printf("\n"); } printf("\n"); //log_abort(); } void clear_range(addr_t begin_addr, addr_t end_addr) { for(auto addr = begin_addr; addr != end_addr; addr++) reference.erase(addr); state.clear_range(begin_addr, end_addr); check(); } void insert_concatenated(addr_t addr, RTLIL::Const const &values) { addr_t words = ((addr_t) values.size() + data_width - 1) / data_width; for(addr_t i = 0; i < words; i++) { reference.erase(addr + i); reference.emplace(addr + i, values.extract(i * data_width, data_width)); } state.insert_concatenated(addr, values); check(); } template void run(Rnd &rnd, int n) { std::uniform_int_distribution addr_dist(0, (1< length_dist(10); std::uniform_int_distribution data_dist(0, ((uint64_t)1< 0) { addr_t low = addr_dist(rnd); //addr_t length = std::min((1< high) std::swap(low, high); if((rnd() & 7) == 0) { log_debug("clear %.2x to %.2x\n", (int)low, (int)high); clear_range(low, high + 1); } else { log_debug("insert %.2x to %.2x\n", (int)low, (int)high); RTLIL::Const values; for(addr_t addr = low; addr <= high; addr++) { RTLIL::Const word(data_dist(rnd), data_width); values.bits().insert(values.bits().end(), word.begin(), word.end()); } insert_concatenated(low, values); } } } }; struct FunctionalTestGeneric : public Pass { FunctionalTestGeneric() : Pass("test_generic", "test the generic compute graph") {} void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log("TODO: add help message\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { log_header(design, "Executing Test Generic.\n"); size_t argidx = 1; extra_args(args, argidx, design); /* MemContentsTest test(8, 16); std::random_device seed_dev; std::mt19937 rnd(23); //seed_dev()); test.run(rnd, 1000); */ for (auto module : design->selected_modules()) { log("Dumping module `%s'.\n", module->name.c_str()); auto fir = Functional::IR::from_module(module); for(auto node : fir) std::cout << RTLIL::unescape_id(node.name()) << " = " << node.to_string([](auto n) { return RTLIL::unescape_id(n.name()); }) << "\n"; for(auto output : fir.all_outputs()) std::cout << RTLIL::unescape_id(output->kind) << " " << RTLIL::unescape_id(output->name) << " = " << RTLIL::unescape_id(output->value().name()) << "\n"; for(auto state : fir.all_states()) std::cout << RTLIL::unescape_id(state->kind) << " " << RTLIL::unescape_id(state->name) << " = " << RTLIL::unescape_id(state->next_value().name()) << "\n"; } } } FunctionalCxxBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/intersynth/000077500000000000000000000000001477540374200166245ustar00rootroot00000000000000yosys-0.52/backends/intersynth/Makefile.inc000066400000000000000000000000531477540374200210320ustar00rootroot00000000000000 OBJS += backends/intersynth/intersynth.o yosys-0.52/backends/intersynth/intersynth.cc000066400000000000000000000176641477540374200213600ustar00rootroot00000000000000/* * 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 "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN static std::string netname(std::set &conntypes_code, std::set &celltypes_code, std::set &constcells_code, RTLIL::SigSpec sig) { if (!sig.is_fully_const() && !sig.is_wire()) log_error("Can't export composite or non-word-wide signal %s.\n", log_signal(sig)); conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size())); if (sig.is_fully_const()) { celltypes_code.insert(stringf("celltype CONST_%d b%d *CONST cfg:%d VALUE\n", sig.size(), sig.size(), sig.size())); constcells_code.insert(stringf("node CONST_%d_0x%x CONST_%d CONST CONST_%d_0x%x VALUE 0x%x\n", sig.size(), sig.as_int(), sig.size(), sig.size(), sig.as_int(), sig.as_int())); return stringf("CONST_%d_0x%x", sig.size(), sig.as_int()); } return RTLIL::unescape_id(sig.as_wire()->name); } struct IntersynthBackend : public Backend { IntersynthBackend() : Backend("intersynth", "write design to InterSynth netlist file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_intersynth [options] [filename]\n"); log("\n"); log("Write the current design to an 'intersynth' netlist file. InterSynth is\n"); log("a tool for Coarse-Grain Example-Driven Interconnect Synthesis.\n"); log("\n"); log(" -notypes\n"); log(" do not generate celltypes and conntypes commands. i.e. just output\n"); log(" the netlists. this is used for postsilicon synthesis.\n"); log("\n"); log(" -lib \n"); log(" Use the specified library file for determining whether cell ports are\n"); log(" inputs or outputs. This option can be used multiple times to specify\n"); log(" more than one library.\n"); log("\n"); log(" -selected\n"); log(" only write selected modules. modules must be selected entirely or\n"); log(" not at all.\n"); log("\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 { log_header(design, "Executing INTERSYNTH backend.\n"); log_push(); std::vector libfiles; std::vector libs; bool flag_notypes = false; bool selected = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-notypes") { flag_notypes = true; continue; } if (args[argidx] == "-lib" && argidx+1 < args.size()) { libfiles.push_back(args[++argidx]); continue; } if (args[argidx] == "-selected") { selected = true; continue; } break; } extra_args(f, filename, args, argidx); log("Output filename: %s\n", filename.c_str()); for (auto filename : libfiles) { std::ifstream f; f.open(filename.c_str()); if (f.fail()) log_error("Can't open lib file `%s'.\n", filename.c_str()); RTLIL::Design *lib = new RTLIL::Design; Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "rtlil" : "verilog")); libs.push_back(lib); } if (libs.size() > 0) log_header(design, "Continuing INTERSYNTH backend.\n"); std::set conntypes_code, celltypes_code; std::string netlists_code; CellTypes ct(design); for (auto lib : libs) ct.setup_design(lib); for (auto module : design->modules()) { SigMap sigmap(module); if (module->get_blackbox_attribute()) continue; if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells().size() == 0) continue; if (selected && !design->selected_whole_module(module->name)) { if (design->selected_module(module->name)) log_cmd_error("Can't handle partially selected module %s!\n", log_id(module->name)); continue; } log("Generating netlist %s.\n", log_id(module->name)); if (module->memories.size() != 0 || module->processes.size() != 0) log_error("Can't generate a netlist for a module with unprocessed memories or processes!\n"); std::set constcells_code; netlists_code += stringf("# Netlist of module %s\n", log_id(module->name)); netlists_code += stringf("netlist %s\n", log_id(module->name)); // Module Ports: "std::set celltypes_code" prevents duplicate top level ports for (auto wire : module->wires()) { if (wire->port_input || wire->port_output) { celltypes_code.insert(stringf("celltype !%s b%d %sPORT\n" "%s %s %d %s PORT\n", log_id(wire->name), wire->width, wire->port_input ? "*" : "", wire->port_input ? "input" : "output", log_id(wire->name), wire->width, log_id(wire->name))); netlists_code += stringf("node %s %s PORT %s\n", log_id(wire->name), log_id(wire->name), netname(conntypes_code, celltypes_code, constcells_code, sigmap(wire)).c_str()); } } // Submodules: "std::set celltypes_code" prevents duplicate cell types for (auto cell : module->cells()) { std::string celltype_code, node_code; if (!ct.cell_known(cell->type)) log_error("Found unknown cell type %s in module!\n", log_id(cell->type)); celltype_code = stringf("celltype %s", log_id(cell->type)); node_code = stringf("node %s %s", log_id(cell->name), log_id(cell->type)); for (auto &port : cell->connections()) { RTLIL::SigSpec sig = sigmap(port.second); if (sig.size() != 0) { conntypes_code.insert(stringf("conntype b%d %d 2 %d\n", sig.size(), sig.size(), sig.size())); celltype_code += stringf(" b%d %s%s", sig.size(), ct.cell_output(cell->type, port.first) ? "*" : "", log_id(port.first)); node_code += stringf(" %s %s", log_id(port.first), netname(conntypes_code, celltypes_code, constcells_code, sig).c_str()); } } for (auto ¶m : cell->parameters) { celltype_code += stringf(" cfg:%d %s", int(param.second.size()), log_id(param.first)); if (param.second.size() != 32) { node_code += stringf(" %s '", log_id(param.first)); for (int i = param.second.size()-1; i >= 0; i--) node_code += param.second[i] == State::S1 ? "1" : "0"; } else node_code += stringf(" %s 0x%x", log_id(param.first), param.second.as_int()); } celltypes_code.insert(celltype_code + "\n"); netlists_code += node_code + "\n"; } if (constcells_code.size() > 0) netlists_code += "# constant cells\n"; for (auto code : constcells_code) netlists_code += code; netlists_code += "\n"; } if (!flag_notypes) { *f << stringf("### Connection Types\n"); for (auto code : conntypes_code) *f << stringf("%s", code.c_str()); *f << stringf("\n### Cell Types\n"); for (auto code : celltypes_code) *f << stringf("%s", code.c_str()); } *f << stringf("\n### Netlists\n"); *f << stringf("%s", netlists_code.c_str()); for (auto lib : libs) delete lib; log_pop(); } } IntersynthBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/jny/000077500000000000000000000000001477540374200152155ustar00rootroot00000000000000yosys-0.52/backends/jny/Makefile.inc000066400000000000000000000000341477540374200174220ustar00rootroot00000000000000 OBJS += backends/jny/jny.o yosys-0.52/backends/jny/jny.cc000066400000000000000000000432241477540374200163310ustar00rootroot00000000000000/* * 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/main/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/main/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 yosys-0.52/backends/json/000077500000000000000000000000001477540374200153665ustar00rootroot00000000000000yosys-0.52/backends/json/Makefile.inc000066400000000000000000000000371477540374200175760ustar00rootroot00000000000000 OBJS += backends/json/json.o yosys-0.52/backends/json/json.cc000066400000000000000000000570711477540374200166600ustar00rootroot00000000000000/* * 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 "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/cellaigs.h" #include "kernel/log.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct JsonWriter { std::ostream &f; bool use_selection; bool aig_mode; bool compat_int_mode; bool scopeinfo_mode; Design *design; Module *module; SigMap sigmap; int sigidcounter; dict sigids; pool aig_models; JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode, bool scopeinfo_mode) : f(f), use_selection(use_selection), aig_mode(aig_mode), compat_int_mode(compat_int_mode), scopeinfo_mode(scopeinfo_mode) { } string get_string(string str) { 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; } return newstr + "\""; } string get_name(IdString name) { return get_string(RTLIL::unescape_id(name)); } string get_bits(SigSpec sig) { bool first = true; string str = "["; for (auto bit : sigmap(sig)) { str += first ? " " : ", "; first = false; if (sigids.count(bit) == 0) { string &s = sigids[bit]; if (bit.wire == nullptr) { if (bit == State::S0) s = "\"0\""; else if (bit == State::S1) s = "\"1\""; else if (bit == State::Sz) s = "\"z\""; else s = "\"x\""; } else s = stringf("%d", sigidcounter++); } str += sigids[bit]; } return str + " ]"; } void write_parameter_value(const Const &value) { if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) { string str = value.decode_string(); int state = 0; for (char c : str) { if (state == 0) { if (c == '0' || c == '1' || c == 'x' || c == 'z') state = 0; else if (c == ' ') state = 1; else state = 2; } else if (state == 1 && c != ' ') state = 2; } if (state < 2) str += " "; f << get_string(str); } else if (compat_int_mode && GetSize(value) <= 32 && value.is_fully_def()) { if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0) f << stringf("%d", value.as_int()); else f << stringf("%u", value.as_int()); } else { f << get_string(value.as_string()); } } void write_parameters(const dict ¶meters, bool for_module=false) { bool first = true; for (auto ¶m : parameters) { f << stringf("%s\n", first ? "" : ","); f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first).c_str()); write_parameter_value(param.second); first = false; } } void write_module(Module *module_) { module = module_; log_assert(module->design == design); sigmap.set(module); sigids.clear(); // 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\": {"); write_parameters(module->attributes, /*for_module=*/true); f << stringf("\n },\n"); if (module->parameter_default_values.size()) { f << stringf(" \"parameter_default_values\": {"); write_parameters(module->parameter_default_values, /*for_module=*/true); f << stringf("\n },\n"); } f << stringf(" \"ports\": {"); bool first = true; for (auto n : module->ports) { Wire *w = module->wire(n); if (use_selection && !module->selected(w)) continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(n).c_str()); f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output"); if (w->start_offset) f << stringf(" \"offset\": %d,\n", w->start_offset); if (w->upto) f << stringf(" \"upto\": 1,\n"); if (w->is_signed) f << stringf(" \"signed\": %d,\n", w->is_signed); f << stringf(" \"bits\": %s\n", get_bits(w).c_str()); f << stringf(" }"); first = false; } f << stringf("\n },\n"); f << stringf(" \"cells\": {"); first = true; for (auto c : module->cells()) { if (use_selection && !module->selected(c)) continue; if (!scopeinfo_mode && 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"); f << stringf(" \"type\": %s,\n", get_name(c->type).c_str()); if (aig_mode) { Aig aig(c); if (!aig.name.empty()) { f << stringf(" \"model\": \"%s\",\n", aig.name.c_str()); aig_models.insert(aig); } } f << stringf(" \"parameters\": {"); write_parameters(c->parameters); f << stringf("\n },\n"); f << stringf(" \"attributes\": {"); write_parameters(c->attributes); f << stringf("\n },\n"); if (c->known()) { f << stringf(" \"port_directions\": {"); bool first2 = true; for (auto &conn : c->connections()) { string direction = "output"; if (c->input(conn.first)) direction = c->output(conn.first) ? "inout" : "input"; f << stringf("%s\n", first2 ? "" : ","); f << stringf(" %s: \"%s\"", get_name(conn.first).c_str(), direction.c_str()); first2 = false; } f << stringf("\n },\n"); } f << stringf(" \"connections\": {"); bool first2 = true; for (auto &conn : c->connections()) { f << stringf("%s\n", first2 ? "" : ","); f << stringf(" %s: %s", get_name(conn.first).c_str(), get_bits(conn.second).c_str()); first2 = false; } f << stringf("\n }\n"); f << stringf(" }"); first = false; } 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()) { if (use_selection && !module->selected(w)) continue; f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(w->name).c_str()); f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0"); f << stringf(" \"bits\": %s,\n", get_bits(w).c_str()); if (w->start_offset) f << stringf(" \"offset\": %d,\n", w->start_offset); if (w->upto) f << stringf(" \"upto\": 1,\n"); if (w->is_signed) f << stringf(" \"signed\": %d,\n", w->is_signed); f << stringf(" \"attributes\": {"); write_parameters(w->attributes); f << stringf("\n }\n"); f << stringf(" }"); first = false; } f << stringf("\n }\n"); f << stringf(" }"); } void write_design(Design *design_) { design = design_; design->sort(); f << stringf("{\n"); f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str()); f << stringf(" \"modules\": {\n"); vector modules = use_selection ? design->selected_modules() : design->modules(); bool first_module = true; for (auto mod : modules) { if (!first_module) f << stringf(",\n"); write_module(mod); first_module = false; } f << stringf("\n }"); if (!aig_models.empty()) { f << stringf(",\n \"models\": {\n"); bool first_model = true; for (auto &aig : aig_models) { if (!first_model) f << stringf(",\n"); f << stringf(" \"%s\": [\n", aig.name.c_str()); int node_idx = 0; for (auto &node : aig.nodes) { if (node_idx != 0) f << stringf(",\n"); f << stringf(" /* %3d */ [ ", node_idx); if (node.portbit >= 0) f << stringf("\"%sport\", \"%s\", %d", node.inverter ? "n" : "", log_id(node.portname), node.portbit); else if (node.left_parent < 0 && node.right_parent < 0) f << stringf("\"%s\"", node.inverter ? "true" : "false"); else f << stringf("\"%s\", %d, %d", node.inverter ? "nand" : "and", node.left_parent, node.right_parent); for (auto &op : node.outports) f << stringf(", \"%s\", %d", log_id(op.first), op.second); f << stringf(" ]"); node_idx++; } f << stringf("\n ]"); first_model = false; } f << stringf("\n }"); } f << stringf("\n}\n"); } }; struct JsonBackend : public Backend { JsonBackend() : Backend("json", "write design to a JSON file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_json [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(" -compat-int\n"); log(" emit 32-bit or smaller fully-defined parameter values directly\n"); log(" as JSON numbers (for compatibility with old parsers)\n"); log("\n"); log(" -selected\n"); log(" output only select module\n"); log("\n"); log(" -noscopeinfo\n"); log(" don't include $scopeinfo cells in the output\n"); log("\n"); log("\n"); log("The general syntax of the JSON output created by this command is as follows:\n"); log("\n"); log(" {\n"); log(" \"creator\": \"Yosys \",\n"); log(" \"modules\": {\n"); log(" : {\n"); log(" \"attributes\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"parameter_default_values\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"ports\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"cells\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"memories\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"netnames\": {\n"); log(" : ,\n"); log(" ...\n"); log(" }\n"); log(" }\n"); log(" },\n"); log(" \"models\": {\n"); log(" ...\n"); log(" },\n"); log(" }\n"); log("\n"); log("Where is:\n"); log("\n"); log(" {\n"); log(" \"direction\": <\"input\" | \"output\" | \"inout\">,\n"); 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.\n"); log("They don't affect connection semantics, and are only used to preserve original\n"); log("HDL bit indexing.\n"); log("And is:\n"); log("\n"); log(" {\n"); log(" \"hide_name\": <1 | 0>,\n"); log(" \"type\": ,\n"); log(" \"model\": ,\n"); log(" \"parameters\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"attributes\": {\n"); log(" : ,\n"); log(" ...\n"); log(" },\n"); log(" \"port_directions\": {\n"); log(" : <\"input\" | \"output\" | \"inout\">,\n"); log(" ...\n"); log(" },\n"); log(" \"connections\": {\n"); log(" : ,\n"); log(" ...\n"); 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"); log(" \"hide_name\": <1 | 0>,\n"); 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"); log("automatically created and is likely not of interest for a regular user.\n"); log("\n"); log("The \"port_directions\" section is only included for cells for which the\n"); log("interface is known.\n"); log("\n"); log("Module and cell ports and nets can be single bit wide or vectors of multiple\n"); log("bits. Each individual signal bit is assigned a unique integer. The \n"); log("values referenced above are vectors of this integers. Signal bits that are\n"); 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\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"); log("\n"); log(" module test(input x, y);\n"); log(" (* keep *) foo #(.P(42), .Q(1337))\n"); log(" foo_inst (.A({x, y}), .B({y, x}), .C({4'd10, {4{x}}}));\n"); log(" endmodule\n"); log("\n"); log("Translates to the following JSON output:\n"); log("\n"); log(" {\n"); log(" \"creator\": \"Yosys 0.9+2406 (git sha1 fb1168d8, clang 9.0.1 -fPIC -Os)\",\n"); log(" \"modules\": {\n"); log(" \"test\": {\n"); log(" \"attributes\": {\n"); log(" \"cells_not_processed\": \"00000000000000000000000000000001\",\n"); log(" \"src\": \"test.v:1.1-4.10\"\n"); log(" },\n"); log(" \"ports\": {\n"); log(" \"x\": {\n"); log(" \"direction\": \"input\",\n"); log(" \"bits\": [ 2 ]\n"); log(" },\n"); log(" \"y\": {\n"); log(" \"direction\": \"input\",\n"); log(" \"bits\": [ 3 ]\n"); log(" }\n"); log(" },\n"); log(" \"cells\": {\n"); log(" \"foo_inst\": {\n"); log(" \"hide_name\": 0,\n"); log(" \"type\": \"foo\",\n"); log(" \"parameters\": {\n"); log(" \"P\": \"00000000000000000000000000101010\",\n"); log(" \"Q\": \"00000000000000000000010100111001\"\n"); log(" },\n"); log(" \"attributes\": {\n"); log(" \"keep\": \"00000000000000000000000000000001\",\n"); log(" \"module_not_derived\": \"00000000000000000000000000000001\",\n"); log(" \"src\": \"test.v:3.1-3.55\"\n"); log(" },\n"); log(" \"connections\": {\n"); log(" \"A\": [ 3, 2 ],\n"); log(" \"B\": [ 2, 3 ],\n"); log(" \"C\": [ 2, 2, 2, 2, \"0\", \"1\", \"0\", \"1\" ]\n"); log(" }\n"); log(" }\n"); log(" },\n"); log(" \"netnames\": {\n"); log(" \"x\": {\n"); log(" \"hide_name\": 0,\n"); log(" \"bits\": [ 2 ],\n"); log(" \"attributes\": {\n"); log(" \"src\": \"test.v:1.19-1.20\"\n"); log(" }\n"); log(" },\n"); log(" \"y\": {\n"); log(" \"hide_name\": 0,\n"); log(" \"bits\": [ 3 ],\n"); log(" \"attributes\": {\n"); log(" \"src\": \"test.v:1.22-1.23\"\n"); log(" }\n"); log(" }\n"); log(" }\n"); log(" }\n"); log(" }\n"); log(" }\n"); log("\n"); log("The models are given as And-Inverter-Graphs (AIGs) in the following form:\n"); log("\n"); log(" \"models\": {\n"); log(" : [\n"); log(" /* 0 */ [ ],\n"); log(" /* 1 */ [ ],\n"); log(" /* 2 */ [ ],\n"); log(" ...\n"); log(" ],\n"); log(" ...\n"); log(" },\n"); log("\n"); log("The following node-types may be used:\n"); log("\n"); log(" [ \"port\", , , ]\n"); log(" - the value of the specified input port bit\n"); log("\n"); log(" [ \"nport\", , , ]\n"); log(" - the inverted value of the specified input port bit\n"); log("\n"); log(" [ \"and\", , , ]\n"); log(" - the ANDed value of the specified nodes\n"); log("\n"); log(" [ \"nand\", , , ]\n"); log(" - the inverted ANDed value of the specified nodes\n"); log("\n"); log(" [ \"true\", ]\n"); log(" - the constant value 1\n"); log("\n"); log(" [ \"false\", ]\n"); log(" - the constant value 0\n"); log("\n"); log("All nodes appear in topological order. I.e. only nodes with smaller indices\n"); log("are referenced by \"and\" and \"nand\" nodes.\n"); log("\n"); log("The optional at the end of a node specification is a list of\n"); log("output portname and bitindex pairs, specifying the outputs driven by this node.\n"); log("\n"); log("For example, the following is the model for a 3-input 3-output $reduce_and cell\n"); log("inferred by the following code:\n"); log("\n"); log(" module test(input [2:0] in, output [2:0] out);\n"); log(" assign in = &out;\n"); log(" endmodule\n"); log("\n"); log(" \"$reduce_and:3U:3\": [\n"); log(" /* 0 */ [ \"port\", \"A\", 0 ],\n"); log(" /* 1 */ [ \"port\", \"A\", 1 ],\n"); log(" /* 2 */ [ \"and\", 0, 1 ],\n"); log(" /* 3 */ [ \"port\", \"A\", 2 ],\n"); log(" /* 4 */ [ \"and\", 2, 3, \"Y\", 0 ],\n"); log(" /* 5 */ [ \"false\", \"Y\", 1, \"Y\", 2 ]\n"); log(" ]\n"); log("\n"); log("Future version of Yosys might add support for additional fields in the JSON\n"); log("format. A program processing this format must ignore all unknown fields.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool aig_mode = false; bool compat_int_mode = false; bool use_selection = false; bool scopeinfo_mode = true; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-aig") { aig_mode = true; continue; } if (args[argidx] == "-compat-int") { compat_int_mode = true; continue; } if (args[argidx] == "-selected") { use_selection = true; continue; } if (args[argidx] == "-noscopeinfo") { scopeinfo_mode = false; continue; } break; } extra_args(f, filename, args, argidx); log_header(design, "Executing JSON backend.\n"); JsonWriter json_writer(*f, use_selection, aig_mode, compat_int_mode, scopeinfo_mode); json_writer.write_design(design); } } JsonBackend; struct JsonPass : public Pass { JsonPass() : Pass("json", "write design in JSON format") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" json [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(" also include AIG models for the different gate types\n"); log("\n"); log(" -compat-int\n"); log(" emit 32-bit or smaller fully-defined parameter values directly\n"); log(" as JSON numbers (for compatibility with old parsers)\n"); log("\n"); log(" -noscopeinfo\n"); log(" don't include $scopeinfo cells in the output\n"); log("\n"); log("See 'help write_json' for a description of the JSON format used.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { std::string filename; bool aig_mode = false; bool compat_int_mode = false; bool scopeinfo_mode = true; 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] == "-compat-int") { compat_int_mode = true; continue; } if (args[argidx] == "-noscopeinfo") { scopeinfo_mode = false; continue; } break; } 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; } else { f = &buf; } JsonWriter json_writer(*f, true, aig_mode, compat_int_mode, scopeinfo_mode); json_writer.write_design(design); if (!empty) { delete f; } else { log("%s", buf.str().c_str()); } } } JsonPass; PRIVATE_NAMESPACE_END yosys-0.52/backends/rtlil/000077500000000000000000000000001477540374200155435ustar00rootroot00000000000000yosys-0.52/backends/rtlil/Makefile.inc000066400000000000000000000000511477540374200177470ustar00rootroot00000000000000 OBJS += backends/rtlil/rtlil_backend.o yosys-0.52/backends/rtlil/rtlil_backend.cc000066400000000000000000000402061477540374200206510ustar00rootroot00000000000000/* * 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. * * --- * * A very simple and straightforward backend for the RTLIL text * representation. * */ #include "rtlil_backend.h" #include "kernel/yosys.h" #include USING_YOSYS_NAMESPACE using namespace RTLIL_BACKEND; YOSYS_NAMESPACE_BEGIN void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint) { if (width < 0) width = data.size() - offset; if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.size()) { if (width == 32 && autoint) { int32_t val = 0; for (int i = 0; i < width; i++) { log_assert(offset+i < (int)data.size()); switch (data[offset+i]) { case State::S0: break; case State::S1: val |= 1 << i; break; default: val = -1; break; } } if (val >= 0) { f << stringf("%d", val); return; } } f << stringf("%d'", width); if (data.flags & RTLIL::CONST_FLAG_SIGNED) { f << stringf("s"); } if (data.is_fully_undef_x_only()) { f << "x"; } else { for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.size()); switch (data[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 { f << stringf("\""); std::string str = data.decode_string(); for (size_t i = 0; i < str.size(); i++) { if (str[i] == '\n') f << stringf("\\n"); else if (str[i] == '\t') f << stringf("\\t"); else if (str[i] < 32) f << stringf("\\%03o", (unsigned char)str[i]); else if (str[i] == '"') f << stringf("\\\""); else if (str[i] == '\\') f << stringf("\\\\"); else f << str[i]; } f << stringf("\""); } } void RTLIL_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint) { if (chunk.wire == NULL) { dump_const(f, chunk.data, chunk.width, chunk.offset, autoint); } else { if (chunk.width == chunk.wire->width && chunk.offset == 0) f << stringf("%s", chunk.wire->name.c_str()); else if (chunk.width == 1) f << stringf("%s [%d]", chunk.wire->name.c_str(), chunk.offset); else f << stringf("%s [%d:%d]", chunk.wire->name.c_str(), chunk.offset+chunk.width-1, chunk.offset); } } void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint) { if (sig.is_chunk()) { dump_sigchunk(f, sig.as_chunk(), autoint); } else { f << stringf("{ "); for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) { dump_sigchunk(f, *it, false); f << stringf(" "); } f << stringf("}"); } } void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire) { for (auto &it : wire->attributes) { f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } if (wire->driverCell_) { f << stringf("%s" "# driver %s %s\n", indent.c_str(), wire->driverCell()->name.c_str(), wire->driverPort().c_str()); } f << stringf("%s" "wire ", indent.c_str()); if (wire->width != 1) f << stringf("width %d ", wire->width); if (wire->upto) f << stringf("upto "); if (wire->start_offset != 0) f << stringf("offset %d ", wire->start_offset); if (wire->port_input && !wire->port_output) f << stringf("input %d ", wire->port_id); if (!wire->port_input && wire->port_output) f << stringf("output %d ", wire->port_id); if (wire->port_input && wire->port_output) f << stringf("inout %d ", wire->port_id); if (wire->is_signed) f << stringf("signed "); f << stringf("%s\n", wire->name.c_str()); } void RTLIL_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory) { for (auto &it : memory->attributes) { f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } f << stringf("%s" "memory ", indent.c_str()); if (memory->width != 1) f << stringf("width %d ", memory->width); if (memory->size != 0) f << stringf("size %d ", memory->size); if (memory->start_offset != 0) f << stringf("offset %d ", memory->start_offset); f << stringf("%s\n", memory->name.c_str()); } void RTLIL_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell) { for (auto &it : cell->attributes) { f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str()); for (auto &it : cell->parameters) { f << stringf("%s parameter%s%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", (it.second.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "", it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } for (auto &it : cell->connections()) { f << stringf("%s connect %s ", indent.c_str(), it.first.c_str()); dump_sigspec(f, it.second); f << stringf("\n"); } f << stringf("%s" "end\n", indent.c_str()); } void RTLIL_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs) { for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it) { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, it->first); f << stringf(" "); dump_sigspec(f, it->second); f << stringf("\n"); } for (auto it = cs->switches.begin(); it != cs->switches.end(); ++it) dump_proc_switch(f, indent, *it); } void RTLIL_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw) { for (auto it = sw->attributes.begin(); it != sw->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); } f << stringf("%s" "switch ", indent.c_str()); dump_sigspec(f, sw->signal); f << stringf("\n"); for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) { f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str()); dump_const(f, ait->second); f << stringf("\n"); } f << stringf("%s case ", indent.c_str()); for (size_t i = 0; i < (*it)->compare.size(); i++) { if (i > 0) f << stringf(" , "); dump_sigspec(f, (*it)->compare[i]); } f << stringf("\n"); dump_proc_case_body(f, indent + " ", *it); } f << stringf("%s" "end\n", indent.c_str()); } void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy) { f << stringf("%s" "sync ", indent.c_str()); switch (sy->type) { case RTLIL::ST0: f << stringf("low "); if (0) case RTLIL::ST1: f << stringf("high "); if (0) case RTLIL::STp: f << stringf("posedge "); if (0) case RTLIL::STn: f << stringf("negedge "); if (0) case RTLIL::STe: f << stringf("edge "); dump_sigspec(f, sy->signal); f << stringf("\n"); break; case RTLIL::STa: f << stringf("always\n"); break; case RTLIL::STg: f << stringf("global\n"); break; case RTLIL::STi: f << stringf("init\n"); break; } for (auto &it: sy->actions) { f << stringf("%s update ", indent.c_str()); dump_sigspec(f, it.first); f << stringf(" "); 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"); } } void RTLIL_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc) { for (auto it = proc->attributes.begin(); it != proc->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); } f << stringf("%s" "process %s\n", indent.c_str(), proc->name.c_str()); dump_proc_case_body(f, indent + " ", &proc->root_case); for (auto it = proc->syncs.begin(); it != proc->syncs.end(); ++it) dump_proc_sync(f, indent + " ", *it); f << stringf("%s" "end\n", indent.c_str()); } void RTLIL_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { f << stringf("%s" "connect ", indent.c_str()); dump_sigspec(f, left); f << stringf(" "); dump_sigspec(f, right); f << stringf("\n"); } void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n) { bool print_header = flag_m || design->selected_whole_module(module->name); bool print_body = !flag_n || !design->selected_whole_module(module->name); if (print_header) { for (auto it = module->attributes.begin(); it != module->attributes.end(); ++it) { f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str()); dump_const(f, it->second); f << stringf("\n"); } f << stringf("%s" "module %s\n", indent.c_str(), module->name.c_str()); if (!module->avail_parameters.empty()) { if (only_selected) f << stringf("\n"); for (const auto &p : module->avail_parameters) { const auto &it = module->parameter_default_values.find(p); if (it == module->parameter_default_values.end()) { f << stringf("%s" " parameter %s\n", indent.c_str(), p.c_str()); } else { f << stringf("%s" " parameter %s ", indent.c_str(), p.c_str()); dump_const(f, it->second); f << stringf("\n"); } } } } if (print_body) { for (auto it : module->wires()) if (!only_selected || design->selected(module, it)) { if (only_selected) f << stringf("\n"); dump_wire(f, indent + " ", it); } for (auto it : module->memories) if (!only_selected || design->selected(module, it.second)) { if (only_selected) f << stringf("\n"); dump_memory(f, indent + " ", it.second); } for (auto it : module->cells()) if (!only_selected || design->selected(module, it)) { if (only_selected) f << stringf("\n"); dump_cell(f, indent + " ", it); } for (auto it : module->processes) if (!only_selected || design->selected(module, it.second)) { if (only_selected) f << stringf("\n"); dump_proc(f, indent + " ", it.second); } bool first_conn_line = true; for (auto it = module->connections().begin(); it != module->connections().end(); ++it) { 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()) { if (c.wire == NULL || !design->selected(module, c.wire)) continue; show_conn = true; } } if (show_conn) { if (only_selected && first_conn_line) f << stringf("\n"); dump_conn(f, indent + " ", it->first, it->second); first_conn_line = false; } } } if (print_header) f << stringf("%s" "end\n", indent.c_str()); } void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n) { int init_autoidx = autoidx; if (!flag_m) { int count_selected_mods = 0; for (auto module : design->modules()) { if (design->selected_whole_module(module->name)) flag_m = true; if (design->selected(module)) count_selected_mods++; } if (count_selected_mods > 1) flag_m = true; } if (!only_selected || flag_m) { if (only_selected) f << stringf("\n"); f << stringf("autoidx %d\n", autoidx); } for (auto module : design->modules()) { if (!only_selected || design->selected(module)) { if (only_selected) f << stringf("\n"); dump_module(f, "", module, design, only_selected, flag_m, flag_n); } } log_assert(init_autoidx == autoidx); } YOSYS_NAMESPACE_END PRIVATE_NAMESPACE_BEGIN struct RTLILBackend : public Backend { RTLILBackend() : Backend("rtlil", "write design to RTLIL file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_rtlil [filename]\n"); log("\n"); log("Write the current design to an RTLIL file. (RTLIL is a text representation\n"); log("of a design in yosys's internal format.)\n"); log("\n"); log(" -selected\n"); log(" only write selected parts of the design.\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool selected = false; log_header(design, "Executing RTLIL backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-selected") { selected = true; continue; } break; } extra_args(f, filename, args, argidx); design->sort(); log("Output filename: %s\n", filename.c_str()); *f << stringf("# Generated by %s\n", yosys_version_str); RTLIL_BACKEND::dump_design(*f, design, selected, true, false); } } RTLILBackend; struct DumpPass : public Pass { DumpPass() : Pass("dump", "print parts of the design in RTLIL format") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" dump [options] [selection]\n"); log("\n"); log("Write the selected parts of the design to the console or specified file in\n"); log("RTLIL format.\n"); log("\n"); log(" -m\n"); log(" also dump the module headers, even if only parts of a single\n"); log(" module is selected\n"); log("\n"); log(" -n\n"); log(" only dump the module headers if the entire module is selected\n"); log("\n"); log(" -o \n"); log(" write to the specified file.\n"); log("\n"); log(" -a \n"); log(" like -outfile but append instead of overwrite\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { std::string filename; bool flag_m = false, flag_n = false, append = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if ((arg == "-o" || arg == "-outfile") && argidx+1 < args.size()) { filename = args[++argidx]; append = false; continue; } if ((arg == "-a" || arg == "-append") && argidx+1 < args.size()) { filename = args[++argidx]; append = true; continue; } if (arg == "-m") { flag_m = true; continue; } if (arg == "-n") { flag_n = true; continue; } break; } 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(), append ? std::ofstream::app : 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; } else { f = &buf; } RTLIL_BACKEND::dump_design(*f, design, true, flag_m, flag_n); if (!empty) { delete f; } else { log("%s", buf.str().c_str()); } } } DumpPass; PRIVATE_NAMESPACE_END yosys-0.52/backends/rtlil/rtlil_backend.h000066400000000000000000000045471477540374200205230ustar00rootroot00000000000000/* * 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. * * --- * * A very simple and straightforward backend for the RTLIL text * representation. * */ #ifndef RTLIL_BACKEND_H #define RTLIL_BACKEND_H #include "kernel/yosys.h" #include YOSYS_NAMESPACE_BEGIN namespace RTLIL_BACKEND { void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true); void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint = true); void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint = true); void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire); void dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory); void dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell); void dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs); void dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw); void dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy); void dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc); void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right); void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m = true, bool flag_n = false); void dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m = true, bool flag_n = false); } YOSYS_NAMESPACE_END #endif yosys-0.52/backends/simplec/000077500000000000000000000000001477540374200160515ustar00rootroot00000000000000yosys-0.52/backends/simplec/.gitignore000066400000000000000000000000271477540374200200400ustar00rootroot00000000000000test00_tb test00_uut.c yosys-0.52/backends/simplec/Makefile.inc000066400000000000000000000000451477540374200202600ustar00rootroot00000000000000 OBJS += backends/simplec/simplec.o yosys-0.52/backends/simplec/simplec.cc000066400000000000000000000632741477540374200200300ustar00rootroot00000000000000/* * 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 "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/utils.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct HierDirtyFlags; static pool reserved_cids; static dict id2cid; static string cid(IdString id) { if (id2cid.count(id) == 0) { string s = id.str(); if (GetSize(s) < 2) log_abort(); if (s[0] == '\\') s = s.substr(1); if ('0' <= s[0] && s[0] <= '9') { s = "_" + s; } for (int i = 0; i < GetSize(s); i++) { if ('0' <= s[i] && s[i] <= '9') continue; if ('A' <= s[i] && s[i] <= 'Z') continue; if ('a' <= s[i] && s[i] <= 'z') continue; s[i] = '_'; } while (reserved_cids.count(s)) s += "_"; reserved_cids.insert(s); id2cid[id] = s; } return id2cid.at(id); } struct HierDirtyFlags { int dirty; Module *module; IdString hiername; HierDirtyFlags *parent; pool dirty_bits; pool dirty_cells; pool sticky_dirty_bits; dict children; string prefix, log_prefix; HierDirtyFlags(Module *module, IdString hiername, HierDirtyFlags *parent, const string &prefix, const string &log_prefix) : dirty(0), module(module), hiername(hiername), parent(parent), prefix(prefix), log_prefix(log_prefix) { for (Cell *cell : module->cells()) { Module *mod = module->design->module(cell->type); if (mod) children[cell->name] = new HierDirtyFlags(mod, cell->name, this, prefix + cid(cell->name) + ".", log_prefix + "." + prefix + log_id(cell->name)); } } ~HierDirtyFlags() { for (auto &child : children) delete child.second; } void set_dirty(SigBit bit) { if (dirty_bits.count(bit)) return; dirty_bits.insert(bit); sticky_dirty_bits.insert(bit); HierDirtyFlags *p = this; while (p != nullptr) { p->dirty++; p = p->parent; } } void unset_dirty(SigBit bit) { if (dirty_bits.count(bit) == 0) return; dirty_bits.erase(bit); HierDirtyFlags *p = this; while (p != nullptr) { p->dirty--; log_assert(p->dirty >= 0); p = p->parent; } } void set_dirty(Cell *cell) { if (dirty_cells.count(cell)) return; dirty_cells.insert(cell); HierDirtyFlags *p = this; while (p != nullptr) { p->dirty++; p = p->parent; } } void unset_dirty(Cell *cell) { if (dirty_cells.count(cell) == 0) return; dirty_cells.erase(cell); HierDirtyFlags *p = this; while (p != nullptr) { p->dirty--; log_assert(p->dirty >= 0); p = p->parent; } } }; struct SimplecWorker { bool verbose = false; int max_uintsize = 32; Design *design; dict sigmaps; vector signal_declarations; pool generated_sigtypes; vector util_declarations; pool generated_utils; vector struct_declarations; pool generated_structs; vector funct_declarations; dict>>> bit2cell; dict>> bit2output; dict> driven_bits; dict topoidx; pool activated_cells; pool reactivated_cells; SimplecWorker(Design *design) : design(design) { } string sigtype(int n) { string struct_name = stringf("signal%d_t", n); if (generated_sigtypes.count(n) == 0) { signal_declarations.push_back(""); signal_declarations.push_back(stringf("#ifndef YOSYS_SIMPLEC_SIGNAL%d_T", n)); signal_declarations.push_back(stringf("#define YOSYS_SIMPLEC_SIGNAL%d_T", n)); signal_declarations.push_back(stringf("typedef struct {")); for (int k = 8; k <= max_uintsize; k = 2*k) if (n <= k && k <= max_uintsize) { signal_declarations.push_back(stringf(" uint%d_t value_%d_0 : %d;", k, n-1, n)); goto end_struct; } for (int k = 0; k < n; k += max_uintsize) { int bits = std::min(max_uintsize, n-k); signal_declarations.push_back(stringf(" uint%d_t value_%d_%d : %d;", max_uintsize, k+bits-1, k, bits)); } end_struct: signal_declarations.push_back(stringf("} signal%d_t;", n)); signal_declarations.push_back(stringf("#endif")); generated_sigtypes.insert(n); } return struct_name; } void util_ifdef_guard(string s) { for (int i = 0; i < GetSize(s); i++) if ('a' <= s[i] && s[i] <= 'z') s[i] -= 'a' - 'A'; util_declarations.push_back(""); util_declarations.push_back(stringf("#ifndef %s", s.c_str())); util_declarations.push_back(stringf("#define %s", s.c_str())); } string util_get_bit(const string &signame, int n, int idx) { if (n == 1 && idx == 0) return signame + ".value_0_0"; string util_name = stringf("yosys_simplec_get_bit_%d_of_%d", idx, n); if (generated_utils.count(util_name) == 0) { util_ifdef_guard(util_name); util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str())); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize); util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset)); util_declarations.push_back(stringf("}")); util_declarations.push_back(stringf("#endif")); generated_utils.insert(util_name); } return stringf("%s(&%s)", util_name.c_str(), signame.c_str()); } string util_set_bit(const string &signame, int n, int idx, const string &expr) { if (n == 1 && idx == 0) return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str()); string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n); if (generated_utils.count(util_name) == 0) { util_ifdef_guard(util_name); util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str())); util_declarations.push_back(stringf("{")); int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize); #if 0 util_declarations.push_back(stringf(" if (value)")); util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset)); util_declarations.push_back(stringf(" else")); util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset)); #else util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);", value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset)); #endif util_declarations.push_back(stringf("}")); util_declarations.push_back(stringf("#endif")); generated_utils.insert(util_name); } return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str()); } void create_module_struct(Module *mod) { if (generated_structs.count(mod->name)) return; generated_structs.insert(mod->name); sigmaps[mod].set(mod); for (Wire *w : mod->wires()) { if (w->port_output) for (auto bit : SigSpec(w)) bit2output[mod][sigmaps.at(mod)(bit)].insert(bit); } for (Cell *c : mod->cells()) { for (auto &conn : c->connections()) { if (!c->input(conn.first)) { for (auto bit : sigmaps.at(mod)(conn.second)) driven_bits[mod].insert(bit); continue; } int idx = 0; for (auto bit : sigmaps.at(mod)(conn.second)) bit2cell[mod][bit].insert(tuple(c, conn.first, idx++)); } if (design->module(c->type)) create_module_struct(design->module(c->type)); } TopoSort topo; for (Cell *c : mod->cells()) { topo.node(c->name); for (auto &conn : c->connections()) { if (!c->input(conn.first)) continue; for (auto bit : sigmaps.at(mod)(conn.second)) for (auto &it : bit2cell[mod][bit]) topo.edge(c->name, std::get<0>(it)->name); } } topo.analyze_loops = false; topo.sort(); for (int i = 0; i < GetSize(topo.sorted); i++) topoidx[mod->cell(topo.sorted[i])] = i; string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str()); for (int i = 0; i < GetSize(ifdef_name); i++) if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z') ifdef_name[i] -= 'a' - 'A'; struct_declarations.push_back(""); struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str())); struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str())); struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str())); struct_declarations.push_back("{"); struct_declarations.push_back(" // Input Ports"); for (Wire *w : mod->wires()) if (w->port_input) struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); struct_declarations.push_back(""); struct_declarations.push_back(" // Output Ports"); for (Wire *w : mod->wires()) if (!w->port_input && w->port_output) struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); struct_declarations.push_back(""); struct_declarations.push_back(" // Internal Wires"); for (Wire *w : mod->wires()) if (!w->port_input && !w->port_output) struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); for (Cell *c : mod->cells()) if (design->module(c->type)) struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type).c_str(), cid(c->name).c_str(), log_id(c))); struct_declarations.push_back(stringf("};")); struct_declarations.push_back("#endif"); } void eval_cell(HierDirtyFlags *work, Cell *cell) { if (cell->type.in(ID($_BUF_), ID($_NOT_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string expr; if (cell->type == ID($_BUF_)) expr = a_expr; if (cell->type == ID($_NOT_)) expr = "!" + a_expr; log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit b = sigmaps.at(work->module)(cell->getPort(ID::B)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string expr; if (cell->type == ID($_AND_)) expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_NAND_)) expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_OR_)) expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_NOR_)) expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_XOR_)) expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_XNOR_)) expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_ANDNOT_)) expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str()); if (cell->type == ID($_ORNOT_)) expr = stringf("%s | (!%s)", a_expr.c_str(), b_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } if (cell->type.in(ID($_AOI3_), ID($_OAI3_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit b = sigmaps.at(work->module)(cell->getPort(ID::B)); SigBit c = sigmaps.at(work->module)(cell->getPort(ID::C)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0"; string expr; if (cell->type == ID($_AOI3_)) expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); if (cell->type == ID($_OAI3_)) expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } if (cell->type.in(ID($_AOI4_), ID($_OAI4_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit b = sigmaps.at(work->module)(cell->getPort(ID::B)); SigBit c = sigmaps.at(work->module)(cell->getPort(ID::C)); SigBit d = sigmaps.at(work->module)(cell->getPort(ID::D)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0"; string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0"; string expr; if (cell->type == ID($_AOI4_)) expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); if (cell->type == ID($_OAI4_)) expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } if (cell->type.in(ID($_MUX_), ID($_NMUX_))) { SigBit a = sigmaps.at(work->module)(cell->getPort(ID::A)); SigBit b = sigmaps.at(work->module)(cell->getPort(ID::B)); SigBit s = sigmaps.at(work->module)(cell->getPort(ID::S)); SigBit y = sigmaps.at(work->module)(cell->getPort(ID::Y)); string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0"; // casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933) string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr.c_str(), cell->type == ID($_NMUX_) ? "!" : "", b_expr.c_str(), cell->type == ID($_NMUX_) ? "!" : "", a_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); work->set_dirty(y); return; } log_error("No C model for %s available at the moment (FIXME).\n", log_id(cell->type)); } void eval_dirty(HierDirtyFlags *work) { while (work->dirty) { if (verbose && (!work->dirty_bits.empty() || !work->dirty_cells.empty())) log(" In %s:\n", work->log_prefix.c_str()); while (!work->dirty_bits.empty() || !work->dirty_cells.empty()) { if (!work->dirty_bits.empty()) { SigSpec dirtysig(work->dirty_bits); dirtysig.sort_and_unify(); for (SigChunk chunk : dirtysig.chunks()) { if (chunk.wire == nullptr) continue; if (verbose) log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset); funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk))); } for (SigBit bit : dirtysig) { if (bit2output[work->module].count(bit) && work->parent) for (auto outbit : bit2output[work->module][bit]) { Module *parent_mod = work->parent->module; Cell *parent_cell = parent_mod->cell(work->hiername); IdString port_name = outbit.wire->name; int port_offset = outbit.offset; SigBit parent_bit = sigmaps.at(parent_mod)(parent_cell->getPort(port_name)[port_offset]); log_assert(bit.wire && parent_bit.wire); funct_declarations.push_back(util_set_bit(work->parent->prefix + cid(parent_bit.wire->name), parent_bit.wire->width, parent_bit.offset, util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset))); work->parent->set_dirty(parent_bit); if (verbose) log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset, work->parent->log_prefix.c_str(), log_id(parent_bit.wire), parent_bit.offset); } for (auto &port : bit2cell[work->module][bit]) { if (work->children.count(std::get<0>(port)->name)) { HierDirtyFlags *child = work->children.at(std::get<0>(port)->name); SigBit child_bit = sigmaps.at(child->module)(SigBit(child->module->wire(std::get<1>(port)), std::get<2>(port))); log_assert(bit.wire && child_bit.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(child->hiername) + "." + cid(child_bit.wire->name), child_bit.wire->width, child_bit.offset, util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset))); child->set_dirty(child_bit); if (verbose) log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset, work->log_prefix.c_str(), log_id(std::get<0>(port)), log_id(child_bit.wire), child_bit.offset); } else { if (verbose) log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix.c_str(), log_id(std::get<0>(port)), work->log_prefix.c_str(), log_id(bit.wire), bit.offset); work->set_dirty(std::get<0>(port)); } } work->unset_dirty(bit); } } if (!work->dirty_cells.empty()) { Cell *cell = nullptr; for (auto c : work->dirty_cells) if (cell == nullptr || topoidx.at(cell) < topoidx.at(c)) cell = c; string hiername = work->log_prefix + "." + log_id(cell); if (verbose) log(" Evaluating %s (%s, best of %d).\n", hiername.c_str(), log_id(cell->type), GetSize(work->dirty_cells)); if (activated_cells.count(hiername)) reactivated_cells.insert(hiername); activated_cells.insert(hiername); eval_cell(work, cell); work->unset_dirty(cell); } } for (auto &child : work->children) eval_dirty(child.second); } } void eval_sticky_dirty(HierDirtyFlags *work) { Module *mod = work->module; for (Wire *w : mod->wires()) for (SigBit bit : SigSpec(w)) { SigBit canonical_bit = sigmaps.at(mod)(bit); if (canonical_bit == bit) continue; if (work->sticky_dirty_bits.count(canonical_bit) == 0) continue; if (bit.wire == nullptr || canonical_bit.wire == nullptr) continue; funct_declarations.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, util_get_bit(work->prefix + cid(canonical_bit.wire->name), canonical_bit.wire->width, canonical_bit.offset).c_str())); if (verbose) log(" Propagating alias %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix.c_str(), log_id(canonical_bit.wire), canonical_bit.offset, work->log_prefix.c_str(), log_id(bit.wire), bit.offset); } work->sticky_dirty_bits.clear(); for (auto &child : work->children) eval_sticky_dirty(child.second); } void make_func(HierDirtyFlags *work, const string &func_name, const vector &preamble) { log("Generating function %s():\n", func_name.c_str()); activated_cells.clear(); reactivated_cells.clear(); funct_declarations.push_back(""); funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str())); funct_declarations.push_back("{"); for (auto &line : preamble) funct_declarations.push_back(line); eval_dirty(work); eval_sticky_dirty(work); funct_declarations.push_back("}"); log(" Activated %d cells (%d activated more than once).\n", GetSize(activated_cells), GetSize(reactivated_cells)); } void eval_init(HierDirtyFlags *work, vector &preamble) { Module *module = work->module; for (Wire *w : module->wires()) { if (w->attributes.count(ID::init)) { SigSpec sig = sigmaps.at(module)(w); Const val = w->attributes.at(ID::init); val.bits().resize(GetSize(sig), State::Sx); for (int i = 0; i < GetSize(sig); i++) if (val[i] == State::S0 || val[i] == State::S1) { SigBit bit = sig[i]; preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false")); work->set_dirty(bit); } } for (SigBit bit : SigSpec(w)) { SigBit val = sigmaps.at(module)(bit); if (val == State::S0 || val == State::S1) preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false")); if (driven_bits.at(module).count(val) == 0) work->set_dirty(val); } } work->set_dirty(State::S0); work->set_dirty(State::S1); for (auto &child : work->children) eval_init(child.second, preamble); } void make_init_func(HierDirtyFlags *work) { vector preamble; eval_init(work, preamble); make_func(work, cid(work->module->name) + "_init", preamble); } void make_eval_func(HierDirtyFlags *work) { Module *mod = work->module; vector preamble; for (Wire *w : mod->wires()) { if (w->port_input) for (SigBit bit : sigmaps.at(mod)(w)) work->set_dirty(bit); } make_func(work, cid(work->module->name) + "_eval", preamble); } void make_tick_func(HierDirtyFlags* /* work */) { // FIXME } void run(Module *mod) { create_module_struct(mod); HierDirtyFlags work(mod, IdString(), nullptr, "state->", log_id(mod->name)); make_init_func(&work); make_eval_func(&work); make_tick_func(&work); } void write(std::ostream &f) { f << "#include " << std::endl; f << "#include " << std::endl; for (auto &line : signal_declarations) f << line << std::endl; for (auto &line : util_declarations) f << line << std::endl; for (auto &line : struct_declarations) f << line << std::endl; for (auto &line : funct_declarations) f << line << std::endl; } }; struct SimplecBackend : public Backend { SimplecBackend() : Backend("simplec", "convert design to simple C code") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_simplec [options] [filename]\n"); log("\n"); log("Write simple C code for simulating the design. The C code written can be used to\n"); log("simulate the design in a C environment, but the purpose of this command is to\n"); log("generate code that works well with C-based formal verification.\n"); log("\n"); log(" -verbose\n"); log(" this will print the recursive walk used to export the modules.\n"); log("\n"); log(" -i8, -i16, -i32, -i64\n"); log(" set the maximum integer bit width to use in the generated code.\n"); log("\n"); log("THIS COMMAND IS UNDER CONSTRUCTION\n"); log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { reserved_cids.clear(); id2cid.clear(); SimplecWorker worker(design); log_header(design, "Executing SIMPLEC backend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-verbose") { worker.verbose = true; continue; } if (args[argidx] == "-i8") { worker.max_uintsize = 8; continue; } if (args[argidx] == "-i16") { worker.max_uintsize = 16; continue; } if (args[argidx] == "-i32") { worker.max_uintsize = 32; continue; } if (args[argidx] == "-i64") { worker.max_uintsize = 64; continue; } break; } extra_args(f, filename, args, argidx); Module *topmod = design->top_module(); if (topmod == nullptr) log_error("Current design has no top module.\n"); worker.run(topmod); worker.write(*f); } } SimplecBackend; PRIVATE_NAMESPACE_END yosys-0.52/backends/simplec/test00.sh000066400000000000000000000002361477540374200175250ustar00rootroot00000000000000#!/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 ./test00_tb yosys-0.52/backends/simplec/test00_tb.c000066400000000000000000000041251477540374200200230ustar00rootroot00000000000000#include #include #include "test00_uut.c" uint32_t xorshift32() { static uint32_t x32 = 314159265; x32 ^= x32 << 13; x32 ^= x32 >> 17; x32 ^= x32 << 5; return x32; } int main() { struct test_state_t state; uint32_t a, b, c, x, y, z, w; bool first_eval = true; for (int i = 0; i < 10; i++) { a = xorshift32(); b = xorshift32(); c = xorshift32(); x = (a & b) | c; y = a & (b | c); z = a ^ b ^ c; w = z; state.a.value_7_0 = a; state.a.value_15_8 = a >> 8; state.a.value_23_16 = a >> 16; state.a.value_31_24 = a >> 24; state.b.value_7_0 = b; state.b.value_15_8 = b >> 8; state.b.value_23_16 = b >> 16; state.b.value_31_24 = b >> 24; state.c.value_7_0 = c; state.c.value_15_8 = c >> 8; state.c.value_23_16 = c >> 16; state.c.value_31_24 = c >> 24; if (first_eval) { first_eval = false; test_init(&state); } else { test_eval(&state); } uint32_t uut_x = 0; uut_x |= (uint32_t)state.x.value_7_0; uut_x |= (uint32_t)state.x.value_15_8 << 8; uut_x |= (uint32_t)state.x.value_23_16 << 16; uut_x |= (uint32_t)state.x.value_31_24 << 24; uint32_t uut_y = 0; uut_y |= (uint32_t)state.y.value_7_0; uut_y |= (uint32_t)state.y.value_15_8 << 8; uut_y |= (uint32_t)state.y.value_23_16 << 16; uut_y |= (uint32_t)state.y.value_31_24 << 24; uint32_t uut_z = 0; uut_z |= (uint32_t)state.z.value_7_0; uut_z |= (uint32_t)state.z.value_15_8 << 8; uut_z |= (uint32_t)state.z.value_23_16 << 16; uut_z |= (uint32_t)state.z.value_31_24 << 24; uint32_t uut_w = 0; uut_w |= (uint32_t)state.w.value_7_0; uut_w |= (uint32_t)state.w.value_15_8 << 8; uut_w |= (uint32_t)state.w.value_23_16 << 16; uut_w |= (uint32_t)state.w.value_31_24 << 24; printf("---\n"); printf("A: 0x%08x\n", a); printf("B: 0x%08x\n", b); printf("C: 0x%08x\n", c); printf("X: 0x%08x 0x%08x\n", x, uut_x); printf("Y: 0x%08x 0x%08x\n", y, uut_y); printf("Z: 0x%08x 0x%08x\n", z, uut_z); printf("W: 0x%08x 0x%08x\n", w, uut_w); assert(x == uut_x); assert(y == uut_y); assert(z == uut_z); assert(w == uut_w); } return 0; } yosys-0.52/backends/simplec/test00_uut.v000066400000000000000000000006031477540374200202530ustar00rootroot00000000000000module test(input [31:0] a, b, c, output [31:0] x, y, z, w); unit_x unit_x_inst (.a(a), .b(b), .c(c), .x(x)); unit_y unit_y_inst (.a(a), .b(b), .c(c), .y(y)); assign z = a ^ b ^ c, w = z; endmodule module unit_x(input [31:0] a, b, c, output [31:0] x); assign x = (a & b) | c; endmodule module unit_y(input [31:0] a, b, c, output [31:0] y); assign y = a & (b | c); endmodule yosys-0.52/backends/smt2/000077500000000000000000000000001477540374200153025ustar00rootroot00000000000000yosys-0.52/backends/smt2/.gitignore000066400000000000000000000000131477540374200172640ustar00rootroot00000000000000test_cells yosys-0.52/backends/smt2/Makefile.inc000066400000000000000000000042401477540374200175120ustar00rootroot00000000000000 OBJS += backends/smt2/smt2.o ifneq ($(CONFIG),mxe) 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) $(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 $(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 yosys-0.52/backends/smt2/example.v000066400000000000000000000003531477540374200171250ustar00rootroot00000000000000module main(input clk); reg [3:0] counter = 0; always @(posedge clk) begin if (counter == 10) counter <= 0; else counter <= counter + 1; end assert property (counter != 15); // assert property (counter <= 10); endmodule yosys-0.52/backends/smt2/example.ys000066400000000000000000000001761477540374200173160ustar00rootroot00000000000000read_verilog -formal example.v hierarchy; proc; opt; memory -nordff -nomap; opt -fast write_smt2 -bv -mem -wires example.smt2 yosys-0.52/backends/smt2/smt2.cc000066400000000000000000002175551477540374200165150ustar00rootroot00000000000000/* * 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 "kernel/rtlil.h" #include "kernel/register.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/mem.h" #include "libs/json11/json11.hpp" #include "kernel/utils.h" #include USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct Smt2Worker { CellTypes ct; SigMap sigmap; RTLIL::Module *module; bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode; dict &mod_stbv_width; int idcounter = 0, statebv_width = 0; std::vector decls, trans, hier, dtmembers; std::map bit_driver; std::set exported_cells, hiercells, hiercells_queue; pool recursive_cells, registers; std::vector memories; dict mem_cells; std::set memory_queue; pool clock_posedge, clock_negedge; vector ex_state_eq, ex_input_eq; std::map> fcache; std::map memarrays; std::map bvsizes; dict ids; bool is_smtlib2_module; const char *get_id(IdString n) { if (ids.count(n) == 0) { std::string str = log_id(n); for (int i = 0; i < GetSize(str); i++) { if (str[i] == '\\') str[i] = '/'; } ids[n] = strdup(str.c_str()); } return ids[n]; } template const char *get_id(T *obj) { return get_id(obj->name); } void makebits(std::string name, int width = 0, std::string comment = std::string()) { std::string decl_str; if (statebv) { if (width == 0) { decl_str = stringf("(define-fun |%s| ((state |%s_s|)) Bool (= ((_ extract %d %d) state) #b1))", name.c_str(), get_id(module), statebv_width, statebv_width); statebv_width += 1; } else { decl_str = stringf("(define-fun |%s| ((state |%s_s|)) (_ BitVec %d) ((_ extract %d %d) state))", name.c_str(), get_id(module), width, statebv_width+width-1, statebv_width); statebv_width += width; } } else if (statedt) { if (width == 0) { decl_str = stringf(" (|%s| Bool)", name.c_str()); } else { decl_str = stringf(" (|%s| (_ BitVec %d))", name.c_str(), width); } } else { if (width == 0) { decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name.c_str(), get_id(module)); } else { decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name.c_str(), get_id(module), width); } } if (!comment.empty()) decl_str += " ; " + comment; if (statedt) dtmembers.push_back(decl_str + "\n"); else decls.push_back(decl_str + "\n"); } 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), is_smtlib2_module(module->has_attribute(ID::smtlib2_module)) { pool noclock; makebits(stringf("%s_is", get_id(module))); dict mem_dict; 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) { if (port.clk_enable) { SigSpec clk = sigmap(port.clk); for (int i = 0; i < GetSize(clk); i++) { if (clk[i].wire == nullptr) continue; if (port.clk_polarity) clock_posedge.insert(clk[i]); else clock_negedge.insert(clk[i]); } } for (auto bit : sigmap(port.en)) noclock.insert(bit); for (auto bit : sigmap(port.addr)) noclock.insert(bit); for (auto bit : sigmap(port.data)) noclock.insert(bit); } for (auto &port : mem.rd_ports) { if (port.clk_enable) { SigSpec clk = sigmap(port.clk); for (int i = 0; i < GetSize(clk); i++) { if (clk[i].wire == nullptr) continue; if (port.clk_polarity) clock_posedge.insert(clk[i]); else clock_negedge.insert(clk[i]); } } for (auto bit : sigmap(port.en)) noclock.insert(bit); for (auto bit : sigmap(port.addr)) noclock.insert(bit); for (auto bit : sigmap(port.data)) noclock.insert(bit); Cell *driver = port.cell ? port.cell : mem.cell; for (auto bit : sigmap(port.data)) { if (bit_driver.count(bit)) log_error("Found multiple drivers for %s.\n", log_signal(bit)); bit_driver[bit] = driver; } } } for (auto cell : module->cells()) for (auto &conn : cell->connections()) { if (GetSize(conn.second) == 0) continue; // Handled above. if (cell->is_mem_cell()) { mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()]; continue; } bool is_input = ct.cell_input(cell->type, conn.first); bool is_output = ct.cell_output(cell->type, conn.first); if (is_output && !is_input) for (auto bit : sigmap(conn.second)) { if (bit_driver.count(bit)) log_error("Found multiple drivers for %s.\n", log_signal(bit)); bit_driver[bit] = cell; } else if (is_output || !is_input) log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n", log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type)); if (cell->type.in(ID($dff), ID($_DFF_P_), ID($_DFF_N_)) && conn.first.in(ID::CLK, ID::C)) { bool posedge = (cell->type == ID($_DFF_N_)) || (cell->type == ID($dff) && cell->getParam(ID::CLK_POLARITY).as_bool()); for (auto bit : sigmap(conn.second)) { if (posedge) clock_posedge.insert(bit); else clock_negedge.insert(bit); } } else if (mod_clk_cache.count(cell->type) && mod_clk_cache.at(cell->type).count(conn.first)) { for (auto bit : sigmap(conn.second)) { if (mod_clk_cache.at(cell->type).at(conn.first).first) clock_posedge.insert(bit); if (mod_clk_cache.at(cell->type).at(conn.first).second) clock_negedge.insert(bit); } } else { for (auto bit : sigmap(conn.second)) noclock.insert(bit); } } for (auto bit : noclock) { clock_posedge.erase(bit); 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) continue; SigBit bit = sigmap(wire); if (clock_posedge.count(bit)) mod_clk_cache[module->name][wire->name].first = true; if (clock_negedge.count(bit)) mod_clk_cache[module->name][wire->name].second = true; } } ~Smt2Worker() { for (auto &it : ids) free(it.second); ids.clear(); } const char *get_id(Module *m) { return get_id(m->name); } const char *get_id(Cell *c) { return get_id(c->name); } const char *get_id(Wire *w) { return get_id(w->name); } void register_bool(RTLIL::SigBit bit, int id) { if (verbose) log("%*s-> register_bool: %s %d\n", 2+2*GetSize(recursive_cells), "", log_signal(bit), id); sigmap.apply(bit); log_assert(fcache.count(bit) == 0); fcache[bit] = std::pair(id, -1); } void register_bv(RTLIL::SigSpec sig, int id) { if (verbose) log("%*s-> register_bv: %s %d\n", 2+2*GetSize(recursive_cells), "", log_signal(sig), id); log_assert(bvmode); sigmap.apply(sig); log_assert(bvsizes.count(id) == 0); bvsizes[id] = GetSize(sig); for (int i = 0; i < GetSize(sig); i++) { log_assert(fcache.count(sig[i]) == 0); fcache[sig[i]] = std::pair(id, i); } } void register_boolvec(RTLIL::SigSpec sig, int id) { if (verbose) log("%*s-> register_boolvec: %s %d\n", 2+2*GetSize(recursive_cells), "", log_signal(sig), id); log_assert(bvmode); sigmap.apply(sig); register_bool(sig[0], id); for (int i = 1; i < GetSize(sig); i++) sigmap.add(sig[i], RTLIL::State::S0); } std::string get_bool(RTLIL::SigBit bit, const char *state_name = "state") { sigmap.apply(bit); if (bit_driver.count(bit)) { export_cell(bit_driver.at(bit)); sigmap.apply(bit); } if (bit.wire == nullptr) return bit == RTLIL::State::S1 ? "true" : "false"; if (fcache.count(bit) == 0) { if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "", log_signal(bit)); makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(bit)); register_bool(bit, idcounter++); } auto f = fcache.at(bit); if (f.second >= 0) return stringf("(= ((_ extract %d %d) (|%s#%d| %s)) #b1)", f.second, f.second, get_id(module), f.first, state_name); return stringf("(|%s#%d| %s)", get_id(module), f.first, state_name); } std::string get_bool(RTLIL::SigSpec sig, const char *state_name = "state") { return get_bool(sig.as_bit(), state_name); } std::string get_bv(RTLIL::SigSpec sig, const char *state_name = "state") { log_assert(bvmode); sigmap.apply(sig); std::vector subexpr; SigSpec orig_sig; while (orig_sig != sig) { for (auto bit : sig) if (bit_driver.count(bit)) export_cell(bit_driver.at(bit)); orig_sig = sig; sigmap.apply(sig); } for (int i = 0, j = 1; i < GetSize(sig); i += j, j = 1) { if (sig[i].wire == nullptr) { while (i+j < GetSize(sig) && sig[i+j].wire == nullptr) j++; subexpr.push_back("#b"); for (int k = i+j-1; k >= i; k--) subexpr.back() += sig[k] == RTLIL::State::S1 ? "1" : "0"; continue; } if (fcache.count(sig[i]) && fcache.at(sig[i]).second == -1) { subexpr.push_back(stringf("(ite %s #b1 #b0)", get_bool(sig[i], state_name).c_str())); continue; } if (fcache.count(sig[i])) { auto t1 = fcache.at(sig[i]); while (i+j < GetSize(sig)) { if (fcache.count(sig[i+j]) == 0) break; auto t2 = fcache.at(sig[i+j]); if (t1.first != t2.first) break; if (t1.second+j != t2.second) break; j++; } if (t1.second == 0 && j == bvsizes.at(t1.first)) subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), t1.first, state_name)); else subexpr.push_back(stringf("((_ extract %d %d) (|%s#%d| %s))", t1.second + j - 1, t1.second, get_id(module), t1.first, state_name)); continue; } std::set seen_bits = { sig[i] }; while (i+j < GetSize(sig) && sig[i+j].wire && !fcache.count(sig[i+j]) && !seen_bits.count(sig[i+j])) seen_bits.insert(sig[i+j]), j++; if (verbose) log("%*s-> external bv: %s\n", 2+2*GetSize(recursive_cells), "", log_signal(sig.extract(i, j))); for (auto bit : sig.extract(i, j)) log_assert(bit_driver.count(bit) == 0); makebits(stringf("%s#%d", get_id(module), idcounter), j, log_signal(sig.extract(i, j))); subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), idcounter, state_name)); register_bv(sig.extract(i, j), idcounter++); } if (GetSize(subexpr) > 1) { std::string expr = "", end_str = ""; for (int i = GetSize(subexpr)-1; i >= 0; i--) { if (i > 0) expr += " (concat", end_str += ")"; expr += " " + subexpr[i]; } return expr.substr(1) + end_str; } else { log_assert(GetSize(subexpr) == 1); return subexpr[0]; } } void export_gate(RTLIL::Cell *cell, std::string expr) { RTLIL::SigBit bit = sigmap(cell->getPort(ID::Y).as_bit()); std::string processed_expr; for (char ch : expr) { if (ch == 'A') processed_expr += get_bool(cell->getPort(ID::A)); else if (ch == 'B') processed_expr += get_bool(cell->getPort(ID::B)); else if (ch == 'C') processed_expr += get_bool(cell->getPort(ID::C)); else if (ch == 'D') processed_expr += get_bool(cell->getPort(ID::D)); else if (ch == 'S') processed_expr += get_bool(cell->getPort(ID::S)); else processed_expr += ch; } if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(bit))); register_bool(bit, idcounter++); recursive_cells.erase(cell); } void export_bvop(RTLIL::Cell *cell, std::string expr, char type = 0) { RTLIL::SigSpec sig_a, sig_b; RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y)); bool is_signed = type == 'U' ? false : cell->getParam(ID::A_SIGNED).as_bool(); int width = GetSize(sig_y); 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))); } if (cell->hasPort(ID::A)) { sig_a = cell->getPort(ID::A); sig_a.extend_u0(width, is_signed); } if (cell->hasPort(ID::B)) { sig_b = cell->getPort(ID::B); sig_b.extend_u0(width, (type == 'S') || (is_signed && !(type == 's'))); } std::string processed_expr; for (char ch : expr) { 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; } if (width != GetSize(sig_y) && type != 'b') processed_expr = stringf("((_ extract %d 0) %s)", GetSize(sig_y)-1, processed_expr.c_str()); if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); if (type == 'b') { decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(sig_y))); register_boolvec(sig_y, idcounter++); } else { decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), idcounter, get_id(module), GetSize(sig_y), processed_expr.c_str(), log_signal(sig_y))); register_bv(sig_y, idcounter++); } recursive_cells.erase(cell); } void export_reduce(RTLIL::Cell *cell, std::string expr, bool identity_val) { RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y)); std::string processed_expr; for (char ch : expr) if (ch == 'A' || ch == 'B') { RTLIL::SigSpec sig = sigmap(cell->getPort(stringf("\\%c", ch))); for (auto bit : sig) processed_expr += " " + get_bool(bit); if (GetSize(sig) == 1) processed_expr += identity_val ? " true" : " false"; } else processed_expr += ch; if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool %s) ; %s\n", get_id(module), idcounter, get_id(module), processed_expr.c_str(), log_signal(sig_y))); register_boolvec(sig_y, idcounter++); recursive_cells.erase(cell); } void export_cell(RTLIL::Cell *cell) { if (verbose) log("%*s=> export_cell %s (%s) [%s]\n", 2+2*GetSize(recursive_cells), "", log_id(cell), log_id(cell->type), exported_cells.count(cell) ? "old" : "new"); if (recursive_cells.count(cell)) log_error("Found logic loop in module %s! See cell %s.\n", get_id(module), get_id(cell)); if (exported_cells.count(cell)) return; exported_cells.insert(cell); recursive_cells.insert(cell); if (cell->type == ID($initstate)) { SigBit bit = sigmap(cell->getPort(ID::Y).as_bit()); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool (|%s_is| state)) ; %s\n", get_id(module), idcounter, get_id(module), get_id(module), log_signal(bit))); register_bool(bit, idcounter++); recursive_cells.erase(cell); return; } 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); return; } if (cell->type == ID($_BUF_)) return export_gate(cell, "A"); if (cell->type == ID($_NOT_)) return export_gate(cell, "(not A)"); if (cell->type == ID($_AND_)) return export_gate(cell, "(and A B)"); if (cell->type == ID($_NAND_)) return export_gate(cell, "(not (and A B))"); if (cell->type == ID($_OR_)) return export_gate(cell, "(or A B)"); if (cell->type == ID($_NOR_)) return export_gate(cell, "(not (or A B))"); if (cell->type == ID($_XOR_)) return export_gate(cell, "(xor A B)"); if (cell->type == ID($_XNOR_)) return export_gate(cell, "(not (xor A B))"); if (cell->type == ID($_ANDNOT_)) return export_gate(cell, "(and A (not B))"); if (cell->type == ID($_ORNOT_)) return export_gate(cell, "(or A (not B))"); if (cell->type == ID($_MUX_)) return export_gate(cell, "(ite S B A)"); if (cell->type == ID($_NMUX_)) return export_gate(cell, "(not (ite S B A))"); if (cell->type == ID($_AOI3_)) return export_gate(cell, "(not (or (and A B) C))"); if (cell->type == ID($_OAI3_)) return export_gate(cell, "(not (and (or A B) C))"); if (cell->type == ID($_AOI4_)) return export_gate(cell, "(not (or (and A B) (and C D)))"); if (cell->type == ID($_OAI4_)) return export_gate(cell, "(not (and (or A B) (or C D)))"); // FIXME: $lut if (bvmode) { 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($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(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(QY).as_wire()->name.str().c_str()); } 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(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(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(QY), idcounter++); recursive_cells.erase(cell); return; } if (cell->type == ID($and)) return export_bvop(cell, "(bvand A B)"); if (cell->type == ID($or)) return export_bvop(cell, "(bvor A B)"); 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'); if (cell->type == ID($sshr)) return export_bvop(cell, "(bvLshr A B)", 's'); 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) (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'); } } if (cell->type == ID($lt)) return export_bvop(cell, "(bvUlt A B)", 'b'); if (cell->type == ID($le)) return export_bvop(cell, "(bvUle A B)", 'b'); if (cell->type == ID($ge)) return export_bvop(cell, "(bvUge A B)", 'b'); if (cell->type == ID($gt)) return export_bvop(cell, "(bvUgt A B)", 'b'); if (cell->type == ID($ne)) return export_bvop(cell, "(distinct A B)", 'b'); if (cell->type == ID($nex)) return export_bvop(cell, "(distinct A B)", 'b'); if (cell->type == ID($eq)) return export_bvop(cell, "(= A B)", 'b'); if (cell->type == ID($eqx)) return export_bvop(cell, "(= A B)", 'b'); if (cell->type == ID($not)) return export_bvop(cell, "(bvnot A)"); if (cell->type == ID($pos)) return export_bvop(cell, "A"); if (cell->type == ID($neg)) return export_bvop(cell, "(bvneg A)"); if (cell->type == ID($add)) return export_bvop(cell, "(bvadd A B)"); if (cell->type == ID($sub)) return export_bvop(cell, "(bvsub A B)"); if (cell->type == ID($mul)) return export_bvop(cell, "(bvmul A B)"); if (cell->type == ID($div)) return export_bvop(cell, "(bvUdiv A B)", 'd'); // "rem" = truncating modulo if (cell->type == ID($mod)) return export_bvop(cell, "(bvUrem A B)", 'd'); // "mod" = flooring modulo if (cell->type == ID($modfloor)) { // bvumod doesn't exist because it's the same as bvurem if (cell->getParam(ID::A_SIGNED).as_bool()) { return export_bvop(cell, "(bvsmod A B)", 'd'); } else { 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))) { bool is_and = cell->type == ID($reduce_and); string bits(GetSize(cell->getPort(ID::A)), is_and ? '1' : '0'); return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits.c_str()), 'b'); } if (cell->type == ID($reduce_and)) return export_reduce(cell, "(and A)", true); if (cell->type == ID($reduce_or)) return export_reduce(cell, "(or A)", false); if (cell->type == ID($reduce_xor)) return export_reduce(cell, "(xor A)", false); if (cell->type == ID($reduce_xnor)) return export_reduce(cell, "(not (xor A))", false); if (cell->type == ID($reduce_bool)) return export_reduce(cell, "(or A)", false); if (cell->type == ID($logic_not)) return export_reduce(cell, "(not (or A))", false); if (cell->type == ID($logic_and)) return export_reduce(cell, "(and (or A) (or B))", false); if (cell->type == ID($logic_or)) return export_reduce(cell, "(or A B)", false); if (cell->type.in(ID($mux), ID($pmux))) { int width = GetSize(cell->getPort(ID::Y)); std::string processed_expr = get_bv(cell->getPort(ID::A)); RTLIL::SigSpec sig_b = cell->getPort(ID::B); RTLIL::SigSpec sig_s = cell->getPort(ID::S); get_bv(sig_b); get_bv(sig_s); for (int i = 0; i < GetSize(sig_s); i++) processed_expr = stringf("(ite %s %s %s)", get_bool(sig_s[i]).c_str(), get_bv(sig_b.extract(i*width, width)).c_str(), processed_expr.c_str()); if (verbose) log("%*s-> import cell: %s\n", 2+2*GetSize(recursive_cells), "", log_id(cell)); RTLIL::SigSpec sig = sigmap(cell->getPort(ID::Y)); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), idcounter, get_id(module), width, processed_expr.c_str(), log_signal(sig))); register_bv(sig, idcounter++); recursive_cells.erase(cell); return; } // FIXME: $slice $concat } if (memmode && cell->is_mem_cell()) { Mem *mem = mem_cells[cell]; if (memarrays.count(mem)) { recursive_cells.erase(cell); return; } int arrayid = idcounter++; memarrays[mem] = arrayid; int abits = max(1, ceil_log2(mem->size)); bool has_sync_wr = false; bool has_async_wr = false; for (auto &port : mem->wr_ports) { if (port.clk_enable) has_sync_wr = true; else has_async_wr = true; } if (has_async_wr && has_sync_wr) 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) { memstate = stringf("%s#%d#final", get_id(module), arrayid); } else { memstate = stringf("%s#%d#0", get_id(module), arrayid); } if (statebv) { makebits(memstate, mem->width*mem->size, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s| state))\n", get_id(module), get_id(mem->memid), get_id(module), mem->width*mem->size, memstate.c_str())); for (int i = 0; i < GetSize(mem->rd_ports); i++) { auto &port = mem->rd_ports[i]; SigSpec addr_sig = port.addr; addr_sig.extend_u0(abits); std::string addr = get_bv(addr_sig); if (port.clk_enable) log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! " "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), log_id(mem->memid), log_id(module)); decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); std::string read_expr = "#b"; for (int k = 0; k < mem->width; k++) read_expr += "0"; for (int k = 0; k < mem->size; k++) read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)", get_id(module), i, get_id(mem->memid), Const(k+mem->start_offset, abits).as_string().c_str(), mem->width*(k+1)-1, mem->width*k, memstate.c_str(), read_expr.c_str()); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d)\n %s) ; %s\n", get_id(module), idcounter, get_id(module), mem->width, read_expr.c_str(), log_signal(port.data))); decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, get_id(module), idcounter)); register_bv(port.data, idcounter++); } } else { if (statedt) dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", memstate.c_str(), abits, mem->width, get_id(mem->memid))); else decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", memstate.c_str(), get_id(module), abits, mem->width, get_id(mem->memid))); decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s| state))\n", get_id(module), get_id(mem->memid), get_id(module), abits, mem->width, memstate.c_str())); for (int i = 0; i < GetSize(mem->rd_ports); i++) { auto &port = mem->rd_ports[i]; SigSpec addr_sig = port.addr; addr_sig.extend_u0(abits); std::string addr = get_bv(addr_sig); if (port.clk_enable) log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! " "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), log_id(mem->memid), log_id(module)); decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n", get_id(module), idcounter, get_id(module), mem->width, memstate.c_str(), get_id(module), i, get_id(mem->memid), log_signal(port.data))); decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, get_id(module), idcounter)); register_bv(port.data, idcounter++); } } memory_queue.insert(mem); recursive_cells.erase(cell); return; } Module *m = module->design->module(cell->type); 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()) { if (GetSize(conn.second) == 0) continue; Wire *w = m->wire(conn.first); SigSpec sig = sigmap(conn.second); if (w->port_output && !w->port_input) { if (GetSize(w) > 1) { if (bvmode) { makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(w), log_signal(sig)); register_bv(sig, idcounter++); } else { for (int i = 0; i < GetSize(w); i++) { makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig[i])); register_bool(sig[i], idcounter++); } } } else { makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig)); register_bool(sig, idcounter++); } } } if (statebv) makebits(stringf("%s_h %s", get_id(module), get_id(cell->name)), mod_stbv_width.at(cell->type)); else if (statedt) dtmembers.push_back(stringf(" (|%s_h %s| |%s_s|)\n", get_id(module), get_id(cell->name), get_id(cell->type))); else decls.push_back(stringf("(declare-fun |%s_h %s| (|%s_s|) |%s_s|)\n", get_id(module), get_id(cell->name), get_id(module), get_id(cell->type))); hiercells.insert(cell); hiercells_queue.insert(cell); recursive_cells.erase(cell); 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_), 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; bool contains_clock = false; for (auto bit : SigSpec(wire)) { if (reg_bits.count(bit)) is_register = true; 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) comments.push_back(stringf("; yosys-smt2-input %s %d\n", get_id(wire), wire->width)); if (wire->port_output) comments.push_back(stringf("; yosys-smt2-output %s %d\n", get_id(wire), wire->width)); if (is_register) 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 (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 = is_smtlib2_comb_expr ? smtlib2_comb_expr : get_bv(sig); if (!comments.empty()) 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))", get_id(module), get_id(wire), get_id(module), get_id(wire))); } else { std::vector sig_bool; for (int i = 0; i < GetSize(sig); i++) { sig_bool.push_back(is_smtlib2_comb_expr ? smtlib2_comb_expr : get_bool(sig[i])); } if (!comments.empty()) out_decls.insert(out_decls.end(), comments.begin(), comments.end()); for (int i = 0; i < GetSize(sig); i++) { if (GetSize(sig) > 1) { 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 { 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))", get_id(module), get_id(wire), get_id(module), get_id(wire))); } } } } } 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); if (bvmode && GetSize(sig) > 1) { Const mask(State::S1, GetSize(sig)); bool use_mask = false; for (int i = 0; i < GetSize(sig); i++) if (val[i] != State::S0 && val[i] != State::S1) { val.bits()[i] = State::S0; mask.bits()[i] = State::S0; use_mask = true; } if (use_mask) init_list.push_back(stringf("(= (bvand %s #b%s) #b%s) ; %s", get_bv(sig).c_str(), mask.as_string().c_str(), val.as_string().c_str(), get_id(wire))); else init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire))); } else { for (int i = 0; i < GetSize(sig); i++) if (val[i] == State::S0 || val[i] == State::S1) init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val[i] == State::S1 ? "true" : "false", get_id(wire))); } } if (verbose) log("=> export logic driving asserts\n"); int assert_id = 0, assume_id = 0, cover_id = 0; vector assert_list, assume_list, cover_list; for (auto cell : module->cells()) { if (cell->type.in(ID($assert), ID($assume), ID($cover))) { int &id = cell->type == ID($assert) ? assert_id : cell->type == ID($assume) ? assume_id : cell->type == ID($cover) ? cover_id : *(int*)nullptr; char postfix = cell->type == ID($assert) ? 'a' : cell->type == ID($assume) ? 'u' : cell->type == ID($cover) ? 'c' : 0; string name_a = get_bool(cell->getPort(ID::A)); string name_en = get_bool(cell->getPort(ID::EN)); 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", get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell))); else decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n", get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell))); if (cell->type == ID($assert)) assert_list.push_back(stringf("(|%s_a %d| state)", get_id(module), id)); else if (cell->type == ID($assume)) assume_list.push_back(stringf("(|%s_u %d| state)", get_id(module), id)); id++; } } if (verbose) log("=> export logic driving hierarchical cells\n"); for (auto cell : module->cells()) if (module->design->module(cell->type) != nullptr) export_cell(cell); while (!hiercells_queue.empty()) { std::set queue; queue.swap(hiercells_queue); for (auto cell : queue) { string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name)); Module *m = module->design->module(cell->type); log_assert(m != nullptr); hier.push_back(stringf(" (= (|%s_is| state) (|%s_is| %s))\n", get_id(module), get_id(cell->type), cell_state.c_str())); for (auto &conn : cell->connections()) { if (GetSize(conn.second) == 0) continue; Wire *w = m->wire(conn.first); SigSpec sig = sigmap(conn.second); if (bvmode || GetSize(w) == 1) { hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(), get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w))); } else { for (int i = 0; i < GetSize(w); i++) hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(), get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i)); } } } } for (int iter = 1; !registers.empty() || !memory_queue.empty(); iter++) { pool this_regs; this_regs.swap(registers); if (verbose) log("=> export logic driving registers [iteration %d]\n", iter); for (auto cell : this_regs) { if (cell->type.in(ID($_FF_), ID($_DFF_P_), ID($_DFF_N_))) { std::string expr_d = get_bool(cell->getPort(ID::D)); std::string expr_q = get_bool(cell->getPort(ID::Q), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Q)))); 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), ID($anyinit))) { std::string expr_d = get_bv(cell->getPort(ID::D)); std::string expr_q = get_bv(cell->getPort(ID::Q), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Q)))); ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Q)).c_str(), get_bv(cell->getPort(ID::Q), "other_state").c_str())); } if (cell->type.in(ID($anyconst), ID($allconst))) { std::string expr_d = get_bv(cell->getPort(ID::Y)); std::string expr_q = get_bv(cell->getPort(ID::Y), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort(ID::Y)))); if (cell->type == ID($anyconst)) ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Y)).c_str(), get_bv(cell->getPort(ID::Y), "other_state").c_str())); } } std::set this_mems; this_mems.swap(memory_queue); for (auto mem : this_mems) { int arrayid = memarrays.at(mem); int abits = max(1, ceil_log2(mem->size)); bool has_sync_wr = false; bool has_async_wr = false; for (auto &port : mem->wr_ports) { if (port.clk_enable) has_sync_wr = true; else has_async_wr = true; } string initial_memstate, final_memstate; if (has_async_wr) { log_assert(!has_sync_wr); initial_memstate = stringf("%s#%d#0", get_id(module), arrayid); final_memstate = stringf("%s#%d#final", get_id(module), arrayid); } if (statebv) { if (has_async_wr) { makebits(final_memstate, mem->width*mem->size, get_id(mem->memid)); } for (int i = 0; i < GetSize(mem->wr_ports); i++) { auto &port = mem->wr_ports[i]; SigSpec addr_sig = port.addr; addr_sig.extend_u0(abits); std::string addr = get_bv(addr_sig); std::string data = get_bv(port.data); std::string mask = get_bv(port.en); decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, data.c_str(), log_signal(port.data))); data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, mask.c_str(), log_signal(port.en))); mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(mem->memid)); std::string data_expr; for (int k = mem->size-1; k >= 0; k--) { std::string new_data = stringf("(bvor (bvand %s %s) (bvand ((_ extract %d %d) (|%s#%d#%d| state)) (bvnot %s)))", data.c_str(), mask.c_str(), mem->width*(k+1)-1, mem->width*k, get_id(module), arrayid, i, mask.c_str()); data_expr += stringf("\n (ite (= %s #b%s) %s ((_ extract %d %d) (|%s#%d#%d| state)))", addr.c_str(), Const(k+mem->start_offset, abits).as_string().c_str(), new_data.c_str(), mem->width*(k+1)-1, mem->width*k, get_id(module), arrayid, i); } decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (_ BitVec %d) (concat%s)) ; %s\n", get_id(module), arrayid, i+1, get_id(module), mem->width*mem->size, data_expr.c_str(), get_id(mem->memid))); } } else { if (has_async_wr) { if (statedt) dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", initial_memstate.c_str(), abits, mem->width, get_id(mem->memid))); else decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", initial_memstate.c_str(), get_id(module), abits, mem->width, get_id(mem->memid))); } for (int i = 0; i < GetSize(mem->wr_ports); i++) { auto &port = mem->wr_ports[i]; SigSpec addr_sig = port.addr; addr_sig.extend_u0(abits); std::string addr = get_bv(addr_sig); std::string data = get_bv(port.data); std::string mask = get_bv(port.en); decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, data.c_str(), log_signal(port.data))); data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(mem->memid)); decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(mem->memid), get_id(module), mem->width, mask.c_str(), log_signal(port.en))); mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(mem->memid)); 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)) " "(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, 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))); } } std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, GetSize(mem->wr_ports)); std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid); trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(mem->memid))); ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid)); if (has_async_wr) hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(mem->memid))); Const init_data = mem->get_init_data(); for (int i = 0; i < mem->size; i++) { if (i*mem->width >= GetSize(init_data)) break; Const initword = init_data.extract(i*mem->width, mem->width, State::Sx); Const initmask = initword; bool gen_init_constr = false; for (int k = 0; k < GetSize(initword); k++) { if (initword[k] == State::S0 || initword[k] == State::S1) { gen_init_constr = true; initmask.bits()[k] = State::S1; } else { initmask.bits()[k] = State::S0; initword.bits()[k] = State::S0; } } if (gen_init_constr) { if (statebv) /* FIXME */; else init_list.push_back(stringf("(= (bvand (select (|%s#%d#0| state) #b%s) #b%s) #b%s) ; %s[%d]", get_id(module), arrayid, Const(i, abits).as_string().c_str(), initmask.as_string().c_str(), initword.as_string().c_str(), get_id(mem->memid), i)); } } } } if (verbose) log("=> finalizing SMT2 representation of %s.\n", log_id(module)); for (auto c : hiercells) { assert_list.push_back(stringf("(|%s_a| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name))); assume_list.push_back(stringf("(|%s_u| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name))); init_list.push_back(stringf("(|%s_i| (|%s_h %s| state))", get_id(c->type), get_id(module), get_id(c->name))); hier.push_back(stringf(" (|%s_h| (|%s_h %s| state))\n", get_id(c->type), get_id(module), get_id(c->name))); trans.push_back(stringf(" (|%s_t| (|%s_h %s| state) (|%s_h %s| next_state))\n", get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name))); ex_state_eq.push_back(stringf("(|%s_ex_state_eq| (|%s_h %s| state) (|%s_h %s| other_state))\n", get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name))); } if (forallmode) { string expr = ex_state_eq.empty() ? "true" : "(and"; if (!ex_state_eq.empty()) { if (GetSize(ex_state_eq) == 1) { expr = "\n " + ex_state_eq.front() + "\n"; } else { for (auto &str : ex_state_eq) expr += stringf("\n %s", str.c_str()); expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_ex_state_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), get_id(module), expr.c_str())); expr = ex_input_eq.empty() ? "true" : "(and"; if (!ex_input_eq.empty()) { if (GetSize(ex_input_eq) == 1) { expr = "\n " + ex_input_eq.front() + "\n"; } else { for (auto &str : ex_input_eq) expr += stringf("\n %s", str.c_str()); expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_ex_input_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), get_id(module), expr.c_str())); } string assert_expr = assert_list.empty() ? "true" : "(and"; if (!assert_list.empty()) { if (GetSize(assert_list) == 1) { assert_expr = "\n " + assert_list.front() + "\n"; } else { for (auto &str : assert_list) assert_expr += stringf("\n %s", str.c_str()); assert_expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_a| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), assert_expr.c_str())); string assume_expr = assume_list.empty() ? "true" : "(and"; if (!assume_list.empty()) { if (GetSize(assume_list) == 1) { assume_expr = "\n " + assume_list.front() + "\n"; } else { for (auto &str : assume_list) assume_expr += stringf("\n %s", str.c_str()); assume_expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_u| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), assume_expr.c_str())); string init_expr = init_list.empty() ? "true" : "(and"; if (!init_list.empty()) { if (GetSize(init_list) == 1) { init_expr = "\n " + init_list.front() + "\n"; } else { for (auto &str : init_list) init_expr += stringf("\n %s", str.c_str()); init_expr += "\n)"; } } decls.push_back(stringf("(define-fun |%s_i| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(module), init_expr.c_str())); } void write(std::ostream &f) { f << stringf("; yosys-smt2-module %s\n", get_id(module)); if (statebv) { f << stringf("(define-sort |%s_s| () (_ BitVec %d))\n", get_id(module), statebv_width); mod_stbv_width[module->name] = statebv_width; } else if (statedt) { f << stringf("(declare-datatype |%s_s| ((|%s_mk|\n", get_id(module), get_id(module)); for (auto it : dtmembers) f << it; f << stringf(")))\n"); } else f << stringf("(declare-sort |%s_s| 0)\n", get_id(module)); for (auto it : decls) f << it; f << stringf("(define-fun |%s_h| ((state |%s_s|)) Bool ", get_id(module), get_id(module)); if (GetSize(hier) > 1) { f << "(and\n"; for (auto it : hier) f << it; f << "))\n"; } else if (GetSize(hier) == 1) f << "\n" + hier.front() + ")\n"; else f << "true)\n"; f << stringf("(define-fun |%s_t| ((state |%s_s|) (next_state |%s_s|)) Bool ", get_id(module), get_id(module), get_id(module)); if (GetSize(trans) > 1) { f << "(and\n"; for (auto it : trans) f << it; f << "))"; } else if (GetSize(trans) == 1) f << "\n" + trans.front() + ")"; else 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 { Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_smt2 [options] [filename]\n"); log("\n"); log("Write a SMT-LIBv2 [1] description of the current design. For a module with name\n"); log("'' this will declare the sort '_s' (state of the module) and will\n"); log("define and declare functions operating on that state.\n"); log("\n"); log("The following SMT2 functions are generated for a module with name ''.\n"); log("Some declarations/definitions are printed with a special comment. A prover\n"); log("using the SMT2 files can use those comments to collect all relevant metadata\n"); log("about the design.\n"); log("\n"); log(" ; yosys-smt2-module \n"); log(" (declare-sort |_s| 0)\n"); log(" The sort representing a state of module .\n"); log("\n"); log(" (define-fun |_h| ((state |_s|)) Bool (...))\n"); log(" This function must be asserted for each state to establish the\n"); log(" design hierarchy.\n"); log("\n"); log(" ; yosys-smt2-input \n"); log(" ; yosys-smt2-output \n"); log(" ; yosys-smt2-register \n"); log(" ; yosys-smt2-wire \n"); log(" (define-fun |_n | (|_s|) (_ BitVec ))\n"); log(" (define-fun |_n | (|_s|) Bool)\n"); log(" For each port, register, and wire with the 'keep' attribute set an\n"); log(" accessor function is generated. Single-bit wires are returned as Bool,\n"); log(" multi-bit wires as BitVec.\n"); log("\n"); log(" ; yosys-smt2-cell \n"); log(" (declare-fun |_h | (|_s|) |_s|)\n"); log(" There is a function like that for each hierarchical instance. It\n"); log(" returns the sort that represents the state of the sub-module that\n"); log(" implements the instance.\n"); log("\n"); log(" (declare-fun |_is| (|_s|) Bool)\n"); log(" This function must be asserted 'true' for initial states, and 'false'\n"); log(" otherwise.\n"); log("\n"); log(" (define-fun |_i| ((state |_s|)) Bool (...))\n"); log(" This function must be asserted 'true' for initial states. For\n"); log(" non-initial states it must be left unconstrained.\n"); log("\n"); log(" (define-fun |_t| ((state |_s|) (next_state |_s|)) Bool (...))\n"); log(" This function evaluates to 'true' if the states 'state' and\n"); log(" 'next_state' form a valid state transition.\n"); log("\n"); log(" (define-fun |_a| ((state |_s|)) Bool (...))\n"); log(" This function evaluates to 'true' if all assertions hold in the state.\n"); log("\n"); log(" (define-fun |_u| ((state |_s|)) Bool (...))\n"); log(" This function evaluates to 'true' if all assumptions hold in the state.\n"); log("\n"); log(" ; yosys-smt2-assert \n"); log(" (define-fun |_a | ((state |_s|)) Bool (...))\n"); log(" Each $assert cell is converted into one of this functions. The function\n"); log(" evaluates to 'true' if the assert statement holds in the state.\n"); log("\n"); log(" ; yosys-smt2-assume \n"); log(" (define-fun |_u | ((state |_s|)) Bool (...))\n"); log(" Each $assume cell is converted into one of this functions. The function\n"); log(" evaluates to 'true' if the assume statement holds in the state.\n"); log("\n"); log(" ; yosys-smt2-cover \n"); log(" (define-fun |_c | ((state |_s|)) Bool (...))\n"); log(" Each $cover cell is converted into one of this functions. The function\n"); log(" evaluates to 'true' if the cover statement is activated in the state.\n"); log("\n"); log("Options:\n"); log("\n"); log(" -verbose\n"); log(" this will print the recursive walk used to export the modules.\n"); log("\n"); log(" -stbv\n"); log(" Use a BitVec sort to represent a state instead of an uninterpreted\n"); log(" sort. As a side-effect this will prevent use of arrays to model\n"); log(" memories.\n"); log("\n"); log(" -stdt\n"); log(" Use SMT-LIB 2.6 style datatypes to represent a state instead of an\n"); log(" uninterpreted sort.\n"); log("\n"); log(" -nobv\n"); log(" disable support for BitVec (FixedSizeBitVectors theory). without this\n"); log(" option multi-bit wires are represented using the BitVec sort and\n"); log(" support for coarse grain cells (incl. arithmetic) is enabled.\n"); log("\n"); log(" -nomem\n"); log(" disable support for memories (via ArraysEx theory). this option is\n"); log(" implied by -nobv. only $mem cells without merged registers in\n"); log(" read ports are supported. call \"memory\" with -nordff to make sure\n"); log(" that no registers are merged into $mem read ports. '_m' functions\n"); log(" will be generated for accessing the arrays that are used to represent\n"); log(" memories.\n"); log("\n"); log(" -wires\n"); log(" create '_n' functions for all public wires. by default only ports,\n"); log(" registers, and wires with the 'keep' attribute are exported.\n"); log("\n"); log(" -tpl \n"); log(" use the given template file. the line containing only the token '%%%%'\n"); log(" is replaced with the regular output of this command.\n"); log("\n"); log(" -solver-option PR preview limitations.' html_theme_options["light_css_variables"]["color-announcement-background"] = "var(--color-admonition-title-background--caution)" html_theme_options["light_css_variables"]["color-announcement-text"] = "var(--color-content-foreground)" # Ensure that autosectionlabel will produce unique names autosectionlabel_prefix_document = True autosectionlabel_maxdepth = 1 # include todos for previews extensions.append('sphinx.ext.todo') # set version if os.getenv("READTHEDOCS"): rtds_version = os.getenv("READTHEDOCS_VERSION") if rtds_version == "latest": release = yosys_ver + "-dev" todo_include_todos = False elif rtds_version.startswith("docs"): release = rtds_version todo_include_todos = True else: release = yosys_ver todo_include_todos = False elif os.getenv("YOSYS_DOCS_RELEASE") is not None: release = yosys_ver todo_include_todos = False else: release = yosys_ver todo_include_todos = True # assign figure numbers numfig = True bibtex_bibfiles = ['literature.bib'] latex_elements = { 'releasename': 'Version', 'preamble': r''' \pdfinfoomitdate 1 \pdfsuppressptexinfo 1 \pdftrailerid{} \usepackage{lmodern} \usepackage{comment} ''' } # custom cmd-ref parsing/linking sys.path += [os.path.dirname(__file__) + "/../"] extensions.append('util.cmdref') # use autodocs extensions.append('sphinx.ext.autodoc') extensions.append('util.cellref') cells_json = Path(__file__).parent / 'generated' / 'cells.json' from sphinx.application import Sphinx def setup(app: Sphinx) -> None: from util.RtlilLexer import RtlilLexer app.add_lexer("RTLIL", RtlilLexer) try: from furo_ys.lexers.YoscryptLexer import YoscryptLexer app.add_lexer("yoscrypt", YoscryptLexer) except ModuleNotFoundError: from pygments.lexers.special import TextLexer app.add_lexer("yoscrypt", TextLexer) yosys-0.52/docs/source/getting_started/000077500000000000000000000000001477540374200202625ustar00rootroot00000000000000yosys-0.52/docs/source/getting_started/example_synth.rst000066400000000000000000000777611477540374200237160ustar00rootroot00000000000000Synthesis starter ----------------- This page will be a guided walkthrough of the prepackaged iCE40 FPGA synthesis script - `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 `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 While the open source `read_verilog` frontend generally does a pretty good job at processing valid Verilog input, it does not provide very good error handling or reporting. Using an external tool such as `verilator`_ before running Yosys is highly recommended. We can quickly check the Verilog syntax of our design by calling ``verilator --lint-only fifo.v``. .. _verilator: https://www.veripool.org/verilator/ 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 `help` output for `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:: `hierarchy` should always be the first command after the design has been read. By specifying the top module, `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 invert-helper :name: addr_gen_hier ``addr_gen`` module after `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`. `proc` is a macro command like `synth_ice40`. Rather than modifying the design directly, it instead calls a series of other commands. In the case of `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 invert-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 `clean`. We're also going to call `opt_expr` now, which would normally be called at the end of `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 invert-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 `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 `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 `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 `read_verilog` only read the abstract syntax tree and defer actual compilation to a later `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:: `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 `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 `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 `show`. .. figure:: /_images/code_examples/fifo/rdata_proc.* :class: width-helper invert-helper :name: rdata_proc ``rdata`` output after `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 `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 `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 invert-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 `clean` which we can see in the :ref:`flat_clean`. .. note:: `flatten` and `clean` would normally be combined into a single :yoterm:`yosys> flatten;;` output, but they appear separately here as a side effect of using `echo` for generating the terminal style output. Depending on the target architecture, this stage of synthesis might also see commands such as `tribuf` with the ``-logic`` option and `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 `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 `opt_expr`, and `opt_clean` is the same as `clean` but with more verbose output. The `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 `opt` and `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 `opt` does cause a change. Specifically, the call to `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 `opt_dff` .. figure:: /_images/code_examples/fifo/rdata_adffe.* :class: width-helper invert-helper :name: rdata_adffe ``rdata`` output after `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 `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 `wreduce` and/or `opt_clean` .. figure:: /_images/code_examples/fifo/rdata_wreduce.* :class: width-helper invert-helper :name: rdata_wreduce ``rdata`` output after `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 `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 `memory_dff` .. figure:: /_images/code_examples/fifo/rdata_memrdv2.* :class: width-helper invert-helper :name: rdata_memrdv2 ``rdata`` output after `memory_dff` As the title suggests, `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 `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 `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 `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. `setattr` removes the now unnecessary ``mul2dsp`` attribute. `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 `ice40_dsp`: similar to the `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_v2` cells which can help identify opportunities for reusing logic. We do this by running `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 `alumacc` .. figure:: /_images/code_examples/fifo/rdata_alumacc.* :class: width-helper invert-helper :name: rdata_alumacc ``rdata`` output after `alumacc` Once these cells have been inserted, the call to `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`. `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: `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. `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 invert-helper :name: rdata_coarse ``rdata`` output after `memory_collect` Looking at the schematic after running `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 `memory_libmap` and `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 invert-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 `memory_libmap` under the `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 `techmap` using |techlibs/ice40/brams_map.v|_. Any leftover memory cells are then converted into flip flops (the ``logic fallback``) with `memory_map`. .. |techlibs/ice40/brams.txt| replace:: :file:`techlibs/ice40/brams.txt` .. _techlibs/ice40/brams.txt: https://github.com/YosysHQ/yosys/tree/main/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/main/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 invert-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 `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 `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 invert-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 `dfflegalize`, and then use `techmap` to map them. In our example, this converts the `$_DFFE_PP0P_` cells to ``SB_DFFER``. We also run `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 invert-helper :name: rdata_map_ffs ``rdata`` output after :ref:`map_ffs` .. seealso:: Advanced usage docs for :doc:`/using_yosys/synthesis/techmap_synth` LUTs ^^^^ `abc` and `techmap` are used to map LUTs; converting primitive cell types to use `$lut` and ``SB_CARRY`` cells. Note that the iCE40 flow uses `abc9` rather than `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 invert-helper :name: rdata_map_luts ``rdata`` output after :ref:`map_luts` Finally we use `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 invert-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: `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. `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 `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 `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. `stat` will automatically use the module with the ``top`` attribute set, which ``fifo`` was when we called `hierarchy`. If no module is marked ``top``, then stats will be shown for each module selected. The `stat` output is also useful as a kind of sanity-check: Since we have already run `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 `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 `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` yosys-0.52/docs/source/getting_started/index.rst000066400000000000000000000005251477540374200221250ustar00rootroot00000000000000Getting 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 yosys-0.52/docs/source/getting_started/installation.rst000066400000000000000000000245671477540374200235330ustar00rootroot00000000000000Installation ------------ 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: - **linux-x64** - Most personal Linux based computers - **darwin-x64** - macOS 12 or later with Intel CPU - **darwin-arm64** - macOS 12 or later with M1/M2 CPU - **windows-x64** - Targeted for Windows 10 and 11 - **linux-arm64** - Devices such as Raspberry Pi with 64bit OS For more information about the targeted architectures, and the current build status, check the `OSS CAD Suite`_ git repository. .. _OSS CAD Suite: https://github.com/YosysHQ/oss-cad-suite-build .. _nightly builds: https://github.com/YosysHQ/oss-cad-suite-build/releases/latest Building from source ~~~~~~~~~~~~~~~~~~~~ The Yosys source files can be obtained from the `YosysHQ/Yosys git repository`_. `ABC`_ and some of the other libraries used are included as git submodules. To clone these submodules at the same time, use e.g.: .. code:: console git clone --recurse-submodules https://github.com/YosysHQ/yosys.git # ..or.. git clone https://github.com/YosysHQ/yosys.git cd yosys git submodule update --init --recursive .. _YosysHQ/Yosys git repository: https://github.com/yosyshq/yosys/ .. _ABC: https://github.com/berkeley-abc/abc .. note:: As of Yosys v0.47, releases include a ``yosys.tar.gz`` file which includes all source code and all sub-modules in a single archive. This can be used as an alternative which does not rely on ``git``. 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++17 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 `show` command to display schematics. Installing all prerequisites for Ubuntu 20.04: .. code:: console 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 Installing all prerequisites for macOS 13 (with Homebrew): .. code:: console brew tap Homebrew/bundle && brew bundle or MacPorts: .. code:: console sudo port install bison flex readline gawk libffi graphviz \ pkgconfig python311 boost zlib tcl On FreeBSD use the following command to install all prerequisites: .. code:: console pkg install bison flex readline gawk libffi graphviz \ pkgconf python311 tcl-wrapper boost-libs .. note:: On FreeBSD system use gmake instead of make. To run tests use: ``MAKE=gmake CXX=cxx CC=cc gmake test`` For Cygwin use the following command to install all prerequisites, or select these additional packages: .. code:: console 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 .. warning:: As of this writing, Cygwin only supports up to Python 3.9.16 while the minimum required version of Python is 3.11. This means that Cygwin is not compatible with many of the Python-based frontends. While this does not currently prevent Yosys itself from working, no guarantees are made for continued support. It is instead recommended to use Windows Subsystem for Linux (WSL) and follow the instructions for Ubuntu. .. For MSYS2 (MINGW64): .. code:: console pacman -S bison flex mingw-w64-x86_64-gcc git libffi-devel libreadline-devel make pkg-config python3 tcl-devel mingw-w64-x86_64-boost zlib-devel Not that I can get this to work; it's failing during ld with what looks like math library issues: ``multiple definition of `tanh'`` and ``undefined reference to `__imp_acosh'``, as well as issues in `aiger2` with ``seekg`` et al not being available. .. note:: The ``config-msys2-64`` target uses the ``mingw-w64-x86_64-`` prefixed compiler in order to allow compiled exe files to be run without an MSYS2 shell. Build configuration ^^^^^^^^^^^^^^^^^^^ The Yosys build is based solely on Makefiles, and uses a number of variables which influence the build process. The recommended method for configuring builds is with a ``Makefile.conf`` file in the root ``yosys`` directory. The following commands will clean the directory and provide an initial configuration file: .. code:: console make config-clang # ..or.. make config-gcc Check the root Makefile to see what other configuration targets are available. Other variables can then be added to the ``Makefile.conf`` as needed, for example: .. code:: console echo "ENABLE_ZLIB := 0" >> Makefile.conf Using one of these targets will set the ``CONFIG`` variable to something other than ``none``, and will override the environment variable for ``CXX``. To use a different compiler than the default when building, use: .. code:: console make CXX=$CXX # ..or.. make CXX="g++-11" .. note:: Setting the compiler in this way will prevent some other options such as ``ENABLE_CCACHE`` from working as expected. If you have clang, and (a compatible version of) ``ld.lld`` available in PATH, it's recommended to speed up incremental builds with lld by enabling LTO with ``ENABLE_LTO=1``. On macOS, LTO requires using clang from homebrew rather than clang from xcode. For example: .. code:: console make ENABLE_LTO=1 CXX=$(brew --prefix)/opt/llvm/bin/clang++ By default, building (and installing) yosys will build (and install) `ABC`_, using :program:`yosys-abc` as the executable name. To use an existing ABC executable instead, set the ``ABCEXTERNAL`` make variable to point to the desired executable. Running the build system ^^^^^^^^^^^^^^^^^^^^^^^^ From the root ``yosys`` directory, call the following commands: .. code:: console make sudo make install To use a separate (out-of-tree) build directory, provide a path to the Makefile. .. code:: console mkdir build; cd build make -f ../Makefile Out-of-tree builds require a clean source tree. .. seealso:: Refer to :doc:`/yosys_internals/extending_yosys/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. ``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: `hierarchy`, `submod`, and `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:`/yosys_internals/extending_yosys/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`. 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. yosys-0.52/docs/source/getting_started/scripting_intro.rst000066400000000000000000000240411477540374200242320ustar00rootroot00000000000000Scripting 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 `read_verilog` and `hierarchy`. Script parsing ~~~~~~~~~~~~~~ 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, and anything after a hash sign ``#`` is a comment (i.e. it is ignored). It is also possible to terminate commands with a semicolon ``;``. This is particularly useful in conjunction with the ``-p `` command line option, where ```` can be a string with multiple commands separated by semicolon. In-line comments can also be made with the colon ``:``, where the end of the comment is a semicolon ``;`` or a new line. .. code-block:: :caption: Using the ``-p`` option $ yosys -p "read_verilog fifo.v; :this is a comment; prep" .. warning:: The space after the semicolon is required for correct parsing. ``log a;log b;`` for example will display ``a;log b`` instead of ``a`` and ``b`` as might be expected. Another special character that can be used in Yosys scripts is the bang ``!``. Anything after the bang will be executed as a shell command. This can only be terminated with a new line. Any semicolons, hashes, or other special characters will be passed to the shell. If an error code is returned from the shell it will be raised by Yosys. `exec` provides a much more flexible way of executing commands, allowing the output to be logged and more control over when to generate errors. 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 `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 `select` when it came up in `synth_ice40`, but let's look at it more now. .. _select_intro: Selections intro ^^^^^^^^^^^^^^^^ The `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 `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 `select` command is very useful, sometimes nothing beats being able to see a design for yourself. This is where `show` comes in. Note that this document is just an introduction to the `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 invert-helper :name: addr_gen_show Calling :yoscrypt:`show addr_gen` after `hierarchy` .. note:: The `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 `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 `show` on that selection just as easily: .. figure:: /_images/code_examples/fifo/new_cells_show.* :class: width-helper invert-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 invert-helper Calling :yoscrypt:`show -color maroon3 @new_cells -color cornflowerblue p:* -notitle` As described in the the `help` output for `show` (or by clicking on the `show` link), colors are specified as :yoscrypt:`-color `. Color names for the ```` portion can be found on the `GraphViz color docs`_. Unlike the final `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 `show`, check the command reference at :doc:`/cmd/show`. .. seealso:: :ref:`interactive_show` on the :doc:`/using_yosys/more_scripting/interactive_investigation` page. yosys-0.52/docs/source/index.rst000066400000000000000000000021451477540374200167360ustar00rootroot00000000000000================================================================================ 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`. .. 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 in pdf uses link formatting instead of code formatting .. todolist:: .. toctree:: :maxdepth: 3 :includehidden: Yosys (index) introduction getting_started/index using_yosys/index yosys_internals/index .. toctree:: :caption: Appendix :titlesonly: :includehidden: appendix/primer appendix/rtlil_text appendix/auxlibs appendix/auxprogs bib cell_index cmd_ref yosys-0.52/docs/source/introduction.rst000066400000000000000000000262461477540374200203600ustar00rootroot00000000000000What 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 - Rely on built-in syntax checking - Use an external tool like `verilator`_ instead .. todo:: nextpnr for FPGAs, consider mentioning openlane, vpr, coriolis .. _nextpnr: https://github.com/YosysHQ/nextpnr .. _verilator: https://www.veripool.org/verilator/ 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 invert-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. yosys-0.52/docs/source/literature.bib000066400000000000000000000172271477540374200177420ustar00rootroot00000000000000 @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} } yosys-0.52/docs/source/requirements.txt000066400000000000000000000001221477540374200203520ustar00rootroot00000000000000furo-ys @ git+https://github.com/YosysHQ/furo-ys sphinxcontrib-bibtex rtds-action yosys-0.52/docs/source/using_yosys/000077500000000000000000000000001477540374200174665ustar00rootroot00000000000000yosys-0.52/docs/source/using_yosys/index.rst000066400000000000000000000011611477540374200213260ustar00rootroot00000000000000Using 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 yosys-0.52/docs/source/using_yosys/more_scripting/000077500000000000000000000000001477540374200225125ustar00rootroot00000000000000yosys-0.52/docs/source/using_yosys/more_scripting/index.rst000066400000000000000000000003731477540374200243560ustar00rootroot00000000000000More scripting -------------- .. todo:: brief overview for the more scripting index .. todo:: troubleshooting document(?) .. toctree:: :maxdepth: 3 load_design selections interactive_investigation model_checking .. troubleshooting yosys-0.52/docs/source/using_yosys/more_scripting/interactive_investigation.rst000066400000000000000000001027301477540374200305270ustar00rootroot00000000000000Interactive 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 `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/main/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 `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 `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 invert-helper Output of the first `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:`/cell_index`. 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 `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 invert-helper Output of the second `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 `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 `clean` (or `opt`, which includes `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 `clean` before calling `show`. In this script we directly call `opt` as the next step, which finally leads us to the third diagram: .. figure:: /_images/code_examples/show/example_third.* :class: width-helper invert-helper :name: example_out Output of the third `show` command in :ref:`example_ys` Here we see that the `opt` command not only has removed the artifacts left behind by `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 `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 invert-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 invert-helper :name: first_pitfall A half-adder built from simple CMOS gates, demonstrating common pitfalls when using `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 invert-helper :name: second_pitfall Effects of `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 `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 `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 `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 `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 `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 `techmap` command already fails to verify, it is better to troubleshoot the coarse-grain version of the circuit before `techmap` than the gate-level circuit after `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 `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 `ls` can now be used to create a list of all modules. The command `cd` can be used to switch to one of the modules (type ``cd ..`` to switch back). Now the `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 `ls` and `cd` after running :file:`yosys example.v` :name: lscd When a module is selected using the `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 `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 `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 `submod`, `expose`, and `splice` can be used to transform the design into an equivalent design that is easier to analyse. - Commands such as `eval` and `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/main/docs/source/code_examples/scrambler Changing design hierarchy ^^^^^^^^^^^^^^^^^^^^^^^^^ Commands such as `flatten` and `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 `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 invert-helper .. figure:: /_images/code_examples/scrambler/scrambler_p02.* :class: width-helper invert-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 `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 `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 invert-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 `submod` command to split the circuit into three sections: ``outstage``, ``selstage``, and ``scramble``. .. literalinclude:: /code_examples/selections/submod.ys :language: yoscrypt :caption: Using `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 invert-helper ``outstage`` .. figure:: /_images/code_examples/selections/submod_03.* :class: width-helper invert-helper :name: selstage ``selstage`` .. figure:: /_images/code_examples/selections/submod_01.* :class: width-helper invert-helper ``scramble`` Evaluation of combinatorial circuits ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The `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 `eval` command (as well as the `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 `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 `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 `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 `sat` command works very similar to the `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 `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 `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 `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 `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 `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 `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. yosys-0.52/docs/source/using_yosys/more_scripting/load_design.rst000066400000000000000000000025321477540374200255160ustar00rootroot00000000000000Loading 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 `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 invert-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 invert-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 `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 invert-helper Output of :yoscrypt:`show prod` on :numref:`sumprod` .. figure:: /_images/code_examples/selections/sumprod_03.* :class: width-helper invert-helper Output of :yoscrypt:`show prod %ci` on :numref:`sumprod` .. figure:: /_images/code_examples/selections/sumprod_04.* :class: width-helper invert-helper Output of :yoscrypt:`show prod %ci %ci` on :numref:`sumprod` .. figure:: /_images/code_examples/selections/sumprod_05.* :class: width-helper invert-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 `show` is able to distinguish between ``$3_Y`` being a wire and ``c`` being an input. We can see this better with the `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 `show` to see the diagram in :numref:`memdemo_00`. .. figure:: /_images/code_examples/selections/memdemo_00.* :class: width-helper invert-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 invert-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 invert-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 invert-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 invert-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 invert-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 `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 `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/main/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 invert-helper Circuit diagram produced by :numref:`select_ys` yosys-0.52/docs/source/using_yosys/synthesis/000077500000000000000000000000001477540374200215175ustar00rootroot00000000000000yosys-0.52/docs/source/using_yosys/synthesis/abc.rst000066400000000000000000000205511477540374200230010ustar00rootroot00000000000000The 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 `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 `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 `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 Setting up a flow for ABC9 -------------------------- Much of the configuration comes from attributes and ``specify`` blocks in Verilog simulation models. ``specify`` syntax ~~~~~~~~~~~~~~~~~~ Since ``specify`` is a relatively obscure part of the Verilog standard, a quick guide to the syntax: .. code-block:: verilog specify // begins a specify block (A => 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 `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 `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 `abc9`, and then mapped back to the original flop. This is used in `synth_intel_alm` and `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. yosys-0.52/docs/source/using_yosys/synthesis/cell_libs.rst000066400000000000000000000076671477540374200242210ustar00rootroot00000000000000Mapping 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/main/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 invert-helper :name: counter-hierarchy ``counter`` after `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 invert-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 invert-helper ``counter`` after `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: .. todo:: find a Liberty pygments style? .. literalinclude:: /code_examples/intro/mycells.lib :language: text :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 `dfflibmap` and `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 invert-helper ``counter`` after hardware cell mapping Before finally being output as a verilog file with `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 yosys-0.52/docs/source/using_yosys/synthesis/extract.rst000066400000000000000000000162331477540374200237300ustar00rootroot00000000000000The extract pass ---------------- - Like the `techmap` pass, the `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 `extract` pass will replace the matching subcircuit with an instance of the module from the map file. - In a way the `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/main/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 invert-helper before `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 invert-helper after `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 invert-helper .. figure:: /_images/code_examples/macc/macc_simple_test_01b.* :class: width-helper invert-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 invert-helper .. figure:: /_images/code_examples/macc/macc_simple_test_02b.* :class: width-helper invert-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 `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 `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 `extract` command can be used to replace circuits with cells of the target architecture. unwrap The remaining wrapper cell can be unwrapped using `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 `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 invert-helper .. figure:: /_images/code_examples/macc/macc_xilinx_test1b.* :class: width-helper invert-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 invert-helper .. figure:: /_images/code_examples/macc/macc_xilinx_test2b.* :class: width-helper invert-helper Wrapping in ``test1``: .. figure:: /_images/code_examples/macc/macc_xilinx_test1b.* :class: width-helper invert-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 invert-helper Wrapping in ``test2``: .. figure:: /_images/code_examples/macc/macc_xilinx_test2b.* :class: width-helper invert-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 invert-helper Extract in ``test1``: .. figure:: /_images/code_examples/macc/macc_xilinx_test1c.* :class: width-helper invert-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 invert-helper Extract in ``test2``: .. figure:: /_images/code_examples/macc/macc_xilinx_test2c.* :class: width-helper invert-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 invert-helper Unwrap in ``test2``: .. figure:: /_images/code_examples/macc/macc_xilinx_test2d.* :class: width-helper invert-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 invert-helperyosys-0.52/docs/source/using_yosys/synthesis/fsm.rst000066400000000000000000000131661477540374200230450ustar00rootroot00000000000000FSM handling ============ The `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 `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 `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 `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 `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 `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 `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 `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 `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. yosys-0.52/docs/source/using_yosys/synthesis/index.rst000066400000000000000000000023021477540374200233550ustar00rootroot00000000000000Synthesis 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 `proc`, `fsm`, and `memory` rely on the additional information in the coarse-grain representation, along with a number of optimizations such as `wreduce`, `share`, and `alumacc`. `opt` provides optimizations which are useful in both states, while `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 `abc`\ /`abc9`, `simplemap`, `dfflegalize`, and `memory_map`. .. toctree:: :maxdepth: 3 synth proc fsm memory opt techmap_synth extract abc cell_libs yosys-0.52/docs/source/using_yosys/synthesis/memory.rst000066400000000000000000000546101477540374200235670ustar00rootroot00000000000000Memory handling =============== The `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 `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 `memory` .. todo:: Make ``memory_*`` notes less quick Some quick notes: - `memory_dff` merges registers into the memory read- and write cells. - `memory_collect` collects all read and write cells for a memory and transforms them into one multi-port memory cell. - `memory_map` takes the multi-port memory cell and transforms it to address decoder logic and registers. For more information about `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/main/docs/source/code_examples/synth_flow .. figure:: /_images/code_examples/synth_flow/memory_01.* :class: width-helper invert-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 invert-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 `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 `techmap` to map to hardware primitives. Any leftover memory cells unable to be converted are then picked up by `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 `debug` command, or by using the ``-g`` flag when calling Yosys to globally enable debug messages. For more on the lib format for `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 ------------------------------------ The following patterns are only supported when the design is read in using the Verific front-end. 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]; yosys-0.52/docs/source/using_yosys/synthesis/opt.rst000066400000000000000000000217051477540374200230600ustar00rootroot00000000000000Optimization 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 `opt` macro command -------------------------------- The Yosys pass `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 `opt` .. _adv_opt_expr: Constant folding and simple expression rewriting - `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:`/cell_index`. 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 `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 `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 `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 <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 `dump` and `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 invert-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 `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/main/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` yosys-0.52/docs/source/yosys_internals/extending_yosys/functional_ir.rst000066400000000000000000000610411477540374200271630ustar00rootroot00000000000000Writing a new backend using FunctionalIR ======================================== What is FunctionalIR -------------------- To simplify the writing of backends for functional languages or similar targets, Yosys provides an alternative intermediate representation called FunctionalIR which maps more directly on those targets. FunctionalIR represents the design as a function ``(inputs, current_state) -> (outputs, next_state)``. This function is broken down into a series of assignments to variables. Each assignment is a simple operation, such as an addition. Complex operations are broken up into multiple steps. For example, an RTLIL addition will be translated into a sign/zero extension of the inputs, followed by an addition. Like SSA form, each variable is assigned to exactly once. We can thus treat variables and assignments as equivalent and, since this is a graph-like representation, those variables are also called "nodes". Unlike RTLIL's cells and wires representation, this representation is strictly ordered (topologically sorted) with definitions preceding their use. Every node has a "sort" (the FunctionalIR term for what might otherwise be called a "type"). The sorts available are - ``bit[n]`` for an ``n``-bit bitvector, and - ``memory[n,m]`` for an immutable array of ``2**n`` values of sort ``bit[m]``. In terms of actual code, Yosys provides a class ``Functional::IR`` that represents a design in FunctionalIR. ``Functional::IR::from_module`` generates an instance from an RTLIL module. The entire design is stored as a whole in an internal data structure. To access the design, the ``Functional::Node`` class provides a reference to a particular node in the design. The ``Functional::IR`` class supports the syntax ``for(auto node : ir)`` to iterate over every node. ``Functional::IR`` also keeps track of inputs, outputs and states. By a "state" we mean a pair of a "current state" input and a "next state" output. One such pair is created for every register and for every memory. Every input, output and state has a name (equal to their name in RTLIL), a sort and a kind. The kind field usually remains as the default value ``$input``, ``$output`` or ``$state``, however some RTLIL cells such as ``$assert`` or ``$anyseq`` generate auxiliary inputs/outputs/states that are given a different kind to distinguish them from ordinary RTLIL inputs/outputs/states. - To access an individual input/output/state, use ``ir.input(name, kind)``, ``ir.output(name, kind)`` or ``ir.state(name, kind)``. ``kind`` defaults to the default kind. - To iterate over all inputs/outputs/states of a certain kind, methods ``ir.inputs``, ``ir.outputs``, ``ir.states`` are provided. Their argument defaults to the default kinds mentioned. - To iterate over inputs/outputs/states of any kind, use ``ir.all_inputs``, ``ir.all_outputs`` and ``ir.all_states``. - Outputs have a node that indicate the value of the output, this can be retrieved via ``output.value()``. - States have a node that indicate the next value of the state, this can be retrieved via ``state.next_value()``. They also have an initial value that is accessed as either ``state.initial_value_signal()`` or ``state.initial_value_memory()``, depending on their sort. Each node has a "function", which defines its operation (for a complete list of functions and a specification of their operation, see ``functional.h``). Functions are represented as an enum ``Functional::Fn`` and the function field can be accessed as ``node.fn()``. Since the most common operation is a switch over the function that also accesses the arguments, the ``Node`` class provides a method ``visit`` that implements the visitor pattern. For example, for an addition node ``node`` with arguments ``n1`` and ``n2``, ``node.visit(visitor)`` would call ``visitor.add(node, n1, n2)``. Thus typically one would implement a class with a method for every function. Visitors should inherit from either ``Functional::AbstractVisitor`` or ``Functional::DefaultVisitor``. The former will produce a compiler error if a case is unhandled, the latter will call ``default_handler(node)`` instead. Visitor methods should be marked as ``override`` to provide compiler errors if the arguments are wrong. Utility classes ~~~~~~~~~~~~~~~ ``functional.h`` also provides utility classes that are independent of the main FunctionalIR representation but are likely to be useful for backends. ``Functional::Writer`` provides a simple formatting class that wraps a ``std::ostream`` and provides the following methods: - ``writer << value`` wraps ``os << value``. - ``writer.print(fmt, value0, value1, value2, ...)`` replaces ``{0}``, ``{1}``, ``{2}``, etc in the string ``fmt`` with ``value0``, ``value1``, ``value2``, resp. Each value is formatted using ``os << value``. It is also possible to write ``{}`` to refer to one past the last index, i.e. ``{1} {} {} {7} {}`` is equivalent to ``{1} {2} {3} {7} {8}``. - ``writer.print_with(fn, fmt, value0, value1, value2, ...)`` functions much the same as ``print`` but it uses ``os << fn(value)`` to print each value and falls back to ``os << value`` if ``fn(value)`` is not legal. ``Functional::Scope`` keeps track of variable names in a target language. It is used to translate between different sets of legal characters and to avoid accidentally re-defining identifiers. Users should derive a class from ``Scope`` and supply the following: - ``Scope`` takes a template argument that specifies a type that's used to uniquely distinguish variables. Typically this would be ``int`` (if variables are used for ``Functional::IR`` nodes) or ``IdString``. - The derived class should provide a constructor that calls ``reserve`` for every reserved word in the target language. - A method ``bool is_character_legal(char c, int index)`` has to be provided that returns ``true`` iff ``c`` is legal in an identifier at position ``index``. Given an instance ``scope`` of the derived class, the following methods are then available: - ``scope.reserve(std::string name)`` marks the given name as being in-use - ``scope.unique_name(IdString suggestion)`` generates a previously unused name and attempts to make it similar to ``suggestion``. - ``scope(Id id, IdString suggestion)`` functions similar to ``unique_name``, except that multiple calls with the same ``id`` are guaranteed to retrieve the same name (independent of ``suggestion``). ``sexpr.h`` provides classes that represent and pretty-print s-expressions. S-expressions can be constructed with ``SExpr::list``, for example ``SExpr expr = SExpr::list("add", "x", SExpr::list("mul", "y", "z"))`` represents ``(add x (mul y z))`` (by adding ``using SExprUtil::list`` to the top of the file, ``list`` can be used as shorthand for ``SExpr::list``). For prettyprinting, ``SExprWriter`` wraps an ``std::ostream`` and provides the following methods: - ``writer << sexpr`` writes the provided expression to the output, breaking long lines and adding appropriate indentation. - ``writer.open(sexpr)`` is similar to ``writer << sexpr`` but will omit the last closing parenthesis. Further arguments can then be added separately with ``<<`` or ``open``. This allows for printing large s-expressions without needing to construct the whole expression in memory first. - ``writer.open(sexpr, false)`` is similar to ``writer.open(sexpr)`` but further arguments will not be indented. This is used to avoid unlimited indentation on structures with unlimited nesting. - ``writer.close(n = 1)`` closes the last ``n`` open s-expressions. - ``writer.push()`` and ``writer.pop()`` are used to automatically close s-expressions. ``writer.pop()`` closes all s-expressions opened since the last call to ``writer.push()``. - ``writer.comment(string)`` writes a comment on a separate-line. ``writer.comment(string, true)`` appends a comment to the last printed s-expression. - ``writer.flush()`` flushes any buffering and should be called before any direct access to the underlying ``std::ostream``. It does not close unclosed parentheses. - The destructor calls ``flush`` but also closes all unclosed parentheses. .. _minimal backend: Example: A minimal functional backend ------------------------------------- At its most basic, there are three steps we need to accomplish for a minimal functional backend. First, we need to convert our design into FunctionalIR. This is most easily done by calling the ``Functional::IR::from_module()`` static method with our top-level module, or iterating over and converting each of the modules in our design. Second, we need to handle each of the ``Functional::Node``\ s in our design. Iterating over the ``Functional::IR`` includes reading the module inputs and current state, but not writing the results. So our final step is to handle the outputs and next state. In order to add an output command to Yosys, we implement the ``Yosys::Backend`` class and provide an instance of it: .. literalinclude:: /code_examples/functional/dummy.cc :language: c++ :caption: Example source code for a minimal functional backend, ``dummy.cc`` Because we are using the ``Backend`` class, our ``"functional_dummy"`` is registered as the ``write_functional_dummy`` command. The ``execute`` method is the part that runs when the user calls the command, handling any options, preparing the output file for writing, and iterating over selected modules in the design. Since we don't have any options here, we set ``argidx = 1`` and call the ``extra_args()`` method. This method will read the command arguments, raising an error if there are any unexpected ones. It will also assign the pointer ``f`` to the output file, or stdout if none is given. .. note:: For more on adding new commands to Yosys and how they work, refer to :doc:`/yosys_internals/extending_yosys/extensions`. For this minimal example all we are doing is printing out each node. The ``node.name()`` method returns an ``RTLIL::IdString``, which we convert for printing with ``id2cstr()``. Then, to print the function of the node, we use ``node.to_string()`` which gives us a string of the form ``function(args)``. The ``function`` part is the result of ``Functional::IR::fn_to_string(node.fn())``; while ``args`` is the zero or more arguments passed to the function, most commonly the name of another node. Behind the scenes, the ``node.to_string()`` method actually wraps ``node.visit(visitor)`` with a private visitor whose return type is ``std::string``. Finally we iterate over the module's outputs and states, using ``Functional::IROutput::value()`` and ``Functional::IRState::next_value()`` respectively in order to get the results of the transfer function. Example: Adapting SMT-LIB backend for Rosette --------------------------------------------- This section will introduce the SMT-LIB functional backend (`write_functional_smt2`) and what changes are needed to work with another s-expression target, `Rosette`_ (`write_functional_rosette`). .. _Rosette: http://emina.github.io/rosette/ Overview ~~~~~~~~ Rosette is a solver-aided programming language that extends `Racket`_ with language constructs for program synthesis, verification, and more. To verify or synthesize code, Rosette compiles it to logical constraints solved with off-the-shelf `SMT`_ solvers. -- https://emina.github.io/rosette/ .. _Racket: http://racket-lang.org/ .. _SMT: http://smtlib.cs.uiowa.edu/ Rosette, being backed by SMT solvers and written with s-expressions, uses code very similar to the `write_functional_smt2` output. As a result, the SMT-LIB functional backend can be used as a starting point for implementing a Rosette backend. Full code listings for the initial SMT-LIB backend and the converted Rosette backend are included in the Yosys source repository under :file:`backends/functional` as ``smtlib.cc`` and ``smtlib_rosette.cc`` respectively. Note that the Rosette language is an extension of the Racket language; this guide tends to refer to Racket when talking about the underlying semantics/syntax of the language. The major changes from the SMT-LIB backend are as follows: - all of the ``Smt`` prefixes in names are replaced with ``Smtr`` to mean ``smtlib_rosette``; - syntax is adjusted for Racket; - data structures for input/output/state are changed from using ``declare-datatype`` with statically typed fields, to using ``struct`` with no static typing; - the transfer function also loses its static typing; - sign/zero extension in Rosette use the output width instead of the number of extra bits, gaining static typing; - the single scope is traded for a global scope with local scope for each struct; - initial state is provided as a constant value instead of a set of assertions; - and the ``-provides`` option is introduced to more easily use generated code within Rosette based applications. Scope ~~~~~ Our first addition to the `minimal backend`_ above is that for both SMT-LIB and Rosette backends, we are now targetting real languages which bring with them their own sets of constraints with what we can use as identifiers. This is where the ``Functional::Scope`` class described above comes in; by using this class we can safely rename our identifiers in the generated output without worrying about collisions or illegal names/characters. In the SMT-LIB version, the ``SmtScope`` class implements ``Scope``; provides a constructor that iterates over a list of reserved keywords, calling ``reserve`` on each; and defines the ``is_character_legal`` method to reject any characters which are not allowed in SMT-LIB variable names to then be replaced with underscores in the output. To use this scope we create an instance of it, and call the ``Scope::unique_name()`` method to generate a unique and legal name for each of our identifiers. In the Rosette version we update the list of legal ascii characters in the ``is_character_legal`` method to only those allowed in Racket variable names. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Scope`` class :start-at: -struct SmtScope : public Functional::Scope { :end-at: }; For the reserved keywords we trade the SMT-LIB specification for Racket to prevent parts of our design from accidentally being treated as Racket code. We also no longer need to reserve ``pair``, ``first``, and ``second``. In `write_functional_smt2` these are used for combining the ``(inputs, current_state)`` and ``(outputs, next_state)`` into a single variable. Racket provides this functionality natively with ``cons``, which we will see later. .. inlined diff for skipping the actual lists .. code-block:: diff :caption: diff of ``reserved_keywords`` list const char *reserved_keywords[] = { - // reserved keywords from the smtlib spec - ... + // reserved keywords from the racket spec + ... // reserved for our own purposes - "pair", "Pair", "first", "second", - "inputs", "state", + "inputs", "state", "name", nullptr }; .. note:: We skip over the actual list of reserved keywords from both the smtlib and racket specifications to save on space in this document. Sort ~~~~ Next up in `write_functional_smt2` we see the ``Sort`` class. This is a wrapper for the ``Functional::Sort`` class, providing the additional functionality of mapping variable declarations to s-expressions with the ``to_sexpr()`` method. The main change from ``SmtSort`` to ``SmtrSort`` is a syntactical one with signals represented as ``bitvector``\ s, and memories as ``list``\ s of signals. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Sort`` wrapper :start-at: SExpr to_sexpr() const { :end-before: }; Struct ~~~~~~ As we saw in the `minimal backend`_ above, the ``Functional::IR`` class tracks the set of inputs, the set of outputs, and the set of "state" variables. The SMT-LIB backend maps each of these sets into its own ``SmtStruct``, with each variable getting a corresponding field in the struct and a specified `Sort`_. `write_functional_smt2` then defines each of these structs as a new ``datatype``, with each element being strongly-typed. In Rosette, rather than defining new datatypes for our structs, we use the native ``struct``. We also only declare each field by name because Racket provides less static typing. For ease of use, we provide the expected type for each field as comments. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``write_definition`` method :start-at: void write_definition :end-before: template void write_value Each field is added to the ``SmtStruct`` with the ``insert`` method, which also reserves a unique name (or accessor) within the `Scope`_. These accessors combine the struct name and field name and are globally unique, being used in the ``access`` method for reading values from the input/current state. .. literalinclude:: /generated/functional/smtlib.cc :language: c++ :caption: ``Struct::access()`` method :start-at: SExpr access( :end-before: }; In Rosette, struct fields are accessed as ``-`` so including the struct name in the field name would be redundant. For `write_functional_rosette` we instead choose to make field names unique only within the struct, while accessors are unique across the whole module. We thus modify the class constructor and ``insert`` method to support this; providing one scope that is local to the struct (``local_scope``) and one which is shared across the whole module (``global_scope``), leaving the ``access`` method unchanged. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of struct constructor :start-at: SmtStruct(std::string name, SmtScope &scope) :end-before: void write_definition Finally, ``SmtStruct`` also provides a ``write_value`` template method which calls a provided function on each element in the struct. This is used later for assigning values to the output/next state pair. The only change here is to remove the check for zero-argument constructors since this is not necessary with Rosette ``struct``\ s. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``write_value`` method :start-at: template void write_value :end-before: SExpr access PrintVisitor ~~~~~~~~~~~~ Remember in the `minimal backend`_ we converted nodes into strings for writing using the ``node.to_string()`` method, which wrapped ``node.visit()`` with a private visitor. We now want a custom visitor which can convert nodes into s-expressions. This is where the ``PrintVisitor`` comes in, implementing the abstract ``Functional::AbstractVisitor`` class with a return type of ``SExpr``. For most functions, the Rosette output is very similar to the corresponding SMT-LIB function with minor adjustments for syntax. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing similarities :start-at: SExpr logical_shift_left :end-at: "list-set-bv" However there are some differences in the two formats with regards to how booleans are handled, with Rosette providing built-in functions for conversion. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: portion of ``Functional::AbstractVisitor`` implementation diff showing differences :start-at: SExpr from_bool :end-before: SExpr extract Of note here is the rare instance of the Rosette implementation *gaining* static typing rather than losing it. Where SMT_LIB calls zero/sign extension with the number of extra bits needed (given by ``out_width - a.width()``), Rosette instead specifies the type of the output (given by ``list("bitvector", out_width)``). .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: zero/sign extension implementation diff :start-after: SExpr buf( :end-before: SExpr concat( :lines: 2-3, 5-6 .. note:: Be sure to check the source code for the full list of differences here. Module ~~~~~~ With most of the supporting classes out of the way, we now reach our three main steps from the `minimal backend`_. These are all handled by the ``SmtModule`` class, with the mapping from RTLIL module to FunctionalIR happening in the constructor. Each of the three ``SmtStruct``\ s; inputs, outputs, and state; are also created in the constructor, with each value in the corresponding lists in the IR being ``insert``\ ed. .. literalinclude:: /generated/functional/smtlib.cc :language: c++ :caption: ``SmtModule`` constructor :start-at: SmtModule(Module :end-at: } Since Racket uses the ``-`` to access struct fields, the ``SmtrModule`` instead uses an underscore for the name of the initial state. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Module`` constructor :start-at: scope.reserve(name :end-before: for (auto input The ``write`` method is then responsible for writing the FunctionalIR to the output file, formatted for the corresponding backend. ``SmtModule::write()`` breaks the output file down into four parts: defining the three structs, declaring the ``pair`` datatype, defining the transfer function ``(inputs, current_state) -> (outputs, next_state)`` with ``write_eval``, and declaring the initial state with ``write_initial``. The only change for the ``SmtrModule`` is that the ``pair`` declaration isn't needed. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Module::write()`` method :start-at: void write(std::ostream &out) :end-at: } The ``write_eval`` method is where the FunctionalIR nodes, outputs, and next state are handled. Just as with the `minimal backend`_, we iterate over the nodes with ``for(auto n : ir)``, and then use the ``Struct::write_value()`` method for the ``output_struct`` and ``state_struct`` to iterate over the outputs and next state respectively. .. literalinclude:: /generated/functional/smtlib.cc :language: c++ :caption: iterating over FunctionalIR nodes in ``SmtModule::write_eval()`` :start-at: for(auto n : ir) :end-at: } The main differences between our two backends here are syntactical. First we change the ``define-fun`` for the Racket style ``define`` which drops the explicitly typed inputs/outputs. And then we change the final result from a ``pair`` to the native ``cons`` which acts in much the same way, returning both the ``outputs`` and the ``next_state`` in a single variable. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Module::write_eval()`` transfer function declaration :start-at: w.open(list("define-fun" :end-at: w.open(list("define" .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of output/next state handling ``Module::write_eval()`` :start-at: w.open(list("pair" :end-at: w.pop(); For the ``write_initial`` method, the SMT-LIB backend uses ``declare-const`` and ``assert``\ s which must always hold true. For Rosette we instead define the initial state as any other variable that can be used by external code. This variable, ``[name]_initial``, can then be used in the ``[name]`` function call; allowing the Rosette code to be used in the generation of the ``next_state``, whereas the SMT-LIB code can only verify that a given ``next_state`` is correct. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: diff of ``Module::write_initial()`` method :start-at: void write_initial :end-before: void write Backend ~~~~~~~ The final part is the ``Backend`` itself, with much of the same boiler plate as the `minimal backend`_. The main difference is that we use the `Module`_ to perform the actual processing. .. literalinclude:: /generated/functional/smtlib.cc :language: c++ :caption: The ``FunctionalSmtBackend`` :start-at: struct FunctionalSmtBackend :end-at: } FunctionalSmtBackend; There are two additions here for Rosette. The first is that the output file needs to start with the ``#lang`` definition which tells the compiler/interpreter that we want to use the Rosette language module. The second is that the `write_functional_rosette` command takes an optional argument, ``-provides``. If this argument is given, then the output file gets an additional line declaring that everything in the file should be exported for use; allowing the file to be treated as a Racket package with structs and mapping function available for use externally. .. literalinclude:: /generated/functional/rosette.diff :language: diff :caption: relevant portion of diff of ``Backend::execute()`` method :start-at: lang rosette/safe :end-before: for (auto module yosys-0.52/docs/source/yosys_internals/extending_yosys/index.rst000066400000000000000000000006071477540374200254370ustar00rootroot00000000000000Working with the Yosys codebase ------------------------------- This section goes into additional detail on the Yosys source code and git repository. This information is not needed for simply using Yosys, but may be of interest for developers looking to customise Yosys builds. .. toctree:: :maxdepth: 3 extensions build_verific functional_ir contributing test_suites yosys-0.52/docs/source/yosys_internals/extending_yosys/test_suites.rst000066400000000000000000000057351477540374200267120ustar00rootroot00000000000000Testing Yosys ============= .. TODO:: more about the included test suite and how to add tests Automatic testing ----------------- The `Yosys Git repo`_ has automatic testing of builds and running of the included test suite on both Ubuntu and macOS, as well as across range of compiler versions. For up to date information, including OS versions, refer to `the git actions page`_. .. _Yosys Git repo: https://github.com/YosysHQ/yosys .. _the git actions page: https://github.com/YosysHQ/yosys/actions .. todo:: are unit tests currently working .. How to add a 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, `google test`_ was chosen, because it is widely used and it is relatively easy learn. .. _google test: https://github.com/google/googletest 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: .. code-block:: console 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 a new unit test ~~~~~~~~~~~~~~~~~~~~~~ If you want to add new unit tests for Yosys, just follow the steps below: * Go to directory :file:`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 tests ~~~~~~~~~~~~~~ To compile and run all unit tests, just go to yosys root directory and type: .. code-block:: console make unit-test If you want to remove all unit test files, type: .. code-block:: console make clean-unit-test yosys-0.52/docs/source/yosys_internals/flow/000077500000000000000000000000001477540374200213075ustar00rootroot00000000000000yosys-0.52/docs/source/yosys_internals/flow/control_and_data.rst000066400000000000000000000025221477540374200253350ustar00rootroot00000000000000Control 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 invert-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. yosys-0.52/docs/source/yosys_internals/flow/index.rst000066400000000000000000000007221477540374200231510ustar00rootroot00000000000000Internal 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 yosys-0.52/docs/source/yosys_internals/flow/overview.rst000066400000000000000000000042051477540374200237100ustar00rootroot00000000000000Flow 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:`/appendix/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 invert-helper :name: fig:Overview_flow Yosys simplified data flow (ellipses: data structures, rectangles: program modules) yosys-0.52/docs/source/yosys_internals/flow/verilog_frontend.rst000066400000000000000000000650561477540374200254230ustar00rootroot00000000000000.. _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 invert-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 `proc` pass and the passes it launches: - | `proc_clean` and `proc_rmdead` | These two passes just clean up the ``RTLIL::Process`` structure. The `proc_clean` pass removes empty parts (eg. empty assignments) from the process and `proc_rmdead` detects and removes unreachable branches from the process's decision trees. - | `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. - | `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. - | `proc_dff` | This pass replaces the ``RTLIL::SyncRule``\ s to d-type flip-flops (with asynchronous resets if necessary). - | `proc_dff` | This pass replaces the ``RTLIL::MemWriteAction``\ s with `$memwr` cells. - | `proc_clean` | A final call to `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. yosys-0.52/docs/source/yosys_internals/formats/000077500000000000000000000000001477540374200220135ustar00rootroot00000000000000yosys-0.52/docs/source/yosys_internals/formats/index.rst000066400000000000000000000050271477540374200236600ustar00rootroot00000000000000Internal formats ================ 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. .. toctree:: :maxdepth: 3 rtlil_rep .. [1] In Yosys the term pass is only used to refer to commands that operate on the RTLIL data structure. yosys-0.52/docs/source/yosys_internals/formats/rtlil_rep.rst000066400000000000000000000441011477540374200245410ustar00rootroot00000000000000The 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 invert-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, `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 `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``). yosys-0.52/docs/source/yosys_internals/hashing.rst000066400000000000000000000170121477540374200225140ustar00rootroot00000000000000Hashing and associative data structures in Yosys ------------------------------------------------ Container classes based on hashing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Yosys uses ``dict`` and ``pool`` as main container classes. ``dict`` is essentially a replacement for ``std::unordered_map`` and ``pool`` is a replacement for ``std::unordered_set``. The main characteristics are: * ``dict`` and ``pool`` are about 2x faster than the std containers (though this claim hasn't been verified for over 10 years) * references to elements in a ``dict`` or ``pool`` are invalidated by insert and remove operations (similar to ``std::vector`` on ``push_back()``). * some iterators are invalidated by ``erase()``. specifically, iterators that have not passed the erased element yet are invalidated. (``erase()`` itself returns valid iterator to the next element.) * no iterators are invalidated by ``insert()``. elements are inserted at ``begin()``. i.e. only a new iterator that starts at ``begin()`` will see the inserted elements. * the method ``.count(key, iterator)`` is like ``.count(key)`` but only considers elements that can be reached via the iterator. * iterators can be compared. ``it1 < it2`` means that the position of ``t2`` can be reached via ``t1`` but not vice versa. * the method ``.sort()`` can be used to sort the elements in the container the container stays sorted until elements are added or removed. * ``dict`` and ``pool`` will have the same order of iteration across all compilers, standard libraries and architectures. In addition to ``dict`` and ``pool`` there is also an ``idict`` that creates a bijective map from ``K`` to the integers. For example: :: idict si; log("%d\n", si("hello")); // will print 42 log("%d\n", si("world")); // will print 43 log("%d\n", si.at("world")); // will print 43 log("%d\n", si.at("dummy")); // will throw exception log("%s\n", si[42].c_str())); // will print hello log("%s\n", si[43].c_str())); // will print world log("%s\n", si[44].c_str())); // will throw exception It is not possible to remove elements from an idict. Finally ``mfp`` implements a merge-find set data structure (aka. disjoint-set or union-find) over the type ``K`` ("mfp" = merge-find-promote). The hash function ~~~~~~~~~~~~~~~~~ The hash function generally used in Yosys is the XOR version of DJB2: :: state = ((state << 5) + state) ^ value This is an old-school hash designed to hash ASCII characters. Yosys doesn't hash a lot of ASCII text, but it still happens to be a local optimum due to factors described later. Hash function quality is multi-faceted and highly dependent on what is being hashed. Yosys isn't concerned by any cryptographic qualities, instead the goal is minimizing total hashing collision risk given the data patterns within Yosys. In general, a good hash function typically folds values into a state accumulator with a mathematical function that is fast to compute and has some beneficial properties. One of these is the avalanche property, which demands that a small change such as flipping a bit or incrementing by one in the input produces a large, unpredictable change in the output. Additionally, the bit independence criterion states that any pair of output bits should change independently when any single input bit is inverted. These properties are important for avoiding hash collision on data patterns like the hash of a sequence not colliding with its permutation, not losing from the state the information added by hashing preceding elements, etc. DJB2 lacks these properties. Instead, since Yosys hashes large numbers of data structures composed of incrementing integer IDs, Yosys abuses the predictability of DJB2 to get lower hash collisions, with regular nature of the hashes surviving through the interaction with the "modulo prime" operations in the associative data structures. For example, some most common objects in Yosys are interned ``IdString``\ s of incrementing indices or ``SigBit``\ s with bit offsets into wire (represented by its unique ``IdString`` name) as the typical case. This is what makes DJB2 a local optimum. Additionally, the ADD version of DJB2 (like above but with addition instead of XOR) is used to this end for some types, abandoning the general pattern of folding values into a state value. Making a type hashable ~~~~~~~~~~~~~~~~~~~~~~ Let's first take a look at the external interface on a simplified level. Generally, to get the hash for ``T obj``, you would call the utility function ``run_hash(const T& obj)``, corresponding to ``hash_ops::hash(obj)``, the default implementation of which uses ``hash_ops::hash_into(Hasher(), obj)``. ``Hasher`` is the class actually implementing the hash function, hiding its initialized internal state, and passing it out on ``hash_t yield()`` with perhaps some finalization steps. ``hash_ops`` is the star of the show. By default it pulls the ``Hasher h`` through a ``Hasher T::hash_into(Hasher h)`` method. That's the method you have to implement to make a record (class or struct) type easily hashable with Yosys hashlib associative data structures. ``hash_ops`` is specialized for built-in types like ``int`` or ``bool`` and treats pointers the same as integers, so it doesn't dereference pointers. Since many RTLIL data structures like ``RTLIL::Wire`` carry their own unique index ``Hasher::hash_t hashidx_;``, there are specializations for ``hash_ops`` and others in ``kernel/hashlib.h`` that actually dereference the pointers and call ``hash_into`` on the instances pointed to. ``hash_ops`` is also specialized for simple compound types like ``std::pair`` by calling hash_into in sequence on its members. For flexible size containers like ``std::vector`` the size of the container is hashed first. That is also how implementing hashing for a custom record data type should be - unless there is strong reason to do otherwise, call ``h.eat(m)`` on the ``Hasher h`` you have received for each member in sequence and ``return h;``. The ``hash_ops::hash(obj)`` method is not indended to be called when context of implementing the hashing for a record or other compound type. When writing it, you should connect it to ``hash_ops::hash_into(Hasher h)`` as shown below. If you have a strong reason to do so, and you have to create a special implementation for top-level hashing, look at how ``hash_ops::hash(...)`` is implemented in ``kernel/rtlil.h``. Porting plugins from the legacy interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Previously, the interface to implement hashing on custom types was just ``unsigned int T::hash() const``. This meant hashes for members were computed independently and then ad-hoc combined with the hash function with some xorshift operations thrown in to mix bits together somewhat. A plugin can stay compatible with both versions prior and after the break by implementing both interfaces based on the existance and value of `YS_HASHING_VERSION`. .. code-block:: cpp :caption: Example hash compatibility wrapper :name: hash_plugin_compat #ifndef YS_HASHING_VERSION unsigned int T::hash() const { return mkhash(a, b); } #elif YS_HASHING_VERSION == 1 Hasher T::hash_into(Hasher h) const { h.eat(a); h.eat(b); return h; } Hasher T::hash() const { Hasher h; h.eat(*this); return h; } #else #error "Unsupported hashing interface" #endif Feel free to contact Yosys maintainers with related issues. yosys-0.52/docs/source/yosys_internals/index.rst000066400000000000000000000026251477540374200222060ustar00rootroot00000000000000.. _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++17 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 verilog hashing yosys-0.52/docs/source/yosys_internals/techmap.rst000066400000000000000000000145641477540374200225250ustar00rootroot00000000000000Techmap by example ------------------ As a quick recap, the `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/main/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 invert-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 invert-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 `proc` - Perform expensive optimizations (such as `freduce`) on cells where this is known to work well. - Interacting with custom commands. .. note:: PROTIP: Commands such as `shell`, ``show -pause``, and `dump` can be used in the ``_TECHMAP_DO_*`` scripts for debugging map modules. Example: .. figure:: /_images/code_examples/techmap/mymul.* :class: width-helper invert-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 invert-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 invert-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. yosys-0.52/docs/source/yosys_internals/verilog.rst000066400000000000000000000412041477540374200225420ustar00rootroot00000000000000Notes on Verilog support in Yosys ================================= .. TODO:: how much of this is specific to the read_verilog and should be in :doc:`/yosys_internals/flow/verilog_frontend`? Unsupported Verilog-2005 Features --------------------------------- The following Verilog-2005 features are not supported by Yosys and there are currently no plans to add support for them: - Non-synthesizable language features as defined in IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 - The ``tri``, ``triand`` and ``trior`` net types - The ``config`` and ``disable`` keywords and library map files Verilog Attributes and non-standard features -------------------------------------------- - The ``full_case`` attribute on case statements is supported (also the non-standard ``// synopsys full_case`` directive) - The ``parallel_case`` attribute on case statements is supported (also the non-standard ``// synopsys parallel_case`` directive) - The ``// synopsys translate_off`` and ``// synopsys translate_on`` directives are also supported (but the use of ``` `ifdef .. `endif ``` is strongly recommended instead). - The ``nomem2reg`` attribute on modules or arrays prohibits the automatic early conversion of arrays to separate registers. This is potentially dangerous. Usually the front-end has good reasons for converting an array to a list of registers. Prohibiting this step will likely result in incorrect synthesis results. - The ``mem2reg`` attribute on modules or arrays forces the early conversion of arrays to separate registers. - The ``nomeminit`` attribute on modules or arrays prohibits the creation of initialized memories. This effectively puts ``mem2reg`` on all memories that are written to in an ``initial`` block and are not ROMs. - The ``nolatches`` attribute on modules or always-blocks prohibits the generation of logic-loops for latches. Instead all not explicitly assigned values default to x-bits. This does not affect clocked storage elements such as flip-flops. - The ``nosync`` attribute on registers prohibits the generation of a storage element. The register itself will always have all bits set to 'x' (undefined). The variable may only be used as blocking assigned temporary variable within an always block. This is mostly used internally by Yosys to synthesize Verilog functions and access arrays. - The ``nowrshmsk`` attribute on a register prohibits the generation of shift-and-mask type circuits for writing to bit slices of that register. - The ``onehot`` attribute on wires mark them as one-hot state register. This is used for example for memory port sharing and set by the fsm_map pass. - The ``blackbox`` attribute on modules is used to mark empty stub modules that have the same ports as the real thing but do not contain information on the internal configuration. This modules are only used by the synthesis passes to identify input and output ports of cells. The Verilog backend also does not output blackbox modules on default. `read_verilog`, unless called with ``-noblackbox`` will automatically set the blackbox attribute on any empty module it reads. - The ``noblackbox`` attribute set on an empty module prevents `read_verilog` from automatically setting the blackbox attribute on the module. - The ``whitebox`` attribute on modules triggers the same behavior as ``blackbox``, but is for whitebox modules, i.e. library modules that contain a behavioral model of the cell type. - The ``lib_whitebox`` attribute overwrites ``whitebox`` when `read_verilog` is run in ``-lib`` mode. Otherwise it's automatically removed. - The ``dynports`` attribute is used by the Verilog front-end to mark modules that have ports with a width that depends on a parameter. - The ``hdlname`` attribute is used by some passes to document the original (HDL) name of a module when renaming a module. It should contain a single name, or, when describing a hierarchical name in a flattened design, multiple names separated by a single space character. - The ``keep`` attribute on cells and wires is used to mark objects that should never be removed by the optimizer. This is used for example for cells that have hidden connections that are not part of the netlist, such as IO pads. Setting the ``keep`` attribute on a module has the same effect as setting it on all instances of the module. - The ``keep_hierarchy`` attribute on cells and modules keeps the `flatten` command from flattening the indicated cells and modules. - The `gate_cost_equivalent` attribute on a module can be used to specify the estimated cost of the module as a number of basic gate instances. See the help message of command `keep_hierarchy` which interprets this attribute. - The ``init`` attribute on wires is set by the frontend when a register is initialized "FPGA-style" with ``reg foo = val``. It can be used during synthesis to add the necessary reset logic. - The ``top`` attribute on a module marks this module as the top of the design hierarchy. The `hierarchy` command sets this attribute when called with ``-top``. Other commands, such as `flatten` and various backends use this attribute to determine the top module. - The ``src`` attribute is set on cells and wires created by to the string ``:`` by the HDL front-end and is then carried through the synthesis. When entities are combined, a new \|-separated string is created that contains all the strings from the original entities. - The ``defaultvalue`` attribute is used to store default values for module inputs. The attribute is attached to the input wire by the HDL front-end when the input is declared with a default value. - The ``parameter`` and ``localparam`` attributes are used to mark wires that represent module parameters or localparams (when the HDL front-end is run in ``-pwires`` mode). - Wires marked with the ``hierconn`` attribute are connected to wires with the same name (format ``cell_name.identifier``) when they are imported from sub-modules by `flatten`. - The ``clkbuf_driver`` attribute can be set on an output port of a blackbox module to mark it as a clock buffer output, and thus prevent `clkbufmap` from inserting another clock buffer on a net driven by such output. - The ``clkbuf_sink`` attribute can be set on an input port of a module to request clock buffer insertion by the `clkbufmap` pass. - The ``clkbuf_inv`` attribute can be set on an output port of a module with the value set to the name of an input port of that module. When the `clkbufmap` would otherwise insert a clock buffer on this output, it will instead try inserting the clock buffer on the input port (this is used to implement clock inverter cells that clock buffer insertion will "see through"). - The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent automatic clock buffer insertion by `clkbufmap`. This behaviour can be overridden by providing a custom selection to `clkbufmap`. - The ``invertible_pin`` attribute can be set on a port to mark it as invertible via a cell parameter. The name of the inversion parameter is specified as the value of this attribute. The value of the inversion parameter must be of the same width as the port, with 1 indicating an inverted bit and 0 indicating a non-inverted bit. - The ``iopad_external_pin`` attribute on a blackbox module's port marks it as the external-facing pin of an I/O pad, and prevents `iopadmap` from inserting another pad cell on it. - The module attribute ``abc9_lut`` is an integer attribute indicating to `abc9` that this module describes a LUT with an area cost of this value, and propagation delays described using ``specify`` statements. - The module attribute ``abc9_box`` is a boolean specifying a black/white-box definition, with propagation delays described using ``specify`` statements, for use by `abc9`. - The port attribute ``abc9_carry`` marks the carry-in (if an input port) and carry-out (if output port) ports of a box. This information is necessary for `abc9` to preserve the integrity of carry-chains. Specifying this attribute onto a bus port will affect only its most significant bit. - The module attribute ``abc9_flop`` is a boolean marking the module as a flip-flop. This allows `abc9` to analyse its contents in order to perform sequential synthesis. - The frontend sets attributes ``always_comb``, ``always_latch`` and ``always_ff`` on processes derived from SystemVerilog style always blocks according to the type of the always. These are checked for correctness in ``proc_dlatch``. - The cell attribute ``wildcard_port_conns`` represents wildcard port connections (SystemVerilog ``.*``). These are resolved to concrete connections to matching wires in `hierarchy`. - In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset by adding an empty ``{* *}`` statement.) - In module parameter and port declarations, and cell port and parameter lists, a trailing comma is ignored. This simplifies writing Verilog code generators a bit in some cases. - Modules can be declared with ``module mod_name(...);`` (with three dots instead of a list of module ports). With this syntax it is sufficient to simply declare a module port as 'input' or 'output' in the module body. - When defining a macro with ``\`define``, all text between triple double quotes is interpreted as macro body, even if it contains unescaped newlines. The triple double quotes are removed from the macro body. For example: .. code-block:: verilog `define MY_MACRO(a, b) """ assign a = 23; assign b = 42; """ - The attribute ``via_celltype`` can be used to implement a Verilog task or function by instantiating the specified cell type. The value is the name of the cell type to use. For functions the name of the output port can be specified by appending it to the cell type separated by a whitespace. The body of the task or function is unused in this case and can be used to specify a behavioral model of the cell type for simulation. For example: .. code-block:: verilog module my_add3(A, B, C, Y); parameter WIDTH = 8; input [WIDTH-1:0] A, B, C; output [WIDTH-1:0] Y; ... endmodule module top; ... (* via_celltype = "my_add3 Y" *) (* via_celltype_defparam_WIDTH = 32 *) function [31:0] add3; input [31:0] A, B, C; begin add3 = A + B + C; end endfunction ... endmodule - The ``wiretype`` attribute is added by the verilog parser for wires of a typedef'd type to indicate the type identifier. - Various ``enum_value_{value}`` attributes are added to wires of an enumerated type to give a map of possible enum items to their values. - The ``enum_base_type`` attribute is added to enum items to indicate which enum they belong to (enums -- anonymous and otherwise -- are automatically named with an auto-incrementing counter). Note that enums are currently not strongly typed. - A limited subset of DPI-C functions is supported. The plugin mechanism (see ``help plugin``) can be used to load .so files with implementations of DPI-C routines. As a non-standard extension it is possible to specify a plugin alias using the ``:`` syntax. For example: .. code-block:: verilog module dpitest; import "DPI-C" function foo:round = real my_round (real); parameter real r = my_round(12.345); endmodule .. code-block:: $ yosys -p 'plugin -a foo -i /lib/libm.so; read_verilog dpitest.v' - Sized constants (the syntax ``'s?[bodh]``) support constant expressions as ````. If the expression is not a simple identifier, it must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010`` - The system tasks ``$finish``, ``$stop`` and ``$display`` are supported in initial blocks in an unconditional context (only if/case statements on expressions over parameters and constant values are allowed). The intended use for this is synthesis-time DRC. - There is limited support for converting ``specify`` .. ``endspecify`` statements to special ``$specify2``, ``$specify3``, and ``$specrule`` cells, 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: .. code-block:: verilog (* 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 -------------------------------------------------------------- - Support for ``assert``, ``assume``, ``restrict``, and ``cover`` is enabled when `read_verilog` is called with ``-formal``. - The system task ``$initstate`` evaluates to 1 in the initial state and to 0 otherwise. - The system function ``$anyconst`` evaluates to any constant value. This is equivalent to declaring a reg as ``rand const``, but also works outside of checkers. (Yosys also supports ``rand const`` outside checkers.) - The system function ``$anyseq`` evaluates to any value, possibly a different value in each cycle. This is equivalent to declaring a reg as ``rand``, but also works outside of checkers. (Yosys also supports ``rand`` variables outside checkers.) - The system functions ``$allconst`` and ``$allseq`` can be used to construct formal exist-forall problems. Assumptions only hold if the trace satisfies the assumption for all ``$allconst/$allseq`` values. For assertions and cover statements it is sufficient if just one ``$allconst/$allseq`` value triggers the property (similar to ``$anyconst/$anyseq``). - Wires/registers declared using the ``anyconst/anyseq/allconst/allseq`` attribute (for example ``(* anyconst *) reg [7:0] foobar;``) will behave as if driven by a ``$anyconst/$anyseq/$allconst/$allseq`` function. - The SystemVerilog tasks ``$past``, ``$stable``, ``$rose`` and ``$fell`` are supported in any clocked block. - The syntax ``@($global_clock)`` can be used to create FFs that have no explicit clock input (``$ff`` cells). The same can be achieved by using ``@(posedge )`` or ``@(negedge )`` when ```` is marked with the ``(* gclk *)`` Verilog attribute. Supported features from SystemVerilog ------------------------------------- When `read_verilog` is called with ``-sv``, it accepts some language features from SystemVerilog: - The ``assert`` statement from SystemVerilog is supported in its most basic form. In module context: ``assert property ();`` and within an always block: ``assert();``. It is transformed to an ``$assert`` cell. - The ``assume``, ``restrict``, and ``cover`` statements from SystemVerilog are also supported. The same limitations as with the ``assert`` statement apply. - The keywords ``always_comb``, ``always_ff`` and ``always_latch``, ``logic`` and ``bit`` are supported. - Declaring free variables with ``rand`` and ``rand const`` is supported. - Checkers without a port list that do not need to be instantiated (but instead behave like a named block) are supported. - SystemVerilog packages are supported. Once a SystemVerilog file is read into a design with `read_verilog`, all its packages are available to SystemVerilog files being read into the same design afterwards. - typedefs are supported (including inside packages) - type casts are currently not supported - enums are supported (including inside packages) - but are currently not strongly typed - 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. yosys-0.52/docs/tests/000077500000000000000000000000001477540374200147355ustar00rootroot00000000000000yosys-0.52/docs/tests/macro_commands.py000077500000000000000000000074001477540374200202750ustar00rootroot00000000000000#!/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.absolute() ROOT_DIR = TESTS_DIR.parent.parent logging.log(logging.INFO, f"Using {ROOT_DIR} as root directory") 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 = ROOT_DIR / "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) yosys-0.52/docs/util/000077500000000000000000000000001477540374200145505ustar00rootroot00000000000000yosys-0.52/docs/util/RtlilLexer.py000066400000000000000000000032611477540374200172120ustar00rootroot00000000000000from 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 ] } yosys-0.52/docs/util/__init__.py000066400000000000000000000000001477540374200166470ustar00rootroot00000000000000yosys-0.52/docs/util/cellref.py000066400000000000000000000330751477540374200165460ustar00rootroot00000000000000#!/usr/bin/env python3 from __future__ import annotations from dataclasses import dataclass import json from pathlib import Path, PosixPath, WindowsPath import re from typing import Any from sphinx.application import Sphinx from sphinx.ext import autodoc from sphinx.ext.autodoc import Documenter from sphinx.util import logging logger = logging.getLogger(__name__) # cell signature cell_ext_sig_re = re.compile( r'''^ ([^:\s]+::)? # optional group or file name ([\w$._]+?) # module name (?:\.([\w_]+))? # optional: thing name (::[\w_]+)? # attribute \s* $ # and nothing more ''', re.VERBOSE) @dataclass class YosysCell: name: str title: str ports: str source: str desc: str code: str inputs: list[str] outputs: list[str] properties: list[str] class YosysCellGroupDocumenter(Documenter): objtype = 'cellgroup' priority = 10 object: tuple[str, list[str]] lib_key = 'groups' option_spec = { 'caption': autodoc.annotation_option, 'members': autodoc.members_option, 'source': autodoc.bool_option, 'linenos': autodoc.bool_option, } __cell_lib: dict[str, list[str] | dict[str]] | None = None @property def cell_lib(self) -> dict[str, list[str] | dict[str]]: if not self.__cell_lib: self.__cell_lib = {} cells_obj: dict[str, dict[str, list[str] | dict[str]]] try: with open(self.config.cells_json, "r") as f: cells_obj = json.loads(f.read()) except FileNotFoundError: logger.warning( f"unable to find cell lib at {self.config.cells_json}", type = 'cellref', subtype = 'cell_lib' ) else: for (name, obj) in cells_obj.get(self.lib_key, {}).items(): self.__cell_lib[name] = obj return self.__cell_lib @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any ) -> bool: return False def parse_name(self) -> bool: if not self.options.caption: self.content_indent = '' self.fullname = self.modname = self.name return True def import_object(self, raiseerror: bool = False) -> bool: # get cell try: self.object = (self.modname, self.cell_lib[self.modname]) except KeyError: if raiseerror: raise return False self.real_modname = self.modname return True def get_sourcename(self) -> str: return self.env.doc2path(self.env.docname) def format_name(self) -> str: return self.options.caption or '' def format_signature(self, **kwargs: Any) -> str: return self.modname def add_directive_header(self, sig: str) -> None: domain = getattr(self, 'domain', 'cell') directive = getattr(self, 'directivetype', 'group') name = self.format_name() sourcename = self.get_sourcename() cell_list = self.object # cell definition self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) self.add_line(f' :caption: {name}', sourcename) if self.options.noindex: self.add_line(' :noindex:', sourcename) def add_content(self, more_content: Any | None) -> None: # groups have no native content # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) def filter_members( self, members: list[tuple[str, Any]], want_all: bool ) -> list[tuple[str, Any, bool]]: return [(x[0], x[1], False) for x in members] def get_object_members( self, want_all: bool ) -> tuple[bool, list[tuple[str, Any]]]: ret: list[tuple[str, str]] = [] if want_all: for member in self.object[1]: ret.append((member, self.modname)) else: memberlist = self.options.members or [] for name in memberlist: if name in self.object: ret.append((name, self.modname)) else: logger.warning(('unknown module mentioned in :members: option: ' f'group {self.modname}, module {name}'), type='cellref') return False, ret def document_members(self, all_members: bool = False) -> None: want_all = (all_members or self.options.inherited_members or self.options.members is autodoc.ALL) # find out which members are documentable members_check_module, members = self.get_object_members(want_all) # document non-skipped members memberdocumenters: list[tuple[Documenter, bool]] = [] for (mname, member, isattr) in self.filter_members(members, want_all): classes = [cls for cls in self.documenters.values() if cls.can_document_member(member, mname, isattr, self)] if not classes: # don't know how to document this member continue # prefer the documenter with the highest priority classes.sort(key=lambda cls: cls.priority) # give explicitly separated module name, so that members # of inner classes can be documented full_mname = self.format_signature() + '::' + mname documenter = classes[-1](self.directive, full_mname, self.indent) memberdocumenters.append((documenter, isattr)) member_order = self.options.member_order or self.config.autodoc_member_order memberdocumenters = self.sort_members(memberdocumenters, member_order) for documenter, isattr in memberdocumenters: documenter.generate( all_members=True, real_modname=self.real_modname, check_module=members_check_module and not isattr) def generate( self, more_content: Any | None = None, real_modname: str | None = None, check_module: bool = False, all_members: bool = False ) -> None: if not self.parse_name(): # need a cell lib to import from logger.warning( f"don't know which cell lib to import for autodocumenting {self.name}", type = 'cellref' ) return if not self.import_object(): logger.warning( f"unable to load {self.name}", type = 'cellref' ) return # check __module__ of object (for members not given explicitly) # if check_module: # if not self.check_module(): # return sourcename = self.get_sourcename() self.add_line('', sourcename) # format the object's signature, if any try: sig = self.format_signature() except Exception as exc: logger.warning(('error while formatting signature for %s: %s'), self.fullname, exc, type='cellref') return # generate the directive header and options, if applicable self.add_directive_header(sig) self.add_line('', sourcename) # e.g. the module directive doesn't have content self.indent += self.content_indent # add all content (from docstrings, attribute docs etc.) self.add_content(more_content) # document members, if possible self.document_members(all_members) class YosysCellDocumenter(YosysCellGroupDocumenter): objtype = 'cell' priority = 15 object: YosysCell lib_key = 'cells' @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any ) -> bool: if membername == "__source": return False if not membername.startswith('$'): return False return isinstance(parent, YosysCellGroupDocumenter) def parse_name(self) -> bool: try: matched = cell_ext_sig_re.match(self.name) group, modname, thing, attribute = matched.groups() except AttributeError: logger.warning(('invalid signature for auto%s (%r)') % (self.objtype, self.name), type='cellref') return False self.modname = modname self.groupname = group or '' self.attribute = attribute or '' self.fullname = ((self.modname) + (thing or '')) return True def import_object(self, raiseerror: bool = False) -> bool: if super().import_object(raiseerror): self.object = YosysCell(self.modname, **self.object[1]) return True return False def get_sourcename(self) -> str: return self.object.source.split(":")[0] def format_name(self) -> str: return self.object.name def format_signature(self, **kwargs: Any) -> str: return self.groupname + self.fullname + self.attribute def add_directive_header(self, sig: str) -> None: domain = getattr(self, 'domain', self.objtype) directive = getattr(self, 'directivetype', 'def') name = self.format_name() sourcename = self.get_sourcename() cell = self.object # cell definition self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) # options opt_attrs = ["title", "properties", ] for attr in opt_attrs: val = getattr(cell, attr, None) if isinstance(val, list): val = ' '.join(val) if val: self.add_line(f' :{attr}: {val}', sourcename) self.add_line('\n', sourcename) if self.options.noindex: self.add_line(' :noindex:', sourcename) def add_content(self, more_content: Any | None) -> None: # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() startline = int(self.object.source.split(":")[1]) for i, line in enumerate(self.object.desc.splitlines(), startline): self.add_line(line, sourcename, i) # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) # fields self.add_line('\n', sourcename) field_attrs = ["properties", ] for field in field_attrs: attr = getattr(self.object, field, []) for val in attr: self.add_line(f':{field} {val}:', sourcename) def get_object_members( self, want_all: bool ) -> tuple[bool, list[tuple[str, Any]]]: ret: list[tuple[str, str]] = [] if self.options.source: ret.append(('__source', self.real_modname)) return False, ret class YosysCellSourceDocumenter(YosysCellDocumenter): objtype = 'cellsource' priority = 20 @classmethod def can_document_member( cls, member: Any, membername: str, isattr: bool, parent: Any ) -> bool: if membername != "__source": return False if isinstance(parent, YosysCellDocumenter): return True return False def add_directive_header(self, sig: str) -> None: domain = getattr(self, 'domain', 'cell') directive = getattr(self, 'directivetype', 'source') name = self.format_name() sourcename = self.get_sourcename() cell = self.object # cell definition self.add_line(f'.. {domain}:{directive}:: {sig}', sourcename) if self.options.linenos: self.add_line(f' :source: {cell.source.split(":")[0]}', sourcename) else: self.add_line(f' :source: {cell.source}', sourcename) self.add_line(f' :language: verilog', sourcename) if self.options.linenos: startline = int(self.object.source.split(":")[1]) self.add_line(f' :lineno-start: {startline}', sourcename) if self.options.noindex: self.add_line(' :noindex:', sourcename) def add_content(self, more_content: Any | None) -> None: # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() startline = int(self.object.source.split(":")[1]) for i, line in enumerate(self.object.code.splitlines(), startline-1): self.add_line(line, sourcename, i) # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) def get_object_members( self, want_all: bool ) -> tuple[bool, list[tuple[str, Any]]]: return False, [] def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value('cells_json', False, 'html', [Path, PosixPath, WindowsPath]) app.setup_extension('sphinx.ext.autodoc') app.add_autodocumenter(YosysCellDocumenter) app.add_autodocumenter(YosysCellSourceDocumenter) app.add_autodocumenter(YosysCellGroupDocumenter) return { 'version': '1', 'parallel_read_safe': True, } yosys-0.52/docs/util/cmdref.py000066400000000000000000000543151477540374200163720ustar00rootroot00000000000000# based on https://github.com/ofosos/sphinxrecipes/blob/master/sphinxrecipes/sphinxrecipes.py from __future__ import annotations import re from typing import cast from docutils import nodes from docutils.nodes import Node, Element, system_message from docutils.parsers.rst import directives from docutils.parsers.rst.states import Inliner from sphinx.application import Sphinx from sphinx.domains import Domain, Index from sphinx.domains.std import StandardDomain from sphinx.environment import BuildEnvironment from sphinx.roles import XRefRole from sphinx.directives import ObjectDescription from sphinx.directives.code import container_wrapper from sphinx.util.nodes import make_refnode from sphinx.util.docfields import Field from sphinx import addnodes class TocNode(ObjectDescription): def add_target_and_index( self, name: str, sig: str, signode: addnodes.desc_signature ) -> None: idx = ".".join(name.split("::")) signode['ids'].append(idx) def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature) -> tuple[str, ...]: if 'fullname' not in sig_node: return () modname = sig_node.get('module') fullname = sig_node['fullname'] if modname: return (modname, *fullname.split('::')) else: return tuple(fullname.split('::')) def _toc_entry_name(self, sig_node: addnodes.desc_signature) -> str: if not sig_node.get('_toc_parts'): return '' config = self.env.app.config objtype = sig_node.parent.get('objtype') *parents, name = sig_node['_toc_parts'] if config.toc_object_entries_show_parents == 'domain': return sig_node.get('tocname', name) if config.toc_object_entries_show_parents == 'hide': return name if config.toc_object_entries_show_parents == 'all': return '.'.join(parents + [name]) return '' class CommandNode(TocNode): """A custom node that describes a command.""" name = 'cmd' required_arguments = 1 option_spec = { 'title': directives.unchanged, 'tags': directives.unchanged } def handle_signature(self, sig, signode: addnodes.desc_signature): signode['fullname'] = sig signode += addnodes.desc_addname(text="yosys> help ") signode += addnodes.desc_name(text=sig) return signode['fullname'] def add_target_and_index(self, name_cls, sig, signode): idx = type(self).name + '-' + sig signode['ids'].append(idx) if 'noindex' not in self.options: name = "{}.{}.{}".format(self.name, type(self).__name__, sig) tagmap = self.env.domaindata[type(self).name]['obj2tag'] tagmap[name] = list(self.options.get('tags', '').split(' ')) title = self.options.get('title', sig) titlemap = self.env.domaindata[type(self).name]['obj2title'] titlemap[name] = title objs = self.env.domaindata[type(self).name]['objects'] # (name, sig, typ, docname, anchor, prio) objs.append((name, sig, type(self).name, self.env.docname, idx, 0)) class PropNode(TocNode): name = 'prop' fieldname = 'props' def handle_signature(self, sig: str, signode: addnodes.desc_signature): signode['fullname'] = sig signode['tocname'] = tocname = sig.split('::')[-1] signode += addnodes.desc_name(text=tocname) return signode['fullname'] def add_target_and_index( self, name: str, sig: str, signode: addnodes.desc_signature ) -> None: idx = ".".join(name.split("::")) signode['ids'].append(idx) if 'noindex' not in self.options: tocname: str = signode.get('tocname', name) objs = self.env.domaindata[self.domain]['objects'] # (name, sig, typ, docname, anchor, prio) objs.append((name, tocname, type(self).name, self.env.docname, idx, 1)) class CellGroupedField(Field): """Custom version of GroupedField which doesn't require content.""" is_grouped = True list_type = nodes.bullet_list def __init__(self, name: str, names: tuple[str, ...] = (), label: str = None, rolename: str = None, can_collapse: bool = False) -> None: super().__init__(name, names, label, True, rolename) self.can_collapse = can_collapse def make_field(self, types: dict[str, list[Node]], domain: str, items: tuple, env: BuildEnvironment = None, inliner: Inliner = None, location: Node = None) -> nodes.field: fieldname = nodes.field_name('', self.label) listnode = self.list_type() for fieldarg, content in items: par = nodes.paragraph() if fieldarg: par.extend(self.make_xrefs(self.rolename, domain, fieldarg, nodes.Text, env=env, inliner=inliner, location=location)) if len(content) == 1 and ( isinstance(content[0], nodes.Text) or (isinstance(content[0], nodes.inline) and len(content[0]) == 1 and isinstance(content[0][0], nodes.Text))): par += nodes.Text(' -- ') par += content listnode += nodes.list_item('', par) if len(items) == 1 and self.can_collapse: list_item = cast(nodes.list_item, listnode[0]) fieldbody = nodes.field_body('', list_item[0]) return nodes.field('', fieldname, fieldbody) fieldbody = nodes.field_body('', listnode) return nodes.field('', fieldname, fieldbody) class CellNode(TocNode): """A custom node that describes an internal cell.""" name = 'cell' option_spec = { 'title': directives.unchanged, 'ports': directives.unchanged, 'properties': directives.unchanged, } doc_field_types = [ CellGroupedField('props', label='Properties', rolename='prop', names=('properties', 'property', 'tag', 'tags'), can_collapse=True), ] def handle_signature(self, sig: str, signode: addnodes.desc_signature): signode['fullname'] = sig signode['tocname'] = tocname = sig.split('::')[-1] signode += addnodes.desc_addname(text="yosys> help ") signode += addnodes.desc_name(text=tocname) return signode['fullname'] def add_target_and_index( self, name: str, sig: str, signode: addnodes.desc_signature ) -> None: idx = ".".join(name.split("::")) signode['ids'].append(idx) if 'noindex' not in self.options: tocname: str = signode.get('tocname', name) title: str = self.options.get('title', sig) titlemap = self.env.domaindata[self.domain]['obj2title'] titlemap[name] = title props = self.options.get('properties', '') if props: propmap = self.env.domaindata[self.domain]['obj2prop'] propmap[name] = props.split(' ') objs = self.env.domaindata[self.domain]['objects'] # (name, sig, typ, docname, anchor, prio) objs.append((name, tocname, type(self).name, self.env.docname, idx, 0)) def transform_content(self, contentnode: addnodes.desc_content) -> None: # Add the cell title to the body if 'title' in self.options: titlenode = nodes.paragraph() titlenode += nodes.strong() titlenode[-1] += nodes.Text(self.options['title']) contentnode.insert(0, titlenode) class CellSourceNode(TocNode): """A custom code block for including cell source.""" name = 'cellsource' option_spec = { "source": directives.unchanged_required, "language": directives.unchanged_required, 'lineno-start': int, } def handle_signature( self, sig, signode: addnodes.desc_signature ) -> str: language = self.options.get('language') signode['fullname'] = sig signode['tocname'] = f"{sig.split('::')[-2]} {language}" signode += addnodes.desc_name(text="Simulation model") signode += addnodes.desc_sig_space() signode += addnodes.desc_addname(text=f'({language})') return signode['fullname'] def run(self) -> list[Node]: """Override run to parse content as a code block""" if ':' in self.name: self.domain, self.objtype = self.name.split(':', 1) else: self.domain, self.objtype = '', self.name self.indexnode = addnodes.index(entries=[]) node = addnodes.desc() node.document = self.state.document source, line = self.get_source_info() if line is not None: line -= 1 self.state.document.note_source(source, line) node['domain'] = self.domain # 'desctype' is a backwards compatible attribute node['objtype'] = node['desctype'] = self.objtype node['noindex'] = noindex = ('noindex' in self.options) node['noindexentry'] = ('noindexentry' in self.options) node['nocontentsentry'] = ('nocontentsentry' in self.options) if self.domain: node['classes'].append(self.domain) node['classes'].append(node['objtype']) self.names = [] signatures = self.get_signatures() for sig in signatures: # add a signature node for each signature in the current unit # and add a reference target for it signode = addnodes.desc_signature(sig, '') self.set_source_info(signode) node.append(signode) try: # name can also be a tuple, e.g. (classname, objname); # this is strictly domain-specific (i.e. no assumptions may # be made in this base class) name = self.handle_signature(sig, signode) except ValueError: # signature parsing failed signode.clear() signode += addnodes.desc_name(sig, sig) continue # we don't want an index entry here finally: # Private attributes for ToC generation. Will be modified or removed # without notice. if self.env.app.config.toc_object_entries: signode['_toc_parts'] = self._object_hierarchy_parts(signode) signode['_toc_name'] = self._toc_entry_name(signode) else: signode['_toc_parts'] = () signode['_toc_name'] = '' if name not in self.names: self.names.append(name) if not noindex: # only add target and index entry if this is the first # description of the object with this name in this desc block self.add_target_and_index(name, sig, signode) # handle code code = '\n'.join(self.content) literal: Element = nodes.literal_block(code, code) if 'lineno-start' in self.options: literal['linenos'] = True literal['highlight_args'] = { 'linenostart': self.options['lineno-start'] } literal['classes'] += self.options.get('class', []) literal['language'] = self.options.get('language') literal = container_wrapper(self, literal, self.options.get('source')) return [self.indexnode, node, literal] class CellGroupNode(TocNode): name = 'cellgroup' option_spec = { 'caption': directives.unchanged, } def add_target_and_index(self, name: str, sig: str, signode: addnodes.desc_signature) -> None: if self.options.get('caption', ''): super().add_target_and_index(name, sig, signode) def handle_signature( self, sig, signode: addnodes.desc_signature ) -> str: signode['fullname'] = fullname = sig caption = self.options.get("caption", fullname) if caption: signode['tocname'] = caption signode += addnodes.desc_name(text=caption) return fullname class TagIndex(Index): """A custom directive that creates a 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[f'obj2{self.name}'] 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 )) ret = [(k, v) for k, v in sorted(content.items())] return (ret, 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() if typ == self.name) items = sorted(items, key=lambda item: item[0]) for name, dispname, typ, docname, anchor in items: lis = content.setdefault(self.shortname, []) lis.append(( dispname, 0, docname, anchor, '', '', typ )) ret = [(k, v) for k, v in sorted(content.items())] return (ret, True) class CellIndex(CommandIndex): name = 'cell' localname = 'Internal cell reference' shortname = 'Internal cell' class PropIndex(TagIndex): """A custom directive that creates a properties matrix.""" name = 'prop' localname = 'Property Index' shortname = 'Prop' fieldname = 'props' def generate(self, docnames=None): content = {} cells = {name: (dispname, docname, anchor) for name, dispname, typ, docname, anchor, _ in self.domain.get_objects() if typ == 'cell'} props = {name: (dispname, docname, anchor) for name, dispname, typ, docname, anchor, _ in self.domain.get_objects() if typ == 'prop'} tmap: dict[str, list[str]] = {} tags: dict[str, list[str]] = self.domain.data[f'obj2{self.name}'] for name, tags in tags.items(): for tag in tags: tmap.setdefault(tag,[]) tmap[tag].append(name) for tag in sorted(tmap.keys()): test = re.match(r'^(\w+[_-])', tag) tag_prefix = test.group(1) lis = content.setdefault(tag_prefix, []) try: dispname, docname, anchor = props[tag] except KeyError: dispname = tag docname = anchor = '' lis.append(( dispname, 1, docname, anchor, '', '', docname or 'unavailable' )) objlis = tmap[tag] for objname in sorted(objlis): dispname, docname, anchor = cells[objname] lis.append(( dispname, 2, docname, anchor, '', '', docname )) ret = [(k, v) for k, v in sorted(content.items())] return (ret, True) class CommandDomain(Domain): name = 'cmd' label = 'Yosys commands' 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(type(self).name, 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 class CellDomain(CommandDomain): name = 'cell' label = 'Yosys internal cells' roles = CommandDomain.roles.copy() roles.update({ 'prop': XRefRole() }) directives = { 'def': CellNode, 'defprop': PropNode, 'source': CellSourceNode, 'group': CellGroupNode, } indices = { CellIndex, PropIndex } initial_data = { 'objects': [], # object list 'obj2prop': {}, # name -> properties 'obj2title': {}, # name -> title } def get_objects(self): for obj in self.data['objects']: yield(obj) def autoref(name, rawtext: str, text: str, lineno, inliner: Inliner, options=None, content=None): role = 'cell:ref' if text[0] == '$' else 'cmd:ref' if text.startswith("help ") and text.count(' ') == 1: _, cmd = text.split(' ', 1) text = f'{text} <{cmd}>' return inliner.interpreted(rawtext, text, role, lineno) def setup(app: Sphinx): app.add_domain(CommandDomain) app.add_domain(CellDomain) StandardDomain.initial_data['labels']['commandindex'] =\ ('cmd-cmd', '', 'Command Reference') StandardDomain.initial_data['labels']['tagindex'] =\ ('cmd-tag', '', 'Tag Index') StandardDomain.initial_data['labels']['cellindex'] =\ ('cell-cell', '', 'Internal cell reference') StandardDomain.initial_data['labels']['propindex'] =\ ('cell-prop', '', 'Property Index') StandardDomain.initial_data['anonlabels']['commandindex'] =\ ('cmd-cmd', '') StandardDomain.initial_data['anonlabels']['tagindex'] =\ ('cmd-tag', '') StandardDomain.initial_data['anonlabels']['cellindex'] =\ ('cell-cell', '') StandardDomain.initial_data['anonlabels']['propindex'] =\ ('cell-prop', '') app.add_role('autoref', autoref) return {'version': '0.2'} yosys-0.52/examples/000077500000000000000000000000001477540374200144615ustar00rootroot00000000000000yosys-0.52/examples/aiger/000077500000000000000000000000001477540374200155505ustar00rootroot00000000000000yosys-0.52/examples/aiger/.gitignore000066400000000000000000000000561477540374200175410ustar00rootroot00000000000000demo.aig demo.aim demo.aiw demo.smt2 demo.vcd yosys-0.52/examples/aiger/README000066400000000000000000000017121477540374200164310ustar00rootroot00000000000000AIGER is a format for And-Inverter Graphs (AIGs). See http://fmv.jku.at/aiger/ for details. AIGER is used in the Hardware Model Checking Competition (HWMCC), therefore all solvers competing in the competition have to support the format. The example in this directory is using super_prove as solver. Check http://downloads.bvsrc.org/super_prove/ for the lates release. (See https://bitbucket.org/sterin/super_prove_build for sources.) The "demo.sh" script in this directory expects a "super_prove" executable in the PATH. E.g. extract the release to /usr/local/libexec/super_prove and then create a /usr/local/bin/super_prove file with the following contents (and "chmod +x" that file): #!/bin/bash exec /usr/local/libexec/super_prove/bin/super_prove.sh "$@" The "demo.sh" script also expects the "z3" SMT2 solver in the PATH for converting the witness file generated by super_prove to VCD using yosys-smtbmc. See https://github.com/Z3Prover/z3 for install notes. yosys-0.52/examples/aiger/demo.sh000066400000000000000000000005341477540374200170320ustar00rootroot00000000000000#!/bin/bash set -ex yosys -p ' read_verilog -formal demo.v prep -flatten -nordff -top demo write_smt2 -wires demo.smt2 flatten demo; delete -output memory_map; opt -full techmap; opt -fast abc -fast -g AND; opt_clean write_aiger -map demo.aim demo.aig ' super_prove demo.aig > demo.aiw yosys-smtbmc --dump-vcd demo.vcd --aig demo demo.smt2 yosys-0.52/examples/aiger/demo.v000066400000000000000000000005461477540374200166700ustar00rootroot00000000000000module demo(input clk, reset, ctrl); localparam NBITS = 10; reg [NBITS-1:0] counter; initial counter[NBITS-2] = 0; initial counter[0] = 1; always @(posedge clk) begin counter <= reset ? 1 : ctrl ? counter + 1 : counter - 1; assume(counter != 0); assume(counter != 1 << (NBITS-1)); assert(counter != (1 << NBITS)-1); end endmodule yosys-0.52/examples/anlogic/000077500000000000000000000000001477540374200160755ustar00rootroot00000000000000yosys-0.52/examples/anlogic/.gitignore000066400000000000000000000000641477540374200200650ustar00rootroot00000000000000demo.bit demo_phy.area full.v *.log *.h *.tde *.svf yosys-0.52/examples/anlogic/README000066400000000000000000000005631477540374200167610ustar00rootroot00000000000000LED Blink project for Anlogic Lichee Tang board. Follow the install instructions for the Tang Dynasty IDE from given link below. https://tang.sipeed.com/en/getting-started/installing-td-ide/linux/ set TD_HOME env variable to the full path to the TD as follow. export TD_HOME= then run "bash build.sh" in this directory. yosys-0.52/examples/anlogic/build.sh000077500000000000000000000000741477540374200175340ustar00rootroot00000000000000#!/bin/bash set -ex yosys demo.ys $TD_HOME/bin/td build.tcl yosys-0.52/examples/anlogic/build.tcl000066400000000000000000000004341477540374200177010ustar00rootroot00000000000000import_device eagle_s20.db -package BG256 read_verilog full.v -top demo read_adc demo.adc optimize_rtl map_macro map pack place route report_area -io_info -file demo_phy.area bitgen -bit demo.bit -version 0X0000 -svf demo.svf -svf_comment_on -g ucode:00000000000000000000000000000000 yosys-0.52/examples/anlogic/demo.adc000066400000000000000000000001601477540374200174670ustar00rootroot00000000000000set_pin_assignment {CLK_IN} { LOCATION = K14; } ##24MHZ set_pin_assignment {R_LED} { LOCATION = R3; } ##R_LED yosys-0.52/examples/anlogic/demo.v000066400000000000000000000005741477540374200172160ustar00rootroot00000000000000module demo ( input wire CLK_IN, output wire R_LED ); parameter time1 = 30'd12_000_000; reg led_state; reg [29:0] count; always @(posedge CLK_IN)begin if(count == time1)begin count<= 30'd0; led_state <= ~led_state; end else count <= count + 1'b1; end assign R_LED = led_state; endmodule yosys-0.52/examples/anlogic/demo.ys000066400000000000000000000001011477540374200173660ustar00rootroot00000000000000read_verilog demo.v synth_anlogic -top demo write_verilog full.v yosys-0.52/examples/basys3/000077500000000000000000000000001477540374200156655ustar00rootroot00000000000000yosys-0.52/examples/basys3/README000066400000000000000000000007011477540374200165430ustar00rootroot00000000000000 A simple example design, based on the Digilent BASYS3 board =========================================================== This example uses Yosys for synthesis and Xilinx Vivado for place&route and bit-stream creation. Running Yosys: yosys run_yosys.ys Running Vivado: vivado -nolog -nojournal -mode batch -source run_vivado.tcl Programming board: vivado -nolog -nojournal -mode batch -source run_prog.tcl All of the above: bash run.sh yosys-0.52/examples/basys3/example.v000066400000000000000000000007201477540374200175060ustar00rootroot00000000000000module example(CLK, LD); input CLK; output [15:0] LD; wire clock; reg [15:0] leds; BUFG CLK_BUF (.I(CLK), .O(clock)); OBUF LD_BUF[15:0] (.I(leds), .O(LD)); parameter COUNTBITS = 26; reg [COUNTBITS-1:0] counter; always @(posedge CLK) begin counter <= counter + 1; if (counter[COUNTBITS-1]) leds <= 16'h8000 >> counter[COUNTBITS-2:COUNTBITS-5]; else leds <= 16'h0001 << counter[COUNTBITS-2:COUNTBITS-5]; end endmodule yosys-0.52/examples/basys3/example.xdc000066400000000000000000000027621477540374200200270ustar00rootroot00000000000000 set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W5 } [get_ports CLK] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U16 } [get_ports {LD[0]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN E19 } [get_ports {LD[1]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U19 } [get_ports {LD[2]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V19 } [get_ports {LD[3]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W18 } [get_ports {LD[4]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U15 } [get_ports {LD[5]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U14 } [get_ports {LD[6]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V14 } [get_ports {LD[7]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V13 } [get_ports {LD[8]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN V3 } [get_ports {LD[9]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN W3 } [get_ports {LD[10]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN U3 } [get_ports {LD[11]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN P3 } [get_ports {LD[12]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN N3 } [get_ports {LD[13]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN P1 } [get_ports {LD[14]}] set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN L1 } [get_ports {LD[15]}] create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports CLK] set_property CONFIG_VOLTAGE 3.3 [current_design] set_property CFGBVS VCCO [current_design] yosys-0.52/examples/basys3/run.sh000066400000000000000000000002251477540374200170240ustar00rootroot00000000000000#!/bin/bash yosys run_yosys.ys vivado -nolog -nojournal -mode batch -source run_vivado.tcl vivado -nolog -nojournal -mode batch -source run_prog.tcl yosys-0.52/examples/basys3/run_prog.tcl000066400000000000000000000002661477540374200202300ustar00rootroot00000000000000open_hw connect_hw_server open_hw_target [lindex [get_hw_targets] 0] set_property PROGRAM.FILE example.bit [lindex [get_hw_devices] 0] program_hw_devices [lindex [get_hw_devices] 0] yosys-0.52/examples/basys3/run_vivado.tcl000066400000000000000000000003041477540374200205420ustar00rootroot00000000000000read_xdc example.xdc read_edif example.edif link_design -part xc7a35tcpg236-1 -top example opt_design place_design route_design report_utilization report_timing write_bitstream -force example.bit yosys-0.52/examples/basys3/run_yosys.ys000066400000000000000000000001041477540374200203070ustar00rootroot00000000000000read_verilog example.v synth_xilinx -edif example.edif -top example yosys-0.52/examples/cmos/000077500000000000000000000000001477540374200154225ustar00rootroot00000000000000yosys-0.52/examples/cmos/.gitignore000066400000000000000000000000531477540374200174100ustar00rootroot00000000000000counter_tb counter_tb.vcd synth.sp synth.v yosys-0.52/examples/cmos/README000066400000000000000000000007431477540374200163060ustar00rootroot00000000000000 In this directory contains an example for generating a spice output using two different spice modes, normal analog transient simulation and event-driven digital simulation as supported by ngspice xspice sub-module. Each test bench can be run separately by either running: - testbench.sh, to start analog simulation or - testbench_digital.sh for mixed-signal digital simulation. The later case also includes pure verilog simulation using the iverilog and gtkwave for comparison. yosys-0.52/examples/cmos/cmos_cells.lib000066400000000000000000000024501477540374200202360ustar00rootroot00000000000000// 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; } ; // empty statement } } yosys-0.52/examples/cmos/cmos_cells.sp000066400000000000000000000012011477540374200201030ustar00rootroot00000000000000 .SUBCKT BUF A Y X1 A B NOT X2 B Y NOT .ENDS NOT .SUBCKT NOT A Y M1 Y A Vdd Vdd cmosp L=1u W=10u M2 Y A Vss Vss cmosn L=1u W=10u .ENDS NOT .SUBCKT NAND A B Y M1 Y A Vdd Vdd cmosp L=1u W=10u M2 Y B Vdd Vdd cmosp L=1u W=10u M3 Y A M34 Vss cmosn L=1u W=10u M4 M34 B Vss Vss cmosn L=1u W=10u .ENDS NAND .SUBCKT NOR A B Y M1 Y A M12 Vdd cmosp L=1u W=10u M2 M12 B Vdd Vdd cmosp L=1u W=10u M3 Y A Vss Vss cmosn L=1u W=10u M4 Y B Vss Vss cmosn L=1u W=10u .ENDS NOR .SUBCKT DLATCH E D Q X1 D E S NAND X2 nD E R NAND X3 S nQ Q NAND X4 Q R nQ NAND X5 D nD NOT .ENDS DLATCH .SUBCKT DFF C D Q X1 nC D t DLATCH X2 C t Q DLATCH X3 C nC NOT .ENDS DFF yosys-0.52/examples/cmos/cmos_cells.v000066400000000000000000000010351477540374200177330ustar00rootroot00000000000000 module BUF(A, Y); input A; output Y; assign Y = A; endmodule module NOT(A, Y); input A; output Y; assign Y = ~A; endmodule module NAND(A, B, Y); input A, B; output Y; assign Y = ~(A & B); endmodule module NOR(A, B, Y); input A, B; output Y; assign 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 yosys-0.52/examples/cmos/cmos_cells_digital.sp000066400000000000000000000006641477540374200216140ustar00rootroot00000000000000 .SUBCKT BUF A Y .model buffer1 d_buffer Abuf A Y buffer1 .ENDS NOT .SUBCKT NOT A Y .model not1 d_inverter Anot A Y not1 .ENDS NOT .SUBCKT NAND A B Y .model nand1 d_nand Anand [A B] Y nand1 .ENDS NAND .SUBCKT NOR A B Y .model nor1 d_nor Anand [A B] Y nor1 .ENDS NOR .SUBCKT DLATCH E D Q .model latch1 d_latch Alatch D E null null Q nQ latch1 .ENDS DLATCH .SUBCKT DFF C D Q .model dff1 d_dff Adff D C null null Q nQ dff1 .ENDS DFF yosys-0.52/examples/cmos/counter.v000066400000000000000000000003301477540374200172640ustar00rootroot00000000000000module counter (clk, rst, en, count); input clk, rst, en; output reg [2:0] count; always @(posedge clk) if (rst) count <= 3'd0; else if (en) count <= count + 3'd1; endmodule yosys-0.52/examples/cmos/counter.ys000066400000000000000000000006161477540374200174610ustar00rootroot00000000000000read_verilog counter.v read_verilog -lib cmos_cells.v synth dfflibmap -liberty cmos_cells.lib abc -liberty cmos_cells.lib opt_clean stat -liberty cmos_cells.lib # http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib # dfflibmap -liberty osu025_stdcells.lib # abc -liberty osu025_stdcells.lib;; write_verilog synth.v write_spice synth.sp yosys-0.52/examples/cmos/counter_digital.ys000066400000000000000000000006171477540374200211570ustar00rootroot00000000000000 read_verilog counter.v read_verilog -lib cmos_cells.v proc;; memory;; techmap;; dfflibmap -liberty cmos_cells.lib abc -liberty cmos_cells.lib;; # http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib # dfflibmap -liberty osu025_stdcells.lib # abc -liberty osu025_stdcells.lib;; write_verilog synth.v write_spice -neg 0s -pos 1s synth.sp yosys-0.52/examples/cmos/counter_tb.gtkw000066400000000000000000000001401477540374200204570ustar00rootroot00000000000000[dumpfile] "counter_tb.vcd" counter_tb.clk counter_tb.count[2:0] counter_tb.en counter_tb.reset yosys-0.52/examples/cmos/counter_tb.v000066400000000000000000000010561477540374200177570ustar00rootroot00000000000000module counter_tb; /* Make a reset pulse and specify dump file */ reg reset = 0; initial begin $dumpfile("counter_tb.vcd"); $dumpvars(0,counter_tb); # 0 reset = 1; # 4 reset = 0; # 36 reset = 1; # 4 reset = 0; # 6 $finish; end /* Make enable with period of 8 and 6,7 low */ reg en = 1; always begin en = 1; #6; en = 0; #2; end /* Make a regular pulsing clock. */ reg clk = 0; always #1 clk = !clk; /* UUT */ wire [2:0] count; counter c1 (clk, reset, en, count); endmodule yosys-0.52/examples/cmos/testbench.sh000066400000000000000000000001031477540374200177270ustar00rootroot00000000000000#!/bin/bash set -ex ../../yosys counter.ys ngspice testbench.sp yosys-0.52/examples/cmos/testbench.sp000066400000000000000000000011111477540374200177370ustar00rootroot00000000000000 * supply voltages .global Vss Vdd Vss Vss 0 DC 0 Vdd Vdd 0 DC 3 * simple transistor model .MODEL cmosn NMOS LEVEL=1 VT0=0.7 KP=110U GAMMA=0.4 LAMBDA=0.04 PHI=0.7 .MODEL cmosp PMOS LEVEL=1 VT0=-0.7 KP=50U GAMMA=0.57 LAMBDA=0.05 PHI=0.8 * load design and library .include cmos_cells.sp .include synth.sp * input signals Vclk clk 0 PULSE(0 3 1 0.1 0.1 0.8 2) Vrst rst 0 PULSE(0 3 0.5 0.1 0.1 2.9 40) Ven en 0 PULSE(0 3 0.5 0.1 0.1 5.9 8) Xuut clk rst en out0 out1 out2 COUNTER .tran 0.01 50 .control run plot v(clk) v(rst)+5 v(en)+10 v(out0)+20 v(out1)+25 v(out2)+30 .endc .end yosys-0.52/examples/cmos/testbench_digital.sh000066400000000000000000000004601477540374200214320ustar00rootroot00000000000000#!/bin/bash set -ex # iverlog simulation echo "Doing Verilog simulation with iverilog" iverilog -o counter_tb counter.v counter_tb.v ./counter_tb; gtkwave counter_tb.gtkw & # yosys synthesis ../../yosys counter_digital.ys # requires ngspice with xspice support enabled: ngspice testbench_digital.sp yosys-0.52/examples/cmos/testbench_digital.sp000066400000000000000000000010711477540374200214410ustar00rootroot00000000000000 * load design and library .include cmos_cells_digital.sp .include synth.sp * input signals Vclk clk 0 PULSE(0 3 1 0.1 0.1 0.8 2) Vrst rst 0 PULSE(0 3 0.5 0.1 0.1 2.9 40) Ven en 0 PULSE(0 3 0.5 0.1 0.1 5.9 8) Xuut dclk drst den dout0 dout1 dout2 counter * Bridge to digital .model adc_buff adc_bridge(in_low = 0.8 in_high=2) .model dac_buff dac_bridge(out_high = 3.5) Aad [clk rst en] [dclk drst den] adc_buff Ada [dout0 dout1 dout2] [out0 out1 out2] dac_buff .tran 0.01 50 .control run plot v(clk) v(rst)+5 v(en)+10 v(out0)+20 v(out1)+25 v(out2)+30 .endc .end yosys-0.52/examples/cxx-api/000077500000000000000000000000001477540374200160325ustar00rootroot00000000000000yosys-0.52/examples/cxx-api/demomain.cc000066400000000000000000000010301477540374200201240ustar00rootroot00000000000000// Note: Set ENABLE_LIBYOSYS=1 in Makefile or Makefile.conf to build libyosys.so // yosys-config --exec --cxx -o demomain --cxxflags --ldflags demomain.cc -lyosys -lstdc++ #include int main() { Yosys::log_streams.push_back(&std::cout); Yosys::log_error_stderr = true; Yosys::yosys_setup(); Yosys::yosys_banner(); Yosys::run_pass("read_verilog example.v"); Yosys::run_pass("synth -noabc"); Yosys::run_pass("clean -purge"); Yosys::run_pass("write_blif example.blif"); Yosys::yosys_shutdown(); return 0; } yosys-0.52/examples/cxx-api/evaldemo.cc000066400000000000000000000025061477540374200201400ustar00rootroot00000000000000/* A simple Yosys plugin. (Copy&paste from http://stackoverflow.com/questions/32093541/how-does-the-yosys-consteval-api-work) Usage example: $ cat > evaldemo.v <, Design *design) override { Module *module = design->top_module(); if (module == nullptr) log_error("No top module found!\n"); Wire *wire_a = module->wire(ID::A); Wire *wire_y = module->wire(ID::Y); if (wire_a == nullptr) log_error("No wire A found!\n"); if (wire_y == nullptr) log_error("No wire Y found!\n"); ConstEval ce(module); for (int v = 0; v < 4; v++) { ce.push(); ce.set(wire_a, Const(v, GetSize(wire_a))); SigSpec sig_y = wire_y, sig_undef; if (ce.eval(sig_y, sig_undef)) log("Eval results for A=%d: Y=%s\n", v, log_signal(sig_y)); else log("Eval failed for A=%d: Missing value for %s\n", v, log_signal(sig_undef)); ce.pop(); } } } EvalDemoPass; PRIVATE_NAMESPACE_END yosys-0.52/examples/cxx-api/scopeinfo_example.cc000066400000000000000000000105361477540374200220460ustar00rootroot00000000000000/* * 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(run_hash(a->name) * 0x2c9277b5) < mkhash_xorshift(run_hash(b->name) * 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 yosys-0.52/examples/gowin/000077500000000000000000000000001477540374200156045ustar00rootroot00000000000000yosys-0.52/examples/gowin/.gitignore000066400000000000000000000001261477540374200175730ustar00rootroot00000000000000demo.bit demo.out demo.rpt demo_syn.v demo_out.v demo_tr.html testbench testbench.vcd yosys-0.52/examples/gowin/README000066400000000000000000000007201477540374200164630ustar00rootroot00000000000000Simple test project for Gowinsemi GW2A-55K Eval Board Mini. Follow the install instructions for the Gowinsemi tools below, then run "bash run.sh" in this directory. Install instructions for gowinTool_linux ---------------------------------------- 1.) extract gowinTool_linux.zip 2.) set GOWIN_HOME env variable to the full path to the gowinTool_linux directory 3.) edit gowinTool_linux/bin/gwlicense.ini. Set lic="..." to the full path to the license file. yosys-0.52/examples/gowin/demo.cst000066400000000000000000000003151477540374200172420ustar00rootroot00000000000000IO_LOC "clk" 35; //IO_LOC "rst_n" 77; IO_LOC "leds[0]" 79; IO_LOC "leds[1]" 80; IO_LOC "leds[2]" 81; IO_LOC "leds[3]" 82; IO_LOC "leds[4]" 83; IO_LOC "leds[5]" 84; IO_LOC "leds[6]" 85; IO_LOC "leds[7]" 86;yosys-0.52/examples/gowin/demo.sdc000066400000000000000000000001051477540374200172170ustar00rootroot00000000000000create_clock -name clk -period 20 -waveform {0 10} [get_ports {clk}] yosys-0.52/examples/gowin/demo.v000066400000000000000000000003421477540374200167160ustar00rootroot00000000000000module demo ( input clk, output [15:0] leds, output unused ); localparam PRESCALE = 20; reg [PRESCALE+3:0] counter = 0; always @(posedge clk) counter <= counter + 1; assign leds = 1 << counter[PRESCALE +: 4]; endmodule yosys-0.52/examples/gowin/device.cfg000066400000000000000000000007001477540374200175210ustar00rootroot00000000000000set JTAG regular_io = false set SSPI regular_io = false set MSPI regular_io = false set READY regular_io = false set DONE regular_io = false set RECONFIG_N regular_io = false set MODE regular_io = false set CRC_check = true set compress = false set encryption = false set security_bit_enable = true set bsram_init_fuse_print = true set download_speed = 250/100 set spi_flash_address = 0x00FFF000 set format = txt set background_programming = false yosys-0.52/examples/gowin/pnr.cfg000066400000000000000000000000601477540374200170600ustar00rootroot00000000000000-sdf -oc -ibs -posp -o -warning_all -tt -timing yosys-0.52/examples/gowin/run.sh000066400000000000000000000006771477540374200167560ustar00rootroot00000000000000#!/bin/bash set -ex yosys -p "synth_gowin -top demo -vout demo_syn.v" demo.v $GOWIN_HOME/bin/gowin -d demo_syn.v -cst demo.cst -sdc demo.sdc -p GW1NR-9-QFN88-6 -pn GW1NR-LV9QN88C6/I5 -cfg device.cfg -bit -tr -ph -timing -gpa -rpt -warning_all # post place&route simulation (icarus verilog) if false; then iverilog -D POST_IMPL -o testbench -s testbench testbench.v \ demo_out.v $(yosys-config --datdir/gowin/cells_sim.v) vvp -N testbench fi yosys-0.52/examples/gowin/run.tcl000066400000000000000000000004071477540374200171150ustar00rootroot00000000000000# gw_sh run.tcl exec yosys -p "synth_gowin -top demo -vout demo_syn.v" demo.v add_file -cst demo.cst add_file -sdc demo.sdc add_file -vm demo_syn.v add_file -cfg device.cfg set_option -device GW1NR-9-QFN88-6 set_option -pn GW1NR-LV9QN88C6/I5 run_pnr -opt pnr.cfg yosys-0.52/examples/gowin/testbench.v000066400000000000000000000012651477540374200177560ustar00rootroot00000000000000module testbench; reg clk; initial begin #5 clk = 0; forever #5 clk = ~clk; end wire [15:0] leds; initial begin // $dumpfile("testbench.vcd"); // $dumpvars(0, testbench); $monitor("%b", leds); end demo uut ( .clk (clk ), `ifdef POST_IMPL .\leds[0] (leds[0]), .\leds[1] (leds[1]), .\leds[2] (leds[2]), .\leds[3] (leds[3]), .\leds[4] (leds[4]), .\leds[5] (leds[5]), .\leds[6] (leds[6]), .\leds[7] (leds[7]), .\leds[8] (leds[8]), .\leds[9] (leds[9]), .\leds[10] (leds[10]), .\leds[11] (leds[11]), .\leds[12] (leds[12]), .\leds[13] (leds[13]), .\leds[14] (leds[14]), .\leds[15] (leds[15]) `else .leds(leds) `endif ); endmodule yosys-0.52/examples/igloo2/000077500000000000000000000000001477540374200156545ustar00rootroot00000000000000yosys-0.52/examples/igloo2/.gitignore000066400000000000000000000000541477540374200176430ustar00rootroot00000000000000/netlist.edn /netlist.vm /example.stp /proj yosys-0.52/examples/igloo2/example.pdc000066400000000000000000000015141477540374200200000ustar00rootroot00000000000000# Add placement constraints here set_io clk -pinname H16 -fixed yes -DIRECTION INPUT set_io SW1 -pinname H12 -fixed yes -DIRECTION INPUT set_io SW2 -pinname H13 -fixed yes -DIRECTION INPUT set_io LED1 -pinname J16 -fixed yes -DIRECTION OUTPUT set_io LED2 -pinname M16 -fixed yes -DIRECTION OUTPUT set_io LED3 -pinname K16 -fixed yes -DIRECTION OUTPUT set_io LED4 -pinname N16 -fixed yes -DIRECTION OUTPUT set_io AA -pinname L12 -fixed yes -DIRECTION OUTPUT set_io AB -pinname L13 -fixed yes -DIRECTION OUTPUT set_io AC -pinname M13 -fixed yes -DIRECTION OUTPUT set_io AD -pinname N15 -fixed yes -DIRECTION OUTPUT set_io AE -pinname L11 -fixed yes -DIRECTION OUTPUT set_io AF -pinname L14 -fixed yes -DIRECTION OUTPUT set_io AG -pinname N14 -fixed yes -DIRECTION OUTPUT set_io CA -pinname M15 -fixed yes -DIRECTION OUTPUT yosys-0.52/examples/igloo2/example.sdc000066400000000000000000000001441477540374200200010ustar00rootroot00000000000000# Add timing constraints here create_clock -period 10.000 -waveform {0.000 5.000} [get_ports {clk}] yosys-0.52/examples/igloo2/example.v000066400000000000000000000025441477540374200175030ustar00rootroot00000000000000module example ( input clk, input SW1, input SW2, output LED1, output LED2, output LED3, output LED4, output AA, AB, AC, AD, output AE, AF, AG, CA ); localparam BITS = 8; localparam LOG2DELAY = 22; reg [BITS+LOG2DELAY-1:0] counter = 0; reg [BITS-1:0] outcnt; always @(posedge clk) begin counter <= counter + SW1 + SW2 + 1; outcnt <= counter >> LOG2DELAY; end assign {LED1, LED2, LED3, LED4} = outcnt ^ (outcnt >> 1); // assign CA = counter[10]; // seg7enc seg7encinst ( // .seg({AA, AB, AC, AD, AE, AF, AG}), // .dat(CA ? outcnt[3:0] : outcnt[7:4]) // ); assign {AA, AB, AC, AD, AE, AF, AG} = ~(7'b 100_0000 >> outcnt[6:4]); assign CA = outcnt[7]; endmodule module seg7enc ( input [3:0] dat, output [6:0] seg ); reg [6:0] seg_inv; always @* begin seg_inv = 0; case (dat) 4'h0: seg_inv = 7'b 0111111; 4'h1: seg_inv = 7'b 0000110; 4'h2: seg_inv = 7'b 1011011; 4'h3: seg_inv = 7'b 1001111; 4'h4: seg_inv = 7'b 1100110; 4'h5: seg_inv = 7'b 1101101; 4'h6: seg_inv = 7'b 1111101; 4'h7: seg_inv = 7'b 0000111; 4'h8: seg_inv = 7'b 1111111; 4'h9: seg_inv = 7'b 1101111; 4'hA: seg_inv = 7'b 1110111; 4'hB: seg_inv = 7'b 1111100; 4'hC: seg_inv = 7'b 0111001; 4'hD: seg_inv = 7'b 1011110; 4'hE: seg_inv = 7'b 1111001; 4'hF: seg_inv = 7'b 1110001; endcase end assign seg = ~seg_inv; endmodule yosys-0.52/examples/igloo2/libero.tcl000066400000000000000000000022611477540374200176350ustar00rootroot00000000000000# Run with "libero SCRIPT:libero.tcl" file delete -force proj new_project \ -name example \ -location proj \ -block_mode 0 \ -hdl "VERILOG" \ -family IGLOO2 \ -die PA4MGL2500 \ -package vf256 \ -speed -1 import_files -hdl_source {netlist.vm} import_files -sdc {example.sdc} import_files -io_pdc {example.pdc} build_design_hierarchy set_option -synth 0 organize_tool_files -tool PLACEROUTE \ -file {proj/constraint/example.sdc} \ -file {proj/constraint/io/example.pdc} \ -input_type constraint organize_tool_files -tool VERIFYTIMING \ -file {proj/constraint/example.sdc} \ -input_type constraint configure_tool -name PLACEROUTE \ -params TDPR:true \ -params PDPR:false \ -params EFFORT_LEVEL:false \ -params REPAIR_MIN_DELAY:false puts "" puts "**> COMPILE" run_tool -name {COMPILE} puts "<** COMPILE" puts "" puts "**> PLACEROUTE" run_tool -name {PLACEROUTE} puts "<** PLACEROUTE" puts "" puts "**> VERIFYTIMING" run_tool -name {VERIFYTIMING} puts "<** VERIFYTIMING" puts "" puts "**> BITSTREAM" export_bitstream_file -trusted_facility_file 1 -trusted_facility_file_components {FABRIC} puts "<** BITSTREAM" puts "" exit 0 yosys-0.52/examples/igloo2/runme.sh000066400000000000000000000004171477540374200173400ustar00rootroot00000000000000#!/bin/bash set -ex yosys -p 'synth_sf2 -top example -edif netlist.edn -vlog netlist.vm' example.v export LM_LICENSE_FILE=${LM_LICENSE_FILE:-1702@localhost} /opt/microsemi/Libero_SoC_v12.0/Libero/bin/libero SCRIPT:libero.tcl cp proj/designer/example/export/example.stp . yosys-0.52/examples/intel/000077500000000000000000000000001477540374200155745ustar00rootroot00000000000000yosys-0.52/examples/intel/DE2i-150/000077500000000000000000000000001477540374200166225ustar00rootroot00000000000000yosys-0.52/examples/intel/DE2i-150/quartus_compile/000077500000000000000000000000001477540374200220365ustar00rootroot00000000000000yosys-0.52/examples/intel/DE2i-150/quartus_compile/de2i.qpf000066400000000000000000000001001477540374200233600ustar00rootroot00000000000000QUARTUS_VERSION = "16.1" # Revisions PROJECT_REVISION = "de2i" yosys-0.52/examples/intel/DE2i-150/quartus_compile/de2i.qsf000066400000000000000000001642401477540374200234030ustar00rootroot00000000000000set_global_assignment -name FAMILY "Cyclone IV GX" set_global_assignment -name DEVICE EP4CGX150DF31C7 set_global_assignment -name TOP_LEVEL_ENTITY "top" set_global_assignment -name DEVICE_FILTER_PACKAGE FBGA set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to CLOCK2_50 set_instance_assignment -name IO_STANDARD "2.5 V" -to CLOCK3_50 set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to CLOCK_50 #============================================================ # DRAM #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[8] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[9] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[10] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[11] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[12] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_BA[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_BA[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_CAS_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_CKE set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_CLK set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_CS_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[8] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[9] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[10] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[11] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[12] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[13] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[14] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[15] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[16] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[17] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[18] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[19] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[20] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[21] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[22] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[23] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[24] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[25] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[26] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[27] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[28] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[29] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[30] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[31] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQM[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQM[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQM[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQM[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_RAS_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_WE_N #============================================================ # EEP #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to EEP_I2C_SCLK set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to EEP_I2C_SDAT #============================================================ # ENET #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_GTX_CLK set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_INT_N set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_LINK100 set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_MDC set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_MDIO set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RST_N set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_CLK set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_COL set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_CRS set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DATA[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DATA[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DATA[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DATA[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DV set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_ER set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_CLK set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_DATA[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_DATA[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_DATA[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_DATA[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_EN set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_ER #============================================================ # FAN #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to FAN_CTRL #============================================================ # FLASH #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_RESET_N #============================================================ # FS #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[8] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[9] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[10] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[11] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[12] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[13] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[14] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[15] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[16] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[17] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[18] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[19] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[20] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[21] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[22] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[23] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[24] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[25] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[26] #============================================================ # FL #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_CE_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_OE_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_RY set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_WE_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_WP_N #============================================================ # FS #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[8] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[9] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[10] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[11] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[12] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[13] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[14] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[15] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[16] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[17] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[18] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[19] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[20] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[21] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[22] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[23] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[24] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[25] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[26] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[27] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[28] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[29] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[30] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[31] #============================================================ # GPIO #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[8] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[9] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[10] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[11] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[12] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[13] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[14] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[15] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[16] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[17] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[18] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[19] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[20] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[21] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[22] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[23] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[24] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[25] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[26] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[27] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[28] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[29] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[30] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[31] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[32] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[33] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[34] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[35] #============================================================ # G #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to G_SENSOR_INT1 set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to G_SENSOR_SCLK set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to G_SENSOR_SDAT #============================================================ # HEX0 #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[6] #============================================================ # HEX1 #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[6] #============================================================ # HEX2 #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[6] #============================================================ # HEX3 #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[6] #============================================================ # HEX4 #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[6] #============================================================ # HEX5 #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[6] #============================================================ # HEX6 #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[6] #============================================================ # HEX7 #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[6] #============================================================ # HSMC #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN0 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN_N1 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN_N2 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN_P1 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN_P2 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT0 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT_N1 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT_N2 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT_P1 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT_P2 set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_D[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_D[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_D[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_D[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_I2C_SCLK set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_I2C_SDAT set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[6] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[7] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[8] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[9] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[10] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[11] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[12] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[13] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[14] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[15] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[16] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[6] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[7] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[8] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[9] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[10] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[11] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[12] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[13] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[14] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[15] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[16] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[6] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[7] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[8] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[9] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[10] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[11] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[12] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[13] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[14] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[15] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[16] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[6] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[7] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[8] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[9] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[10] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[11] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[12] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[13] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[14] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[15] set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[16] #============================================================ # I2C #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to I2C_SCLK set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to I2C_SDAT #============================================================ # IRDA #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to IRDA_RXD #============================================================ # KEY #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to KEY[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to KEY[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to KEY[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to KEY[3] #============================================================ # LCD #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_EN set_instance_assignment -name IO_STANDARD "2.5 V" -to LCD_ON set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_RS set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_RW #============================================================ # LEDG #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[6] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[7] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[8] #============================================================ # LEDR #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[6] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[7] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[8] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[9] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[10] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[11] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[12] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[13] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[14] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[15] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[16] set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[17] #============================================================ # PCIE #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to PCIE_PERST_N set_instance_assignment -name IO_STANDARD HCSL -to PCIE_REFCLK_P set_instance_assignment -name IO_STANDARD "1.5-V PCML" -to PCIE_RX_P[0] set_instance_assignment -name IO_STANDARD "1.5-V PCML" -to PCIE_RX_P[1] set_instance_assignment -name IO_STANDARD "1.5-V PCML" -to PCIE_TX_P[0] set_instance_assignment -name IO_STANDARD "1.5-V PCML" -to PCIE_TX_P[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to PCIE_WAKE_N #============================================================ # SD #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_CLK set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_CMD set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_WP_N #============================================================ # SMA #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SMA_CLKIN set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SMA_CLKOUT #============================================================ # SSRAM0 #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM0_CE_N #============================================================ # SSRAM1 #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM1_CE_N #============================================================ # SSRAM #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_ADSC_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_ADSP_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_ADV_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_BE[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_BE[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_BE[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_BE[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_CLK set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_GW_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_OE_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_WE_N #============================================================ # SW #============================================================ set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[0] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[1] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[2] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[3] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[4] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[5] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[6] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[7] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[8] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[9] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[10] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[11] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[12] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[13] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[14] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[15] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[16] set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[17] #============================================================ # TD #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_CLK27 set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_HS set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_RESET_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_VS #============================================================ # UART #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_CTS set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_RTS set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_RXD set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_TXD #============================================================ # VGA #============================================================ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_BLANK_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_CLK set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_HS set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[2] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_SYNC_N set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_VS #============================================================ # End of pin assignments by Terasic System Builder #============================================================ set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO" set_location_assignment PIN_A15 -to CLOCK2_50 set_location_assignment PIN_V11 -to CLOCK3_50 set_location_assignment PIN_AJ16 -to CLOCK_50 set_location_assignment PIN_AG7 -to DRAM_ADDR[0] set_location_assignment PIN_AJ7 -to DRAM_ADDR[1] set_location_assignment PIN_AG8 -to DRAM_ADDR[2] set_location_assignment PIN_AH8 -to DRAM_ADDR[3] set_location_assignment PIN_AE16 -to DRAM_ADDR[4] set_location_assignment PIN_AF16 -to DRAM_ADDR[5] set_location_assignment PIN_AE14 -to DRAM_ADDR[6] set_location_assignment PIN_AE15 -to DRAM_ADDR[7] set_location_assignment PIN_AE13 -to DRAM_ADDR[8] set_location_assignment PIN_AE12 -to DRAM_ADDR[9] set_location_assignment PIN_AH6 -to DRAM_ADDR[10] set_location_assignment PIN_AE11 -to DRAM_ADDR[11] set_location_assignment PIN_AE10 -to DRAM_ADDR[12] set_location_assignment PIN_AH5 -to DRAM_BA[0] set_location_assignment PIN_AG6 -to DRAM_BA[1] set_location_assignment PIN_AJ4 -to DRAM_CAS_N set_location_assignment PIN_AD6 -to DRAM_CKE set_location_assignment PIN_AE6 -to DRAM_CLK set_location_assignment PIN_AG5 -to DRAM_CS_N set_location_assignment PIN_AD10 -to DRAM_DQ[0] set_location_assignment PIN_AD9 -to DRAM_DQ[1] set_location_assignment PIN_AE9 -to DRAM_DQ[2] set_location_assignment PIN_AE8 -to DRAM_DQ[3] set_location_assignment PIN_AE7 -to DRAM_DQ[4] set_location_assignment PIN_AF7 -to DRAM_DQ[5] set_location_assignment PIN_AF6 -to DRAM_DQ[6] set_location_assignment PIN_AF9 -to DRAM_DQ[7] set_location_assignment PIN_AB13 -to DRAM_DQ[8] set_location_assignment PIN_AF13 -to DRAM_DQ[9] set_location_assignment PIN_AF12 -to DRAM_DQ[10] set_location_assignment PIN_AG9 -to DRAM_DQ[11] set_location_assignment PIN_AA13 -to DRAM_DQ[12] set_location_assignment PIN_AB11 -to DRAM_DQ[13] set_location_assignment PIN_AA12 -to DRAM_DQ[14] set_location_assignment PIN_AA15 -to DRAM_DQ[15] set_location_assignment PIN_AH11 -to DRAM_DQ[16] set_location_assignment PIN_AG11 -to DRAM_DQ[17] set_location_assignment PIN_AH12 -to DRAM_DQ[18] set_location_assignment PIN_AG12 -to DRAM_DQ[19] set_location_assignment PIN_AH13 -to DRAM_DQ[20] set_location_assignment PIN_AG13 -to DRAM_DQ[21] set_location_assignment PIN_AG14 -to DRAM_DQ[22] set_location_assignment PIN_AH14 -to DRAM_DQ[23] set_location_assignment PIN_AH9 -to DRAM_DQ[24] set_location_assignment PIN_AK8 -to DRAM_DQ[25] set_location_assignment PIN_AG10 -to DRAM_DQ[26] set_location_assignment PIN_AK7 -to DRAM_DQ[27] set_location_assignment PIN_AH7 -to DRAM_DQ[28] set_location_assignment PIN_AK6 -to DRAM_DQ[29] set_location_assignment PIN_AJ6 -to DRAM_DQ[30] set_location_assignment PIN_AK5 -to DRAM_DQ[31] set_location_assignment PIN_AF10 -to DRAM_DQM[0] set_location_assignment PIN_AB14 -to DRAM_DQM[1] set_location_assignment PIN_AH15 -to DRAM_DQM[2] set_location_assignment PIN_AH10 -to DRAM_DQM[3] set_location_assignment PIN_AK4 -to DRAM_RAS_N set_location_assignment PIN_AK3 -to DRAM_WE_N set_location_assignment PIN_AG27 -to EEP_I2C_SCLK set_location_assignment PIN_AG25 -to EEP_I2C_SDAT set_location_assignment PIN_A12 -to ENET_GTX_CLK set_location_assignment PIN_E16 -to ENET_INT_N set_location_assignment PIN_F5 -to ENET_LINK100 set_location_assignment PIN_C16 -to ENET_MDC set_location_assignment PIN_C15 -to ENET_MDIO set_location_assignment PIN_C14 -to ENET_RST_N set_location_assignment PIN_L15 -to ENET_RX_CLK set_location_assignment PIN_G15 -to ENET_RX_COL set_location_assignment PIN_D6 -to ENET_RX_CRS set_location_assignment PIN_F15 -to ENET_RX_DATA[0] set_location_assignment PIN_E13 -to ENET_RX_DATA[1] set_location_assignment PIN_A5 -to ENET_RX_DATA[2] set_location_assignment PIN_B7 -to ENET_RX_DATA[3] set_location_assignment PIN_A8 -to ENET_RX_DV set_location_assignment PIN_D11 -to ENET_RX_ER set_location_assignment PIN_F13 -to ENET_TX_CLK set_location_assignment PIN_B12 -to ENET_TX_DATA[0] set_location_assignment PIN_E7 -to ENET_TX_DATA[1] set_location_assignment PIN_C13 -to ENET_TX_DATA[2] set_location_assignment PIN_D15 -to ENET_TX_DATA[3] set_location_assignment PIN_D14 -to ENET_TX_EN set_location_assignment PIN_D13 -to ENET_TX_ER set_location_assignment PIN_AF28 -to FAN_CTRL set_location_assignment PIN_AG18 -to FL_RESET_N set_location_assignment PIN_AB22 -to FS_ADDR[1] set_location_assignment PIN_AH19 -to FS_ADDR[2] set_location_assignment PIN_AK19 -to FS_ADDR[3] set_location_assignment PIN_AJ18 -to FS_ADDR[4] set_location_assignment PIN_AA18 -to FS_ADDR[5] set_location_assignment PIN_AH18 -to FS_ADDR[6] set_location_assignment PIN_AK17 -to FS_ADDR[7] set_location_assignment PIN_Y20 -to FS_ADDR[8] set_location_assignment PIN_AK21 -to FS_ADDR[9] set_location_assignment PIN_AH21 -to FS_ADDR[10] set_location_assignment PIN_AG21 -to FS_ADDR[11] set_location_assignment PIN_AG22 -to FS_ADDR[12] set_location_assignment PIN_AD22 -to FS_ADDR[13] set_location_assignment PIN_AE24 -to FS_ADDR[14] set_location_assignment PIN_AD23 -to FS_ADDR[15] set_location_assignment PIN_AB21 -to FS_ADDR[16] set_location_assignment PIN_AH17 -to FS_ADDR[17] set_location_assignment PIN_AE17 -to FS_ADDR[18] set_location_assignment PIN_AG20 -to FS_ADDR[19] set_location_assignment PIN_AK20 -to FS_ADDR[20] set_location_assignment PIN_AE19 -to FS_ADDR[21] set_location_assignment PIN_AA16 -to FS_ADDR[22] set_location_assignment PIN_AF15 -to FS_ADDR[23] set_location_assignment PIN_AG15 -to FS_ADDR[24] set_location_assignment PIN_Y17 -to FS_ADDR[25] set_location_assignment PIN_AB16 -to FS_ADDR[26] set_location_assignment PIN_AG19 -to FL_CE_N set_location_assignment PIN_AJ19 -to FL_OE_N set_location_assignment PIN_AF19 -to FL_RY set_location_assignment PIN_AG17 -to FL_WE_N set_location_assignment PIN_AK18 -to FL_WP_N set_location_assignment PIN_AK29 -to FS_DQ[0] set_location_assignment PIN_AE23 -to FS_DQ[1] set_location_assignment PIN_AH24 -to FS_DQ[2] set_location_assignment PIN_AH23 -to FS_DQ[3] set_location_assignment PIN_AA21 -to FS_DQ[4] set_location_assignment PIN_AE20 -to FS_DQ[5] set_location_assignment PIN_Y19 -to FS_DQ[6] set_location_assignment PIN_AA17 -to FS_DQ[7] set_location_assignment PIN_AB17 -to FS_DQ[8] set_location_assignment PIN_Y18 -to FS_DQ[9] set_location_assignment PIN_AA20 -to FS_DQ[10] set_location_assignment PIN_AE21 -to FS_DQ[11] set_location_assignment PIN_AH22 -to FS_DQ[12] set_location_assignment PIN_AJ24 -to FS_DQ[13] set_location_assignment PIN_AE22 -to FS_DQ[14] set_location_assignment PIN_AK28 -to FS_DQ[15] set_location_assignment PIN_AK9 -to FS_DQ[16] set_location_assignment PIN_AJ10 -to FS_DQ[17] set_location_assignment PIN_AK11 -to FS_DQ[18] set_location_assignment PIN_AK12 -to FS_DQ[19] set_location_assignment PIN_AJ13 -to FS_DQ[20] set_location_assignment PIN_AK15 -to FS_DQ[21] set_location_assignment PIN_AC16 -to FS_DQ[22] set_location_assignment PIN_AH16 -to FS_DQ[23] set_location_assignment PIN_AG16 -to FS_DQ[24] set_location_assignment PIN_AD16 -to FS_DQ[25] set_location_assignment PIN_AJ15 -to FS_DQ[26] set_location_assignment PIN_AK14 -to FS_DQ[27] set_location_assignment PIN_AK13 -to FS_DQ[28] set_location_assignment PIN_AJ12 -to FS_DQ[29] set_location_assignment PIN_AK10 -to FS_DQ[30] set_location_assignment PIN_AJ9 -to FS_DQ[31] set_location_assignment PIN_G16 -to GPIO[0] set_location_assignment PIN_F17 -to GPIO[1] set_location_assignment PIN_D18 -to GPIO[2] set_location_assignment PIN_F18 -to GPIO[3] set_location_assignment PIN_D19 -to GPIO[4] set_location_assignment PIN_K21 -to GPIO[5] set_location_assignment PIN_F19 -to GPIO[6] set_location_assignment PIN_K22 -to GPIO[7] set_location_assignment PIN_B21 -to GPIO[8] set_location_assignment PIN_C21 -to GPIO[9] set_location_assignment PIN_D22 -to GPIO[10] set_location_assignment PIN_D21 -to GPIO[11] set_location_assignment PIN_D23 -to GPIO[12] set_location_assignment PIN_D24 -to GPIO[13] set_location_assignment PIN_B28 -to GPIO[14] set_location_assignment PIN_C25 -to GPIO[15] set_location_assignment PIN_C26 -to GPIO[16] set_location_assignment PIN_D28 -to GPIO[17] set_location_assignment PIN_D25 -to GPIO[18] set_location_assignment PIN_F20 -to GPIO[19] set_location_assignment PIN_E21 -to GPIO[20] set_location_assignment PIN_F23 -to GPIO[21] set_location_assignment PIN_G20 -to GPIO[22] set_location_assignment PIN_F22 -to GPIO[23] set_location_assignment PIN_G22 -to GPIO[24] set_location_assignment PIN_G24 -to GPIO[25] set_location_assignment PIN_G23 -to GPIO[26] set_location_assignment PIN_A25 -to GPIO[27] set_location_assignment PIN_A26 -to GPIO[28] set_location_assignment PIN_A19 -to GPIO[29] set_location_assignment PIN_A28 -to GPIO[30] set_location_assignment PIN_A27 -to GPIO[31] set_location_assignment PIN_B30 -to GPIO[32] set_location_assignment PIN_AG28 -to GPIO[33] set_location_assignment PIN_AG26 -to GPIO[34] set_location_assignment PIN_Y21 -to GPIO[35] set_location_assignment PIN_AC30 -to G_SENSOR_INT1 set_location_assignment PIN_AK27 -to G_SENSOR_SCLK set_location_assignment PIN_AK26 -to G_SENSOR_SDAT set_location_assignment PIN_E15 -to HEX0[0] set_location_assignment PIN_E12 -to HEX0[1] set_location_assignment PIN_G11 -to HEX0[2] set_location_assignment PIN_F11 -to HEX0[3] set_location_assignment PIN_F16 -to HEX0[4] set_location_assignment PIN_D16 -to HEX0[5] set_location_assignment PIN_F14 -to HEX0[6] set_location_assignment PIN_G14 -to HEX1[0] set_location_assignment PIN_B13 -to HEX1[1] set_location_assignment PIN_G13 -to HEX1[2] set_location_assignment PIN_F12 -to HEX1[3] set_location_assignment PIN_G12 -to HEX1[4] set_location_assignment PIN_J9 -to HEX1[5] set_location_assignment PIN_G10 -to HEX1[6] set_location_assignment PIN_G8 -to HEX2[0] set_location_assignment PIN_G7 -to HEX2[1] set_location_assignment PIN_F7 -to HEX2[2] set_location_assignment PIN_AG30 -to HEX2[3] set_location_assignment PIN_F6 -to HEX2[4] set_location_assignment PIN_F4 -to HEX2[5] set_location_assignment PIN_F10 -to HEX2[6] set_location_assignment PIN_D10 -to HEX3[0] set_location_assignment PIN_D7 -to HEX3[1] set_location_assignment PIN_E6 -to HEX3[2] set_location_assignment PIN_E4 -to HEX3[3] set_location_assignment PIN_E3 -to HEX3[4] set_location_assignment PIN_D5 -to HEX3[5] set_location_assignment PIN_D4 -to HEX3[6] set_location_assignment PIN_A14 -to HEX4[0] set_location_assignment PIN_A13 -to HEX4[1] set_location_assignment PIN_C7 -to HEX4[2] set_location_assignment PIN_C6 -to HEX4[3] set_location_assignment PIN_C5 -to HEX4[4] set_location_assignment PIN_C4 -to HEX4[5] set_location_assignment PIN_C3 -to HEX4[6] set_location_assignment PIN_D3 -to HEX5[0] set_location_assignment PIN_A10 -to HEX5[1] set_location_assignment PIN_A9 -to HEX5[2] set_location_assignment PIN_A7 -to HEX5[3] set_location_assignment PIN_A6 -to HEX5[4] set_location_assignment PIN_A11 -to HEX5[5] set_location_assignment PIN_B6 -to HEX5[6] set_location_assignment PIN_B9 -to HEX6[0] set_location_assignment PIN_B10 -to HEX6[1] set_location_assignment PIN_C8 -to HEX6[2] set_location_assignment PIN_C9 -to HEX6[3] set_location_assignment PIN_D8 -to HEX6[4] set_location_assignment PIN_D9 -to HEX6[5] set_location_assignment PIN_E9 -to HEX6[6] set_location_assignment PIN_E10 -to HEX7[0] set_location_assignment PIN_F8 -to HEX7[1] set_location_assignment PIN_F9 -to HEX7[2] set_location_assignment PIN_C10 -to HEX7[3] set_location_assignment PIN_C11 -to HEX7[4] set_location_assignment PIN_C12 -to HEX7[5] set_location_assignment PIN_D12 -to HEX7[6] set_location_assignment PIN_K15 -to HSMC_CLKIN0 set_location_assignment PIN_V30 -to HSMC_CLKIN_N1 set_location_assignment PIN_T30 -to HSMC_CLKIN_N2 set_location_assignment PIN_V29 -to HSMC_CLKIN_P1 set_location_assignment PIN_T29 -to HSMC_CLKIN_P2 set_location_assignment PIN_G6 -to HSMC_CLKOUT0 set_location_assignment PIN_AB28 -to HSMC_CLKOUT_N1 set_location_assignment PIN_Y28 -to HSMC_CLKOUT_N2 set_location_assignment PIN_AB27 -to HSMC_CLKOUT_P1 set_location_assignment PIN_AA28 -to HSMC_CLKOUT_P2 set_location_assignment PIN_AC25 -to HSMC_D[0] set_location_assignment PIN_E27 -to HSMC_D[1] set_location_assignment PIN_AB26 -to HSMC_D[2] set_location_assignment PIN_E28 -to HSMC_D[3] set_location_assignment PIN_AD26 -to HSMC_I2C_SCLK set_location_assignment PIN_AD25 -to HSMC_I2C_SDAT set_location_assignment PIN_G27 -to HSMC_RX_D_N[0] set_location_assignment PIN_G29 -to HSMC_RX_D_N[1] set_location_assignment PIN_H27 -to HSMC_RX_D_N[2] set_location_assignment PIN_K29 -to HSMC_RX_D_N[3] set_location_assignment PIN_L28 -to HSMC_RX_D_N[4] set_location_assignment PIN_M28 -to HSMC_RX_D_N[5] set_location_assignment PIN_N30 -to HSMC_RX_D_N[6] set_location_assignment PIN_P28 -to HSMC_RX_D_N[7] set_location_assignment PIN_R28 -to HSMC_RX_D_N[8] set_location_assignment PIN_U28 -to HSMC_RX_D_N[9] set_location_assignment PIN_W28 -to HSMC_RX_D_N[10] set_location_assignment PIN_W30 -to HSMC_RX_D_N[11] set_location_assignment PIN_M30 -to HSMC_RX_D_N[12] set_location_assignment PIN_Y27 -to HSMC_RX_D_N[13] set_location_assignment PIN_AA29 -to HSMC_RX_D_N[14] set_location_assignment PIN_AD28 -to HSMC_RX_D_N[15] set_location_assignment PIN_AE28 -to HSMC_RX_D_N[16] set_location_assignment PIN_G26 -to HSMC_RX_D_P[0] set_location_assignment PIN_G28 -to HSMC_RX_D_P[1] set_location_assignment PIN_J27 -to HSMC_RX_D_P[2] set_location_assignment PIN_K28 -to HSMC_RX_D_P[3] set_location_assignment PIN_L27 -to HSMC_RX_D_P[4] set_location_assignment PIN_M27 -to HSMC_RX_D_P[5] set_location_assignment PIN_N29 -to HSMC_RX_D_P[6] set_location_assignment PIN_P27 -to HSMC_RX_D_P[7] set_location_assignment PIN_R27 -to HSMC_RX_D_P[8] set_location_assignment PIN_U27 -to HSMC_RX_D_P[9] set_location_assignment PIN_W27 -to HSMC_RX_D_P[10] set_location_assignment PIN_W29 -to HSMC_RX_D_P[11] set_location_assignment PIN_M29 -to HSMC_RX_D_P[12] set_location_assignment PIN_AA27 -to HSMC_RX_D_P[13] set_location_assignment PIN_AB29 -to HSMC_RX_D_P[14] set_location_assignment PIN_AD27 -to HSMC_RX_D_P[15] set_location_assignment PIN_AE27 -to HSMC_RX_D_P[16] set_location_assignment PIN_H28 -to HSMC_TX_D_N[0] set_location_assignment PIN_F29 -to HSMC_TX_D_N[1] set_location_assignment PIN_D30 -to HSMC_TX_D_N[2] set_location_assignment PIN_E30 -to HSMC_TX_D_N[3] set_location_assignment PIN_G30 -to HSMC_TX_D_N[4] set_location_assignment PIN_J30 -to HSMC_TX_D_N[5] set_location_assignment PIN_K27 -to HSMC_TX_D_N[6] set_location_assignment PIN_K30 -to HSMC_TX_D_N[7] set_location_assignment PIN_T25 -to HSMC_TX_D_N[8] set_location_assignment PIN_N28 -to HSMC_TX_D_N[9] set_location_assignment PIN_V26 -to HSMC_TX_D_N[10] set_location_assignment PIN_Y30 -to HSMC_TX_D_N[11] set_location_assignment PIN_AC28 -to HSMC_TX_D_N[12] set_location_assignment PIN_AD30 -to HSMC_TX_D_N[13] set_location_assignment PIN_AE30 -to HSMC_TX_D_N[14] set_location_assignment PIN_AH30 -to HSMC_TX_D_N[15] set_location_assignment PIN_AG29 -to HSMC_TX_D_N[16] set_location_assignment PIN_J28 -to HSMC_TX_D_P[0] set_location_assignment PIN_F28 -to HSMC_TX_D_P[1] set_location_assignment PIN_D29 -to HSMC_TX_D_P[2] set_location_assignment PIN_F30 -to HSMC_TX_D_P[3] set_location_assignment PIN_H30 -to HSMC_TX_D_P[4] set_location_assignment PIN_J29 -to HSMC_TX_D_P[5] set_location_assignment PIN_K26 -to HSMC_TX_D_P[6] set_location_assignment PIN_L30 -to HSMC_TX_D_P[7] set_location_assignment PIN_U25 -to HSMC_TX_D_P[8] set_location_assignment PIN_N27 -to HSMC_TX_D_P[9] set_location_assignment PIN_V25 -to HSMC_TX_D_P[10] set_location_assignment PIN_AA30 -to HSMC_TX_D_P[11] set_location_assignment PIN_AC27 -to HSMC_TX_D_P[12] set_location_assignment PIN_AD29 -to HSMC_TX_D_P[13] set_location_assignment PIN_AE29 -to HSMC_TX_D_P[14] set_location_assignment PIN_AJ30 -to HSMC_TX_D_P[15] set_location_assignment PIN_AH29 -to HSMC_TX_D_P[16] set_location_assignment PIN_C27 -to I2C_SCLK set_location_assignment PIN_G21 -to I2C_SDAT set_location_assignment PIN_AH28 -to IRDA_RXD set_location_assignment PIN_AA26 -to KEY[0] set_location_assignment PIN_AE25 -to KEY[1] set_location_assignment PIN_AF30 -to KEY[2] set_location_assignment PIN_AE26 -to KEY[3] set_location_assignment PIN_AG4 -to LCD_DATA[0] set_location_assignment PIN_AF3 -to LCD_DATA[1] set_location_assignment PIN_AH3 -to LCD_DATA[2] set_location_assignment PIN_AE5 -to LCD_DATA[3] set_location_assignment PIN_AH2 -to LCD_DATA[4] set_location_assignment PIN_AE3 -to LCD_DATA[5] set_location_assignment PIN_AH4 -to LCD_DATA[6] set_location_assignment PIN_AE4 -to LCD_DATA[7] set_location_assignment PIN_AF4 -to LCD_EN set_location_assignment PIN_AF27 -to LCD_ON set_location_assignment PIN_AG3 -to LCD_RS set_location_assignment PIN_AJ3 -to LCD_RW set_location_assignment PIN_AA25 -to LEDG[0] set_location_assignment PIN_AB25 -to LEDG[1] set_location_assignment PIN_F27 -to LEDG[2] set_location_assignment PIN_F26 -to LEDG[3] set_location_assignment PIN_W26 -to LEDG[4] set_location_assignment PIN_Y22 -to LEDG[5] set_location_assignment PIN_Y25 -to LEDG[6] set_location_assignment PIN_AA22 -to LEDG[7] set_location_assignment PIN_J25 -to LEDG[8] set_location_assignment PIN_T23 -to LEDR[0] set_location_assignment PIN_T24 -to LEDR[1] set_location_assignment PIN_V27 -to LEDR[2] set_location_assignment PIN_W25 -to LEDR[3] set_location_assignment PIN_T21 -to LEDR[4] set_location_assignment PIN_T26 -to LEDR[5] set_location_assignment PIN_R25 -to LEDR[6] set_location_assignment PIN_T27 -to LEDR[7] set_location_assignment PIN_P25 -to LEDR[8] set_location_assignment PIN_R24 -to LEDR[9] set_location_assignment PIN_P21 -to LEDR[10] set_location_assignment PIN_N24 -to LEDR[11] set_location_assignment PIN_N21 -to LEDR[12] set_location_assignment PIN_M25 -to LEDR[13] set_location_assignment PIN_K24 -to LEDR[14] set_location_assignment PIN_L25 -to LEDR[15] set_location_assignment PIN_M21 -to LEDR[16] set_location_assignment PIN_M22 -to LEDR[17] set_location_assignment PIN_A4 -to PCIE_PERST_N set_location_assignment PIN_V15 -to PCIE_REFCLK_P set_location_assignment PIN_AC2 -to PCIE_RX_P[0] set_location_assignment PIN_AA2 -to PCIE_RX_P[1] set_location_assignment PIN_AB4 -to PCIE_TX_P[0] set_location_assignment PIN_Y4 -to PCIE_TX_P[1] set_location_assignment PIN_C29 -to PCIE_WAKE_N set_location_assignment PIN_AH25 -to SD_CLK set_location_assignment PIN_AF18 -to SD_CMD set_location_assignment PIN_AH27 -to SD_DAT[0] set_location_assignment PIN_AJ28 -to SD_DAT[1] set_location_assignment PIN_AD24 -to SD_DAT[2] set_location_assignment PIN_AE18 -to SD_DAT[3] set_location_assignment PIN_AJ27 -to SD_WP_N set_location_assignment PIN_AK16 -to SMA_CLKIN set_location_assignment PIN_AF25 -to SMA_CLKOUT set_location_assignment PIN_AJ21 -to SSRAM0_CE_N set_location_assignment PIN_AG23 -to SSRAM1_CE_N set_location_assignment PIN_AK25 -to SSRAM_ADSC_N set_location_assignment PIN_AJ25 -to SSRAM_ADSP_N set_location_assignment PIN_AH26 -to SSRAM_ADV_N set_location_assignment PIN_AF22 -to SSRAM_BE[0] set_location_assignment PIN_AK22 -to SSRAM_BE[1] set_location_assignment PIN_AJ22 -to SSRAM_BE[2] set_location_assignment PIN_AF21 -to SSRAM_BE[3] set_location_assignment PIN_AF24 -to SSRAM_CLK set_location_assignment PIN_AK23 -to SSRAM_GW_N set_location_assignment PIN_AG24 -to SSRAM_OE_N set_location_assignment PIN_AK24 -to SSRAM_WE_N set_location_assignment PIN_V28 -to SW[0] set_location_assignment PIN_U30 -to SW[1] set_location_assignment PIN_V21 -to SW[2] set_location_assignment PIN_C2 -to SW[3] set_location_assignment PIN_AB30 -to SW[4] set_location_assignment PIN_U21 -to SW[5] set_location_assignment PIN_T28 -to SW[6] set_location_assignment PIN_R30 -to SW[7] set_location_assignment PIN_P30 -to SW[8] set_location_assignment PIN_R29 -to SW[9] set_location_assignment PIN_R26 -to SW[10] set_location_assignment PIN_N26 -to SW[11] set_location_assignment PIN_M26 -to SW[12] set_location_assignment PIN_N25 -to SW[13] set_location_assignment PIN_J26 -to SW[14] set_location_assignment PIN_K25 -to SW[15] set_location_assignment PIN_C30 -to SW[16] set_location_assignment PIN_H25 -to SW[17] set_location_assignment PIN_B15 -to TD_CLK27 set_location_assignment PIN_C17 -to TD_DATA[0] set_location_assignment PIN_D17 -to TD_DATA[1] set_location_assignment PIN_A16 -to TD_DATA[2] set_location_assignment PIN_B16 -to TD_DATA[3] set_location_assignment PIN_G18 -to TD_DATA[4] set_location_assignment PIN_G17 -to TD_DATA[5] set_location_assignment PIN_K18 -to TD_DATA[6] set_location_assignment PIN_K17 -to TD_DATA[7] set_location_assignment PIN_C28 -to TD_HS set_location_assignment PIN_E25 -to TD_RESET_N set_location_assignment PIN_E22 -to TD_VS set_location_assignment PIN_D26 -to UART_CTS set_location_assignment PIN_A29 -to UART_RTS set_location_assignment PIN_B27 -to UART_RXD set_location_assignment PIN_H24 -to UART_TXD set_location_assignment PIN_E24 -to VGA_B[0] set_location_assignment PIN_C24 -to VGA_B[1] set_location_assignment PIN_B25 -to VGA_B[2] set_location_assignment PIN_C23 -to VGA_B[3] set_location_assignment PIN_F24 -to VGA_B[4] set_location_assignment PIN_A23 -to VGA_B[5] set_location_assignment PIN_G25 -to VGA_B[6] set_location_assignment PIN_C22 -to VGA_B[7] set_location_assignment PIN_F25 -to VGA_BLANK_N set_location_assignment PIN_D27 -to VGA_CLK set_location_assignment PIN_D20 -to VGA_G[0] set_location_assignment PIN_C20 -to VGA_G[1] set_location_assignment PIN_A20 -to VGA_G[2] set_location_assignment PIN_K19 -to VGA_G[3] set_location_assignment PIN_A21 -to VGA_G[4] set_location_assignment PIN_F21 -to VGA_G[5] set_location_assignment PIN_A22 -to VGA_G[6] set_location_assignment PIN_B22 -to VGA_G[7] set_location_assignment PIN_B24 -to VGA_HS set_location_assignment PIN_A17 -to VGA_R[0] set_location_assignment PIN_C18 -to VGA_R[1] set_location_assignment PIN_B18 -to VGA_R[2] set_location_assignment PIN_A18 -to VGA_R[3] set_location_assignment PIN_E18 -to VGA_R[4] set_location_assignment PIN_E19 -to VGA_R[5] set_location_assignment PIN_B19 -to VGA_R[6] set_location_assignment PIN_C19 -to VGA_R[7] set_location_assignment PIN_AH20 -to VGA_SYNC_N set_location_assignment PIN_A24 -to VGA_VS set_instance_assignment -name VIRTUAL_PIN ON -to FS_ADDR[0] #============================================================ set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" set_global_assignment -name VQM_FILE ../top.vqm set_global_assignment -name SDC_FILE de2i_150_golden_top.sdc set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Topyosys-0.52/examples/intel/DE2i-150/quartus_compile/runme_quartus000066400000000000000000000001731477540374200246740ustar00rootroot00000000000000#!/bin/bash export REV="de2i" quartus_map -c $REV top && \ quartus_fit -c $REV top && \ quartus_asm -c $REV top yosys-0.52/examples/intel/DE2i-150/run_cycloneiv000066400000000000000000000001371477540374200214250ustar00rootroot00000000000000#/bin/env bash yosys -p "synth_intel -family cycloneiv -top top -vqm top.vqm" top.v sevenseg.v yosys-0.52/examples/intel/DE2i-150/sevenseg.v000066400000000000000000000011111477540374200206220ustar00rootroot00000000000000module sevenseg ( output reg [6:0] HEX0, input [3:0] SW ); always @(*) begin case(SW) 4'h1: HEX0 = 7'b1111001; 4'h2: HEX0 = 7'b0100100; 4'h3: HEX0 = 7'b0110000; 4'h4: HEX0 = 7'b0011001; 4'h5: HEX0 = 7'b0010010; 4'h6: HEX0 = 7'b0000010; 4'h7: HEX0 = 7'b1111000; 4'h8: HEX0 = 7'b0000000; 4'h9: HEX0 = 7'b0011000; 4'ha: HEX0 = 7'b0001000; 4'hb: HEX0 = 7'b0000011; 4'hc: HEX0 = 7'b1000110; 4'hd: HEX0 = 7'b0100001; 4'he: HEX0 = 7'b0000110; 4'hf: HEX0 = 7'b0001110; 4'h0: HEX0 = 7'b1000000; endcase // case (SW) end endmodule yosys-0.52/examples/intel/DE2i-150/top.v000066400000000000000000000010071477540374200176110ustar00rootroot00000000000000`default_nettype none module top ( output wire [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, HEX6, HEX7, input wire [15:0] SW ); sevenseg UUD0 (.HEX0(HEX0), .SW(4'h7)); sevenseg UUD1 (.HEX0(HEX1), .SW(4'h1)); sevenseg UUD2 (.HEX0(HEX2), .SW(4'h0)); sevenseg UUD3 (.HEX0(HEX3), .SW(4'h2)); sevenseg UUD4 (.HEX0(HEX4), .SW(SW[3:0])); sevenseg UUD5 (.HEX0(HEX5), .SW(SW[7:4])); sevenseg UUD6 (.HEX0(HEX6), .SW(SW[11:8])); sevenseg UUD7 (.HEX0(HEX7), .SW(SW[15:12])); endmodule yosys-0.52/examples/intel/MAX10/000077500000000000000000000000001477540374200163625ustar00rootroot00000000000000yosys-0.52/examples/intel/MAX10/run_max10000066400000000000000000000001141477540374200201130ustar00rootroot00000000000000yosys -p "synth_intel -family max10 -top top -vqm top.vqm" top.v sevenseg.v yosys-0.52/examples/intel/MAX10/runme_postsynth000066400000000000000000000002361477540374200215670ustar00rootroot00000000000000#!/bin/bash iverilog -D POST_IMPL -o verif_post -s tb_top tb_top.v top.vqm $(yosys-config --datdir/altera_intel/max10/cells_comb_max10.v) vvp -N verif_post yosys-0.52/examples/intel/MAX10/sevenseg.v000066400000000000000000000011111477540374200203620ustar00rootroot00000000000000module sevenseg ( output reg [6:0] HEX0, input [3:0] SW ); always @(*) begin case(SW) 4'h1: HEX0 = 7'b1111001; 4'h2: HEX0 = 7'b0100100; 4'h3: HEX0 = 7'b0110000; 4'h4: HEX0 = 7'b0011001; 4'h5: HEX0 = 7'b0010010; 4'h6: HEX0 = 7'b0000010; 4'h7: HEX0 = 7'b1111000; 4'h8: HEX0 = 7'b0000000; 4'h9: HEX0 = 7'b0011000; 4'ha: HEX0 = 7'b0001000; 4'hb: HEX0 = 7'b0000011; 4'hc: HEX0 = 7'b1000110; 4'hd: HEX0 = 7'b0100001; 4'he: HEX0 = 7'b0000110; 4'hf: HEX0 = 7'b0001110; 4'h0: HEX0 = 7'b1000000; endcase // case (SW) end endmodule yosys-0.52/examples/intel/MAX10/top.v000066400000000000000000000010071477540374200173510ustar00rootroot00000000000000`default_nettype none module top ( output wire [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, HEX6, HEX7, input wire [15:0] SW ); sevenseg UUD0 (.HEX0(HEX0), .SW(4'h7)); sevenseg UUD1 (.HEX0(HEX1), .SW(4'h1)); sevenseg UUD2 (.HEX0(HEX2), .SW(4'h0)); sevenseg UUD3 (.HEX0(HEX3), .SW(4'h2)); sevenseg UUD4 (.HEX0(HEX4), .SW(SW[3:0])); sevenseg UUD5 (.HEX0(HEX5), .SW(SW[7:4])); sevenseg UUD6 (.HEX0(HEX6), .SW(SW[11:8])); sevenseg UUD7 (.HEX0(HEX7), .SW(SW[15:12])); endmodule yosys-0.52/examples/intel/asicworld_lfsr/000077500000000000000000000000001477540374200206115ustar00rootroot00000000000000yosys-0.52/examples/intel/asicworld_lfsr/README000066400000000000000000000002771477540374200214770ustar00rootroot00000000000000Source of the files: http://www.asic-world.com/examples/verilog/lfsr.html Run first: runme_presynth Generate output netlist with run_max10 or run_cycloneiv Then, check with: runme_postsynth yosys-0.52/examples/intel/asicworld_lfsr/lfsr_updown.v000066400000000000000000000013421477540374200233420ustar00rootroot00000000000000`default_nettype none module lfsr_updown ( clk , // Clock input reset , // Reset input enable , // Enable input up_down , // Up Down input count , // Count output overflow // Overflow output ); input clk; input reset; input enable; input up_down; output [7 : 0] count; output overflow; reg [7 : 0] count; assign overflow = (up_down) ? (count == {{7{1'b0}}, 1'b1}) : (count == {1'b1, {7{1'b0}}}) ; always @(posedge clk) if (reset) count <= {7{1'b0}}; else if (enable) begin if (up_down) begin count <= {~(^(count & 8'b01100011)),count[7:1]}; end else begin count <= {count[5:0],~(^(count & 8'b10110001))}; end end endmodule yosys-0.52/examples/intel/asicworld_lfsr/lfsr_updown_tb.v000066400000000000000000000010311477540374200240220ustar00rootroot00000000000000module tb(); reg clk; reg reset; reg enable; reg up_down; wire [7 : 0] count; wire overflow; initial begin $monitor("rst %b en %b updown %b cnt %b overflow %b", reset,enable,up_down,count, overflow); clk = 0; reset = 1; enable = 0; up_down = 0; #10 reset = 0; #1 enable = 1; #20 up_down = 1; #30 $finish; end always #1 clk = ~clk; lfsr_updown U( .clk ( clk ), .reset ( reset ), .enable ( enable ), .up_down ( up_down ), .count ( count ), .overflow ( overflow ) ); endmodule yosys-0.52/examples/intel/asicworld_lfsr/run_cycloneiv000077500000000000000000000001451477540374200234160ustar00rootroot00000000000000#!/bin/env bash yosys -p "synth_intel -family cycloneiv -top lfsr_updown -vqm top.vqm" lfsr_updown.v yosys-0.52/examples/intel/asicworld_lfsr/run_max10000077500000000000000000000001411477540374200223450ustar00rootroot00000000000000#!/bin/env bash yosys -p "synth_intel -family max10 -top lfsr_updown -vqm top.vqm" lfsr_updown.v yosys-0.52/examples/intel/asicworld_lfsr/runme_postsynth000077500000000000000000000002421477540374200240160ustar00rootroot00000000000000#!/bin/bash iverilog -D POST_IMPL -o verif_post -s tb lfsr_updown_tb.v top.vqm $(yosys-config --datdir/altera_intel/max10/cells_comb_max10.v) vvp -N verif_post yosys-0.52/examples/intel/asicworld_lfsr/runme_presynth000077500000000000000000000001251477540374200236170ustar00rootroot00000000000000#!/bin/bash iverilog -o presynth lfsr_updown_tb.v lfsr_updown.v &&\ vvp -N presynthyosys-0.52/examples/mimas2/000077500000000000000000000000001477540374200156515ustar00rootroot00000000000000yosys-0.52/examples/mimas2/README000066400000000000000000000003771477540374200165400ustar00rootroot00000000000000A simple example design, based on the Numato Labs Mimas V2 board ================================================================ This example uses Yosys for synthesis and Xilinx ISE for place&route and bit-stream creation. To synthesize: bash run.sh yosys-0.52/examples/mimas2/example.ucf000066400000000000000000000016101477540374200200010ustar00rootroot00000000000000CONFIG VCCAUX = "3.3" ; NET "CLK" LOC = D9 | IOSTANDARD = LVCMOS33 | PERIOD = 12MHz ; NET "LED[7]" LOC = P15 | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ; NET "LED[6]" LOC = P16 | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ; NET "LED[5]" LOC = N15 | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ; NET "LED[4]" LOC = N16 | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ; NET "LED[3]" LOC = U17 | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ; NET "LED[2]" LOC = U18 | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ; NET "LED[1]" LOC = T17 | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ; NET "LED[0]" LOC = T18 | IOSTANDARD = LVCMOS33 | DRIVE = 8 | SLEW = SLOW ; yosys-0.52/examples/mimas2/example.v000066400000000000000000000002521477540374200174720ustar00rootroot00000000000000module example( input wire CLK, output wire [7:0] LED ); reg [27:0] ctr; initial ctr = 0; always @(posedge CLK) ctr <= ctr + 1; assign LED = ctr[27:20]; endmodule yosys-0.52/examples/mimas2/run.sh000066400000000000000000000003201477540374200170040ustar00rootroot00000000000000#!/bin/sh set -e yosys run_yosys.ys edif2ngd example.edif ngdbuild example -uc example.ucf -p xc6slx9csg324-3 map -w example par -w example.ncd example_par.ncd bitgen -w example_par.ncd -g StartupClk:JTAGClk yosys-0.52/examples/mimas2/run_yosys.ys000066400000000000000000000001501477540374200202740ustar00rootroot00000000000000read_verilog example.v synth_xilinx -top example -family xc6s -ise write_edif -pvector bra example.edif yosys-0.52/examples/osu035/000077500000000000000000000000001477540374200155175ustar00rootroot00000000000000yosys-0.52/examples/osu035/.gitignore000066400000000000000000000000571477540374200175110ustar00rootroot00000000000000osu035_stdcells.lib example.yslog example.edif yosys-0.52/examples/osu035/Makefile000066400000000000000000000006761477540374200171700ustar00rootroot00000000000000 example.edif: example.ys example.v example.constr osu035_stdcells.lib yosys -l example.yslog -q example.ys osu035_stdcells.lib: rm -f osu035_stdcells.lib.part osu035_stdcells.lib wget -O osu035_stdcells.lib.part https://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/ami035/signalstorm/osu035_stdcells.lib mv osu035_stdcells.lib.part osu035_stdcells.lib clean: rm -f osu035_stdcells.lib rm -f example.yslog example.edif yosys-0.52/examples/osu035/example.constr000066400000000000000000000000461477540374200204040ustar00rootroot00000000000000set_driving_cell INVX1 set_load 0.015 yosys-0.52/examples/osu035/example.v000066400000000000000000000001551477540374200173420ustar00rootroot00000000000000module top (input clk, input [7:0] a, b, output reg [15:0] c); always @(posedge clk) c <= a * b; endmodule yosys-0.52/examples/osu035/example.ys000066400000000000000000000003731477540374200175320ustar00rootroot00000000000000read_verilog example.v read_liberty -lib osu035_stdcells.lib synth -top top dfflibmap -liberty osu035_stdcells.lib abc -D 10000 -constr example.constr -liberty osu035_stdcells.lib opt_clean stat -liberty osu035_stdcells.lib write_edif example.edif yosys-0.52/examples/python-api/000077500000000000000000000000001477540374200165515ustar00rootroot00000000000000yosys-0.52/examples/python-api/.gitignore000066400000000000000000000000071477540374200205360ustar00rootroot00000000000000out/** yosys-0.52/examples/python-api/pass.py000077500000000000000000000017551477540374200201040ustar00rootroot00000000000000#!/usr/bin/python3 import libyosys as ys import matplotlib.pyplot as plt import numpy as np class CellStatsPass(ys.Pass): def __init__(self): super().__init__("cell_stats", "Shows cell stats as plot") def py_help(self): ys.log("This pass uses the matplotlib library to display cell stats\n") def py_execute(self, args, design): ys.log_header(design, "Plotting cell stats\n") cell_stats = {} for module in design.selected_whole_modules_warn(): for cell in module.selected_cells(): if cell.type.str() in cell_stats: cell_stats[cell.type.str()] += 1 else: cell_stats[cell.type.str()] = 1 plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center') plt.xticks(range(len(cell_stats)), list(cell_stats.keys())) plt.show() def py_clear_flags(self): ys.log("Clear Flags - CellStatsPass\n") p = CellStatsPass() yosys-0.52/examples/python-api/script.py000077500000000000000000000012111477540374200204250ustar00rootroot00000000000000#!/usr/bin/python3 from pyosys import libyosys as ys import matplotlib.pyplot as plt import numpy as np design = ys.Design() ys.run_pass("read_verilog ../../tests/simple/fiedler-cooley.v", design); ys.run_pass("prep", design) ys.run_pass("opt -full", design) cell_stats = {} for module in design.selected_whole_modules_warn(): for cell in module.selected_cells(): if cell.type.str() in cell_stats: cell_stats[cell.type.str()] += 1 else: cell_stats[cell.type.str()] = 1 plt.bar(range(len(cell_stats)), height = list(cell_stats.values()),align='center') plt.xticks(range(len(cell_stats)), list(cell_stats.keys())) plt.show() yosys-0.52/examples/smtbmc/000077500000000000000000000000001477540374200157465ustar00rootroot00000000000000yosys-0.52/examples/smtbmc/.gitignore000066400000000000000000000004141477540374200177350ustar00rootroot00000000000000demo1.smt2 demo1.yslog demo2.smt2 demo2.smtc demo2.vcd demo2.yslog demo2_tb demo2_tb.v demo2_tb.vcd demo3.smt2 demo3.vcd demo3.yslog demo4.smt2 demo4.vcd demo4.yslog demo5.smt2 demo5.vcd demo5.yslog demo6.smt2 demo6.yslog demo7.smt2 demo7.yslog demo8.smt2 demo8.yslog yosys-0.52/examples/smtbmc/Makefile000066400000000000000000000050371477540374200174130ustar00rootroot00000000000000 all: demo1 demo2 demo3 demo4 demo5 demo6 demo7 demo8 demo9 glift_mux demo1: demo1.smt2 yosys-smtbmc --dump-vcd demo1.vcd demo1.smt2 yosys-smtbmc -i --dump-vcd demo1.vcd demo1.smt2 demo2: demo2.smt2 yosys-smtbmc -g --dump-vcd demo2.vcd --dump-smtc demo2.smtc --dump-vlogtb demo2_tb.v demo2.smt2 iverilog -g2012 -o demo2_tb demo2_tb.v demo2.v vvp demo2_tb +vcd=demo2_tb.vcd demo3: demo3.smt2 yosys-smtbmc --dump-vcd demo3.vcd --smtc demo3.smtc demo3.smt2 demo4: demo4.smt2 yosys-smtbmc -s yices --dump-vcd demo4.vcd --smtc demo4.smtc demo4.smt2 demo5: demo5.smt2 yosys-smtbmc -g -t 50 --dump-vcd demo5.vcd demo5.smt2 demo6: demo6.smt2 yosys-smtbmc -t 1 demo6.smt2 demo7: demo7.smt2 yosys-smtbmc -t 10 demo7.smt2 demo8: demo8.smt2 yosys-smtbmc -s z3 -t 1 -g 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' demo2.smt2: demo2.v yosys -ql demo2.yslog -p 'read_verilog -formal demo2.v; prep -top demo2 -nordff; write_smt2 -wires demo2.smt2' demo3.smt2: demo3.v yosys -ql demo3.yslog -p 'read_verilog -formal demo3.v; prep -top demo3 -nordff; write_smt2 -wires demo3.smt2' demo4.smt2: demo4.v yosys -ql demo4.yslog -p 'read_verilog -formal demo4.v; prep -top demo4 -nordff; write_smt2 -wires demo4.smt2' demo5.smt2: demo5.v yosys -ql demo5.yslog -p 'read_verilog -formal demo5.v; prep -top demo5 -nordff; write_smt2 -wires demo5.smt2' demo6.smt2: demo6.v yosys -ql demo6.yslog -p 'read_verilog demo6.v; prep -top demo6 -nordff; assertpmux; opt -keepdc -fast; write_smt2 -wires demo6.smt2' demo7.smt2: demo7.v yosys -ql demo7.yslog -p 'read_verilog -formal demo7.v; prep -top demo7 -nordff; write_smt2 -wires demo7.smt2' demo8.smt2: demo8.v yosys -ql demo8.yslog -p 'read_verilog -formal demo8.v; prep -top demo8 -nordff; write_smt2 -stbv -wires demo8.smt2' demo9.smt2: demo9.v yosys -ql demo9.yslog -p 'read_verilog -formal demo9.v; prep -top demo9 -nordff; write_smt2 -stbv -wires demo9.smt2' clean: rm -f demo1.yslog demo1.smt2 demo1.vcd rm -f demo2.yslog demo2.smt2 demo2.vcd demo2.smtc demo2_tb.v demo2_tb demo2_tb.vcd rm -f demo3.yslog demo3.smt2 demo3.vcd rm -f demo4.yslog demo4.smt2 demo4.vcd rm -f demo5.yslog demo5.smt2 demo5.vcd rm -f demo6.yslog demo6.smt2 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 yosys-0.52/examples/smtbmc/demo1.v000066400000000000000000000007031477540374200171420ustar00rootroot00000000000000module demo1(input clk, input addtwo, output iseven); reg [3:0] cnt; wire [3:0] next_cnt; inc inc_inst (addtwo, iseven, cnt, next_cnt); always @(posedge clk) cnt = (iseven ? cnt == 10 : cnt == 11) ? 0 : next_cnt; `ifdef FORMAL assert property (cnt != 15); initial assume (!cnt[2]); `endif endmodule module inc(input addtwo, output iseven, input [3:0] a, output [3:0] y); assign iseven = !a[0]; assign y = a + (addtwo ? 2 : 1); endmodule yosys-0.52/examples/smtbmc/demo2.v000066400000000000000000000012071477540374200171430ustar00rootroot00000000000000// Nothing to prove in this demo. // Just an example for memories, vcd dumps and vlog testbench dumps. `ifdef FORMAL `define assume(_expr_) assume(_expr_) `else `define assume(_expr_) `endif module demo2(input clk, input [4:0] addr, output reg [31:0] data); reg [31:0] mem [0:31]; always @(negedge clk) data <= mem[addr]; reg [31:0] used_addr = 0; reg [31:0] used_dbits = 0; reg initstate = 1; always @(posedge clk) begin initstate <= 0; `assume(!used_addr[addr]); used_addr[addr] <= 1; if (!initstate) begin `assume(data != 0); `assume((used_dbits & data) == 0); used_dbits <= used_dbits | data; end end endmodule yosys-0.52/examples/smtbmc/demo3.smtc000066400000000000000000000000721477540374200176440ustar00rootroot00000000000000initial assume [rst] always -1 assert (= [-1:mem] [mem]) yosys-0.52/examples/smtbmc/demo3.v000066400000000000000000000007321477540374200171460ustar00rootroot00000000000000// Whatever the initial content of this memory is at reset, it will never change // see demo3.smtc for assumptions and assertions module demo3(input clk, rst, input [15:0] addr, output reg [31:0] data); reg [31:0] mem [0:2**16-1]; reg [15:0] addr_q; always @(posedge clk) begin if (rst) begin data <= mem[0] ^ 123456789; addr_q <= 0; end else begin mem[addr_q] <= data ^ 123456789; data <= mem[addr] ^ 123456789; addr_q <= addr; end end endmodule yosys-0.52/examples/smtbmc/demo4.smtc000066400000000000000000000002271477540374200176470ustar00rootroot00000000000000initial assume [rst] always -1 assume (not [rst]) assume (=> [-1:inv2] [inv2]) final -2 assume [-1:inv2] assume (not [-2:inv2]) assert (= [r1] [r2]) yosys-0.52/examples/smtbmc/demo4.v000066400000000000000000000004361477540374200171500ustar00rootroot00000000000000// Demo for "final" smtc constraints module demo4(input clk, rst, inv2, input [15:0] in, output reg [15:0] r1, r2); always @(posedge clk) begin if (rst) begin r1 <= in; r2 <= -in; end else begin r1 <= r1 + in; r2 <= inv2 ? -(r2 - in) : (r2 - in); end end endmodule yosys-0.52/examples/smtbmc/demo5.v000066400000000000000000000005401477540374200171450ustar00rootroot00000000000000// Demo for $anyconst module demo5 (input clk); wire [7:0] step_size = $anyconst; reg [7:0] state = 0, count = 0; reg [31:0] hash = 0; always @(posedge clk) begin count <= count + 1; hash <= ((hash << 5) + hash) ^ state; state <= state + step_size; end always @* begin if (count == 42) assert(hash == 32'h A18FAC0A); end endmodule yosys-0.52/examples/smtbmc/demo6.v000066400000000000000000000003271477540374200171510ustar00rootroot00000000000000// Demo for assertpmux module demo6 (input A, B, C, D, E, output reg Y); always @* begin Y = 0; if (A != B) begin (* parallel_case *) case (C) A: Y = D; B: Y = E; endcase end end endmodule yosys-0.52/examples/smtbmc/demo7.v000066400000000000000000000006051477540374200171510ustar00rootroot00000000000000// Demo for memory initialization module demo7; wire [2:0] addr = $anyseq; reg [15:0] memory [0:7]; initial begin memory[0] = 1331; memory[1] = 1331 + 1; memory[2] = 1331 + 2; memory[3] = 1331 + 4; memory[4] = 1331 + 8; memory[5] = 1331 + 16; memory[6] = 1331 + 32; memory[7] = 1331 + 64; end assert property (1000 < memory[addr] && memory[addr] < 2000); endmodule yosys-0.52/examples/smtbmc/demo8.v000066400000000000000000000003471477540374200171550ustar00rootroot00000000000000// Simple exists-forall demo module demo8; wire [7:0] prime = $anyconst; wire [3:0] factor = $allconst; always @* begin if (1 < factor && factor < prime) assume((prime % factor) != 0); assume(prime > 1); end endmodule yosys-0.52/examples/smtbmc/demo9.v000066400000000000000000000004001477540374200171440ustar00rootroot00000000000000module demo9; (* maximize *) wire[7:0] h = $anyconst; wire [7:0] i = $allconst; wire [7:0] t0 = ((i << 8'b00000010) + 8'b00000011); wire trigger = (t0 > h) && (h < 8'b00000100); always @* begin assume(trigger == 1'b1); cover(1); end endmodule yosys-0.52/examples/smtbmc/glift/000077500000000000000000000000001477540374200170535ustar00rootroot00000000000000yosys-0.52/examples/smtbmc/glift/C7552.v000077500000000000000000006112161477540374200177610ustar00rootroot00000000000000module 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 yosys-0.52/examples/smtbmc/glift/C7552.ys000066400000000000000000000013031477540374200201320ustar00rootroot00000000000000read_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 yosys-0.52/examples/smtbmc/glift/C880.v000077500000000000000000000460001477540374200176670ustar00rootroot00000000000000module 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 yosys-0.52/examples/smtbmc/glift/C880.ys000066400000000000000000000012731477540374200200550ustar00rootroot00000000000000read_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 yosys-0.52/examples/smtbmc/glift/alu2.v000077500000000000000000000423351477540374200201170ustar00rootroot00000000000000module 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 yosys-0.52/examples/smtbmc/glift/alu2.ys000066400000000000000000000012731477540374200202760ustar00rootroot00000000000000read_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 yosys-0.52/examples/smtbmc/glift/alu4.v000077500000000000000000001073711477540374200201230ustar00rootroot00000000000000module 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 yosys-0.52/examples/smtbmc/glift/alu4.ys000066400000000000000000000012731477540374200203000ustar00rootroot00000000000000read_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 yosys-0.52/examples/smtbmc/glift/mux2.ys000066400000000000000000000023101477540374200203170ustar00rootroot00000000000000logger -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 < * 2019 Eddie Hung * * 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. * */ // [[CITE]] The AIGER And-Inverter Graph (AIG) Format Version 20071012 // Armin Biere. The AIGER And-Inverter Graph (AIG) Format Version 20071012. Technical Report 07/1, October 2011, FMV Reports Series, Institute for Formal Models and Verification, Johannes Kepler University, Altenbergerstr. 69, 4040 Linz, Austria. // http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf // https://stackoverflow.com/a/46137633 #ifdef _MSC_VER #include #define __builtin_bswap32 _byteswap_ulong #elif defined(__APPLE__) #include #define __builtin_bswap32 OSSwapInt32 #endif #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "aigerparse.h" YOSYS_NAMESPACE_BEGIN inline int32_t from_big_endian(int32_t i32) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ return __builtin_bswap32(i32); #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ return i32; #else #error "Unknown endianness" #endif } #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; dict values_map; dict sig2driver; dict> sig2deps; ConstEvalAig(RTLIL::Module *module) : module(module) { for (auto &it : module->cells_) { if (!yosys_celltypes.cell_known(it.second->type)) continue; for (auto &it2 : it.second->connections()) if (yosys_celltypes.cell_output(it.second->type, it2.first)) { auto r = sig2driver.insert(std::make_pair(it2.second, it.second)); log_assert(r.second); } } } void clear() { values_map.clear(); sig2deps.clear(); } void set(RTLIL::SigBit sig, RTLIL::State value) { auto it = values_map.find(sig); #ifndef NDEBUG if (it != values_map.end()) { RTLIL::State current_val = it->second; log_assert(current_val == value); } #endif if (it != values_map.end()) it->second = value; else values_map[sig] = value; } void set_incremental(RTLIL::SigSpec sig, RTLIL::Const value) { log_assert(GetSize(sig) == GetSize(value)); for (int i = 0; i < GetSize(sig); i++) { auto it = values_map.find(sig[i]); if (it != values_map.end()) { RTLIL::State current_val = it->second; if (current_val != value[i]) for (auto dep : sig2deps[sig[i]]) values_map.erase(dep); it->second = value[i]; } else values_map[sig[i]] = value[i]; } } void compute_deps(RTLIL::SigBit output, const pool &inputs) { sig2deps[output].insert(output); RTLIL::Cell *cell = sig2driver.at(output); RTLIL::SigBit sig_a = cell->getPort(ID::A); sig2deps[sig_a].reserve(sig2deps[sig_a].size() + sig2deps[output].size()); // Reserve so that any invalidation // that may occur does so here, and // not mid insertion (below) sig2deps[sig_a].insert(sig2deps[output].begin(), sig2deps[output].end()); if (!inputs.count(sig_a)) compute_deps(sig_a, inputs); if (cell->type == ID($_AND_)) { RTLIL::SigSpec sig_b = cell->getPort(ID::B); sig2deps[sig_b].reserve(sig2deps[sig_b].size() + sig2deps[output].size()); // Reserve so that any invalidation // that may occur does so here, and // not mid insertion (below) sig2deps[sig_b].insert(sig2deps[output].begin(), sig2deps[output].end()); if (!inputs.count(sig_b)) compute_deps(sig_b, inputs); } else if (cell->type == ID($_NOT_)) { } else log_abort(); } bool eval(RTLIL::Cell *cell) { RTLIL::SigBit sig_y = cell->getPort(ID::Y); if (values_map.count(sig_y)) return true; RTLIL::SigBit sig_a = cell->getPort(ID::A); if (!eval(sig_a)) return false; RTLIL::State eval_ret = RTLIL::Sx; if (cell->type == ID($_NOT_)) { if (sig_a == State::S0) eval_ret = State::S1; else if (sig_a == State::S1) eval_ret = State::S0; } else if (cell->type == ID($_AND_)) { if (sig_a == State::S0) { eval_ret = State::S0; goto eval_end; } { RTLIL::SigBit sig_b = cell->getPort(ID::B); if (!eval(sig_b)) return false; if (sig_b == State::S0) { eval_ret = State::S0; goto eval_end; } if (sig_a != State::S1 || sig_b != State::S1) goto eval_end; eval_ret = State::S1; } } else log_abort(); eval_end: set(sig_y, eval_ret); return true; } bool eval(RTLIL::SigBit &sig) { auto it = values_map.find(sig); if (it != values_map.end()) { sig = it->second; return true; } RTLIL::Cell *cell = sig2driver.at(sig); if (!eval(cell)) return false; it = values_map.find(sig); if (it != values_map.end()) { sig = it->second; return true; } return false; } }; AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports) : design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports), aiger_autoidx(autoidx++) { module = new RTLIL::Module; module->name = module_name; if (design->module(module->name)) log_error("Duplicate definition of module %s!\n", log_id(module->name)); } void AigerReader::parse_aiger() { std::string header; f >> header; if (header != "aag" && header != "aig") log_error("Unsupported AIGER file!\n"); // Parse rest of header if (!(f >> M >> I >> L >> O >> A)) log_error("Invalid AIGER header\n"); // Optional values B = C = J = F = 0; if (f.peek() != ' ') goto end_of_header; if (!(f >> B)) log_error("Invalid AIGER header\n"); if (f.peek() != ' ') goto end_of_header; if (!(f >> C)) log_error("Invalid AIGER header\n"); if (f.peek() != ' ') goto end_of_header; if (!(f >> J)) log_error("Invalid AIGER header\n"); if (f.peek() != ' ') goto end_of_header; if (!(f >> F)) log_error("Invalid AIGER header\n"); end_of_header: std::string line; std::getline(f, line); // Ignore up to start of next line, as standard // says anything that follows could be used for // optional sections log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F); line_count = 1; piNum = 0; flopNum = 0; if (header == "aag") parse_aiger_ascii(); else if (header == "aig") parse_aiger_binary(); else log_abort(); RTLIL::Wire* n0 = module->wire(stringf("$aiger%d$0", aiger_autoidx)); if (n0) module->connect(n0, State::S0); // Parse footer (symbol table, comments, etc.) unsigned l1; std::string s; for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { if (c == 'i' || c == 'l' || c == 'o' || c == 'b') { f.ignore(1); if (!(f >> l1 >> s)) log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size())) log_error("Line %u has invalid symbol position!\n", line_count); RTLIL::IdString escaped_s = stringf("\\%s", s.c_str()); RTLIL::Wire* wire; if (c == 'i') wire = inputs[l1]; else if (c == 'l') wire = latches[l1]; else if (c == 'o') { wire = module->wire(escaped_s); if (wire) { // Could have been renamed by a latch module->swap_names(wire, outputs[l1]); module->connect(outputs[l1], wire); goto next; } wire = outputs[l1]; } else if (c == 'b') wire = bad_properties[l1]; else log_abort(); module->rename(wire, escaped_s); } else if (c == 'j' || c == 'f') { // TODO } else if (c == 'c') { f.ignore(1); if (f.peek() == '\r') f.ignore(1); if (f.peek() == '\n') break; // Else constraint (TODO) } else log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); next: std::getline(f, line); // Ignore up to start of next line } post_process(); } static uint32_t parse_xaiger_literal(std::istream &f) { uint32_t l; f.read(reinterpret_cast(&l), sizeof(l)); if (f.gcount() != sizeof(l)) #if defined(_WIN32) && defined(__MINGW32__) log_error("Offset %I64d: unable to read literal!\n", static_cast(f.tellg())); #else log_error("Offset %" PRId64 ": unable to read literal!\n", static_cast(f.tellg())); #endif return from_big_endian(l); } RTLIL::Wire* AigerReader::createWireIfNotExists(RTLIL::Module *module, unsigned literal) { const unsigned variable = literal >> 1; const bool invert = literal & 1; RTLIL::IdString wire_name(stringf("$aiger%d$%d%s", aiger_autoidx, variable, invert ? "b" : "")); RTLIL::Wire *wire = module->wire(wire_name); if (wire) return wire; log_debug2("Creating %s\n", wire_name.c_str()); wire = module->addWire(wire_name); wire->port_input = wire->port_output = false; if (!invert) return wire; RTLIL::IdString wire_inv_name(stringf("$aiger%d$%d", aiger_autoidx, variable)); RTLIL::Wire *wire_inv = module->wire(wire_inv_name); if (wire_inv) { if (module->cell(wire_inv_name)) return wire; } else { log_debug2("Creating %s\n", wire_inv_name.c_str()); wire_inv = module->addWire(wire_inv_name); wire_inv->port_input = wire_inv->port_output = false; } log_debug2("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); module->addNotGate(stringf("$not$aiger%d$%d", aiger_autoidx, variable), wire_inv, wire); return wire; } void AigerReader::parse_xaiger() { std::string header; f >> header; if (header != "aag" && header != "aig") log_error("Unsupported AIGER file!\n"); // Parse rest of header if (!(f >> M >> I >> L >> O >> A)) log_error("Invalid AIGER header\n"); // Optional values B = C = J = F = 0; std::string line; std::getline(f, line); // Ignore up to start of next line, as standard // says anything that follows could be used for // optional sections log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); line_count = 1; piNum = 0; flopNum = 0; if (header == "aag") parse_aiger_ascii(); else if (header == "aig") parse_aiger_binary(); else log_abort(); RTLIL::Wire* n0 = module->wire(stringf("$aiger%d$0", aiger_autoidx)); if (n0) module->connect(n0, State::S0); int c = f.get(); if (c != 'c') log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); if (f.peek() == '\n') f.get(); // Parse footer (symbol table, comments, etc.) std::string s; for (int c = f.get(); c != EOF; c = f.get()) { // XAIGER extensions if (c == 'm') { uint32_t dataSize = parse_xaiger_literal(f); uint32_t lutNum = parse_xaiger_literal(f); uint32_t lutSize = parse_xaiger_literal(f); log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); ConstEvalAig ce(module); for (unsigned i = 0; i < lutNum; ++i) { uint32_t rootNodeID = parse_xaiger_literal(f); uint32_t cutLeavesM = parse_xaiger_literal(f); log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM); RTLIL::Wire *output_sig = module->wire(stringf("$aiger%d$%d", aiger_autoidx, rootNodeID)); log_assert(output_sig); uint32_t nodeID; RTLIL::SigSpec input_sig; for (unsigned j = 0; j < cutLeavesM; ++j) { nodeID = parse_xaiger_literal(f); log_debug2("\t%u\n", nodeID); if (nodeID == 0) { log_debug("\tLUT '$lut$aiger%d$%d' input %d is constant!\n", aiger_autoidx, rootNodeID, cutLeavesM); continue; } RTLIL::Wire *wire = module->wire(stringf("$aiger%d$%d", aiger_autoidx, nodeID)); log_assert(wire); input_sig.append(wire); } // Reverse input order as fastest input is returned first input_sig.reverse(); // TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size()) ce.clear(); ce.compute_deps(output_sig, input_sig.to_sigbit_pool()); RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << GetSize(input_sig)); for (int j = 0; j < GetSize(lut_mask); ++j) { int gray = j ^ (j >> 1); ce.set_incremental(input_sig, RTLIL::Const{gray, GetSize(input_sig)}); RTLIL::SigBit o(output_sig); bool success = ce.eval(o); log_assert(success); log_assert(o.wire == nullptr); lut_mask.bits()[gray] = o.data; } RTLIL::Cell *output_cell = module->cell(stringf("$and$aiger%d$%d", aiger_autoidx, rootNodeID)); log_assert(output_cell); module->remove(output_cell); module->addLut(stringf("$lut$aiger%d$%d", aiger_autoidx, rootNodeID), input_sig, output_sig, std::move(lut_mask)); } } else if (c == 'r') { uint32_t dataSize = parse_xaiger_literal(f); flopNum = parse_xaiger_literal(f); log_debug("flopNum = %u\n", flopNum); log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); mergeability.reserve(flopNum); for (unsigned i = 0; i < flopNum; i++) mergeability.emplace_back(parse_xaiger_literal(f)); } else if (c == 's') { uint32_t dataSize = parse_xaiger_literal(f); flopNum = parse_xaiger_literal(f); log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); initial_state.reserve(flopNum); for (unsigned i = 0; i < flopNum; i++) initial_state.emplace_back(parse_xaiger_literal(f)); } else if (c == 'n') { parse_xaiger_literal(f); f >> s; log_debug("n: '%s'\n", s.c_str()); } else if (c == 'h') { f.ignore(sizeof(uint32_t)); uint32_t version = parse_xaiger_literal(f); log_assert(version == 1); uint32_t ciNum = parse_xaiger_literal(f); log_debug("ciNum = %u\n", ciNum); uint32_t coNum = parse_xaiger_literal(f); log_debug("coNum = %u\n", coNum); piNum = parse_xaiger_literal(f); log_debug("piNum = %u\n", piNum); uint32_t poNum = parse_xaiger_literal(f); log_debug("poNum = %u\n", poNum); uint32_t boxNum = parse_xaiger_literal(f); log_debug("boxNum = %u\n", boxNum); for (unsigned i = 0; i < boxNum; i++) { uint32_t boxInputs = parse_xaiger_literal(f); uint32_t boxOutputs = parse_xaiger_literal(f); uint32_t boxUniqueId = parse_xaiger_literal(f); log_assert(boxUniqueId > 0); uint32_t oldBoxNum = parse_xaiger_literal(f); RTLIL::Cell* cell = module->addCell(stringf("$box%u", oldBoxNum), stringf("$__boxid%u", boxUniqueId)); cell->setPort(ID(i), SigSpec(State::S0, boxInputs)); cell->setPort(ID(o), SigSpec(State::S0, boxOutputs)); cell->attributes[ID::abc9_box_seq] = oldBoxNum; boxes.emplace_back(cell); } } else if (c == 'a' || c == 'i' || c == 'o' || c == 's') { uint32_t dataSize = parse_xaiger_literal(f); f.ignore(dataSize); log_debug("ignoring '%c'\n", c); } else { break; } } post_process(); } void AigerReader::parse_aiger_ascii() { std::string line; std::stringstream ss; unsigned l1, l2, l3; // Parse inputs 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); log_debug2("%d is an input\n", l1); log_assert(!(l1 & 1)); // Inputs can't be inverted RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, l1 >> 1)); wire->port_input = true; module->connect(createWireIfNotExists(module, l1), wire); inputs.push_back(wire); } // Parse latches RTLIL::Wire *clk_wire = nullptr; if (L > 0 && !clk_name.empty()) { clk_wire = module->wire(clk_name); log_assert(!clk_wire); log_debug2("Creating %s\n", clk_name.c_str()); clk_wire = module->addWire(clk_name); clk_wire->port_input = true; clk_wire->port_output = false; } 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); log_debug2("%d %d is a latch\n", l1, l2); log_assert(!(l1 & 1)); RTLIL::Wire *q_wire = module->addWire(stringf("$l%0*d", digits, l1 >> 1)); module->connect(createWireIfNotExists(module, l1), q_wire); RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); if (clk_wire) module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); else module->addFfGate(NEW_ID, d_wire, q_wire); // Reset logic is optional in AIGER 1.9 if (f.peek() == ' ') { if (!(f >> l3)) log_error("Line %u cannot be interpreted as a latch!\n", line_count); if (l3 == 0) q_wire->attributes[ID::init] = State::S0; else if (l3 == 1) q_wire->attributes[ID::init] = State::S1; else if (l3 == l1) { //q_wire->attributes[ID::init] = RTLIL::Sx; } else log_error("Line %u has invalid reset literal for latch!\n", line_count); } else { // AIGER latches are assumed to be initialized to zero q_wire->attributes[ID::init] = State::S0; } latches.push_back(q_wire); } // Parse outputs 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); std::getline(f, line); // Ignore up to start of next line log_debug2("%d is an output\n", l1); RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i)); wire->port_output = true; module->connect(wire, createWireIfNotExists(module, l1)); outputs.push_back(wire); } // Parse bad properties for (unsigned i = 0; i < B; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as a bad state property!\n", line_count); std::getline(f, line); // Ignore up to start of next line log_debug2("%d is a bad state property\n", l1); RTLIL::Wire *wire = createWireIfNotExists(module, l1); wire->port_output = true; bad_properties.push_back(wire); } // TODO: Parse invariant constraints for (unsigned i = 0; i < C; ++i, ++line_count) std::getline(f, line); // Ignore up to start of next line // TODO: Parse justice properties for (unsigned i = 0; i < J; ++i, ++line_count) std::getline(f, line); // Ignore up to start of next line // TODO: Parse fairness constraints for (unsigned i = 0; i < F; ++i, ++line_count) std::getline(f, line); // Ignore up to start of next line // Parse AND for (unsigned i = 0; i < A; ++i) { if (!(f >> l1 >> l2 >> l3)) log_error("Line %u cannot be interpreted as an AND!\n", line_count); std::getline(f, line); // Ignore up to start of next line log_debug2("%d %d %d is an AND\n", l1, l2, l3); log_assert(!(l1 & 1)); RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); module->addAndGate("$and" + o_wire->name.str(), i1_wire, i2_wire, o_wire); } } static unsigned parse_next_delta_literal(std::istream &f, unsigned ref) { unsigned x = 0, i = 0; unsigned char ch; while ((ch = f.get()) & 0x80) x |= (ch & 0x7f) << (7 * i++); return ref - (x | (ch << (7 * i))); } void AigerReader::parse_aiger_binary() { unsigned l1, l2, l3; std::string line; // Parse inputs 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)); wire->port_input = true; module->connect(createWireIfNotExists(module, i << 1), wire); inputs.push_back(wire); } // Parse latches RTLIL::Wire *clk_wire = nullptr; if (L > 0 && !clk_name.empty()) { clk_wire = module->wire(clk_name); log_assert(!clk_wire); log_debug2("Creating %s\n", clk_name.c_str()); clk_wire = module->addWire(clk_name); clk_wire->port_input = true; clk_wire->port_output = false; } digits = decimal_digits(L); l1 = (I+1) * 2; for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { if (!(f >> l2)) log_error("Line %u cannot be interpreted as a latch!\n", line_count); log_debug("%d %d is a latch\n", l1, l2); RTLIL::Wire *q_wire = module->addWire(stringf("$l%0*d", digits, l1 >> 1)); module->connect(createWireIfNotExists(module, l1), q_wire); RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); if (clk_wire) module->addDff(NEW_ID, clk_wire, d_wire, q_wire); else module->addFf(NEW_ID, d_wire, q_wire); // Reset logic is optional in AIGER 1.9 if (f.peek() == ' ') { if (!(f >> l3)) log_error("Line %u cannot be interpreted as a latch!\n", line_count); if (l3 == 0) q_wire->attributes[ID::init] = State::S0; else if (l3 == 1) q_wire->attributes[ID::init] = State::S1; else if (l3 == l1) { //q_wire->attributes[ID::init] = RTLIL::Sx; } else log_error("Line %u has invalid reset literal for latch!\n", line_count); } else { // AIGER latches are assumed to be initialized to zero q_wire->attributes[ID::init] = State::S0; } latches.push_back(q_wire); } // Parse outputs 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); std::getline(f, line); // Ignore up to start of next line log_debug2("%d is an output\n", l1); RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i)); wire->port_output = true; module->connect(wire, createWireIfNotExists(module, l1)); outputs.push_back(wire); } // Parse bad properties for (unsigned i = 0; i < B; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as a bad state property!\n", line_count); std::getline(f, line); // Ignore up to start of next line log_debug2("%d is a bad state property\n", l1); RTLIL::Wire *wire = createWireIfNotExists(module, l1); wire->port_output = true; bad_properties.push_back(wire); } // TODO: Parse invariant constraints for (unsigned i = 0; i < C; ++i, ++line_count) std::getline(f, line); // Ignore up to start of next line // TODO: Parse justice properties for (unsigned i = 0; i < J; ++i, ++line_count) std::getline(f, line); // Ignore up to start of next line // TODO: Parse fairness constraints for (unsigned i = 0; i < F; ++i, ++line_count) std::getline(f, line); // Ignore up to start of next line // Parse AND l1 = (I+L+1) << 1; for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) { l2 = parse_next_delta_literal(f, l1); l3 = parse_next_delta_literal(f, l2); log_debug2("%d %d %d is an AND\n", l1, l2, l3); log_assert(!(l1 & 1)); RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); module->addAndGate("$and" + o_wire->name.str(), i1_wire, i2_wire, o_wire); } } void AigerReader::post_process() { unsigned ci_count = 0, co_count = 0; for (auto cell : boxes) { for (auto &bit : cell->connections_.at(ID(i))) { log_assert(bit == State::S0); log_assert(co_count < outputs.size()); bit = outputs[co_count++]; log_assert(bit.wire && GetSize(bit.wire) == 1); log_assert(bit.wire->port_output); bit.wire->port_output = false; } for (auto &bit : cell->connections_.at(ID(o))) { log_assert(bit == State::S0); log_assert((piNum + ci_count) < inputs.size()); bit = inputs[piNum + ci_count++]; log_assert(bit.wire && GetSize(bit.wire) == 1); log_assert(bit.wire->port_input); bit.wire->port_input = false; } } for (uint32_t i = 0; i < flopNum; i++) { RTLIL::Wire *d = outputs[outputs.size() - flopNum + i]; log_assert(d); log_assert(d->port_output); d->port_output = false; RTLIL::Wire *q = inputs[piNum - flopNum + i]; log_assert(q); log_assert(q->port_input); q->port_input = false; Cell* ff = module->addFfGate(NEW_ID, d, q); ff->attributes[ID::abc9_mergeability] = mergeability[i]; q->attributes[ID::init] = initial_state[i]; } dict> wideports_cache; if (!map_filename.empty()) { std::ifstream mf(map_filename); std::string type, symbol; int variable, index; while (mf >> type >> variable >> index >> symbol) { RTLIL::IdString escaped_s = RTLIL::escape_id(symbol); if (type == "input") { log_assert(static_cast(variable) < inputs.size()); RTLIL::Wire* wire = inputs[variable]; log_assert(wire); log_assert(wire->port_input); log_debug("Renaming input %s", log_id(wire)); RTLIL::Wire *existing = nullptr; if (index == 0) { // Cope with the fact that a CI might be identical // to a PI (necessary due to ABC); in those cases // simply connect the latter to the former existing = module->wire(escaped_s); if (!existing) module->rename(wire, escaped_s); else { wire->port_input = false; module->connect(wire, existing); } log_debug(" -> %s\n", log_id(escaped_s)); } else { RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); existing = module->wire(indexed_name); if (!existing) module->rename(wire, indexed_name); else { module->connect(wire, existing); wire->port_input = false; } log_debug(" -> %s\n", log_id(indexed_name)); } if (wideports && !existing) { auto r = wideports_cache.insert(escaped_s); if (r.second) { r.first->second.first = index; r.first->second.second = index; } else { r.first->second.first = std::min(r.first->second.first, index); r.first->second.second = std::max(r.first->second.second, index); } } } else if (type == "output") { log_assert(static_cast(variable + co_count) < outputs.size()); RTLIL::Wire* wire = outputs[variable + co_count]; log_assert(wire); log_assert(wire->port_output); log_debug("Renaming output %s", log_id(wire)); RTLIL::Wire *existing; if (index == 0) { // Cope with the fact that a CO might be identical // to a PO (necessary due to ABC); in those cases // simply connect the latter to the former existing = module->wire(escaped_s); if (!existing) module->rename(wire, escaped_s); else { wire->port_output = false; existing->port_output = true; module->connect(wire, existing); wire = existing; } log_debug(" -> %s\n", log_id(escaped_s)); } else { RTLIL::IdString indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); existing = module->wire(indexed_name); if (!existing) module->rename(wire, indexed_name); else { wire->port_output = false; existing->port_output = true; module->connect(wire, existing); } log_debug(" -> %s\n", log_id(indexed_name)); } if (wideports && !existing) { auto r = wideports_cache.insert(escaped_s); if (r.second) { r.first->second.first = index; r.first->second.second = index; } else { r.first->second.first = std::min(r.first->second.first, index); r.first->second.second = std::max(r.first->second.second, index); } } } else if (type == "box") { RTLIL::Cell* cell = module->cell(stringf("$box%d", variable)); if (!cell) log_debug("Box %d (%s) no longer exists.\n", variable, log_id(escaped_s)); else module->rename(cell, escaped_s); } else log_error("Symbol type '%s' not recognised.\n", type.c_str()); } } for (auto &wp : wideports_cache) { auto name = wp.first; int min = wp.second.first; int max = wp.second.second; if (min == 0 && max == 0) continue; RTLIL::Wire *wire = module->wire(name); if (wire) module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); // Do not make ports with a mix of input/output into // wide ports bool port_input = false, port_output = false; for (int i = min; i <= max; i++) { RTLIL::IdString other_name = name.str() + stringf("[%d]", i); RTLIL::Wire *other_wire = module->wire(other_name); if (other_wire) { port_input = port_input || other_wire->port_input; port_output = port_output || other_wire->port_output; } } wire = module->addWire(name, max-min+1); wire->start_offset = min; wire->port_input = port_input; wire->port_output = port_output; for (int i = min; i <= max; i++) { RTLIL::IdString other_name = stringf("%s[%d]", name.c_str(), i); RTLIL::Wire *other_wire = module->wire(other_name); if (other_wire) { other_wire->port_input = false; other_wire->port_output = false; if (wire->port_input) module->connect(other_wire, SigSpec(wire, i-min)); else module->connect(SigSpec(wire, i-min), other_wire); } } } module->fixup_ports(); // Insert into a new (temporary) design so that "clean" will only // operate (and run checks on) this one module RTLIL::Design *mapped_design = new RTLIL::Design; mapped_design->add(module); Pass::call(mapped_design, "clean"); mapped_design->modules_.erase(module->name); delete mapped_design; design->add(module); for (auto cell : module->cells().to_vector()) { if (cell->type != ID($lut)) continue; auto y_port = cell->getPort(ID::Y).as_bit(); if (y_port.wire->width == 1) module->rename(cell, stringf("$lut%s", y_port.wire->name.c_str())); else module->rename(cell, stringf("$lut%s[%d]", y_port.wire->name.c_str(), y_port.offset)); } } struct AigerFrontend : public Frontend { AigerFrontend() : Frontend("aiger", "read AIGER file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_aiger [options] [filename]\n"); log("\n"); log("Load module from an AIGER file into the current design.\n"); log("\n"); log(" -module_name \n"); log(" name of module to be created (default: )\n"); log("\n"); log(" -clk_name \n"); log(" if specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); log(" clocked by wire of this name. otherwise, $_FF_ cells will be used\n"); log("\n"); log(" -map \n"); log(" read file with port and latch symbols\n"); log("\n"); log(" -wideports\n"); log(" merge ports that match the pattern 'name[int]' into a single\n"); log(" multi-bit port 'name'\n"); log("\n"); log(" -xaiger\n"); log(" read XAIGER extensions\n"); log("\n"); } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { log_header(design, "Executing AIGER frontend.\n"); RTLIL::IdString clk_name; RTLIL::IdString module_name; std::string map_filename; bool wideports = false, xaiger = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-module_name" && argidx+1 < args.size()) { module_name = RTLIL::escape_id(args[++argidx]); continue; } if (arg == "-clk_name" && argidx+1 < args.size()) { clk_name = RTLIL::escape_id(args[++argidx]); continue; } if (map_filename.empty() && arg == "-map" && argidx+1 < args.size()) { map_filename = args[++argidx]; continue; } if (arg == "-wideports") { wideports = true; continue; } if (arg == "-xaiger") { xaiger = true; continue; } break; } extra_args(f, filename, args, argidx, true); if (module_name.empty()) { #ifdef _WIN32 char fname[_MAX_FNAME]; _splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */); char* bn = strdup(fname); module_name = RTLIL::escape_id(bn); free(bn); #else char* bn = strdup(filename.c_str()); module_name = RTLIL::escape_id(bn); free(bn); #endif } AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); if (xaiger) reader.parse_xaiger(); else reader.parse_aiger(); } } AigerFrontend; YOSYS_NAMESPACE_END yosys-0.52/frontends/aiger/aigerparse.h000066400000000000000000000037211477540374200202320ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * 2019 Eddie Hung * * 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 ABC_AIGERPARSE #define ABC_AIGERPARSE #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN struct AigerReader { RTLIL::Design *design; std::istream &f; RTLIL::IdString clk_name; RTLIL::Module *module; std::string map_filename; bool wideports; const int aiger_autoidx; unsigned M, I, L, O, A; unsigned B, C, J, F; // Optional in AIGER 1.9 unsigned line_count; uint32_t piNum, flopNum; std::vector inputs; std::vector latches; std::vector outputs; std::vector bad_properties; std::vector boxes; std::vector mergeability, initial_state; AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); void parse_aiger(); void parse_xaiger(); void parse_aiger_ascii(); void parse_aiger_binary(); void post_process(); RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal); }; YOSYS_NAMESPACE_END #endif yosys-0.52/frontends/aiger2/000077500000000000000000000000001477540374200160165ustar00rootroot00000000000000yosys-0.52/frontends/aiger2/Makefile.inc000066400000000000000000000000431477540374200202230ustar00rootroot00000000000000 OBJS += frontends/aiger2/xaiger.o yosys-0.52/frontends/aiger2/xaiger.cc000066400000000000000000000335441477540374200176150ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) Martin PoviÅ¡er * * 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/register.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN uint32_t read_be32(std::istream &f) { return ((uint32_t) f.get() << 24) | ((uint32_t) f.get() << 16) | ((uint32_t) f.get() << 8) | (uint32_t) f.get(); } IdString read_idstring(std::istream &f) { std::string str; std::getline(f, str, '\0'); if (!f.good()) log_error("failed to read string\n"); return RTLIL::escape_id(str); } struct Xaiger2Frontend : public Frontend { Xaiger2Frontend() : Frontend("xaiger2", "(experimental) read XAIGER file") { experimental(); } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_xaiger2 -sc_mapping [options] \n"); log("\n"); log("Read a standard cell mapping from a XAIGER file into an existing module.\n"); log("\n"); log(" -module_name \n"); log(" name of the target module\n"); log("\n"); log(" -map2 \n"); log(" read file with symbol information\n"); log("\n"); } void read_sc_mapping(std::istream *&f, std::string filename, std::vector args, Design *design) { IdString module_name; std::string map_filename; size_t argidx; for (argidx = 2; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-module_name" && argidx + 1 < args.size()) { module_name = RTLIL::escape_id(args[++argidx]); continue; } if (arg == "-map2" && argidx + 1 < args.size()) { map_filename = args[++argidx]; continue; } break; } extra_args(f, filename, args, argidx, true); if (map_filename.empty()) log_error("A '-map2' argument required\n"); if (module_name.empty()) log_error("A '-module_name' argument required\n"); Module *module = design->module(module_name); if (!module) log_error("Module '%s' not found\n", log_id(module_name)); std::ifstream map_file; map_file.open(map_filename); if (!map_file) log_error("Failed to open map file '%s'\n", map_filename.c_str()); unsigned int M, I, L, O, A; std::string header; if (!(*f >> header >> M >> I >> L >> O >> A) || header != "aig") log_error("Bad header\n"); std::string line; std::getline(*f, line); log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); if (L != 0) log_error("Latches unsupported\n"); if (I + L + A != M) log_error("Inconsistent header\n"); std::vector outputs; for (int i = 0; i < (int) O; i++) { int po; *f >> po; log_assert(f->get() == '\n'); outputs.push_back(po); } std::vector> boxes; std::vector retained_boxes; std::vector bits(2 + 2*M, RTLIL::Sm); bits[0] = RTLIL::S0; bits[1] = RTLIL::S1; std::string type; while (map_file >> type) { if (type == "pi") { int pi_idx; int woffset; std::string name; if (!(map_file >> pi_idx >> woffset >> name)) log_error("Bad map file (1)\n"); int lit = (2 * pi_idx) + 2; if (lit < 0 || lit >= (int) bits.size()) log_error("Bad map file (2)\n"); Wire *w = module->wire(name); if (!w || woffset < 0 || woffset >= w->width) log_error("Map file references non-existent signal bit %s[%d]\n", name.c_str(), woffset); bits[lit] = SigBit(w, woffset); } else if (type == "box") { int box_seq; std::string name; if (!(map_file >> box_seq >> name)) log_error("Bad map file (20)\n"); if (box_seq < 0) log_error("Bad map file (21)\n"); Cell *box = module->cell(RTLIL::escape_id(name)); if (!box) log_error("Map file references non-existent box %s\n", name.c_str()); Module *def = design->module(box->type); if (def && !box->parameters.empty()) { // TODO: This is potentially costly even if a cached derivation exists def = design->module(def->derive(design, box->parameters)); log_assert(def); } if (!def) log_error("Bad map file (22)\n"); if (box_seq >= (int) boxes.size()) { boxes.resize(box_seq + 1); retained_boxes.resize(box_seq + 1); } boxes[box_seq] = std::make_pair(box, def); } else { std::string scratch; std::getline(map_file, scratch); } } for (int i = 0; i < (int) A; i++) { while (f->get() & 0x80 && !f->eof()); while (f->get() & 0x80 && !f->eof()); } if (f->get() != 'c') log_error("Missing 'c' ahead of extensions\n"); if (f->peek() == '\n') f->get(); auto extensions_start = f->tellg(); log_debug("reading 'h' (first pass)\n"); for (int c = f->get(); c != EOF; c = f->get()) { if (c == 'h') { uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes; len = read_be32(*f); read_be32(*f); ci_num = read_be32(*f); co_num = read_be32(*f); pi_num = read_be32(*f); po_num = read_be32(*f); no_boxes = read_be32(*f); log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n", len, ci_num, co_num, pi_num, po_num, no_boxes); int ci_counter = 0; for (uint32_t i = 0; i < no_boxes; i++) { /* unused box_inputs = */ read_be32(*f); YS_MAYBE_UNUSED auto box_outputs = read_be32(*f); /* unused box_id = */ read_be32(*f); auto box_seq = read_be32(*f); log_assert(box_seq < boxes.size()); auto [cell, def] = boxes[box_seq]; log_assert(cell && def); retained_boxes[box_seq] = true; int box_ci_idx = 0; for (auto port_id : def->ports) { Wire *port = def->wire(port_id); if (port->port_output) { if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width) log_error("Malformed design (1)\n"); SigSpec &conn = cell->connections_[port_id]; for (int j = 0; j < port->width; j++) { if (conn[j].wire && conn[j].wire->port_output) conn[j] = module->addWire(module->uniquify( stringf("$box$%s$%s$%d", cell->name.isPublic() ? cell->name.c_str() + 1 : cell->name.c_str(), port_id.isPublic() ? port_id.c_str() + 1 : port_id.c_str(), j))); bits[2*(pi_num + ci_counter + box_ci_idx++) + 2] = conn[j]; } } } log_assert(box_ci_idx == (int) box_outputs); ci_counter += box_ci_idx; } log_assert(pi_num + ci_counter == ci_num); } else if (c == '\n') { break; } else if (c == 'c') { break; } else { uint32_t len = read_be32(*f); f->ignore(len); log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); } } log_debug("reading 'M' (second pass)\n"); f->seekg(extensions_start); bool read_mapping = false; uint32_t no_cells, no_instances; for (int c = f->get(); c != EOF; c = f->get()) { if (c == 'M') { uint32_t len = read_be32(*f); read_mapping = true; no_cells = read_be32(*f); no_instances = read_be32(*f); log_debug("M: len=%u no_cells=%u no_instances=%u\n", len, no_cells, no_instances); struct MappingCell { RTLIL::IdString type; RTLIL::IdString out; std::vector ins; }; std::vector cells; cells.resize(no_cells); for (unsigned i = 0; i < no_cells; ++i) { auto &cell = cells[i]; cell.type = read_idstring(*f); cell.out = read_idstring(*f); uint32_t nins = read_be32(*f); for (uint32_t j = 0; j < nins; j++) cell.ins.push_back(read_idstring(*f)); log_debug("M: Cell %s (out %s, ins", log_id(cell.type), log_id(cell.out)); for (auto in : cell.ins) log_debug(" %s", log_id(in)); log_debug(")\n"); } for (unsigned i = 0; i < no_instances; ++i) { uint32_t cell_id = read_be32(*f); uint32_t out_lit = read_be32(*f); log_assert(out_lit < bits.size()); log_assert(bits[out_lit] == RTLIL::Sm); log_assert(cell_id < cells.size()); auto &cell = cells[cell_id]; Cell *instance = module->addCell(module->uniquify(stringf("$sc%d", out_lit)), cell.type); auto out_w = module->addWire(module->uniquify(stringf("$lit%d", out_lit))); instance->setPort(cell.out, out_w); bits[out_lit] = out_w; for (auto in : cell.ins) { uint32_t in_lit = read_be32(*f); log_assert(out_lit < bits.size()); log_assert(bits[in_lit] != RTLIL::Sm); instance->setPort(in, bits[in_lit]); } } } else if (c == '\n') { break; } else if (c == 'c') { break; } else { uint32_t len = read_be32(*f); f->ignore(len); log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); } } if (!read_mapping) log_error("Missing mapping (no 'M' section)\n"); log("Read %d instances with cell library of size %d.\n", no_instances, no_cells); f->seekg(extensions_start); log_debug("reading 'h' (second pass)\n"); int co_counter = 0; for (int c = f->get(); c != EOF; c = f->get()) { if (c == 'h') { uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes; len = read_be32(*f); read_be32(*f); ci_num = read_be32(*f); co_num = read_be32(*f); pi_num = read_be32(*f); po_num = read_be32(*f); no_boxes = read_be32(*f); log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n", len, ci_num, co_num, pi_num, po_num, no_boxes); for (uint32_t i = 0; i < no_boxes; i++) { YS_MAYBE_UNUSED auto box_inputs = read_be32(*f); /* unused box_outputs = */ read_be32(*f); /* unused box_id = */ read_be32(*f); auto box_seq = read_be32(*f); log_assert(box_seq < boxes.size()); auto [cell, def] = boxes[box_seq]; log_assert(cell && def); int box_co_idx = 0; for (auto port_id : def->ports) { Wire *port = def->wire(port_id); SigSpec conn; if (port->port_input) { if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width) log_error("Malformed design (2)\n"); SigSpec conn; for (int j = 0; j < port->width; j++) { log_assert(co_counter + box_co_idx < (int) outputs.size()); int lit = outputs[co_counter + box_co_idx++]; log_assert(lit >= 0 && lit < (int) bits.size()); SigBit bit = bits[lit]; if (bit == RTLIL::Sm) log_error("Malformed mapping (1)\n"); conn.append(bit); } cell->setPort(port_id, conn); } } log_assert(box_co_idx == (int) box_inputs); co_counter += box_co_idx; } log_assert(po_num + co_counter == co_num); } else if (c == '\n') { break; } else if (c == 'c') { break; } else { uint32_t len = read_be32(*f); f->ignore(len); log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); } } while (true) { std::string scratch; std::getline(*f, scratch); if (f->eof()) break; log_assert(!f->fail()); log("input file: %s\n", scratch.c_str()); } log_debug("co_counter=%d\n", co_counter); // TODO: seek without close/open map_file.close(); map_file.open(map_filename); while (map_file >> type) { if (type == "po") { int po_idx; int woffset; std::string name; if (!(map_file >> po_idx >> woffset >> name)) log_error("Bad map file (3)\n"); po_idx += co_counter; if (po_idx < 0 || po_idx >= (int) outputs.size()) log_error("Bad map file (4)\n"); int lit = outputs[po_idx]; if (lit < 0 || lit >= (int) bits.size()) log_error("Bad map file (5)\n"); if (bits[lit] == RTLIL::Sm) log_error("Bad map file (6)\n"); Wire *w = module->wire(name); if (!w || woffset < 0 || woffset >= w->width) log_error("Map file references non-existent signal bit %s[%d]\n", name.c_str(), woffset); module->connect(SigBit(w, woffset), bits[lit]); } else if (type == "pseudopo") { int po_idx; int poffset; std::string box_name; std::string box_port; if (!(map_file >> po_idx >> poffset >> box_name >> box_port)) log_error("Bad map file (7)\n"); po_idx += co_counter; if (po_idx < 0 || po_idx >= (int) outputs.size()) log_error("Bad map file (8)\n"); int lit = outputs[po_idx]; if (lit < 0 || lit >= (int) bits.size()) log_error("Bad map file (9)\n"); if (bits[lit] == RTLIL::Sm) log_error("Bad map file (10)\n"); Cell *cell = module->cell(box_name); if (!cell || !cell->hasPort(box_port)) log_error("Map file references non-existent box port %s/%s\n", box_name.c_str(), box_port.c_str()); SigSpec &port = cell->connections_[box_port]; if (poffset < 0 || poffset >= port.size()) log_error("Map file references non-existent box port bit %s/%s[%d]\n", box_name.c_str(), box_port.c_str(), poffset); port[poffset] = bits[lit]; } else { std::string scratch; std::getline(map_file, scratch); } } int box_seq = 0; for (auto [cell, def] : boxes) { if (!retained_boxes[box_seq++]) module->remove(cell); } } void execute(std::istream *&f, std::string filename, std::vector args, Design *design) override { log_header(design, "Executing XAIGER2 frontend.\n"); if (args.size() > 1 && args[1] == "-sc_mapping") { read_sc_mapping(f, filename, args, design); return; } log_cmd_error("Mode '-sc_mapping' must be selected\n"); } } Xaiger2Frontend; PRIVATE_NAMESPACE_END yosys-0.52/frontends/ast/000077500000000000000000000000001477540374200154345ustar00rootroot00000000000000yosys-0.52/frontends/ast/Makefile.inc000066400000000000000000000002441477540374200176440ustar00rootroot00000000000000 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 yosys-0.52/frontends/ast/ast.cc000066400000000000000000001572431477540374200165460ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * Copyright (C) 2018 Ruben Undheim * * 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 is the AST frontend library. * * The AST frontend library is not a frontend on its own but provides an * abstract syntax tree (AST) abstraction for the open source Verilog frontend * at frontends/verilog. * * */ #include "kernel/yosys.h" #include "libs/sha1/sha1.h" #include "ast.h" YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; // instantiate global variables (public API) namespace AST { std::string current_filename; void (*set_line_num)(int) = NULL; int (*get_line_num)() = NULL; unsigned long long astnodes = 0; unsigned long long astnode_count() { return astnodes; } } // instantiate global variables (private API) namespace AST_INTERNAL { 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; Module *current_module; bool current_always_clocked; dict current_memwr_count; dict> current_memwr_visible; } // convert node types to string std::string AST::type2str(AstNodeType type) { switch (type) { #define X(_item) case _item: return #_item; X(AST_NONE) X(AST_DESIGN) X(AST_MODULE) X(AST_TASK) X(AST_FUNCTION) X(AST_DPI_FUNCTION) X(AST_WIRE) X(AST_MEMORY) X(AST_AUTOWIRE) X(AST_PARAMETER) X(AST_LOCALPARAM) X(AST_DEFPARAM) X(AST_PARASET) X(AST_ARGUMENT) X(AST_RANGE) X(AST_MULTIRANGE) X(AST_CONSTANT) X(AST_REALVALUE) X(AST_CELLTYPE) X(AST_IDENTIFIER) X(AST_PREFIX) X(AST_ASSERT) X(AST_ASSUME) X(AST_LIVE) X(AST_FAIR) X(AST_COVER) X(AST_ENUM) X(AST_ENUM_ITEM) X(AST_FCALL) X(AST_TO_BITS) X(AST_TO_SIGNED) X(AST_TO_UNSIGNED) X(AST_SELFSZ) X(AST_CAST_SIZE) X(AST_CONCAT) X(AST_REPLICATE) X(AST_BIT_NOT) X(AST_BIT_AND) X(AST_BIT_OR) X(AST_BIT_XOR) X(AST_BIT_XNOR) X(AST_REDUCE_AND) X(AST_REDUCE_OR) X(AST_REDUCE_XOR) X(AST_REDUCE_XNOR) X(AST_REDUCE_BOOL) X(AST_SHIFT_LEFT) X(AST_SHIFT_RIGHT) X(AST_SHIFT_SLEFT) X(AST_SHIFT_SRIGHT) X(AST_SHIFTX) X(AST_SHIFT) X(AST_LT) X(AST_LE) X(AST_EQ) X(AST_NE) X(AST_EQX) X(AST_NEX) X(AST_GE) X(AST_GT) X(AST_ADD) X(AST_SUB) X(AST_MUL) X(AST_DIV) X(AST_MOD) X(AST_POW) X(AST_POS) X(AST_NEG) X(AST_LOGIC_AND) X(AST_LOGIC_OR) X(AST_LOGIC_NOT) X(AST_TERNARY) X(AST_MEMRD) X(AST_MEMWR) X(AST_MEMINIT) X(AST_TCALL) X(AST_ASSIGN) X(AST_CELL) X(AST_PRIMITIVE) X(AST_CELLARRAY) X(AST_ALWAYS) X(AST_INITIAL) X(AST_BLOCK) X(AST_ASSIGN_EQ) X(AST_ASSIGN_LE) X(AST_CASE) X(AST_COND) X(AST_CONDX) X(AST_CONDZ) X(AST_DEFAULT) X(AST_FOR) X(AST_WHILE) X(AST_REPEAT) X(AST_GENVAR) X(AST_GENFOR) X(AST_GENIF) X(AST_GENCASE) X(AST_GENBLOCK) X(AST_TECALL) X(AST_POSEDGE) X(AST_NEGEDGE) X(AST_EDGE) X(AST_INTERFACE) X(AST_INTERFACEPORT) X(AST_INTERFACEPORTTYPE) X(AST_MODPORT) X(AST_MODPORTMEMBER) X(AST_PACKAGE) X(AST_WIRETYPE) X(AST_TYPEDEF) X(AST_STRUCT) X(AST_UNION) X(AST_STRUCT_ITEM) X(AST_BIND) #undef X default: log_abort(); } } // check if attribute exists and has non-zero value bool AstNode::get_bool_attribute(RTLIL::IdString id) { if (attributes.count(id) == 0) return false; AstNode *attr = attributes.at(id); if (attr->type != AST_CONSTANT) 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 *child4) { static unsigned int hashidx_count = 123456789; hashidx_count = mkhash_xorshift(hashidx_count); hashidx_ = hashidx_count; astnodes++; this->type = type; filename = current_filename; is_input = false; is_output = false; is_reg = false; is_logic = false; is_signed = false; is_string = false; is_enum = false; is_wand = false; is_wor = false; is_unsized = false; was_checked = false; range_valid = false; range_swapped = false; is_custom_type = false; 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); if (child2) 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 AstNode *AstNode::clone() const { AstNode *that = new AstNode; *that = *this; for (auto &it : that->children) 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; } // create a (deep recursive) copy of a node use 'other' as target root node 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; } // delete all children in this node void AstNode::delete_children() { for (auto &it : children) delete it; children.clear(); for (auto &it : attributes) delete it.second; attributes.clear(); } // AstNode destructor AstNode::~AstNode() { astnodes--; delete_children(); } // create a nice text representation of the node // (traverse tree by recursion, use 'other' pointer for diffing two AST trees) void AstNode::dumpAst(FILE *f, std::string indent) const { if (f == NULL) { for (auto f : log_files) dumpAst(f, indent); return; } std::string type_name = type2str(type); fprintf(f, "%s%s <%s>", indent.c_str(), type_name.c_str(), loc_string().c_str()); if (!flag_no_dump_ptr) { if (id2ast) fprintf(f, " [%p -> %p]", this, id2ast); else fprintf(f, " [%p]", this); } if (!str.empty()) fprintf(f, " str='%s'", str.c_str()); if (!bits.empty()) { fprintf(f, " bits='"); for (size_t i = bits.size(); i > 0; i--) fprintf(f, "%c", bits[i-1] == State::S0 ? '0' : bits[i-1] == State::S1 ? '1' : bits[i-1] == RTLIL::Sx ? 'x' : bits[i-1] == RTLIL::Sz ? 'z' : '?'); fprintf(f, "'(%d)", GetSize(bits)); } if (is_input) fprintf(f, " input"); if (is_output) fprintf(f, " output"); if (is_logic) fprintf(f, " logic"); if (is_reg) // this is an AST dump, not Verilog - if we see "logic reg" that's fine. fprintf(f, " reg"); if (is_signed) fprintf(f, " signed"); if (is_unsized) fprintf(f, " unsized"); if (basic_prep) fprintf(f, " basic_prep"); if (lookahead) fprintf(f, " lookahead"); if (port_id > 0) fprintf(f, " port=%d", port_id); if (range_valid || range_left != -1 || range_right != 0) fprintf(f, " %srange=[%d:%d]%s", range_swapped ? "swapped_" : "", range_left, range_right, range_valid ? "" : "!"); if (integer != 0) fprintf(f, " int=%u", (int)integer); if (realvalue != 0) fprintf(f, " real=%e", realvalue); 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) { fprintf(f, "%s ATTR %s:\n", indent.c_str(), it.first.c_str()); it.second->dumpAst(f, indent + " "); } for (size_t i = 0; i < children.size(); i++) children[i]->dumpAst(f, indent + " "); fflush(f); } // helper function for AstNode::dumpVlog() static std::string id2vl(std::string txt) { if (txt.size() > 1 && txt[0] == '\\') txt = txt.substr(1); for (size_t i = 0; i < txt.size(); i++) { if ('A' <= txt[i] && txt[i] <= 'Z') continue; if ('a' <= txt[i] && txt[i] <= 'z') continue; if ('0' <= txt[i] && txt[i] <= '9') continue; if (txt[i] == '_') continue; txt = "\\" + txt + " "; break; } return txt; } // dump AST node as Verilog pseudo-code void AstNode::dumpVlog(FILE *f, std::string indent) const { bool first = true; std::string txt; std::vector rem_children1, rem_children2; if (f == NULL) { for (auto f : log_files) dumpVlog(f, indent); return; } for (auto &it : attributes) { fprintf(f, "%s" "(* %s = ", indent.c_str(), id2vl(it.first.str()).c_str()); it.second->dumpVlog(f, ""); fprintf(f, " *)%s", indent.empty() ? "" : "\n"); } switch (type) { case AST_MODULE: fprintf(f, "%s" "module %s(", indent.c_str(), id2vl(str).c_str()); for (auto child : children) if (child->type == AST_WIRE && (child->is_input || child->is_output)) { fprintf(f, "%s%s", first ? "" : ", ", id2vl(child->str).c_str()); first = false; } fprintf(f, ");\n"); for (auto child : children) if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_DEFPARAM) child->dumpVlog(f, indent + " "); else rem_children1.push_back(child); for (auto child : rem_children1) if (child->type == AST_WIRE || child->type == AST_AUTOWIRE || child->type == AST_MEMORY) child->dumpVlog(f, indent + " "); else rem_children2.push_back(child); rem_children1.clear(); for (auto child : rem_children2) if (child->type == AST_TASK || child->type == AST_FUNCTION) child->dumpVlog(f, indent + " "); else rem_children1.push_back(child); rem_children2.clear(); for (auto child : rem_children1) child->dumpVlog(f, indent + " "); rem_children1.clear(); fprintf(f, "%s" "endmodule\n", indent.c_str()); break; case AST_WIRE: if (is_input && is_output) fprintf(f, "%s" "inout", indent.c_str()); else if (is_input) fprintf(f, "%s" "input", indent.c_str()); else if (is_output) fprintf(f, "%s" "output", indent.c_str()); else if (!is_reg) fprintf(f, "%s" "wire", indent.c_str()); if (is_reg) fprintf(f, "%s" "reg", (is_input || is_output) ? " " : indent.c_str()); if (is_signed) fprintf(f, " signed"); for (auto child : children) { fprintf(f, " "); child->dumpVlog(f, ""); } fprintf(f, " %s", id2vl(str).c_str()); fprintf(f, ";\n"); break; case AST_WIRETYPE: fprintf(f, "%s", id2vl(str).c_str()); break; case AST_MEMORY: fprintf(f, "%s" "memory", indent.c_str()); if (is_signed) fprintf(f, " signed"); for (auto child : children) { fprintf(f, " "); child->dumpVlog(f, ""); if (first) fprintf(f, " %s", id2vl(str).c_str()); first = false; } 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) fprintf(f, "[%d:%d]", range_right, range_left); else fprintf(f, "[%d:%d]", range_left, range_right); } else { for (auto child : children) { fprintf(f, "%c", first ? '[' : ':'); child->dumpVlog(f, ""); first = false; } fprintf(f, "]"); } 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) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) continue; fprintf(f, first ? "(" : ", "); child->dumpVlog(f, ""); first = false; } fprintf(f, first ? "*\n" : ")\n"); for (auto child : children) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) child->dumpVlog(f, indent + " "); } break; case AST_INITIAL: fprintf(f, "%s" "initial\n", indent.c_str()); for (auto child : children) { if (child->type != AST_POSEDGE && child->type != AST_NEGEDGE && child->type != AST_EDGE) child->dumpVlog(f, indent + " "); } break; case AST_POSEDGE: case AST_NEGEDGE: case AST_EDGE: if (type == AST_POSEDGE) fprintf(f, "posedge "); if (type == AST_NEGEDGE) fprintf(f, "negedge "); for (auto child : children) child->dumpVlog(f, ""); break; case AST_IDENTIFIER: { 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()); else if (bits.size() == 32) fprintf(f, "%d", RTLIL::Const(bits).as_int()); else fprintf(f, "%d'b %s", GetSize(bits), RTLIL::Const(bits).as_string().c_str()); break; case AST_REALVALUE: fprintf(f, "%e", realvalue); break; case AST_BLOCK: if (children.size() == 1) { children[0]->dumpVlog(f, indent); } else { fprintf(f, "%s" "begin\n", indent.c_str()); for (auto child : children) child->dumpVlog(f, indent + " "); fprintf(f, "%s" "end\n", indent.c_str()); } break; case AST_CASE: if (children.size() > 1 && children[1]->type == AST_CONDX) fprintf(f, "%s" "casex (", indent.c_str()); 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()); children[0]->dumpVlog(f, ""); fprintf(f, ")\n"); for (size_t i = 1; i < children.size(); i++) { AstNode *child = children[i]; child->dumpVlog(f, indent + " "); } fprintf(f, "%s" "endcase\n", indent.c_str()); break; case AST_COND: case AST_CONDX: case AST_CONDZ: for (auto child : children) { if (child->type == AST_BLOCK) { fprintf(f, ":\n"); child->dumpVlog(f, indent + " "); first = true; } else { fprintf(f, "%s", first ? indent.c_str() : ", "); if (child->type == AST_DEFAULT) fprintf(f, "default"); else child->dumpVlog(f, ""); first = false; } } break; case AST_ASSIGN: fprintf(f, "%sassign ", indent.c_str()); children[0]->dumpVlog(f, ""); fprintf(f, " = "); children[1]->dumpVlog(f, ""); fprintf(f, ";\n"); break; case AST_ASSIGN_EQ: case AST_ASSIGN_LE: fprintf(f, "%s", indent.c_str()); children[0]->dumpVlog(f, ""); fprintf(f, " %s ", type == AST_ASSIGN_EQ ? "=" : "<="); children[1]->dumpVlog(f, ""); fprintf(f, ";\n"); break; case AST_CONCAT: fprintf(f, "{"); for (int i = GetSize(children)-1; i >= 0; i--) { auto child = children[i]; if (!first) fprintf(f, ", "); child->dumpVlog(f, ""); first = false; } fprintf(f, "}"); break; case AST_REPLICATE: fprintf(f, "{"); children[0]->dumpVlog(f, ""); fprintf(f, "{"); children[1]->dumpVlog(f, ""); fprintf(f, "}}"); break; if (0) { case AST_BIT_NOT: txt = "~"; } if (0) { case AST_REDUCE_AND: txt = "&"; } if (0) { case AST_REDUCE_OR: txt = "|"; } if (0) { case AST_REDUCE_XOR: txt = "^"; } if (0) { case AST_REDUCE_XNOR: txt = "~^"; } if (0) { case AST_REDUCE_BOOL: txt = "|"; } if (0) { case AST_POS: txt = "+"; } 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: switch (children[0]->type) { case AST_WIRE: if (children[0]->children.size() > 0) children[0]->children[0]->dumpVlog(f, ""); else fprintf(f, "%d'", children[0]->range_left - children[0]->range_right + 1); break; default: 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 = "^"; } if (0) { case AST_BIT_XNOR: txt = "~^"; } if (0) { case AST_SHIFT_LEFT: txt = "<<"; } if (0) { case AST_SHIFT_RIGHT: txt = ">>"; } if (0) { case AST_SHIFT_SLEFT: txt = "<<<"; } if (0) { case AST_SHIFT_SRIGHT: txt = ">>>"; } if (0) { case AST_SHIFTX: txt = "@shiftx@"; } if (0) { case AST_SHIFT: txt = "@shift@"; } if (0) { case AST_LT: txt = "<"; } if (0) { case AST_LE: txt = "<="; } if (0) { case AST_EQ: txt = "=="; } if (0) { case AST_NE: txt = "!="; } if (0) { case AST_EQX: txt = "==="; } if (0) { case AST_NEX: txt = "!=="; } if (0) { case AST_GE: txt = ">="; } if (0) { case AST_GT: txt = ">"; } if (0) { case AST_ADD: txt = "+"; } if (0) { case AST_SUB: txt = "-"; } if (0) { case AST_MUL: txt = "*"; } if (0) { case AST_DIV: txt = "/"; } if (0) { case AST_MOD: txt = "%"; } if (0) { case AST_POW: txt = "**"; } if (0) { case AST_LOGIC_AND: txt = "&&"; } if (0) { case AST_LOGIC_OR: txt = "||"; } fprintf(f, "("); children[0]->dumpVlog(f, ""); fprintf(f, ")%s(", txt.c_str()); children[1]->dumpVlog(f, ""); fprintf(f, ")"); break; case AST_TERNARY: fprintf(f, "("); children[0]->dumpVlog(f, ""); fprintf(f, ") ? ("); children[1]->dumpVlog(f, ""); fprintf(f, ") : ("); children[2]->dumpVlog(f, ""); fprintf(f, ")"); break; default: std::string type_name = type2str(type); fprintf(f, "%s" "/** %s **/%s", indent.c_str(), type_name.c_str(), indent.empty() ? "" : "\n"); // dumpAst(f, indent, NULL); } fflush(f); } // check if two AST nodes are identical bool AstNode::operator==(const AstNode &other) const { if (type != other.type) return false; if (children.size() != other.children.size()) return false; if (str != other.str) return false; if (bits != other.bits) return false; if (is_input != other.is_input) return false; if (is_output != other.is_output) return false; if (is_logic != other.is_logic) return false; if (is_reg != other.is_reg) return false; if (is_signed != other.is_signed) return false; if (is_string != other.is_string) return false; if (range_valid != other.range_valid) return false; if (range_swapped != other.range_swapped) return false; if (port_id != other.port_id) return false; if (range_left != other.range_left) return false; if (range_right != other.range_right) return false; if (integer != other.integer) return false; for (size_t i = 0; i < children.size(); i++) if (*children[i] != *other.children[i]) return false; return true; } // check if two AST nodes are not identical bool AstNode::operator!=(const AstNode &other) const { return !(*this == other); } // check if this AST contains the given node bool AstNode::contains(const AstNode *other) const { if (this == other) return true; for (auto child : children) if (child->contains(other)) return true; return false; } // create an AST node for a constant (using a 32 bit int as value) AstNode *AstNode::mkconst_int(uint32_t v, bool is_signed, int width) { AstNode *node = new AstNode(AST_CONSTANT); node->integer = v; node->is_signed = is_signed; for (int i = 0; i < width; i++) { node->bits.push_back((v & 1) ? State::S1 : State::S0); v = v >> 1; } node->range_valid = true; node->range_left = width-1; node->range_right = 0; return node; } // create an AST node for a constant (using a bit vector as value) AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signed, bool is_unsized) { AstNode *node = new AstNode(AST_CONSTANT); node->is_signed = is_signed; node->bits = v; for (size_t i = 0; i < 32; i++) { if (i < node->bits.size()) node->integer |= (node->bits[i] == State::S1) << i; else if (is_signed && !node->bits.empty()) node->integer |= (node->bits.back() == State::S1) << i; } node->range_valid = true; node->range_left = node->bits.size()-1; node->range_right = 0; node->is_unsized = is_unsized; return node; } AstNode *AstNode::mkconst_bits(const std::vector &v, bool is_signed) { return mkconst_bits(v, is_signed, false); } // create an AST node for a constant (using a string in bit vector form as value) AstNode *AstNode::mkconst_str(const std::vector &v) { AstNode *node = mkconst_str(RTLIL::Const(v).decode_string()); while (GetSize(node->bits) < GetSize(v)) node->bits.push_back(RTLIL::State::S0); log_assert(node->bits == v); return node; } // create an AST node for a constant (using a string as value) AstNode *AstNode::mkconst_str(const std::string &str) { AstNode *node; // LRM 1364-2005 5.2.3.3 The empty string literal ("") shall be considered // equivalent to the ASCII NUL ("\0") if (str.empty()) { node = AstNode::mkconst_int(0, false, 8); } else { std::vector data; data.reserve(str.size() * 8); for (size_t i = 0; i < str.size(); i++) { unsigned char ch = str[str.size() - i - 1]; for (int j = 0; j < 8; j++) { data.push_back((ch & 1) ? State::S1 : State::S0); ch = ch >> 1; } } node = AstNode::mkconst_bits(data, false); } node->is_string = true; node->str = 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) if (bit != State::S0 && bit != State::S1) return false; return true; } RTLIL::Const AstNode::bitsAsUnsizedConst(int width) { RTLIL::State extbit = bits.back(); while (width > GetSize(bits)) bits.push_back(extbit); return RTLIL::Const(bits); } RTLIL::Const AstNode::bitsAsConst(int width, bool is_signed) { std::vector bits = this->bits; if (width >= 0 && width < GetSize(bits)) bits.resize(width); if (width >= 0 && width > GetSize(bits)) { RTLIL::State extbit = RTLIL::State::S0; if ((is_signed || is_unsized) && !bits.empty()) extbit = bits.back(); while (width > GetSize(bits)) bits.push_back(extbit); } return RTLIL::Const(bits); } RTLIL::Const AstNode::bitsAsConst(int width) { return bitsAsConst(width, is_signed); } RTLIL::Const AstNode::asAttrConst() const { log_assert(type == AST_CONSTANT); return is_string ? RTLIL::Const(str) : RTLIL::Const(bits); } 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; return val; } bool AstNode::asBool() const { log_assert(type == AST_CONSTANT); for (auto &bit : bits) if (bit == RTLIL::State::S1) return true; return false; } int AstNode::isConst() const { if (type == AST_CONSTANT) return 1; if (type == AST_REALVALUE) return 2; return 0; } uint64_t AstNode::asInt(bool is_signed) { if (type == AST_CONSTANT) { RTLIL::Const v = bitsAsConst(64, is_signed); uint64_t ret = 0; for (int i = 0; i < 64; i++) if (v.at(i) == RTLIL::State::S1) ret |= uint64_t(1) << i; return ret; } if (type == AST_REALVALUE) return uint64_t(realvalue); log_abort(); } double AstNode::asReal(bool is_signed) { if (type == AST_CONSTANT) { RTLIL::Const val(bits); bool is_negative = is_signed && !val.empty() && val.back() == RTLIL::State::S1; if (is_negative) val = const_neg(val, val, false, false, val.size()); double v = 0; for (auto i = 0; i < val.size(); i++) // IEEE Std 1800-2012 Par 6.12.2: Individual bits that are x or z in // the net or the variable shall be treated as zero upon conversion. if (val.at(i) == RTLIL::State::S1) v += exp2(i); if (is_negative) v *= -1; return v; } if (type == AST_REALVALUE) return realvalue; log_abort(); } RTLIL::Const AstNode::realAsConst(int width) { double v = round(realvalue); RTLIL::Const result; #ifdef EMSCRIPTEN if (!isfinite(v)) { #else if (!std::isfinite(v)) { #endif result = std::vector(width, RTLIL::State::Sx); } else { bool is_negative = v < 0; if (is_negative) v *= -1; for (int i = 0; i < width; i++, v /= 2) result.bits().push_back((fmod(floor(v), 2) != 0) ? RTLIL::State::S1 : RTLIL::State::S0); if (is_negative) result = const_neg(result, result, false, false, result.size()); } return result; } 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); if (defer) log("Storing AST representation for module `%s'.\n", ast->str.c_str()); else if (!quiet) { log("Generating RTLIL representation for module `%s'.\n", ast->str.c_str()); } 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; if (original_ast != NULL) ast_before_simplify = original_ast; else ast_before_simplify = ast->clone(); if (flag_dump_ast1) { log("Dumping AST before simplification:\n"); ast->dumpAst(NULL, " "); log("--- END OF AST DUMP ---\n"); } if (flag_dump_vlog1) { log("Dumping Verilog AST before simplification:\n"); ast->dumpVlog(NULL, " "); log("--- END OF AST DUMP ---\n"); } 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) { blackbox_module = true; for (auto child : ast->children) { if (child->type == AST_WIRE && (child->is_input || child->is_output)) continue; if (child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) continue; if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE && (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) continue; blackbox_module = false; break; } } // 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"); ast->dumpAst(NULL, " "); log("--- END OF AST DUMP ---\n"); } if (flag_dump_vlog2) { log("Dumping Verilog AST after simplification:\n"); ast->dumpVlog(NULL, " "); log("--- END OF AST DUMP ---\n"); } if (flag_nowb && ast->attributes.count(ID::whitebox)) { delete ast->attributes.at(ID::whitebox); ast->attributes.erase(ID::whitebox); } if (ast->attributes.count(ID::lib_whitebox)) { if (!flag_lib || flag_nowb) { delete ast->attributes.at(ID::lib_whitebox); ast->attributes.erase(ID::lib_whitebox); } else { if (ast->attributes.count(ID::whitebox)) { delete ast->attributes.at(ID::whitebox); ast->attributes.erase(ID::whitebox); } AstNode *n = ast->attributes.at(ID::lib_whitebox); ast->set_attribute(ID::whitebox, n); ast->attributes.erase(ID::lib_whitebox); } } if (!blackbox_module && ast->attributes.count(ID::blackbox)) { AstNode *n = ast->attributes.at(ID::blackbox); if (n->type != AST_CONSTANT) 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) ast->input_error("Got whitebox attribute with non-constant value!\n"); blackbox_module = !n->asBool(); } if (ast->attributes.count(ID::noblackbox)) { if (blackbox_module) { AstNode *n = ast->attributes.at(ID::noblackbox); if (n->type != AST_CONSTANT) ast->input_error("Got noblackbox attribute with non-constant value!\n"); blackbox_module = !n->asBool(); } delete ast->attributes.at(ID::noblackbox); ast->attributes.erase(ID::noblackbox); } if (blackbox_module) { if (ast->attributes.count(ID::whitebox)) { delete ast->attributes.at(ID::whitebox); ast->attributes.erase(ID::whitebox); } if (ast->attributes.count(ID::lib_whitebox)) { delete ast->attributes.at(ID::lib_whitebox); ast->attributes.erase(ID::lib_whitebox); } std::vector new_children; for (auto child : ast->children) { if (child->type == AST_WIRE && (child->is_input || child->is_output)) { new_children.push_back(child); } else if (child->type == AST_PARAMETER) { new_children.push_back(child); } else if (child->type == AST_CELL && child->children.size() > 0 && child->children[0]->type == AST_CELLTYPE && (child->children[0]->str == "$specify2" || child->children[0]->str == "$specify3" || child->children[0]->str == "$specrule")) { new_children.push_back(child); } else { delete child; } } ast->children.swap(new_children); if (ast->attributes.count(ID::blackbox) == 0) { ast->set_attribute(ID::blackbox, AstNode::mkconst_int(1, false)); } } ignoreThisSignalsInInitial = RTLIL::SigSpec(); for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) 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]; if (node->type == AST_WIRE || node->type == AST_MEMORY) node->genRTLIL(); } for (size_t i = 0; i < ast->children.size(); i++) { AstNode *node = ast->children[i]; if (node->type != AST_WIRE && node->type != AST_MEMORY && node->type != AST_INITIAL) node->genRTLIL(); } ignoreThisSignalsInInitial.sort_and_unify(); for (size_t i = 0; i < ast->children.size(); i++) { AstNode *node = ast->children[i]; if (node->type == AST_INITIAL) node->genRTLIL(); } ignoreThisSignalsInInitial = RTLIL::SigSpec(); current_scope.clear(); } else { for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) continue; 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) 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(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 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; flag_dump_vlog1 = dump_vlog1; flag_dump_vlog2 = dump_vlog2; flag_dump_rtlil = dump_rtlil; flag_nolatches = nolatches; flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_noblackbox = noblackbox; flag_lib = lib; flag_nowb = nowb; flag_noopt = noopt; flag_icells = icells; flag_pwires = pwires; flag_autowire = autowire; ast->fixup_hierarchy_flags(true); log_assert(current_ast->type == AST_DESIGN); for (AstNode *child : current_ast->children) { if (child->type == AST_MODULE || child->type == AST_INTERFACE) { for (auto n : design->verilog_globals) child->children.push_back(n->clone()); // append nodes from previous packages using package-qualified names for (auto &n : design->verilog_packages) { for (auto &o : n->children) { AstNode *cloned_node = o->clone(); // log("cloned node %s\n", type2str(cloned_node->type).c_str()); if (cloned_node->type == AST_ENUM) { for (auto &e : cloned_node->children) { log_assert(e->type == AST_ENUM_ITEM); e->str = n->str + std::string("::") + e->str.substr(1); } } else { cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1); } child->children.push_back(cloned_node); } } if (flag_icells && child->str.compare(0, 2, "\\$") == 0) child->str = child->str.substr(1); 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(child->str)) { RTLIL::Module *existing_mod = design->module(child->str); if (!nooverwrite && !overwrite && !existing_mod->get_blackbox_attribute()) { 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.\n", child->str.c_str(), child->loc_string().c_str()); continue; } else { log("Replacing existing%s module `%s' at %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", child->str.c_str(), child->loc_string().c_str()); design->remove(existing_mod); } } process_module(design, child, defer_local); current_ast_mod = nullptr; } else if (child->type == AST_PACKAGE) { // process enum/other declarations 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 if (child->type == AST_PARAMETER) child->type = AST_LOCALPARAM; // cannot be overridden design->verilog_globals.push_back(child->clone()); current_scope.clear(); } } } // AstModule destructor AstModule::~AstModule() { if (ast != NULL) delete ast; } // An interface port with modport is specified like this: // . // This function splits the interface_name from the modport_name, and fails if it is not a valid combination std::pair AST::split_modport_from_type(std::string name_type) { std::string interface_type = ""; std::string interface_modport = ""; size_t ndots = std::count(name_type.begin(), name_type.end(), '.'); // Separate the interface instance name from any modports: if (ndots == 0) { // Does not have modport interface_type = name_type; } else { std::stringstream name_type_stream(name_type); std::string segment; std::vector seglist; while(std::getline(name_type_stream, segment, '.')) { seglist.push_back(segment); } if (ndots == 1) { // Has modport interface_type = seglist[0]; interface_modport = seglist[1]; } else { // Erroneous port type log_error("More than two '.' in signal port type (%s)\n", name_type.c_str()); } } return std::pair(interface_type, interface_modport); } AstNode * AST::find_modport(AstNode *intf, std::string name) { for (auto &ch : intf->children) if (ch->type == AST_MODPORT) if (ch->str == name) // Modport found return ch; return NULL; } // Iterate over all wires in an interface and add them as wires in the AST module: void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport) { for (auto w : intfmodule->wires()){ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(w->width -1, true), AstNode::mkconst_int(0, true))); std::string origname = log_id(w->name); std::string newname = intfname + "." + origname; wire->str = newname; if (modport != NULL) { bool found_in_modport = false; // Search for the current wire in the wire list for the current modport for (auto &ch : modport->children) { if (ch->type == AST_MODPORTMEMBER) { std::string compare_name = "\\" + origname; if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output found_in_modport = true; wire->is_input = ch->is_input; wire->is_output = ch->is_output; break; } } } if (found_in_modport) { module_ast->children.push_back(wire); } else { // If not found in modport, do not create port delete wire; } } else { // If no modport, set inout wire->is_input = true; wire->is_output = true; module_ast->children.push_back(wire); } } } // 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::expand_interfaces(RTLIL::Design *design, const dict &local_interfaces) { loadconfig(); AstNode *new_ast = ast->clone(); for (auto &intf : local_interfaces) { std::string intfname = intf.first.str(); RTLIL::Module *intfmodule = intf.second; for (auto w : intfmodule->wires()){ AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(w->width -1, true), AstNode::mkconst_int(0, true))); std::string newname = log_id(w->name); newname = intfname + "." + newname; wire->str = newname; new_ast->children.push_back(wire); } } AstNode *ast_before_replacing_interface_ports = new_ast->clone(); // Explode all interface ports. Note this will only have an effect on 'top // level' modules. Other sub-modules will have their interface ports // exploded via the derive(..) function for (size_t i =0; ichildren.size(); i++) { AstNode *ch2 = new_ast->children[i]; if (ch2->type == AST_INTERFACEPORT) { // Is an interface port std::string name_port = ch2->str; // Name of the interface port if (ch2->children.size() > 0) { for(size_t j=0; jchildren.size();j++) { AstNode *ch = ch2->children[j]; if(ch->type == AST_INTERFACEPORTTYPE) { // Found the AST node containing the type of the interface std::pair res = split_modport_from_type(ch->str); std::string interface_type = res.first; std::string interface_modport = res.second; // Is "", if no modport if (design->module(interface_type) != nullptr) { // Add a cell to the module corresponding to the interface port such that // it can further propagated down if needed: AstNode *celltype_for_intf = new AstNode(AST_CELLTYPE); celltype_for_intf->str = interface_type; AstNode *cell_for_intf = new AstNode(AST_CELL, celltype_for_intf); cell_for_intf->str = name_port + "_inst_from_top_dummy"; new_ast->children.push_back(cell_for_intf); // Get all members of this non-overridden dummy interface instance: RTLIL::Module *intfmodule = design->module(interface_type); // All interfaces should at this point in time (assuming // reprocess_module is called from the hierarchy pass) be // present in design->modules_ AstModule *ast_module_of_interface = (AstModule*)intfmodule; std::string interface_modport_compare_str = "\\" + interface_modport; AstNode *modport = find_modport(ast_module_of_interface->ast, interface_modport_compare_str); // modport == NULL if no modport // Iterate over all wires in the interface and add them to the module: explode_interface_port(new_ast, intfmodule, name_port, modport); } break; } } } } } // 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); delete new_ast; // Set the attribute "interfaces_replaced_in_module" so that it does not happen again. 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 // This method is used to explode the interface when the interface is a port of the module (not instantiated inside) RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict ¶meters, const dict &interfaces, const dict &modports, bool /*mayfail*/) { AstNode *new_ast = NULL; std::string modname = derive_common(design, parameters, &new_ast); // Since interfaces themselves may be instantiated with different parameters, // "modname" must also take those into account, so that unique modules // are derived for any variant of interface connections: std::string interf_info = ""; bool has_interfaces = false; for(auto &intf : interfaces) { interf_info += log_id(intf.second->name); has_interfaces = true; } std::string new_modname = modname; if (has_interfaces) new_modname += "$interfaces$" + interf_info; if (!design->has(new_modname)) { if (!new_ast) { auto mod = dynamic_cast(design->module(modname)); new_ast = mod->ast->clone(); } modname = new_modname; new_ast->str = modname; // Iterate over all interfaces which are ports in this module: for(auto &intf : interfaces) { RTLIL::Module * intfmodule = intf.second; std::string intfname = intf.first.str(); // Check if a modport applies for the interface port: AstNode *modport = NULL; if (modports.count(intfname) > 0) { std::string interface_modport = modports.at(intfname).str(); AstModule *ast_module_of_interface = (AstModule*)intfmodule; AstNode *ast_node_of_interface = ast_module_of_interface->ast; modport = find_modport(ast_node_of_interface, interface_modport); } // Iterate over all wires in the interface and add them to the module: explode_interface_port(new_ast, intfmodule, intfname, modport); } process_module(design, new_ast, false); design->module(modname)->check(); RTLIL::Module* mod = design->module(modname); // Now that the interfaces have been exploded, we can delete the dummy port related to every interface. for(auto &intf : interfaces) { if(mod->wire(intf.first) != nullptr) { // Normally, removing wires would be batched together as it's an // expensive operation, however, in this case doing so would mean // that a cell with the same name cannot be created (below)... // Since we won't expect many interfaces to exist in a module, // we can let this slide... pool to_remove; to_remove.insert(mod->wire(intf.first)); mod->remove(to_remove); mod->fixup_ports(); // We copy the cell of the interface to the sub-module such that it // can further be found if it is propagated down to sub-sub-modules etc. RTLIL::Cell *new_subcell = mod->addCell(intf.first, intf.second->name); new_subcell->set_bool_attribute(ID::is_interface); } else { log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname.c_str()); } } // If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module': if (interfaces.size() > 0) { mod->set_bool_attribute(ID::interfaces_replaced_in_module); } } else { modname = new_modname; log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); } delete new_ast; return modname; } // create a new parametric module (when needed) and return the name of the generated module - without support for interfaces RTLIL::IdString AstModule::derive(RTLIL::Design *design, const dict ¶meters, bool /*mayfail*/) { bool quiet = lib || attributes.count(ID::blackbox) || attributes.count(ID::whitebox); AstNode *new_ast = NULL; std::string modname = derive_common(design, parameters, &new_ast, quiet); if (!design->has(modname) && new_ast) { new_ast->str = modname; 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()); } delete new_ast; return modname; } static std::string serialize_param_value(const RTLIL::Const &val) { std::string res; if (val.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) res.push_back('t'); if (val.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) res.push_back('s'); if (val.flags & RTLIL::ConstFlags::CONST_FLAG_REAL) res.push_back('r'); res += stringf("%d", GetSize(val)); res.push_back('\''); res.append(val.as_string("?")); 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); int para_counter = 0; std::vector> named_parameters; for (const auto child : ast->children) { if (child->type != AST_PARAMETER) continue; para_counter++; auto it = parameters.find(child->str); if (it != parameters.end()) { if (!quiet) log("Parameter %s = %s\n", 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)); named_parameters.emplace_back(child->str, it->second); continue; } } 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; if (!quiet) log_header(design, "Executing AST frontend in derive mode using pre-parsed AST for module `%s'.\n", stripped_name.c_str()); loadconfig(); pool rewritten; rewritten.reserve(GetSize(parameters)); AstNode *new_ast = ast->clone(); if (!new_ast->attributes.count(ID::hdlname)) new_ast->set_attribute(ID::hdlname, AstNode::mkconst_str(stripped_name.substr(1))); para_counter = 0; for (auto child : new_ast->children) { if (child->type != AST_PARAMETER) continue; para_counter++; auto it = parameters.find(child->str); if (it != parameters.end()) { if (!quiet) log("Parameter %s = %s\n", child->str.c_str(), log_signal(it->second)); goto rewrite_parameter; } 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)); goto rewrite_parameter; } continue; rewrite_parameter: if (param_has_no_default(child)) child->children.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); child->children[0]->realvalue = std::stod(it->second.decode_string()); } else if ((it->second.flags & RTLIL::CONST_FLAG_STRING) != 0) child->children[0] = AstNode::mkconst_str(it->second.decode_string()); else child->children[0] = AstNode::mkconst_bits(it->second.to_bits(), (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0); rewritten.insert(it->first); } if (GetSize(rewritten) < GetSize(parameters)) for (const auto ¶m : parameters) { if (rewritten.count(param.first)) continue; AstNode *defparam = new AstNode(AST_DEFPARAM, new AstNode(AST_IDENTIFIER)); defparam->children[0]->str = param.first.str(); if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0) defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string())); else defparam->children.push_back(AstNode::mkconst_bits(param.second.to_bits(), (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0)); new_ast->children.push_back(defparam); } new_ast->fixup_hierarchy_flags(true); (*new_ast_out) = new_ast; return modname; } RTLIL::Module *AstModule::clone() const { AstModule *new_mod = new AstModule; new_mod->name = name; cloneInto(new_mod); new_mod->ast = ast->clone(); new_mod->nolatches = nolatches; new_mod->nomeminit = nomeminit; new_mod->nomem2reg = nomem2reg; new_mod->mem2reg = mem2reg; new_mod->noblackbox = noblackbox; new_mod->lib = lib; new_mod->nowb = nowb; new_mod->noopt = noopt; new_mod->icells = icells; new_mod->pwires = pwires; new_mod->autowire = autowire; return new_mod; } void AstModule::loadconfig() const { current_ast = NULL; flag_dump_ast1 = false; flag_dump_ast2 = false; flag_dump_vlog1 = false; flag_dump_vlog2 = false; flag_nolatches = nolatches; flag_nomeminit = nomeminit; flag_nomem2reg = nomem2reg; flag_mem2reg = mem2reg; flag_noblackbox = noblackbox; flag_lib = lib; flag_nowb = nowb; flag_noopt = noopt; flag_icells = icells; flag_pwires = pwires; 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 yosys-0.52/frontends/ast/ast.h000066400000000000000000000410421477540374200163750ustar00rootroot00000000000000/* -*- 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. * * --- * * The AST frontend library is not a frontend on its own but provides an * abstract syntax tree (AST) abstraction for the open source Verilog frontend * at frontends/verilog. * */ #ifndef AST_H #define AST_H #include "kernel/rtlil.h" #include "kernel/fmt.h" #include #include YOSYS_NAMESPACE_BEGIN namespace AST { // all node types, type2str() must be extended // whenever a new node type is added here enum AstNodeType { AST_NONE, AST_DESIGN, AST_MODULE, AST_TASK, AST_FUNCTION, AST_DPI_FUNCTION, AST_WIRE, AST_MEMORY, AST_AUTOWIRE, AST_PARAMETER, AST_LOCALPARAM, AST_DEFPARAM, AST_PARASET, AST_ARGUMENT, AST_RANGE, AST_MULTIRANGE, AST_CONSTANT, AST_REALVALUE, AST_CELLTYPE, AST_IDENTIFIER, AST_PREFIX, AST_ASSERT, AST_ASSUME, AST_LIVE, AST_FAIR, AST_COVER, AST_ENUM, AST_ENUM_ITEM, AST_FCALL, AST_TO_BITS, AST_TO_SIGNED, AST_TO_UNSIGNED, AST_SELFSZ, AST_CAST_SIZE, AST_CONCAT, AST_REPLICATE, AST_BIT_NOT, AST_BIT_AND, AST_BIT_OR, AST_BIT_XOR, AST_BIT_XNOR, AST_REDUCE_AND, AST_REDUCE_OR, AST_REDUCE_XOR, AST_REDUCE_XNOR, AST_REDUCE_BOOL, AST_SHIFT_LEFT, AST_SHIFT_RIGHT, AST_SHIFT_SLEFT, AST_SHIFT_SRIGHT, AST_SHIFTX, AST_SHIFT, AST_LT, AST_LE, AST_EQ, AST_NE, AST_EQX, AST_NEX, AST_GE, AST_GT, AST_ADD, AST_SUB, AST_MUL, AST_DIV, AST_MOD, AST_POW, AST_POS, AST_NEG, AST_LOGIC_AND, AST_LOGIC_OR, AST_LOGIC_NOT, AST_TERNARY, AST_MEMRD, AST_MEMWR, AST_MEMINIT, AST_TCALL, AST_ASSIGN, AST_CELL, AST_PRIMITIVE, AST_CELLARRAY, AST_ALWAYS, AST_INITIAL, AST_BLOCK, AST_ASSIGN_EQ, AST_ASSIGN_LE, AST_CASE, AST_COND, AST_CONDX, AST_CONDZ, AST_DEFAULT, AST_FOR, AST_WHILE, AST_REPEAT, AST_GENVAR, AST_GENFOR, AST_GENIF, AST_GENCASE, AST_GENBLOCK, AST_TECALL, AST_POSEDGE, AST_NEGEDGE, AST_EDGE, AST_INTERFACE, AST_INTERFACEPORT, AST_INTERFACEPORTTYPE, AST_MODPORT, AST_MODPORTMEMBER, AST_PACKAGE, AST_WIRETYPE, AST_TYPEDEF, AST_STRUCT, AST_UNION, AST_STRUCT_ITEM, AST_BIND }; struct AstSrcLocType { unsigned int first_line, last_line; unsigned int first_column, last_column; AstSrcLocType() : first_line(0), last_line(0), first_column(0), last_column(0) {} AstSrcLocType(int _first_line, int _first_column, int _last_line, int _last_column) : first_line(_first_line), last_line(_last_line), first_column(_first_column), last_column(_last_column) {} }; // convert an node type to a string (e.g. for debug output) std::string type2str(AstNodeType type); // The AST is built using instances of this struct struct AstNode { // for dict<> and pool<> unsigned int hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } // this nodes type AstNodeType type; // the list of child nodes for this node std::vector children; // the list of attributes assigned to this node std::map attributes; bool get_bool_attribute(RTLIL::IdString id); // node content - most of it is unused in most node types std::string str; std::vector bits; bool is_input, is_output, is_reg, is_logic, is_signed, is_string, is_wand, is_wor, range_valid, range_swapped, was_checked, is_unsized, is_custom_type; int port_id, range_left, range_right; uint32_t integer; double realvalue; // set for IDs typed to an enumeration, not used bool is_enum; // 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; // this is used by simplify to detect if basic analysis has been performed already on the node bool basic_prep; // this is used for ID references in RHS expressions that should use the "new" value for non-blocking assignments bool lookahead; // this is the original sourcecode location that resulted in this AST node // it is automatically set by the constructor using AST::current_filename and // the AST::get_line_num() callback function. 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 = nullptr, AstNode *child2 = nullptr, AstNode *child3 = nullptr, AstNode *child4 = nullptr); AstNode *clone() const; void cloneInto(AstNode *other) const; void delete_children(); ~AstNode(); enum mem2reg_flags { /* status flags */ MEM2REG_FL_ALL = 0x00000001, MEM2REG_FL_ASYNC = 0x00000002, MEM2REG_FL_INIT = 0x00000004, /* candidate flags */ MEM2REG_FL_FORCED = 0x00000100, MEM2REG_FL_SET_INIT = 0x00000200, MEM2REG_FL_SET_ELSE = 0x00000400, MEM2REG_FL_SET_ASYNC = 0x00000800, MEM2REG_FL_EQ2 = 0x00001000, MEM2REG_FL_CMPLX_LHS = 0x00002000, MEM2REG_FL_CONST_LHS = 0x00004000, MEM2REG_FL_VAR_LHS = 0x00008000, /* proc flags */ MEM2REG_FL_EQ1 = 0x01000000, }; // 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, 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(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); bool mem2reg_check(pool &mem2reg_set); 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 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(); // 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); // create RTLIL code for this AST node // 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, bool sgn, const dict *new_subst_ptr = NULL); // compare AST nodes bool operator==(const AstNode &other) const; bool operator!=(const AstNode &other) const; bool contains(const AstNode *other) const; // helper functions for creating AST nodes for constants static AstNode *mkconst_int(uint32_t v, bool is_signed, int width = 32); static AstNode *mkconst_bits(const std::vector &v, bool is_signed, bool is_unsized); static AstNode *mkconst_bits(const std::vector &v, bool is_signed); 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() const; RTLIL::Const asParaConst() const; uint64_t asInt(bool is_signed); bool bits_only_01() const; bool asBool() const; // helper functions for real valued const eval int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE double asReal(bool is_signed); RTLIL::Const realAsConst(int width); // 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 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 // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; bool nolatches, nomeminit, nomem2reg, mem2reg, noblackbox, lib, nowb, noopt, icells, pwires, autowire; ~AstModule() override; 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 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; }; // this must be set by the language frontend before parsing the sources // the AstNode constructor then uses current_filename and get_line_num() // to initialize the filename and linenum properties of new nodes extern std::string current_filename; extern void (*set_line_num)(int); extern int (*get_line_num)(); // for stats unsigned long long astnode_count(); // set set_line_num and get_line_num to internal dummy functions (done by simplify() and AstModule::derive // to control the filename and linenum properties of new nodes not generated by a frontend parser) void use_internal_line_num(); // call a DPI function AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector &args); // Helper functions related to handling SystemVerilog interfaces 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_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 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 #endif yosys-0.52/frontends/ast/ast_binding.cc000066400000000000000000000027451477540374200202340ustar00rootroot00000000000000/* * 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 yosys-0.52/frontends/ast/ast_binding.h000066400000000000000000000033341477540374200200710ustar00rootroot00000000000000/* -*- 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 yosys-0.52/frontends/ast/dpicall.cc000066400000000000000000000141321477540374200173540ustar00rootroot00000000000000/* * 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.h" #ifdef YOSYS_ENABLE_PLUGINS #include #include YOSYS_NAMESPACE_BEGIN typedef void (*ffi_fptr) (); static ffi_fptr resolve_fn (std::string symbol_name) { if (symbol_name.find(':') != std::string::npos) { int pos = symbol_name.find(':'); std::string plugin_name = symbol_name.substr(0, pos); std::string real_symbol_name = symbol_name.substr(pos+1); while (loaded_plugin_aliases.count(plugin_name)) plugin_name = loaded_plugin_aliases.at(plugin_name); if (loaded_plugins.count(plugin_name) == 0) log_error("unable to resolve '%s': can't find plugin `%s'\n", symbol_name.c_str(), plugin_name.c_str()); void *symbol = dlsym(loaded_plugins.at(plugin_name), real_symbol_name.c_str()); if (symbol == nullptr) log_error("unable to resolve '%s': can't find symbol `%s' in plugin `%s'\n", symbol_name.c_str(), real_symbol_name.c_str(), plugin_name.c_str()); return (ffi_fptr) symbol; } for (auto &it : loaded_plugins) { void *symbol = dlsym(it.second, symbol_name.c_str()); if (symbol != nullptr) return (ffi_fptr) symbol; } void *symbol = dlsym(RTLD_DEFAULT, symbol_name.c_str()); if (symbol != nullptr) return (ffi_fptr) symbol; log_error("unable to resolve '%s'.\n", symbol_name.c_str()); } 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 value { double f64; float f32; int32_t i32; void *ptr; }; std::vector value_store(args.size() + 1); std::vector types(args.size() + 1); std::vector values(args.size() + 1); ffi_cif cif; int status; log("Calling DPI function `%s' and returning `%s':\n", fname.c_str(), rtype.c_str()); log_assert(GetSize(args) == GetSize(argtypes)); for (int i = 0; i < GetSize(args); i++) { if (argtypes[i] == "real") { log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed)); value_store[i].f64 = args[i]->asReal(args[i]->is_signed); values[i] = &value_store[i].f64; types[i] = &ffi_type_double; } else if (argtypes[i] == "shortreal") { log(" arg %d (%s): %f\n", i, argtypes[i].c_str(), args[i]->asReal(args[i]->is_signed)); value_store[i].f32 = args[i]->asReal(args[i]->is_signed); values[i] = &value_store[i].f32; types[i] = &ffi_type_double; } else if (argtypes[i] == "integer") { log(" arg %d (%s): %lld\n", i, argtypes[i].c_str(), (long long)args[i]->asInt(args[i]->is_signed)); 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); } } if (rtype == "integer") { types[args.size()] = &ffi_type_slong; values[args.size()] = &value_store[args.size()].i32; } else if (rtype == "shortreal") { types[args.size()] = &ffi_type_float; values[args.size()] = &value_store[args.size()].f32; } 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()); } if ((status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, args.size(), types[args.size()], types.data())) != FFI_OK) log_error("ffi_prep_cif failed: status %d.\n", status); ffi_call(&cif, resolve_fn(fname.c_str()), values[args.size()], values.data()); if (rtype == "real") { newNode = new AstNode(AST_REALVALUE); newNode->realvalue = value_store[args.size()].f64; log(" return realvalue: %g\n", newNode->asReal(true)); } else if (rtype == "shortreal") { 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)); } return newNode; } YOSYS_NAMESPACE_END #else /* YOSYS_ENABLE_PLUGINS */ YOSYS_NAMESPACE_BEGIN AST::AstNode *AST::dpi_call(const std::string&, const std::string &fname, const std::vector&, const std::vector&) { log_error("Can't call DPI function `%s': this version of yosys is built without plugin support\n", fname.c_str()); } YOSYS_NAMESPACE_END #endif /* YOSYS_ENABLE_PLUGINS */ yosys-0.52/frontends/ast/genrtlil.cc000066400000000000000000002430341477540374200175710ustar00rootroot00000000000000/* * 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 is the AST frontend library. * * The AST frontend library is not a frontend on it's own but provides a * generic abstract syntax tree (AST) abstraction for HDL code and can be * used by HDL frontends. See "ast.h" for an overview of the API and the * Verilog frontend for an usage example. * */ #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 #include YOSYS_NAMESPACE_BEGIN using namespace AST; 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(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); 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) that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } cell->parameters[ID::A_SIGNED] = RTLIL::Const(that->children[0]->is_signed); cell->parameters[ID::A_WIDTH] = RTLIL::Const(arg.size()); cell->setPort(ID::A, arg); cell->parameters[ID::Y_WIDTH] = result_width; cell->setPort(ID::Y, wire); return wire; } // helper function for extending bit width (preferred over SigSpec::extend() because of correct undef propagation in ConstEval) static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_signed) { if (width <= sig.size()) { sig.extend_u0(width, is_signed); return; } 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)); set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width); 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) that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } cell->parameters[ID::A_SIGNED] = RTLIL::Const(is_signed); cell->parameters[ID::A_WIDTH] = RTLIL::Const(sig.size()); cell->setPort(ID::A, sig); cell->parameters[ID::Y_WIDTH] = width; cell->setPort(ID::Y, wire); sig = wire; } // 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(), RTLIL::encode_filename(that->filename).c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); set_src_attr(wire, that); wire->is_signed = that->is_signed; for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } cell->parameters[ID::A_SIGNED] = RTLIL::Const(that->children[0]->is_signed); cell->parameters[ID::B_SIGNED] = RTLIL::Const(that->children[1]->is_signed); cell->parameters[ID::A_WIDTH] = RTLIL::Const(left.size()); cell->parameters[ID::B_WIDTH] = RTLIL::Const(right.size()); cell->setPort(ID::A, left); cell->setPort(ID::B, right); cell->parameters[ID::Y_WIDTH] = result_width; cell->setPort(ID::Y, wire); return wire; } // helper function for creating RTLIL code for multiplexers static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { log_assert(cond.size() == 1); std::stringstream sstr; sstr << "$ternary$" << RTLIL::encode_filename(that->filename) << ":" << that->location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); set_src_attr(cell, that); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size()); set_src_attr(wire, that); wire->is_signed = that->is_signed; for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) that->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } cell->parameters[ID::WIDTH] = RTLIL::Const(left.size()); cell->setPort(ID::A, right); cell->setPort(ID::B, left); cell->setPort(ID::S, cond); cell->setPort(ID::Y, wire); 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 { dict> lookaheadids; void collect_lookaheadids(AstNode *node) { if (node->lookahead) { log_assert(node->type == AST_IDENTIFIER); if (!lookaheadids.count(node->str)) { 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->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); wire->is_logic = true; while (wire->simplify(true, 1, -1, false)) { } current_ast_mod->children.push_back(wire); lookaheadids[node->str] = make_pair(node->id2ast, wire); wire->genRTLIL(); } } for (auto child : node->children) collect_lookaheadids(child); } bool has_lookaheadids(AstNode *node) { if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) != 0) return true; for (auto child : node->children) if (has_lookaheadids(child)) return true; return false; } bool has_nonlookaheadids(AstNode *node) { if (node->type == AST_IDENTIFIER && lookaheadids.count(node->str) == 0) return true; for (auto child : node->children) if (has_nonlookaheadids(child)) return true; return false; } void rewrite_lookaheadids(AstNode *node, bool lhs = false) { if (node->type == AST_ASSIGN_LE) { if (has_lookaheadids(node->children[0])) { if (has_nonlookaheadids(node->children[0])) log_error("incompatible mix of lookahead and non-lookahead IDs in LHS expression.\n"); rewrite_lookaheadids(node->children[0], true); node->type = AST_ASSIGN_EQ; } rewrite_lookaheadids(node->children[1], lhs); return; } if (node->type == AST_IDENTIFIER && (node->lookahead || lhs)) { AstNode *newwire = lookaheadids.at(node->str).second; node->str = newwire->str; node->id2ast = newwire; lhs = false; } for (auto child : node->children) rewrite_lookaheadids(child, lhs); } LookaheadRewriter(AstNode *top) { // top->dumpAst(NULL, "REWRITE-BEFORE> "); // top->dumpVlog(NULL, "REWRITE-BEFORE> "); AstNode *block = nullptr; for (auto c : top->children) if (c->type == AST_BLOCK) { log_assert(block == nullptr); block = c; } log_assert(block != nullptr); collect_lookaheadids(block); rewrite_lookaheadids(block); for (auto it : lookaheadids) { AstNode *ref_orig = new AstNode(AST_IDENTIFIER); ref_orig->str = it.second.first->str; ref_orig->id2ast = it.second.first; ref_orig->was_checked = true; AstNode *ref_temp = new AstNode(AST_IDENTIFIER); ref_temp->str = it.second.second->str; ref_temp->id2ast = it.second.second; ref_temp->was_checked = true; AstNode *init_assign = new AstNode(AST_ASSIGN_EQ, ref_temp->clone(), ref_orig->clone()); AstNode *final_assign = new AstNode(AST_ASSIGN_LE, ref_orig, ref_temp); block->children.insert(block->children.begin(), init_assign); block->children.push_back(final_assign); } // top->dumpAst(NULL, "REWRITE-AFTER> "); // top->dumpVlog(NULL, "REWRITE-AFTER> "); } }; // helper class for converting AST always nodes to RTLIL processes struct AST_INTERNAL::ProcessGenerator { // input and output structures AstNode *always; RTLIL::SigSpec initSyncSignals; RTLIL::Process *proc; RTLIL::SigSpec outputSignals; // This always points to the RTLIL::CaseRule being filled at the moment RTLIL::CaseRule *current_case; // This map contains the replacement pattern to be used in the right hand side // of an assignment. E.g. in the code "foo = bar; foo = func(foo);" the foo in the right // hand side of the 2nd assignment needs to be replace with the temporary signal holding // the value assigned in the first assignment. So when the first assignment is processed // the according information is appended to subst_rvalue_from and subst_rvalue_to. stackmap subst_rvalue_map; // This map contains the replacement pattern to be used in the left hand side // of an assignment. E.g. in the code "always @(posedge clk) foo <= bar" the signal bar // should not be connected to the signal foo. Instead it must be connected to the temporary // signal that is used as input for the register that drives the signal foo. stackmap subst_lvalue_map; // The code here generates a number of temporary signal for each output register. This // map helps generating nice numbered names for all this temporary signals. std::map new_temp_count; // Buffer for generating the init action RTLIL::SigSpec init_lvalue, init_rvalue; // 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 = 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) always->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); proc->attributes[attr.first] = attr.second->asAttrConst(); } current_case = &proc->root_case; // create initial temporary signal for all output registers RTLIL::SigSpec subst_lvalue_from, subst_lvalue_to; collect_lvalues(subst_lvalue_from, always, true, true); subst_lvalue_to = new_temp_signal(subst_lvalue_from); subst_lvalue_map = subst_lvalue_from.to_sigbit_map(subst_lvalue_to); bool found_global_syncs = false; bool found_anyedge_syncs = false; for (auto child : always->children) { if ((child->type == AST_POSEDGE || child->type == AST_NEGEDGE) && GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->id2ast && child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute(ID::gclk)) { found_global_syncs = true; } if (child->type == AST_EDGE) { if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->str == "\\$global_clock") found_global_syncs = true; else found_anyedge_syncs = true; } } if (found_anyedge_syncs) { if (found_global_syncs) 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"); } // create syncs for the process bool found_clocked_sync = false; for (auto child : always->children) if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) { if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->id2ast && child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute(ID::gclk)) continue; found_clocked_sync = true; if (found_global_syncs || found_anyedge_syncs) 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) 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); } if (proc->syncs.empty()) { RTLIL::SyncRule *syncrule = new RTLIL::SyncRule; syncrule->type = found_global_syncs ? RTLIL::STg : RTLIL::STa; syncrule->signal = RTLIL::SigSpec(); addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true); proc->syncs.push_back(syncrule); } // create initial assignments for the temporary signals if ((flag_nolatches || always->get_bool_attribute(ID::nolatches) || current_module->get_bool_attribute(ID::nolatches)) && !found_clocked_sync) { subst_rvalue_map = subst_lvalue_from.to_sigbit_dict(RTLIL::SigSpec(RTLIL::State::Sx, GetSize(subst_lvalue_from))); } else { addChunkActions(current_case->actions, subst_lvalue_to, subst_lvalue_from); } // process the AST for (auto child : always->children) if (child->type == AST_BLOCK) processAst(child); for (auto sync: proc->syncs) processMemWrites(sync); if (initSyncSignals.size() > 0) { RTLIL::SyncRule *sync = new RTLIL::SyncRule; sync->type = RTLIL::SyncType::STi; proc->syncs.push_back(sync); log_assert(init_lvalue.size() == init_rvalue.size()); int offset = 0; for (auto &init_lvalue_c : init_lvalue.chunks()) { RTLIL::SigSpec lhs = init_lvalue_c; RTLIL::SigSpec rhs = init_rvalue.extract(offset, init_lvalue_c.width); remove_unwanted_lvalue_bits(lhs, rhs); sync->actions.push_back(RTLIL::SigSig(lhs, rhs)); offset += lhs.size(); } } outputSignals = RTLIL::SigSpec(subst_lvalue_from); } void remove_unwanted_lvalue_bits(RTLIL::SigSpec &lhs, RTLIL::SigSpec &rhs) { RTLIL::SigSpec new_lhs, new_rhs; log_assert(GetSize(lhs) == GetSize(rhs)); for (int i = 0; i < GetSize(lhs); i++) { if (lhs[i].wire == nullptr) continue; new_lhs.append(lhs[i]); new_rhs.append(rhs[i]); } lhs = new_lhs; rhs = new_rhs; } // create new temporary signals RTLIL::SigSpec new_temp_signal(RTLIL::SigSpec sig) { std::vector chunks = sig.chunks(); for (int i = 0; i < GetSize(chunks); i++) { RTLIL::SigChunk &chunk = chunks[i]; if (chunk.wire == NULL) continue; std::string wire_name; do { wire_name = stringf("$%d%s[%d:%d]", new_temp_count[chunk.wire]++, chunk.wire->name.c_str(), chunk.width+chunk.offset-1, chunk.offset);; if (chunk.wire->name.str().find('$') != std::string::npos) wire_name += stringf("$%d", autoidx++); } while (current_module->wires_.count(wire_name) > 0); RTLIL::Wire *wire = current_module->addWire(wire_name, chunk.width); set_src_attr(wire, always); chunk.wire = wire; chunk.offset = 0; } return chunks; } // recursively traverse the AST and collect all assigned signals void collect_lvalues(RTLIL::SigSpec ®, AstNode *ast, bool type_eq, bool type_le, bool run_sort_and_unify = true) { switch (ast->type) { case AST_CASE: for (auto child : ast->children) if (child != ast->children[0]) { log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); collect_lvalues(reg, child, type_eq, type_le, false); } break; case AST_COND: case AST_CONDX: case AST_CONDZ: case AST_ALWAYS: case AST_INITIAL: for (auto child : ast->children) if (child->type == AST_BLOCK) collect_lvalues(reg, child, type_eq, type_le, false); break; case AST_BLOCK: for (auto child : ast->children) { if (child->type == AST_ASSIGN_EQ && type_eq) reg.append(child->children[0]->genRTLIL()); if (child->type == AST_ASSIGN_LE && type_le) reg.append(child->children[0]->genRTLIL()); if (child->type == AST_CASE || child->type == AST_BLOCK) collect_lvalues(reg, child, type_eq, type_le, false); } break; default: log_abort(); } if (run_sort_and_unify) { std::set sorted_reg; for (auto bit : reg) if (bit.wire) sorted_reg.insert(bit); reg = RTLIL::SigSpec(sorted_reg); } } // remove all assignments to the given signal pattern in a case and all its children. // e.g. when the last statement in the code "a = 23; if (b) a = 42; a = 0;" is processed this // function is called to clean up the first two assignments as they are overwritten by // the third assignment. void removeSignalFromCaseTree(const RTLIL::SigSpec &pattern, RTLIL::CaseRule *cs) { for (auto it = cs->actions.begin(); it != cs->actions.end(); it++) it->first.remove2(pattern, &it->second); for (auto it = cs->switches.begin(); it != cs->switches.end(); it++) for (auto it2 = (*it)->cases.begin(); it2 != (*it)->cases.end(); it2++) removeSignalFromCaseTree(pattern, *it2); } // add an assignment (aka "action") but split it up in chunks. this way huge assignments // are avoided and the generated $mux cells have a more "natural" size. void addChunkActions(std::vector &actions, RTLIL::SigSpec lvalue, RTLIL::SigSpec rvalue, bool inSyncRule = false) { if (inSyncRule && initSyncSignals.size() > 0) { init_lvalue.append(lvalue.extract(initSyncSignals)); init_rvalue.append(lvalue.extract(initSyncSignals, &rvalue)); lvalue.remove2(initSyncSignals, &rvalue); } log_assert(lvalue.size() == rvalue.size()); int offset = 0; for (auto &lvalue_c : lvalue.chunks()) { RTLIL::SigSpec lhs = lvalue_c; RTLIL::SigSpec rhs = rvalue.extract(offset, lvalue_c.width); if (inSyncRule && lvalue_c.wire && lvalue_c.wire->get_bool_attribute(ID::nosync)) rhs = RTLIL::SigSpec(RTLIL::State::Sx, rhs.size()); remove_unwanted_lvalue_bits(lhs, rhs); actions.push_back(RTLIL::SigSig(lhs, rhs)); offset += lhs.size(); } } // recursively process the AST and fill the RTLIL::Process void processAst(AstNode *ast) { switch (ast->type) { case AST_BLOCK: for (auto child : ast->children) processAst(child); break; case AST_ASSIGN_EQ: case AST_ASSIGN_LE: { RTLIL::SigSpec unmapped_lvalue = ast->children[0]->genRTLIL(), lvalue = unmapped_lvalue; 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++) { if (lvalue_sigbits.count(lvalue[i]) > 0) { unmapped_lvalue.remove(i); lvalue.remove(i); rvalue.remove(i--); } else lvalue_sigbits.insert(lvalue[i]); } lvalue.replace(subst_lvalue_map.stdmap()); if (ast->type == AST_ASSIGN_EQ) { for (int i = 0; i < GetSize(unmapped_lvalue); i++) subst_rvalue_map.set(unmapped_lvalue[i], rvalue[i]); } removeSignalFromCaseTree(lvalue, current_case); remove_unwanted_lvalue_bits(lvalue, rvalue); current_case->actions.push_back(RTLIL::SigSig(lvalue, rvalue)); } break; case AST_CASE: { int width_hint; bool sign_hint; ast->detectSignWidth(width_hint, sign_hint); RTLIL::SwitchRule *sw = new RTLIL::SwitchRule; 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) ast->input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); sw->attributes[attr.first] = attr.second->asAttrConst(); } RTLIL::SigSpec this_case_eq_lvalue; collect_lvalues(this_case_eq_lvalue, ast, true, false); RTLIL::SigSpec this_case_eq_ltemp = new_temp_signal(this_case_eq_lvalue); RTLIL::SigSpec this_case_eq_rvalue = this_case_eq_lvalue; this_case_eq_rvalue.replace(subst_rvalue_map.stdmap()); RTLIL::CaseRule *default_case = NULL; RTLIL::CaseRule *last_generated_case = NULL; for (auto child : ast->children) { if (child == ast->children[0]) continue; log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); subst_lvalue_map.save(); subst_rvalue_map.save(); for (int i = 0; i < GetSize(this_case_eq_lvalue); i++) subst_lvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); RTLIL::CaseRule *backup_case = current_case; current_case = new RTLIL::CaseRule; 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) { if (node->type == AST_DEFAULT) default_case = current_case; else if (node->type == AST_BLOCK) processAst(node); else 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); else log_assert(current_case->compare.size() == 0); current_case = backup_case; subst_lvalue_map.restore(); subst_rvalue_map.restore(); } if (last_generated_case != NULL && ast->get_bool_attribute(ID::full_case) && default_case == NULL) { #if 0 // this is a valid transformation, but as optimization it is premature. // better: add a default case that assigns 'x' to everything, and let later // optimizations take care of the rest last_generated_case->compare.clear(); #else default_case = new RTLIL::CaseRule; addChunkActions(default_case->actions, this_case_eq_ltemp, SigSpec(State::Sx, GetSize(this_case_eq_rvalue))); sw->cases.push_back(default_case); #endif } else { if (default_case == NULL) { default_case = new RTLIL::CaseRule; addChunkActions(default_case->actions, this_case_eq_ltemp, this_case_eq_rvalue); } sw->cases.push_back(default_case); } for (int i = 0; i < GetSize(this_case_eq_lvalue); i++) subst_rvalue_map.set(this_case_eq_lvalue[i], this_case_eq_ltemp[i]); this_case_eq_lvalue.replace(subst_lvalue_map.stdmap()); removeSignalFromCaseTree(this_case_eq_lvalue, current_case); addChunkActions(current_case->actions, this_case_eq_lvalue, this_case_eq_ltemp); } break; case AST_WIRE: ast->input_error("Found reg declaration in block without label!\n"); break; case AST_ASSIGN: ast->input_error("Found continous assignment in always/initial block!\n"); break; case AST_PARAMETER: case AST_LOCALPARAM: ast->input_error("Found parameter declaration in block without label!\n"); break; 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_literal("\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; default: // ast->dumpAst(NULL, "ast> "); // current_ast_mod->dumpAst(NULL, "mod> "); 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[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) { std::string type_name; bool sub_sign_hint = true; int sub_width_hint = -1; int this_width = 0; AstNode *range = NULL; AstNode *id_ast = NULL; bool local_found_real = false; if (found_real == NULL) found_real = &local_found_real; switch (type) { case AST_NONE: // unallocated enum, ignore break; case AST_CONSTANT: width_hint = max(width_hint, GetSize(bits)); if (!is_signed) sign_hint = false; break; case AST_REALVALUE: *found_real = true; width_hint = max(width_hint, 32); break; case AST_IDENTIFIER: id_ast = id2ast; 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) 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, 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) { if (!id_ast->range_valid) { if (id_ast->type == AST_AUTOWIRE) this_width = 1; else { // current_ast_mod->dumpAst(NULL, "mod> "); // log("---\n"); // id_ast->dumpAst(NULL, "decl> "); // dumpAst(NULL, "ref> "); 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; if (children.size() != 0) range = children[0]; } } else if (id_ast->type == AST_GENVAR) { this_width = 32; } else if (id_ast->type == AST_MEMORY) { if (!id_ast->children[0]->range_valid) 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 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_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) 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; } else this_width = range->range_left - range->range_right + 1; sign_hint = false; } width_hint = max(width_hint, this_width); if (!id_ast->is_signed) sign_hint = false; break; case AST_TO_BITS: while (children[0]->simplify(true, 1, -1, false) == true) { } if (children[0]->type != AST_CONSTANT) 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; case AST_TO_SIGNED: children.at(0)->detectSignWidthWorker(width_hint, sub_sign_hint); break; case AST_TO_UNSIGNED: children.at(0)->detectSignWidthWorker(width_hint, sub_sign_hint); sign_hint = false; break; case AST_SELFSZ: sub_width_hint = 0; children.at(0)->detectSignWidthWorker(sub_width_hint, sign_hint); break; case AST_CAST_SIZE: while (children.at(0)->simplify(true, 1, -1, false)) { } if (children.at(0)->type != AST_CONSTANT) input_error("Static cast with non constant expression!\n"); children.at(1)->detectSignWidthWorker(width_hint, sign_hint); this_width = children.at(0)->bitsAsConst().as_int(); width_hint = max(width_hint, this_width); if (width_hint <= 0) input_error("Static cast with zero or negative size!\n"); break; case AST_CONCAT: for (auto child : children) { sub_width_hint = 0; sub_sign_hint = true; child->detectSignWidthWorker(sub_width_hint, sub_sign_hint); this_width += sub_width_hint; } width_hint = max(width_hint, this_width); sign_hint = false; break; case AST_REPLICATE: while (children[0]->simplify(true, 1, -1, false) == true) { } if (children[0]->type != AST_CONSTANT) 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; break; case AST_NEG: case AST_BIT_NOT: case AST_POS: children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_BIT_AND: case AST_BIT_OR: case AST_BIT_XOR: case AST_BIT_XNOR: for (auto child : children) child->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_REDUCE_AND: case AST_REDUCE_OR: case AST_REDUCE_XOR: case AST_REDUCE_XNOR: case AST_REDUCE_BOOL: width_hint = max(width_hint, 1); sign_hint = false; break; case AST_SHIFT_LEFT: case AST_SHIFT_RIGHT: case AST_SHIFT_SLEFT: case AST_SHIFT_SRIGHT: case AST_SHIFTX: case AST_SHIFT: case AST_POW: children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_LT: case AST_LE: case AST_EQ: case AST_NE: case AST_EQX: case AST_NEX: case AST_GE: case AST_GT: width_hint = max(width_hint, 1); sign_hint = false; break; case AST_ADD: case AST_SUB: case AST_MUL: case AST_DIV: case AST_MOD: for (auto child : children) child->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_LOGIC_AND: case AST_LOGIC_OR: case AST_LOGIC_NOT: width_hint = max(width_hint, 1); sign_hint = false; break; case AST_TERNARY: children.at(1)->detectSignWidthWorker(width_hint, sign_hint, found_real); children.at(2)->detectSignWidthWorker(width_hint, sign_hint, found_real); break; case AST_MEMRD: if (!id2ast->is_signed) sign_hint = false; if (!id2ast->children[0]->range_valid) 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, 1, -1, false) == true) { } if (children[0]->type != AST_CONSTANT) 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))); } break; } if (str == "\\$past") { if (GetSize(children) > 0) { sub_width_hint = 0; sub_sign_hint = true; children.at(0)->detectSignWidthWorker(sub_width_hint, sub_sign_hint); width_hint = max(width_hint, sub_width_hint); 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_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) sign_hint = true; } // detect sign and width of an expression void AstNode::detectSignWidth(int &width_hint, bool &sign_hint, bool *found_real) { width_hint = -1; sign_hint = true; 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 // all generated cells, wires and processes are added to the module pointed to by 'current_module' // when the AST node is an expression (AST_ADD, AST_BIT_XOR, etc.), the result signal is returned. // // note that this function is influenced by a number of global variables that might be set when // called from genWidthRTLIL(). also note that this function recursively calls itself to transform // larger expressions into a netlist of cells. RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { // in the following big switch() statement there are some uses of // Clifford's Device (http://www.clifford.at/cfun/cliffdev/). In this // cases this variable is used to hold the type of the cell that should // be instantiated for this type of AST node. IdString type_name; current_filename = filename; switch (type) { // simply ignore this nodes. // they are either leftovers from simplify() or are referenced by other nodes // and are only accessed here thru this references case AST_NONE: case AST_TASK: case AST_FUNCTION: case AST_DPI_FUNCTION: case AST_AUTOWIRE: case AST_DEFPARAM: case AST_GENVAR: case AST_GENFOR: case AST_GENBLOCK: case AST_GENIF: case AST_GENCASE: case AST_PACKAGE: case AST_ENUM: case AST_MODPORT: case AST_MODPORTMEMBER: case AST_TYPEDEF: case AST_STRUCT: case AST_UNION: break; case AST_INTERFACEPORT: { // 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::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; wire->port_output = true; wire->set_bool_attribute(ID::is_interface); if (children.size() > 0) { for(size_t i=0; itype == AST_INTERFACEPORTTYPE) { std::pair res = AST::split_modport_from_type(children[i]->str); wire->attributes[ID::interface_type] = res.first; if (res.second != "") wire->attributes[ID::interface_modport] = res.second; break; } } } wire->upto = 0; } break; case AST_INTERFACEPORTTYPE: break; // remember the parameter, needed for example in techmap case AST_PARAMETER: current_module->avail_parameters(str); if (GetSize(children) >= 1 && children[0]->type == AST_CONSTANT) { current_module->parameter_default_values[str] = children[0]->asParaConst(); } YS_FALLTHROUGH case AST_LOCALPARAM: if (flag_pwires) { if (GetSize(children) < 1 || children[0]->type != AST_CONSTANT) input_error("Parameter `%s' with non-constant value!\n", str.c_str()); RTLIL::Const val = children[0]->bitsAsConst(); 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; 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) input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); wire->attributes[attr.first] = attr.second->asAttrConst(); } } break; // create an RTLIL::Wire for an AST_WIRE node case AST_WIRE: { if (!range_valid) input_error("Signal `%s' with non-constant width!\n", str.c_str()); if (!(range_left + 1 >= range_right)) input_error("Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1); 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; wire->port_output = is_output; wire->upto = range_swapped; wire->is_signed = is_signed; for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); wire->attributes[attr.first] = attr.second->asAttrConst(); } if (is_wand) wire->set_bool_attribute(ID::wand); if (is_wor) wire->set_bool_attribute(ID::wor); } break; // create an RTLIL::Memory for an AST_MEMORY node case AST_MEMORY: { 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) input_error("Memory `%s' with non-constant width or size!\n", str.c_str()); RTLIL::Memory *memory = new RTLIL::Memory; 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) { memory->start_offset = children[1]->range_right; memory->size = children[1]->range_left - children[1]->range_right + 1; } else { 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) input_error("Attribute `%s' with non-constant value!\n", attr.first.c_str()); memory->attributes[attr.first] = attr.second->asAttrConst(); } } break; // simply return the corresponding RTLIL::SigSpec for an AST_CONSTANT node case AST_CONSTANT: case AST_REALVALUE: { if (width_hint < 0) detectSignWidth(width_hint, sign_hint); is_signed = sign_hint; if (type == AST_CONSTANT) { if (is_unsized) { return RTLIL::SigSpec(bitsAsUnsizedConst(width_hint)); } else { return RTLIL::SigSpec(bitsAsConst()); } } RTLIL::SigSpec sig = realAsConst(width_hint); log_file_warning(filename, location.first_line, "converting real value %e to binary %s.\n", realvalue, log_signal(sig)); return sig; } // simply return the corresponding RTLIL::SigSpec for an AST_IDENTIFIER node // for identifiers with dynamic bit ranges (e.g. "foo[bar]" or "foo[bar+3:bar]") a // shifter cell is created and the output signal of this cell is returned case AST_IDENTIFIER: { RTLIL::Wire *wire = NULL; RTLIL::SigChunk chunk; bool is_interface = false; AST::AstNode *member_node = NULL; int add_undef_bits_msb = 0; int add_undef_bits_lsb = 0; log_assert(id2ast != nullptr); if (id2ast->type == AST_AUTOWIRE && current_module->wires_.count(str) == 0) { RTLIL::Wire *wire = current_module->addWire(str); set_src_attr(wire, this); wire->name = str; // 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 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) 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; } else if ((id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) { RTLIL::Wire *current_wire = current_module->wire(str); if (current_wire->get_bool_attribute(ID::is_interface)) is_interface = true; // Ignore } // If an identifier is found that is not already known, assume that it is an interface: else if (1) { // FIXME: Check if sv_mode first? is_interface = true; } else { input_error("Identifier `%s' doesn't map to any signal!\n", str.c_str()); } if (id2ast->type == AST_MEMORY) 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 // with the individual signals: if (is_interface) { IdString dummy_wire_name = stringf("$dummywireforinterface%s", str.c_str()); RTLIL::Wire *dummy_wire = current_module->wire(dummy_wire_name); if (!dummy_wire) { dummy_wire = current_module->addWire(dummy_wire_name); dummy_wire->set_bool_attribute(ID::is_interface); } return dummy_wire; } wire = current_module->wires_[str]; chunk.wire = wire; 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) 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_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) 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 (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) { shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast_sign); fake_ast->children[1]->is_signed = true; } if (GetSize(shift_val) >= 32) fake_ast->children[1]->is_signed = true; RTLIL::SigSpec sig = binop2rtlil(fake_ast, ID($shiftx), width, fake_ast->children[0]->genRTLIL(), shift_val); delete left_at_zero_ast; delete right_at_zero_ast; delete fake_ast; return sig; } else { chunk.width = children[0]->range_left - children[0]->range_right + 1; chunk.offset += children[0]->range_right - source_offset; if (id2ast->range_swapped) 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()); else log_file_warning(filename, location.first_line, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n", children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width); chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); } else { 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 < chunk_right) { add_undef_bits_lsb = chunk_right - chunk.offset; chunk.width -= add_undef_bits_lsb; chunk.offset += add_undef_bits_lsb; } if (add_undef_bits_lsb) log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n", children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb); if (add_undef_bits_msb) log_file_warning(filename, location.first_line, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n", children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb); } } } RTLIL::SigSpec sig = { RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_msb), chunk, RTLIL::SigSpec(RTLIL::State::Sx, add_undef_bits_lsb) }; if (genRTLIL_subst_ptr) sig.replace(*genRTLIL_subst_ptr); is_signed = children.size() > 0 ? false : id2ast->is_signed && sign_hint; return sig; } // just pass thru the signal. the parent will evaluate the is_signed property and interpret the SigSpec accordingly case AST_TO_SIGNED: case AST_TO_UNSIGNED: case AST_SELFSZ: { RTLIL::SigSpec sig = children[0]->genRTLIL(); if (sig.size() < width_hint) sig.extend_u0(width_hint, sign_hint); is_signed = sign_hint; return sig; } // changing the size of signal can be done directly using RTLIL::SigSpec case AST_CAST_SIZE: { RTLIL::SigSpec size = children[0]->genRTLIL(); if (!size.is_fully_const()) input_error("Static cast with non constant expression!\n"); int width = size.as_int(); if (width <= 0) 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; } // concatenation of signals can be done directly using RTLIL::SigSpec case AST_CONCAT: { RTLIL::SigSpec sig; for (auto it = children.begin(); it != children.end(); it++) sig.append((*it)->genRTLIL()); if (sig.size() < width_hint) sig.extend_u0(width_hint, false); return sig; } // replication of signals can be done directly using RTLIL::SigSpec case AST_REPLICATE: { RTLIL::SigSpec left = children[0]->genRTLIL(); RTLIL::SigSpec right = children[1]->genRTLIL(); if (!left.is_fully_const()) 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++) sig.append(right); if (sig.size() < width_hint) sig.extend_u0(width_hint, false); is_signed = false; return sig; } // generate cells for unary operations: $not, $pos, $neg if (0) { case AST_BIT_NOT: type_name = ID($not); } if (0) { case AST_POS: type_name = ID($pos); } if (0) { case AST_NEG: type_name = ID($neg); } { RTLIL::SigSpec arg = children[0]->genRTLIL(width_hint, sign_hint); is_signed = children[0]->is_signed; int width = arg.size(); if (width_hint > 0) { width = width_hint; widthExtend(this, arg, width, is_signed); } return uniop2rtlil(this, type_name, width, arg); } // generate cells for binary operations: $and, $or, $xor, $xnor if (0) { case AST_BIT_AND: type_name = ID($and); } if (0) { case AST_BIT_OR: type_name = ID($or); } if (0) { case AST_BIT_XOR: type_name = ID($xor); } if (0) { case AST_BIT_XNOR: type_name = ID($xnor); } { 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(width_hint, sign_hint); int width = max(left.size(), right.size()); if (width_hint > 0) width = width_hint; is_signed = children[0]->is_signed && children[1]->is_signed; return binop2rtlil(this, type_name, width, left, right); } // generate cells for unary operations: $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor if (0) { case AST_REDUCE_AND: type_name = ID($reduce_and); } if (0) { case AST_REDUCE_OR: type_name = ID($reduce_or); } if (0) { case AST_REDUCE_XOR: type_name = ID($reduce_xor); } if (0) { case AST_REDUCE_XNOR: type_name = ID($reduce_xnor); } { RTLIL::SigSpec arg = children[0]->genRTLIL(); RTLIL::SigSpec sig = uniop2rtlil(this, type_name, max(width_hint, 1), arg); return sig; } // generate cells for unary operations: $reduce_bool // (this is actually just an $reduce_or, but for clarity a different cell type is used) if (0) { case AST_REDUCE_BOOL: type_name = ID($reduce_bool); } { RTLIL::SigSpec arg = children[0]->genRTLIL(); RTLIL::SigSpec sig = arg.size() > 1 ? uniop2rtlil(this, type_name, max(width_hint, 1), arg) : arg; return sig; } // generate cells for binary operations: $shl, $shr, $sshl, $sshr if (0) { case AST_SHIFT_LEFT: type_name = ID($shl); } if (0) { case AST_SHIFT_RIGHT: type_name = ID($shr); } if (0) { case AST_SHIFT_SLEFT: type_name = ID($sshl); } if (0) { case AST_SHIFT_SRIGHT: type_name = ID($sshr); } if (0) { case AST_SHIFTX: type_name = ID($shiftx); } if (0) { case AST_SHIFT: type_name = ID($shift); } { if (width_hint < 0) detectSignWidth(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); // 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); } // generate cells for binary operations: $pow case AST_POW: { int right_width; bool right_signed; children[1]->detectSignWidth(right_width, right_signed); 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(right_width, right_signed); int width = width_hint > 0 ? width_hint : left.size(); is_signed = children[0]->is_signed; if (!flag_noopt && left.is_fully_const() && left.as_int() == 2 && !right_signed) return binop2rtlil(this, ID($shl), width, RTLIL::SigSpec(1, left.size()), right); return binop2rtlil(this, ID($pow), width, left, right); } // generate cells for binary operations: $lt, $le, $eq, $ne, $ge, $gt if (0) { case AST_LT: type_name = ID($lt); } if (0) { case AST_LE: type_name = ID($le); } if (0) { case AST_EQ: type_name = ID($eq); } if (0) { case AST_NE: type_name = ID($ne); } if (0) { case AST_EQX: type_name = ID($eqx); } if (0) { case AST_NEX: type_name = ID($nex); } if (0) { case AST_GE: type_name = ID($ge); } if (0) { case AST_GT: type_name = ID($gt); } { int width = max(width_hint, 1); width_hint = -1, sign_hint = true; children[0]->detectSignWidthWorker(width_hint, sign_hint); children[1]->detectSignWidthWorker(width_hint, sign_hint); RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec sig = binop2rtlil(this, type_name, width, left, right); return sig; } // generate cells for binary operations: $add, $sub, $mul, $div, $mod if (0) { case AST_ADD: type_name = ID($add); } if (0) { case AST_SUB: type_name = ID($sub); } if (0) { case AST_MUL: type_name = ID($mul); } if (0) { case AST_DIV: type_name = ID($div); } if (0) { case AST_MOD: type_name = ID($mod); } { 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(width_hint, sign_hint); #if 0 int width = max(left.size(), right.size()); if (width > width_hint && width_hint > 0) width = width_hint; if (width < width_hint) { if (type == AST_ADD || type == AST_SUB || type == AST_DIV) width++; if (type == AST_SUB && (!children[0]->is_signed || !children[1]->is_signed)) width = width_hint; if (type == AST_MUL) width = min(left.size() + right.size(), width_hint); } #else int width = max(max(left.size(), right.size()), width_hint); #endif is_signed = children[0]->is_signed && children[1]->is_signed; return binop2rtlil(this, type_name, width, left, right); } // generate cells for binary operations: $logic_and, $logic_or if (0) { case AST_LOGIC_AND: type_name = ID($logic_and); } if (0) { case AST_LOGIC_OR: type_name = ID($logic_or); } { RTLIL::SigSpec left = children[0]->genRTLIL(); RTLIL::SigSpec right = children[1]->genRTLIL(); return binop2rtlil(this, type_name, max(width_hint, 1), left, right); } // generate cells for unary operations: $logic_not case AST_LOGIC_NOT: { RTLIL::SigSpec arg = children[0]->genRTLIL(); return uniop2rtlil(this, ID($logic_not), max(width_hint, 1), arg); } // generate multiplexer for ternary operator (aka ?:-operator) case AST_TERNARY: { if (width_hint < 0) detectSignWidth(width_hint, sign_hint); is_signed = sign_hint; RTLIL::SigSpec cond = children[0]->genRTLIL(); RTLIL::SigSpec sig; if (cond.is_fully_def()) { if (cond.as_bool()) { sig = children[1]->genRTLIL(width_hint, sign_hint); log_assert(is_signed == children[1]->is_signed); } else { sig = children[2]->genRTLIL(width_hint, sign_hint); log_assert(is_signed == children[2]->is_signed); } widthExtend(this, sig, sig.size(), is_signed); } else { RTLIL::SigSpec val1 = children[1]->genRTLIL(width_hint, sign_hint); RTLIL::SigSpec val2 = children[2]->genRTLIL(width_hint, sign_hint); if (cond.size() > 1) cond = uniop2rtlil(this, ID($reduce_bool), 1, cond, false); int width = max(val1.size(), val2.size()); log_assert(is_signed == children[1]->is_signed); log_assert(is_signed == children[2]->is_signed); widthExtend(this, val1, width, is_signed); widthExtend(this, val2, width, is_signed); sig = mux2rtlil(this, cond, val1, val2); } if (sig.size() < width_hint) sig.extend_u0(width_hint, sign_hint); return sig; } // generate $memrd cells for memory read ports case AST_MEMRD: { std::stringstream sstr; sstr << "$memrd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); set_src_attr(cell, this); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); 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(); cell->setPort(ID::CLK, RTLIL::SigSpec(RTLIL::State::Sx, 1)); cell->setPort(ID::EN, RTLIL::SigSpec(RTLIL::State::Sx, 1)); cell->setPort(ID::ADDR, addr_sig); cell->setPort(ID::DATA, RTLIL::SigSpec(wire)); cell->parameters[ID::MEMID] = RTLIL::Const(str); cell->parameters[ID::ABITS] = RTLIL::Const(GetSize(addr_sig)); cell->parameters[ID::WIDTH] = RTLIL::Const(wire->width); cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(0); cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(0); cell->parameters[ID::TRANSPARENT] = RTLIL::Const(0); if (!sign_hint) is_signed = false; return RTLIL::SigSpec(wire); } // generate $meminit cells case AST_MEMINIT: { std::stringstream sstr; 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(), ID($meminit_v2)); set_src_attr(cell, this); int mem_width, mem_size, addr_bits; id2ast->meminfo(mem_width, mem_size, addr_bits); 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, 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); cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1); } break; // generate $check cells case AST_ASSERT: case AST_ASSUME: case AST_LIVE: case AST_FAIR: case AST_COVER: { 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", 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::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) 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); // No message is emitted to ensure Verilog code roundtrips correctly. Fmt fmt; fmt.emit_rtlil(cell); } break; // add entries to current_module->connections for assignments (outside of always blocks) case AST_ASSIGN: { RTLIL::SigSpec left = children[0]->genRTLIL(); 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++) if (left[i].wire) { new_left.append(left[i]); new_right.append(right[i]); } log_file_warning(filename, location.first_line, "Ignoring assignment to constant bits:\n" " old assignment: %s = %s\n new assignment: %s = %s.\n", log_signal(left), log_signal(right), log_signal(new_left), log_signal(new_right)); left = new_left; right = new_right; } current_module->connect(RTLIL::SigSig(left, right)); } break; // create an RTLIL::Cell for an AST_CELL case AST_CELL: { int port_counter = 0, para_counter = 0; 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); for (auto it = children.begin(); it != children.end(); it++) { AstNode *child = *it; if (child->type == AST_CELLTYPE) { cell->type = child->str; if (flag_icells && cell->type.begins_with("\\$")) cell->type = cell->type.substr(1); continue; } if (child->type == AST_PARASET) { IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; 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), 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] = value->asParaConst(); continue; } if (child->type == AST_ARGUMENT) { RTLIL::SigSpec sig; 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); cell->setPort(buf, sig); } else { cell->setPort(child->str, sig); } continue; } log_abort(); } for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) 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)) { int src_width = GetSize(cell->getPort(ID::SRC)); int dst_width = GetSize(cell->getPort(ID::DST)); bool full = cell->getParam(ID::FULL).as_bool(); if (!full && src_width != dst_width) 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)); } else if (cell->type == ID($specify3)) { int dat_width = GetSize(cell->getPort(ID::DAT)); int dst_width = GetSize(cell->getPort(ID::DST)); if (dat_width != dst_width) 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)); } else if (cell->type == ID($specrule)) { int src_width = GetSize(cell->getPort(ID::SRC)); int dst_width = GetSize(cell->getPort(ID::DST)); cell->setParam(ID::SRC_WIDTH, Const(src_width)); cell->setParam(ID::DST_WIDTH, Const(dst_width)); } } break; // use ProcessGenerator for always blocks case AST_ALWAYS: { AstNode *always = this->clone(); ProcessGenerator generator(always); ignoreThisSignalsInInitial.append(generator.outputSignals); delete always; } break; case AST_INITIAL: { AstNode *always = this->clone(); ProcessGenerator generator(always, ignoreThisSignalsInInitial); delete always; } break; case AST_TECALL: { int sz = children.size(); if (str == "$info") { if (sz > 0) log_file_info(filename, location.first_line, "%s.\n", children[0]->str.c_str()); else log_file_info(filename, location.first_line, "\n"); } else if (str == "$warning") { if (sz > 0) log_file_warning(filename, location.first_line, "%s.\n", children[0]->str.c_str()); else log_file_warning(filename, location.first_line, "\n"); } else if (str == "$error") { if (sz > 0) input_error("%s.\n", children[0]->str.c_str()); else 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) input_error("FATAL: %s.\n", children[0]->str.c_str()); else input_error("FATAL.\n"); } else { input_error("Unknown elaboration 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") { string myid = stringf("%s$%d", str.c_str() + 1, autoidx++); int width = width_hint; if (GetSize(children) > 1) 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) 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) input_error("Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str()); Cell *cell = current_module->addCell(myid, str.substr(1)); 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) input_error("Attribute `reg' with non-constant value!\n"); cell->attributes[ID::reg] = attr->asAttrConst(); } Wire *wire = current_module->addWire(myid + "_wire", width); set_src_attr(wire, this); cell->setPort(ID::Y, wire); is_signed = sign_hint; return SigSpec(wire); } } YS_FALLTHROUGH // everything should have been handled above -> print error if not. default: for (auto f : log_files) current_ast_mod->dumpAst(f, "verilog-ast> "); input_error("Don't know how to generate RTLIL code for %s node!\n", type2str(type).c_str()); } return RTLIL::SigSpec(); } // 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, 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 = sgn; int width_hint = width; detectSignWidthWorker(width_hint, sign_hint); RTLIL::SigSpec sig = genRTLIL(width_hint, sign_hint); genRTLIL_subst_ptr = backup_subst_ptr; if (width >= 0) sig.extend_u0(width, is_signed); return sig; } YOSYS_NAMESPACE_END yosys-0.52/frontends/ast/simplify.cc000066400000000000000000006072571477540374200176200ustar00rootroot00000000000000/* * 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 is the AST frontend library. * * The AST frontend library is not a frontend on it's own but provides a * generic abstract syntax tree (AST) abstraction for HDL code and can be * used by HDL frontends. See "ast.h" for an overview of the API and the * Verilog frontend for an usage example. * */ #include "kernel/log.h" #include "libs/sha1/sha1.h" #include "frontends/verilog/verilog_frontend.h" #include "ast.h" #include #include #include #include // For std::gcd in C++17 // #include YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; // 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); } 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(); } } 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(); } } 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 in_param = in_param_from_above; 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; 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; 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; default: in_param = in_param_from_above; for (auto child : children) child->set_in_param_flag(in_param, force_descend); } for (auto attr : attributes) attr.second->set_in_param_flag(true, force_descend); in_lvalue = in_lvalue_from_above; 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; default: for (auto child : children) child->set_in_lvalue_flag(in_lvalue, force_descend); } if (force_descend) { for (auto child : children) child->fixup_hierarchy_flags(true); for (auto attr : attributes) attr.second->fixup_hierarchy_flags(true); } } // Process a format string and arguments for $display, $write, $sprintf, etc 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); } args.push_back(arg); } Fmt fmt; fmt.parse_verilog(args, sformat_like, default_base, /*task_name=*/str, current_module->name); return fmt; } void AstNode::annotateTypedEnums(AstNode *template_node) { //check if enum if (template_node->attributes.count(ID::enum_type)) { //get reference to enum node: std::string enum_type = template_node->attributes[ID::enum_type]->str.c_str(); // log("enum_type=%s (count=%lu)\n", enum_type.c_str(), current_scope.count(enum_type)); // log("current scope:\n"); // for (auto &it : current_scope) // log(" %s\n", it.first.c_str()); 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]; log_assert(enum_item0->type == AST_ENUM_ITEM); int width; if (!enum_item0->range_valid) width = 1; else if (enum_item0->range_swapped) width = enum_item0->range_right - enum_item0->range_left + 1; else width = enum_item0->range_left - enum_item0->range_right + 1; log_assert(width > 0); //add declared enum items: for (auto enum_item : enum_node->children){ log_assert(enum_item->type == AST_ENUM_ITEM); //get is_signed bool is_signed; if (enum_item->children.size() == 1){ is_signed = false; } else if (enum_item->children.size() == 2){ log_assert(enum_item->children[1]->type == AST_RANGE); is_signed = enum_item->children[1]->is_signed; } else { 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() ); } //start building attribute string std::string enum_item_str = "\\enum_value_"; //get enum item value if(enum_item->children[0]->type != AST_CONSTANT){ log_error("expected const, got %s for %s (%s)\n", type2str(enum_item->children[0]->type).c_str(), enum_item->str.c_str(), enum_node->str.c_str() ); } 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 set_attribute(enum_item_str.c_str(), mkconst_str(enum_item->str)); } } } static AstNode *make_range(int left, int right, bool is_signed = false) { // generate a pre-validated range node for a fixed signal range. auto range = new AstNode(AST_RANGE); range->range_left = left; range->range_right = right; range->range_valid = true; range->children.push_back(AstNode::mkconst_int(left, true)); range->children.push_back(AstNode::mkconst_int(right, true)); range->is_signed = is_signed; return range; } static int range_width(AstNode *node, AstNode *rnode) { log_assert(rnode->type==AST_RANGE); if (!rnode->range_valid) { 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; } static int add_dimension(AstNode *node, AstNode *rnode) { int width = range_width(node, rnode); node->dimensions.push_back({ rnode->range_right, width, rnode->range_swapped }); return width; } [[noreturn]] static void struct_array_packing_error(AstNode *node) { 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) { // Struct members will be laid out in the structure contiguously from left to right. // Union members all have zero offset from the start of the union. // Determine total packed size and assign offsets. Store these in the member node. bool is_union = (snode->type == AST_UNION); int offset = 0; int packed_width = -1; // examine members from last to first for (auto it = snode->children.rbegin(); it != snode->children.rend(); ++it) { auto node = *it; int width; if (node->type == AST_STRUCT || node->type == AST_UNION) { // embedded struct or union width = size_packed_struct(node, base_offset + offset); } else { log_assert(node->type == AST_STRUCT_ITEM); if (node->children.size() > 0 && node->children[0]->type == AST_RANGE) { // 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] // Pretend it's declared as a packed array, e.g. bit [0:3][63:0] a auto rnode = node->children[1]; 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); } add_dimension(node, node->children[0]); } else { // 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() > 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); } 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 width = node->range_left - node->range_right + 1; } if (is_union) { node->range_right = base_offset; node->range_left = base_offset + width - 1; } else { node->range_right = base_offset + offset; node->range_left = base_offset + offset + width - 1; } node->range_valid = true; } if (is_union) { // check that all members have the same size if (packed_width == -1) { // first member packed_width = width; } else { 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; } } 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) { return AstNode::mkconst_int(ival, true); } static AstNode *multiply_by_const(AstNode *expr_node, int stride) { return new AstNode(AST_MUL, expr_node, node_int(stride)); } static AstNode *normalize_index(AstNode *expr, AstNode *decl_node, int dimension) { expr = expr->clone(); int offset = decl_node->dimensions[dimension].range_right; if (offset) { expr = new AstNode(AST_SUB, expr, node_int(offset)); } // 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 *index_offset(AstNode *offset, AstNode *rnode, AstNode *decl_node, int dimension, int &stride) { 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] add_offset = node_int(stride - 1); } else { // 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)); } } return new AstNode(AST_ADD, lsb_offset, add_offset); } 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 if (children.empty()) { // no range operations apply, return the whole width return make_range(decl_node->range_left - decl_node->range_right, 0); } 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 (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 { 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) { // add all the members in a struct or union to local scope // 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); } } } 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, 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; 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 add_members_to_scope(template_node, 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) { if (node->type == AST_ASSIGN_EQ || node->type == AST_ASSIGN_LE) { // current node is iteslf an assignment log_assert(node->children.size() >= 2); const AstNode* lhs = node->children[0]; if (lhs->type == AST_IDENTIFIER && lhs->str == var->str) return false; } for (const AstNode* child : node->children) { // if this child shadows the given variable if (child != var && child->str == var->str && child->type == AST_WIRE) break; // skip the remainder of this block/scope // depth-first short circuit if (!node_contains_assignment_to(child, var)) return false; } 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, 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 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, stage=%d, width_hint=%d, sign_hint=%d\n", int(const_fold), int(stage), int(width_hint), int(sign_hint)); // dumpAst(NULL, "> "); #endif if (stage == 0) { log_assert(type == AST_MODULE || type == AST_INTERFACE); deep_recursion_warning = true; while (simplify(const_fold, 1, width_hint, sign_hint)) { } if (!flag_nomem2reg && !get_bool_attribute(ID::nomem2reg)) { dict> mem2reg_places; dict mem2reg_candidates, dummy_proc_flags; uint32_t flags = flag_mem2reg ? AstNode::MEM2REG_FL_ALL : 0; mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, dummy_proc_flags, flags); pool mem2reg_set; for (auto &it : mem2reg_candidates) { AstNode *mem = it.first; uint32_t memflags = it.second; bool this_nomeminit = flag_nomeminit; log_assert((memflags & ~0x00ffff00) == 0); if (mem->get_bool_attribute(ID::nomem2reg)) continue; if (mem->get_bool_attribute(ID::nomeminit) || get_bool_attribute(ID::nomeminit)) this_nomeminit = true; if (memflags & AstNode::MEM2REG_FL_FORCED) goto silent_activate; if (memflags & AstNode::MEM2REG_FL_EQ2) goto verbose_activate; if (memflags & AstNode::MEM2REG_FL_SET_ASYNC) goto verbose_activate; if ((memflags & AstNode::MEM2REG_FL_SET_INIT) && (memflags & AstNode::MEM2REG_FL_SET_ELSE) && this_nomeminit) goto verbose_activate; if (memflags & AstNode::MEM2REG_FL_CMPLX_LHS) goto verbose_activate; if ((memflags & AstNode::MEM2REG_FL_CONST_LHS) && !(memflags & AstNode::MEM2REG_FL_VAR_LHS)) goto verbose_activate; // log("Note: Not replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags)); continue; verbose_activate: if (mem2reg_set.count(mem) == 0) { std::string message = stringf("Replacing memory %s with list of registers.", mem->str.c_str()); bool first_element = true; for (auto &place : mem2reg_places[it.first]) { message += stringf("%s%s", first_element ? " See " : ", ", place.c_str()); first_element = false; } log_warning("%s\n", message.c_str()); } silent_activate: // log("Note: Replacing memory %s with list of registers (flags=0x%08lx).\n", mem->str.c_str(), long(memflags)); mem2reg_set.insert(mem); } for (auto node : mem2reg_set) { int mem_width, mem_size, addr_bits; node->meminfo(mem_width, mem_size, addr_bits); int data_range_left = node->children[0]->range_left; int data_range_right = node->children[0]->range_right; if (node->children[0]->range_swapped) std::swap(data_range_left, data_range_right); for (int i = 0; i < mem_size; i++) { AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true))); reg->str = stringf("%s[%d]", node->str.c_str(), i); reg->is_reg = true; reg->is_signed = node->is_signed; for (auto &it : node->attributes) if (it.first != ID::mem2reg) reg->set_attribute(it.first, it.second->clone()); reg->filename = node->filename; reg->location = node->location; children.push_back(reg); while (reg->simplify(true, 1, -1, false)) { } } } AstNode *async_block = NULL; while (mem2reg_as_needed_pass2(mem2reg_set, this, NULL, async_block)) { } vector delnodes; mem2reg_remove(mem2reg_set, delnodes); for (auto node : delnodes) delete node; } while (simplify(const_fold, 2, width_hint, sign_hint)) { } recursion_counter--; return false; } current_filename = filename; // we do not look inside a task or function // (but as soon as a task or function is instantiated we process the generated AST as usual) if (type == AST_FUNCTION || type == AST_TASK) { recursion_counter--; return false; } // deactivate all calls to non-synthesis system tasks // note that $display, $finish, and $stop are used for synthesis-time DRC so they're not in this list if ((type == AST_FCALL || type == AST_TCALL) && (str == "$strobe" || str == "$monitor" || str == "$time" || str == "$dumpfile" || str == "$dumpvars" || str == "$dumpon" || str == "$dumpoff" || str == "$dumpall")) { log_file_warning(filename, location.first_line, "Ignoring call to system %s %s.\n", type == AST_FCALL ? "function" : "task", str.c_str()); delete_children(); str = std::string(); } if ((type == AST_TCALL) && (str == "$display" || str == "$displayb" || str == "$displayh" || str == "$displayo" || str == "$write" || str == "$writeb" || str == "$writeh" || str == "$writeo")) { 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_literal("\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.) if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF) const_fold = true; 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; 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 || 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]; if (node->type == AST_WIRE) { if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) { for (auto c : node->children[0]->children) { if (!c->is_simple_const_expr()) { if (attributes.count(ID::dynports)) delete attributes.at(ID::dynports); set_attribute(ID::dynports, AstNode::mkconst_int(1, true)); } } } if (this_wire_scope.count(node->str) > 0) { AstNode *first_node = this_wire_scope[node->str]; if (first_node->is_input && node->is_reg) goto wires_are_incompatible; if (!node->is_input && !node->is_output && node->is_reg && node->children.size() == 0) goto wires_are_compatible; if (first_node->children.size() == 0 && node->children.size() == 1 && node->children[0]->type == AST_RANGE) { AstNode *r = node->children[0]; if (r->range_valid && r->range_left == 0 && r->range_right == 0) { delete r; node->children.pop_back(); } } if (first_node->children.size() != node->children.size()) goto wires_are_incompatible; for (size_t j = 0; j < node->children.size(); j++) { AstNode *n1 = first_node->children[j], *n2 = node->children[j]; if (n1->type == AST_RANGE && n2->type == AST_RANGE && n1->range_valid && n2->range_valid) { if (n1->range_left != n2->range_left) goto wires_are_incompatible; if (n1->range_right != n2->range_right) goto wires_are_incompatible; } else if (*n1 != *n2) goto wires_are_incompatible; } if (first_node->range_left != node->range_left) goto wires_are_incompatible; if (first_node->range_right != node->range_right) goto wires_are_incompatible; if (first_node->port_id == 0 && (node->is_input || node->is_output)) goto wires_are_incompatible; wires_are_compatible: if (node->is_input) first_node->is_input = true; if (node->is_output) first_node->is_output = true; if (node->is_reg) first_node->is_reg = true; if (node->is_logic) first_node->is_logic = true; if (node->is_signed) first_node->is_signed = true; for (auto &it : node->attributes) { if (first_node->attributes.count(it.first) > 0) delete first_node->attributes[it.first]; first_node->set_attribute(it.first, it.second->clone()); } children.erase(children.begin()+(i--)); did_something = true; delete node; continue; wires_are_incompatible: if (stage > 1) input_error("Incompatible re-declaration of wire %s.\n", node->str.c_str()); continue; } this_wire_scope[node->str] = node; } // these nodes appear at the top level in a module and can define names if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL || node->type == AST_TYPEDEF) { backup_scope[node->str] = current_scope[node->str]; current_scope[node->str] = node; } if (node->type == AST_ENUM) { current_scope[node->str] = node; for (auto enode : node->children) { log_assert(enode->type==AST_ENUM_ITEM); if (current_scope.count(enode->str) == 0) current_scope[enode->str] = enode; else 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, 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, 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 if (type == AST_PACKAGE) { //add names to package scope 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 || node->type == AST_FUNCTION || node->type == AST_TASK) { current_scope[node->str] = node; } if (node->type == AST_ENUM) { current_scope[node->str] = node; for (auto enode : node->children) { log_assert(enode->type==AST_ENUM_ITEM); if (current_scope.count(enode->str) == 0) current_scope[enode->str] = enode; else input_error("enum item %s already exists in package\n", enode->str.c_str()); } } } } auto backup_current_block = current_block; auto backup_current_block_child = current_block_child; auto backup_current_top_block = current_top_block; auto backup_current_always = current_always; auto backup_current_always_clocked = current_always_clocked; if (type == AST_ALWAYS || type == AST_INITIAL) { if (current_always != nullptr) input_error("Invalid nesting of always blocks and/or initializations.\n"); current_always = this; current_always_clocked = false; if (type == AST_ALWAYS) for (auto child : children) { if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) current_always_clocked = true; if (child->type == AST_EDGE && GetSize(child->children) == 1 && child->children[0]->type == AST_IDENTIFIER && child->children[0]->str == "\\$global_clock") current_always_clocked = true; } } 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; bool detect_width_simple = false; bool child_0_is_self_determined = false; bool child_1_is_self_determined = false; bool child_2_is_self_determined = false; bool children_are_self_determined = false; bool reset_width_after_children = false; switch (type) { case AST_ASSIGN_EQ: case AST_ASSIGN_LE: case AST_ASSIGN: 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, stage, -1, false) == true) did_something = true; children[0]->detectSignWidth(backup_width_hint, backup_sign_hint); children[1]->detectSignWidth(width_hint, sign_hint); width_hint = max(width_hint, backup_width_hint); child_0_is_self_determined = true; // test only once, before optimizations and memory mappings but after assignment LHS was mapped to an identifier if (children[0]->id2ast && !children[0]->was_checked) { 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.\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) { if (children[1]->str == "\\$anyconst") is_rand_reg = true; if (children[1]->str == "\\$anyseq") is_rand_reg = true; if (children[1]->str == "\\$allconst") is_rand_reg = true; if (children[1]->str == "\\$allseq") is_rand_reg = true; } if (!is_rand_reg) 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; } break; case AST_STRUCT: case AST_UNION: if (!basic_prep) { for (auto *node : children) { // resolve any ranges while (!node->basic_prep && node->simplify(true, stage, -1, false)) { did_something = true; } } // determine member offsets and widths size_packed_struct(this, 0); // 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, attributes); log_assert(current_ast_mod); current_ast_mod->children.push_back(wnode); } basic_prep = true; } 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, stage, -1, false)) did_something = true; } // allocate values (called more than once) allocateDefaultEnumValues(); } break; case AST_PARAMETER: case AST_LOCALPARAM: // 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, stage, -1, false) == true) did_something = true; if (!children[1]->range_valid) 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, 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, stage, -1, false)) did_something = true; if (!children[1]->range_valid) 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; case AST_CAST_SIZE: { int width = 1; AstNode *node; AstNode *child = children[0]; if (child->type == AST_WIRE) { if (child->children.size() == 0) { // Base type (e.g., int) width = child->range_left - child->range_right +1; node = mkconst_int(width, child->is_signed); } else { // User defined type log_assert(child->children[0]->type == AST_WIRETYPE); const std::string &type_name = child->children[0]->str; if (!current_scope.count(type_name)) 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) 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, stage, width_hint, sign_hint)) {}; switch (template_node->type) { case AST_WIRE: { if (template_node->children.size() > 0 && template_node->children[0]->type == AST_RANGE) width = range_width(this, template_node->children[0]); child->delete_children(); node = mkconst_int(width, true); break; } case AST_STRUCT: case AST_UNION: { child->delete_children(); width = size_packed_struct(template_node, 0); node = mkconst_int(width, false); break; } default: log_error("Don't know how to translate static cast of type %s\n", type2str(template_node->type).c_str()); } } delete child; children.erase(children.begin()); children.insert(children.begin(), node); } detect_width_simple = true; children_are_self_determined = true; break; } case AST_TO_BITS: case AST_TO_SIGNED: case AST_TO_UNSIGNED: case AST_SELFSZ: case AST_CONCAT: case AST_REPLICATE: case AST_REDUCE_AND: case AST_REDUCE_OR: case AST_REDUCE_XOR: case AST_REDUCE_XNOR: case AST_REDUCE_BOOL: detect_width_simple = true; children_are_self_determined = true; break; case AST_NEG: case AST_BIT_NOT: case AST_POS: case AST_BIT_AND: case AST_BIT_OR: case AST_BIT_XOR: case AST_BIT_XNOR: case AST_ADD: case AST_SUB: case AST_MUL: case AST_DIV: case AST_MOD: detect_width_simple = true; break; case AST_SHIFT_LEFT: case AST_SHIFT_RIGHT: case AST_SHIFT_SLEFT: case AST_SHIFT_SRIGHT: case AST_POW: detect_width_simple = true; child_1_is_self_determined = true; break; case AST_LT: case AST_LE: case AST_EQ: case AST_NE: case AST_EQX: case AST_NEX: case AST_GE: case AST_GT: width_hint = -1; sign_hint = true; for (auto child : children) { while (!child->basic_prep && child->simplify(false, stage, -1, false) == true) did_something = true; child->detectSignWidthWorker(width_hint, sign_hint); } reset_width_after_children = true; break; case AST_LOGIC_AND: case AST_LOGIC_OR: case AST_LOGIC_NOT: detect_width_simple = true; children_are_self_determined = true; break; case AST_TERNARY: child_0_is_self_determined = true; break; case AST_MEMRD: detect_width_simple = true; children_are_self_determined = true; break; case AST_FCALL: case AST_TCALL: children_are_self_determined = true; break; default: width_hint = -1; sign_hint = false; } if (detect_width_simple && width_hint < 0) { if (type == AST_REPLICATE) while (children[0]->simplify(true, stage, -1, false) == true) did_something = true; for (auto child : children) while (!child->basic_prep && child->simplify(false, stage, -1, false) == true) did_something = true; detectSignWidth(width_hint, sign_hint); } if (type == AST_FCALL && str == "\\$past") 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; children[1]->detectSignWidth(width_hint_left, sign_hint_left, &found_real_left); children[2]->detectSignWidth(width_hint_right, sign_hint_right, &found_real_right); if (found_real_left || found_real_right) { child_1_is_self_determined = true; child_2_is_self_determined = true; } } if (type == AST_CONDX && children.size() > 0 && children.at(0)->type == AST_CONSTANT) { for (auto &bit : children.at(0)->bits) if (bit == State::Sz || bit == State::Sx) bit = State::Sa; } if (type == AST_CONDZ && children.size() > 0 && children.at(0)->type == AST_CONSTANT) { for (auto &bit : children.at(0)->bits) if (bit == State::Sz) bit = State::Sa; } if (const_fold && type == AST_CASE) { 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++) { AstNode *child = children[i]; log_assert(child->type == AST_COND || child->type == AST_CONDX || child->type == AST_CONDZ); for (auto v : child->children) { if (v->type == AST_DEFAULT) goto keep_const_cond; if (v->type == AST_BLOCK) continue; while (v->simplify(const_fold, stage, width_hint, sign_hint)) { } if (v->type == AST_CONSTANT && v->bits_only_01()) { 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.size() == 1); if (match.front() == RTLIL::State::S1) { while (i+1 < GetSize(children)) delete children[++i]; goto keep_const_cond; } continue; } goto keep_const_cond; } if (0) keep_const_cond: new_children.push_back(child); else delete child; } new_children.swap(children); } } 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; int width_hint_here = width_hint; bool sign_hint_here = sign_hint; if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE)) const_fold_here = true; if (type == AST_PARAMETER || type == AST_LOCALPARAM) const_fold_here = true; if (type == AST_BLOCK) { current_block = this; current_block_child = children[i]; } if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK) current_top_block = children[i]; if (i == 0 && child_0_is_self_determined) width_hint_here = -1, sign_hint_here = false; if (i == 1 && child_1_is_self_determined) width_hint_here = -1, sign_hint_here = false; if (i == 2 && child_2_is_self_determined) 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, stage, width_hint_here, sign_hint_here); if (did_something_here) did_something = true; } if (stage == 2 && children[i]->type == AST_INITIAL && current_ast_mod != this) { current_ast_mod->children.push_back(children[i]); children.erase(children.begin() + (i--)); 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, 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; sign_hint = backup_sign_hint; if (width_hint < 0) detectSignWidth(width_hint, sign_hint); } current_block = backup_current_block; current_block_child = backup_current_block_child; current_top_block = backup_current_top_block; current_always = backup_current_always; current_always_clocked = backup_current_always_clocked; for (auto it = backup_scope.begin(); it != backup_scope.end(); it++) { if (it->second == NULL) current_scope.erase(it->first); else current_scope[it->first] = it->second; } current_filename = filename; 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) input_error("Module name in defparam contains non-constant expressions!\n"); string modname, paramname = children[0]->str; size_t pos = paramname.rfind('.'); while (pos != 0 && pos != std::string::npos) { modname = paramname.substr(0, pos); if (current_scope.count(modname)) break; pos = paramname.rfind('.', pos - 1); } if (pos == std::string::npos) 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) 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); paraset->str = paramname; AstNode *cell = current_scope.at(modname); cell->children.insert(cell->children.begin() + 1, paraset); delete_children(); } // resolve typedefs if (type == AST_TYPEDEF) { 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, stage, width_hint, sign_hint)) { did_something = true; } log_assert(!type_node->is_custom_type); } // resolve types of wires if (type == AST_WIRE || type == AST_MEMORY) { if (is_custom_type) { log_assert(children.size() >= 1); log_assert(children[0]->type == AST_WIRETYPE); auto type_name = children[0]->str; if (!current_scope.count(type_name)) { 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) 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]; // 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; } // 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()); // if an enum then add attributes to support simulator tracing newNode->annotateTypedEnums(template_node); bool add_packed_dimensions = (type == AST_WIRE && GetSize(children) > 1) || (type == AST_MEMORY && GetSize(children) > 2); // 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()); // 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); } // 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; } // Prepare to generate dimensions metadata for the resolved type. newNode->dimensions.clear(); newNode->unpacked_dimensions = 0; goto apply_newNode; } } // resolve types of parameters if (type == AST_LOCALPARAM || type == AST_PARAMETER) { if (is_custom_type) { log_assert(children.size() >= 2); log_assert(children[1]->type == AST_WIRETYPE); // 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); } // resolve constant prefixes if (type == AST_PREFIX) { if (children[0]->type != AST_CONSTANT) { // dumpAst(NULL, "> "); input_error("Index in generate block prefix syntax is not constant!\n"); } if (children[1]->type == AST_PREFIX) 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(); if (second_part[0] == '\\') second_part++; newNode->str = stringf("%s[%d].%s", str.c_str(), children[0]->integer, second_part); goto apply_newNode; } // evaluate TO_BITS nodes if (type == AST_TO_BITS) { if (children[0]->type != AST_CONSTANT) input_error("Left operand of to_bits expression is not constant!\n"); if (children[1]->type != AST_CONSTANT) 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.to_bits(), children[1]->is_signed); goto apply_newNode; } // annotate constant ranges if (type == AST_RANGE) { bool old_range_valid = range_valid; range_valid = false; range_swapped = false; range_left = -1; range_right = 0; log_assert(children.size() >= 1); if (children[0]->type == AST_CONSTANT) { range_valid = true; range_left = children[0]->integer; if (children.size() == 1) range_right = range_left; } if (children.size() >= 2) { if (children[1]->type == AST_CONSTANT) range_right = children[1]->integer; else range_valid = false; } if (old_range_valid != range_valid) did_something = true; if (range_valid && range_right > range_left) { std::swap(range_left, range_right); range_swapped = true; } } // annotate wires with their ranges if (type == AST_WIRE) { if (children.size() > 0) { if (children[0]->range_valid) { if (!range_valid) did_something = true; range_valid = true; range_swapped = children[0]->range_swapped; range_left = children[0]->range_left; range_right = children[0]->range_right; bool force_upto = false, force_downto = false; if (attributes.count(ID::force_upto)) { AstNode *val = attributes[ID::force_upto]; if (val->type != AST_CONSTANT) 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) input_error("Attribute `force_downto' with non-constant value!\n"); force_downto = val->asAttrConst().as_bool(); } if (force_upto && force_downto) 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; } } } else { if (!range_valid) did_something = true; range_valid = true; range_swapped = false; range_left = 0; range_right = 0; } } // 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 }); } } // 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)) { 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 (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 (auto &it : children) delete it; children.clear(); 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; } // trim/extend parameters 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) 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); log_file_warning(filename, location.first_line, "converting real value %e to binary %s.\n", children[0]->realvalue, log_signal(constvalue)); delete children[0]; children[0] = mkconst_bits(constvalue.to_bits(), sign_hint); fixup_hierarchy_flags(); did_something = true; } if (children[0]->type == AST_CONSTANT) { if (width != int(children[0]->bits.size())) { RTLIL::SigSpec sig(children[0]->bits); sig.extend_u0(width, children[0]->is_signed); AstNode *old_child_0 = children[0]; children[0] = mkconst_bits(sig.as_const().to_bits(), is_signed); delete old_child_0; fixup_hierarchy_flags(); } children[0]->is_signed = is_signed; } range_valid = true; range_swapped = children[1]->range_swapped; range_left = children[1]->range_left; range_right = children[1]->range_right; } else if (children.size() > 1 && children[1]->type == AST_REALVALUE && children[0]->type == AST_CONSTANT) { double as_realvalue = children[0]->asReal(sign_hint); 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 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_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; } } } } // annotate identifiers using scope resolution and create auto-wires as needed if (type == AST_IDENTIFIER) { if (current_scope.count(str) == 0) { AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; 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) { case AST_PARAMETER: case AST_LOCALPARAM: case AST_WIRE: case AST_AUTOWIRE: case AST_GENVAR: case AST_MEMORY: case AST_FUNCTION: case AST_TASK: case AST_DPI_FUNCTION: //log("found child %s, %s\n", type2str(node->type).c_str(), node->str.c_str()); if (str == node->str) { //log("add %s, type %s to scope\n", str.c_str(), type2str(node->type).c_str()); current_scope[node->str] = node; } break; case AST_ENUM: current_scope[node->str] = node; for (auto enum_node : node->children) { log_assert(enum_node->type==AST_ENUM_ITEM); if (str == enum_node->str) { //log("\nadding enum item %s to scope\n", str.c_str()); current_scope[str] = enum_node; } } break; default: break; } } } if (current_scope.count(str) == 0) { if (current_ast_mod == nullptr) { 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; current_ast_mod->children.push_back(auto_wire); current_scope[str] = auto_wire; did_something = true; } else { input_error("Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); } } if (id2ast != current_scope[str]) { id2ast = current_scope[str]; did_something = true; } } // 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 && stage == 2) { if (id2ast == NULL || id2ast->type != AST_MEMORY || children[0]->children.size() != 1) input_error("Invalid bit-select on memory access!\n"); int mem_width, mem_size, addr_bits; id2ast->meminfo(mem_width, mem_size, addr_bits); int data_range_left = id2ast->children[0]->range_left; int data_range_right = id2ast->children[0]->range_right; if (id2ast->children[0]->range_swapped) std::swap(data_range_left, data_range_right); std::stringstream sstr; 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->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); current_ast_mod->children.push_back(wire); while (wire->simplify(true, 1, -1, false)) { } AstNode *data = clone(); delete data->children[1]; data->children.pop_back(); AstNode *assign = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), data); assign->children[0]->str = wire_id; assign->children[0]->was_checked = true; if (current_block) { size_t assign_idx = 0; while (assign_idx < current_block->children.size() && current_block->children[assign_idx] != current_block_child) assign_idx++; log_assert(assign_idx < current_block->children.size()); current_block->children.insert(current_block->children.begin()+assign_idx, assign); wire->is_reg = true; } else { AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); proc->children[0]->children.push_back(assign); current_ast_mod->children.push_back(proc); } newNode = new AstNode(AST_IDENTIFIER, children[1]->clone()); newNode->str = wire_id; newNode->integer = integer; // save original number of dimensions for $size() etc. newNode->id2ast = wire; goto apply_newNode; } if (type == AST_WHILE) input_error("While loops are only allowed in constant functions!\n"); if (type == AST_REPEAT) { AstNode *count = children[0]; AstNode *body = children[1]; // eval count expression while (count->simplify(true, stage, 32, true)) { } if (count->type != AST_CONSTANT) input_error("Repeat loops outside must have constant repeat counts!\n"); // convert to a block with the body repeated n times type = AST_BLOCK; children.clear(); for (int i = 0; i < count->bitsAsConst().as_int(); i++) children.insert(children.begin(), body->clone()); delete count; delete body; did_something = true; } // unroll for loops and generate-for blocks if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0) { AstNode *init_ast = children[0]; AstNode *while_ast = children[1]; AstNode *next_ast = children[2]; AstNode *body_ast = children[3]; while (body_ast->type == AST_GENBLOCK && body_ast->str.empty() && body_ast->children.size() == 1 && body_ast->children.at(0)->type == AST_GENBLOCK) body_ast = body_ast->children.at(0); const char* loop_type_str = "procedural"; const char* var_type_str = "register"; AstNodeType var_type = AST_WIRE; if (type == AST_GENFOR) { loop_type_str = "generate"; var_type_str = "genvar"; var_type = AST_GENVAR; } if (init_ast->type != AST_ASSIGN_EQ) input_error("Unsupported 1st expression of %s for-loop!\n", loop_type_str); if (next_ast->type != AST_ASSIGN_EQ) 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) 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) 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) 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(); { int expr_width_hint = -1; bool expr_sign_hint = true; varbuf->detectSignWidth(expr_width_hint, expr_sign_hint); while (varbuf->simplify(true, stage, 32, true)) { } } if (varbuf->type != AST_CONSTANT) 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) { int const_size = varbuf->range_left - varbuf->range_right; int resolved_size = resolved->range_left - resolved->range_right; if (const_size < resolved_size) { for (int i = const_size; i < resolved_size; i++) varbuf->bits.push_back(resolved->is_signed ? varbuf->bits.back() : State::S0); varbuf->range_left = resolved->range_left; varbuf->range_right = resolved->range_right; varbuf->range_swapped = resolved->range_swapped; varbuf->range_valid = resolved->range_valid; } } varbuf = new AstNode(AST_LOCALPARAM, varbuf); varbuf->str = init_ast->children[0]->str; AstNode *backup_scope_varbuf = current_scope[varbuf->str]; current_scope[varbuf->str] = varbuf; size_t current_block_idx = 0; if (type == AST_FOR) { while (current_block_idx < current_block->children.size() && current_block->children[current_block_idx] != current_block_child) current_block_idx++; } while (1) { // eval 2nd expression AstNode *buf = while_ast->clone(); { int expr_width_hint = -1; bool expr_sign_hint = true; buf->detectSignWidth(expr_width_hint, expr_sign_hint); while (buf->simplify(true, stage, expr_width_hint, expr_sign_hint)) { } } if (buf->type != AST_CONSTANT) input_error("2nd expression of %s for-loop is not constant!\n", loop_type_str); if (buf->integer == 0) { delete buf; break; } delete buf; // expand body int index = varbuf->children[0]->integer; 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 << "]."; 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, stage, -1, false); current_ast_mod->children.push_back(buf->children[i]); } } else { for (size_t i = 0; i < buf->children.size(); i++) current_block->children.insert(current_block->children.begin() + current_block_idx++, buf->children[i]); } buf->children.clear(); delete buf; // 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, stage, expr_width_hint, expr_sign_hint)) { } } if (buf->type != AST_CONSTANT) 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; } if (type == AST_FOR) { AstNode *buf = next_ast->clone(); delete buf->children[1]; buf->children[1] = varbuf->children[0]->clone(); current_block->children.insert(current_block->children.begin() + current_block_idx++, buf); } current_scope[varbuf->str] = backup_scope_varbuf; delete varbuf; delete_children(); did_something = true; } // check for local objects in unnamed block if (type == AST_BLOCK && str.empty()) { 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_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()) { 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, stage, -1, false); current_ast_mod->children.push_back(children[i]); current_scope[children[i]->str] = children[i]; } else new_children.push_back(children[i]); children.swap(new_children); did_something = true; str.clear(); } // simplify unconditional generate block if (type == AST_GENBLOCK && children.size() != 0) { if (!str.empty()) { expand_genblock(str + "."); } for (size_t i = 0; i < children.size(); i++) { children[i]->simplify(const_fold, stage, -1, false); current_ast_mod->children.push_back(children[i]); } children.clear(); did_something = true; } // simplify generate-if blocks if (type == AST_GENIF && children.size() != 0) { AstNode *buf = children[0]->clone(); while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); input_error("Condition for generate if is not constant!\n"); } if (buf->asBool() != 0) { delete buf; buf = children[1]->clone(); } else { delete buf; buf = children.size() > 2 ? children[2]->clone() : NULL; } if (buf) { if (buf->type != AST_GENBLOCK) buf = new AstNode(AST_GENBLOCK, buf); if (!buf->str.empty()) { buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { buf->children[i]->simplify(const_fold, stage, -1, false); current_ast_mod->children.push_back(buf->children[i]); } buf->children.clear(); delete buf; } delete_children(); did_something = true; } // simplify generate-case blocks if (type == AST_GENCASE && children.size() != 0) { AstNode *buf = children[0]->clone(); while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); input_error("Condition for generate case is not constant!\n"); } bool ref_signed = buf->is_signed; RTLIL::Const ref_value = buf->bitsAsConst(); delete buf; AstNode *selected_case = NULL; for (size_t i = 1; i < children.size(); i++) { log_assert(children.at(i)->type == AST_COND || children.at(i)->type == AST_CONDX || children.at(i)->type == AST_CONDZ); AstNode *this_genblock = NULL; for (auto child : children.at(i)->children) { log_assert(this_genblock == NULL); if (child->type == AST_GENBLOCK) this_genblock = child; } for (auto child : children.at(i)->children) { if (child->type == AST_DEFAULT) { if (selected_case == NULL) selected_case = this_genblock; continue; } if (child->type == AST_GENBLOCK) continue; buf = child->clone(); 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> "); 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(); delete buf; if (is_selected) { selected_case = this_genblock; i = children.size(); break; } } } if (selected_case != NULL) { log_assert(selected_case->type == AST_GENBLOCK); buf = selected_case->clone(); if (!buf->str.empty()) { buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { buf->children[i]->simplify(const_fold, stage, -1, false); current_ast_mod->children.push_back(buf->children[i]); } buf->children.clear(); delete buf; } delete_children(); did_something = true; } // unroll cell arrays if (type == AST_CELLARRAY) { if (!children.at(0)->range_valid) 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; for (int i = 0; i < num; i++) { int idx = children.at(0)->range_left > children.at(0)->range_right ? children.at(0)->range_right + i : children.at(0)->range_right - i; AstNode *new_cell = children.at(1)->clone(); newNode->children.push_back(new_cell); new_cell->str += stringf("[%d]", idx); if (new_cell->type == AST_PRIMITIVE) { 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()); } } goto apply_newNode; } // replace primitives with assignments if (type == AST_PRIMITIVE) { if (children.size() < 2) input_error("Insufficient number of arguments for primitive `%s'!\n", str.c_str()); std::vector children_list; for (auto child : children) { log_assert(child->type == AST_ARGUMENT); log_assert(child->children.size() == 1); children_list.push_back(child->children[0]); child->children.clear(); delete child; } children.clear(); if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1") { if (children_list.size() != 3) input_error("Invalid number of arguments for primitive `%s'!\n", str.c_str()); std::vector z_const(1, RTLIL::State::Sz); AstNode *mux_input = children_list.at(1); if (str == "notif0" || str == "notif1") { mux_input = new AstNode(AST_BIT_NOT, mux_input); } AstNode *node = new AstNode(AST_TERNARY, children_list.at(2)); if (str == "bufif0") { node->children.push_back(AstNode::mkconst_bits(z_const, false)); node->children.push_back(mux_input); } else { node->children.push_back(mux_input); node->children.push_back(AstNode::mkconst_bits(z_const, false)); } str.clear(); type = AST_ASSIGN; 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 { AstNodeType op_type = AST_NONE; bool invert_results = false; if (str == "and") op_type = AST_BIT_AND; if (str == "nand") op_type = AST_BIT_AND, invert_results = true; if (str == "or") op_type = AST_BIT_OR; if (str == "nor") op_type = AST_BIT_OR, invert_results = true; if (str == "xor") op_type = AST_BIT_XOR; if (str == "xnor") op_type = AST_BIT_XOR, invert_results = true; log_assert(op_type != AST_NONE); AstNode *node = children_list[1]; if (op_type != AST_POS) for (size_t i = 2; i < children_list.size(); i++) { node = new AstNode(op_type, node, children_list[i]); node->location = location; } if (invert_results) node = new AstNode(AST_BIT_NOT, node); str.clear(); type = AST_ASSIGN; children.push_back(children_list[0]); children.back()->was_checked = true; children.push_back(node); fixup_hierarchy_flags(); did_something = true; } } // replace dynamic ranges in left-hand side expressions (e.g. "foo[bar] <= 1'b1;") with // either a big case block that selects the correct single-bit assignment, or mask and // shift operations. if (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) { if (children[0]->type != AST_IDENTIFIER || children[0]->children.size() == 0) goto skip_dynamic_range_lvalue_expansion; if (children[0]->children[0]->range_valid || did_something) goto skip_dynamic_range_lvalue_expansion; if (children[0]->id2ast == NULL || children[0]->id2ast->type != AST_WIRE) goto skip_dynamic_range_lvalue_expansion; if (!children[0]->id2ast->range_valid) goto skip_dynamic_range_lvalue_expansion; 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 (!try_determine_range_width(range, result_width)) input_error("Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); if (range->children.size() >= 2) shift_expr = range->children[1]->clone(); else shift_expr = range->children[0]->clone(); 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 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. if (stride == 0) stride_width = 0; else 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; 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, rvalue->clone()))); caseNode->children.push_back(cond); } } else { // mask and shift operations // dst = (dst & ~(width'1 << lsb)) | unsigned'(width'(src)) << lsb) AstNode *lvalue = children[0]->clone(); lvalue->delete_children(); 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; int shift_width_hint; bool shift_sign_hint; shift_expr->detectSignWidth(shift_width_hint, shift_sign_hint); // All operations are carried out in a new block. newNode = new AstNode(AST_BLOCK); // 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)); // Calculate lsb from position. AstNode *shift_val = pos->clone(); // 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); // 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)); // 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); // AST_SHIFT uses negative amounts for shifting left shift_val = new AstNode(AST_NEG, shift_val); // 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); } goto apply_newNode; } 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; goto apply_newNode; } // assignment with nontrivial member in left-hand concat expression -> split assignment if ((type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_CONCAT && width_hint > 0) { bool found_nontrivial_member = false; for (auto child : children[0]->children) { if (child->type == AST_IDENTIFIER && child->id2ast != NULL && child->id2ast->type == AST_MEMORY) found_nontrivial_member = true; } if (found_nontrivial_member) { 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", 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->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); wire_tmp_id->str = wire_tmp->str; newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, wire_tmp_id, children[1]->clone())); newNode->children.back()->was_checked = true; int cursor = 0; for (auto child : children[0]->children) { int child_width_hint = -1; bool child_sign_hint = true; child->detectSignWidth(child_width_hint, child_sign_hint); AstNode *rhs = wire_tmp_id->clone(); rhs->children.push_back(new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+child_width_hint-1, true), AstNode::mkconst_int(cursor, true))); newNode->children.push_back(new AstNode(type, child->clone(), rhs)); cursor += child_width_hint; } goto apply_newNode; } } // assignment with memory in left-hand side expression -> replace with memory write port if (stage > 1 && (type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE) && children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY && children[0]->id2ast->children.size() >= 2 && 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 << "$" << 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; bool mem_signed = children[0]->id2ast->is_signed; children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); newNode = new AstNode(AST_BLOCK); AstNode *defNode = new AstNode(AST_BLOCK); int data_range_left = children[0]->id2ast->children[0]->range_left; int data_range_right = children[0]->id2ast->children[0]->range_right; int mem_data_range_offset = std::min(data_range_left, data_range_right); int addr_width_hint = -1; bool addr_sign_hint = true; children[0]->children[0]->children[0]->detectSignWidthWorker(addr_width_hint, addr_sign_hint); addr_bits = std::max(addr_bits, addr_width_hint); std::vector x_bits_addr, x_bits_data, set_bits_en; for (int i = 0; i < addr_bits; i++) x_bits_addr.push_back(RTLIL::State::Sx); for (int i = 0; i < mem_width; i++) x_bits_data.push_back(RTLIL::State::Sx); for (int i = 0; i < mem_width; i++) set_bits_en.push_back(RTLIL::State::S1); AstNode *node_addr = nullptr; if (children[0]->children[0]->children[0]->isConst()) { node_addr = children[0]->children[0]->children[0]->clone(); } else { AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); wire_addr->str = id_addr; 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, 1, -1, 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_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); node_addr = new AstNode(AST_IDENTIFIER); node_addr->str = id_addr; } AstNode *node_data = nullptr; if (children[0]->children.size() == 1 && children[1]->isConst()) { node_data = children[1]->clone(); } else { 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->was_checked = true; 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, 1, -1, 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); node_data = new AstNode(AST_IDENTIFIER); node_data->str = id_data; } 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)) { } 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); else delete defNode; AstNode *assign_data = nullptr; AstNode *assign_en = nullptr; if (children[0]->children.size() == 2) { if (children[0]->children[1]->range_valid) { int offset = children[0]->children[1]->range_right; int width = children[0]->children[1]->range_left - offset + 1; offset -= mem_data_range_offset; std::vector padding_x(offset, RTLIL::State::Sx); 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; 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 *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)); 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; 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_EQ, new AstNode(AST_IDENTIFIER), children[1]->clone()); assign_data->children[0]->str = id_data; assign_data->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; 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; 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; newNode = new AstNode(); } goto apply_newNode; } // replace function and task calls with the code from the function or task if ((type == AST_FCALL || type == AST_TCALL) && !str.empty()) { if (type == AST_FCALL) { if (str == "\\$initstate") { int myidx = autoidx++; AstNode *wire = new AstNode(AST_WIRE); wire->str = stringf("$initstate$%d_wire", myidx); current_ast_mod->children.push_back(wire); 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); cell->children[0]->str = "$initstate"; cell->children[1]->str = "\\Y"; 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, 1, -1, false)) { } newNode = new AstNode(AST_IDENTIFIER); newNode->str = wire->str; newNode->id2ast = wire; goto apply_newNode; } if (str == "\\$past") { if (width_hint < 0) goto replace_fcall_later; int num_steps = 1; if (GetSize(children) != 1 && GetSize(children) != 2) 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) 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, stage, -1, false)) { } if (buf->type != AST_CONSTANT) input_error("Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); num_steps = buf->asInt(true); delete buf; } AstNode *block = nullptr; for (auto child : current_always->children) if (child->type == AST_BLOCK) block = child; log_assert(block != nullptr); if (num_steps == 0) { newNode = children[0]->clone(); goto apply_newNode; } int myidx = autoidx++; AstNode *outreg = nullptr; for (int i = 0; i < num_steps; i++) { 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", 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, 1, -1, false)) { } AstNode *regid = new AstNode(AST_IDENTIFIER); regid->str = reg->str; regid->id2ast = reg; regid->was_checked = true; AstNode *rhs = nullptr; if (outreg == nullptr) { rhs = children.at(0)->clone(); } else { rhs = new AstNode(AST_IDENTIFIER); rhs->str = outreg->str; rhs->id2ast = outreg; } block->children.push_back(new AstNode(AST_ASSIGN_LE, regid, rhs)); outreg = reg; } newNode = new AstNode(AST_IDENTIFIER); newNode->str = outreg->str; newNode->id2ast = outreg; goto apply_newNode; } if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell" || str == "\\$changed") { if (GetSize(children) != 1) input_error("System function %s got %d arguments, expected 1.\n", RTLIL::unescape_id(str).c_str(), int(children.size())); if (!current_always_clocked) input_error("System function %s is only allowed in clocked blocks.\n", RTLIL::unescape_id(str).c_str()); AstNode *present = children.at(0)->clone(); AstNode *past = clone(); past->str = "\\$past"; if (str == "\\$stable") newNode = new AstNode(AST_EQ, past, present); else if (str == "\\$changed") newNode = new AstNode(AST_NE, past, present); else if (str == "\\$rose") newNode = new AstNode(AST_LOGIC_AND, new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, past, mkconst_int(1,false))), new AstNode(AST_BIT_AND, present, mkconst_int(1,false))); else if (str == "\\$fell") newNode = new AstNode(AST_LOGIC_AND, new AstNode(AST_BIT_AND, past, mkconst_int(1,false)), new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, present, mkconst_int(1,false)))); else log_abort(); goto apply_newNode; } // $anyconst and $anyseq are mapped in AstNode::genRTLIL() if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { recursion_counter--; return false; } if (str == "\\$clog2") { 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 *buf = children[0]->clone(); while (buf->simplify(true, stage, width_hint, sign_hint)) { } if (buf->type != AST_CONSTANT) 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()) arg_value = const_sub(arg_value, 1, false, false, GetSize(arg_value)); delete buf; uint32_t result = 0; for (auto i = 0; i < arg_value.size(); i++) if (arg_value.at(i) == RTLIL::State::S1) result = i + 1; newNode = mkconst_int(result, true); goto apply_newNode; } 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 == "\\$dimensions" || str == "\\$unpacked_dimensions" || str == "\\$bits") { if (children.size() != 1) 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) 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, stage, width_hint, sign_hint)) { } dim = buf->asInt(false); delete buf; } } 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; buf->detectSignWidth(width_hint, sign_hint); if (buf->type == AST_IDENTIFIER) { id_ast = buf->id2ast; if (id_ast == NULL && current_scope.count(buf->str)) id_ast = current_scope.at(buf->str); if (!id_ast) 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 == "\\$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; else if (str == "\\$left") result = left; else if (str == "\\$right") result = right; else if (str == "\\$increment") result = left >= right ? 1 : -1; else if (str == "\\$size") result = width; else { // str == "\\$bits" result = width * mem_depth; } newNode = mkconst_int(result, true); goto apply_newNode; } if (str == "\\$ln" || str == "\\$log10" || str == "\\$exp" || str == "\\$sqrt" || str == "\\$pow" || str == "\\$floor" || str == "\\$ceil" || str == "\\$sin" || str == "\\$cos" || str == "\\$tan" || str == "\\$asin" || str == "\\$acos" || str == "\\$atan" || str == "\\$atan2" || str == "\\$hypot" || str == "\\$sinh" || str == "\\$cosh" || str == "\\$tanh" || str == "\\$asinh" || str == "\\$acosh" || str == "\\$atanh" || str == "\\$rtoi" || str == "\\$itor") { bool func_with_two_arguments = str == "\\$pow" || str == "\\$atan2" || str == "\\$hypot"; double x = 0, y = 0; if (func_with_two_arguments) { if (children.size() != 2) 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) 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, stage, width_hint, sign_hint)) { } if (!children[0]->isConst()) 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; children[0]->detectSignWidth(child_width_hint, child_sign_hint); x = children[0]->asReal(child_sign_hint); } if (children.size() >= 2) { while (children[1]->simplify(true, stage, width_hint, sign_hint)) { } if (!children[1]->isConst()) 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; children[1]->detectSignWidth(child_width_hint, child_sign_hint); y = children[1]->asReal(child_sign_hint); } if (str == "\\$rtoi") { newNode = AstNode::mkconst_int(x, true); } else { newNode = new AstNode(AST_REALVALUE); if (str == "\\$ln") newNode->realvalue = ::log(x); else if (str == "\\$log10") newNode->realvalue = ::log10(x); else if (str == "\\$exp") newNode->realvalue = ::exp(x); else if (str == "\\$sqrt") newNode->realvalue = ::sqrt(x); else if (str == "\\$pow") newNode->realvalue = ::pow(x, y); else if (str == "\\$floor") newNode->realvalue = ::floor(x); else if (str == "\\$ceil") newNode->realvalue = ::ceil(x); else if (str == "\\$sin") newNode->realvalue = ::sin(x); else if (str == "\\$cos") newNode->realvalue = ::cos(x); else if (str == "\\$tan") newNode->realvalue = ::tan(x); else if (str == "\\$asin") newNode->realvalue = ::asin(x); else if (str == "\\$acos") newNode->realvalue = ::acos(x); else if (str == "\\$atan") newNode->realvalue = ::atan(x); else if (str == "\\$atan2") newNode->realvalue = ::atan2(x, y); else if (str == "\\$hypot") newNode->realvalue = ::hypot(x, y); else if (str == "\\$sinh") newNode->realvalue = ::sinh(x); else if (str == "\\$cosh") newNode->realvalue = ::cosh(x); else if (str == "\\$tanh") newNode->realvalue = ::tanh(x); else if (str == "\\$asinh") newNode->realvalue = ::asinh(x); else if (str == "\\$acosh") newNode->realvalue = ::acosh(x); else if (str == "\\$atanh") newNode->realvalue = ::atanh(x); else if (str == "\\$itor") newNode->realvalue = x; else log_abort(); } goto apply_newNode; } if (str == "\\$sformatf") { 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; } if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) { AstNode *dpi_decl = current_scope[str]; std::string rtype, fname; std::vector argtypes; std::vector args; rtype = RTLIL::unescape_id(dpi_decl->children.at(0)->str); fname = RTLIL::unescape_id(dpi_decl->children.at(1)->str); for (int i = 2; i < GetSize(dpi_decl->children); i++) { if (i-2 >= GetSize(children)) 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, stage, -1, false)) { } if (args.back()->type != AST_CONSTANT && args.back()->type != AST_REALVALUE) input_error("Failed to evaluate DPI function with non-constant argument.\n"); } newNode = dpi_call(rtype, fname, argtypes, args); for (auto arg : args) delete arg; 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) input_error("Can't resolve function name `%s'.\n", str.c_str()); } if (type == AST_TCALL) { if (str == "$finish" || str == "$stop") { if (!current_always || current_always->type != AST_INITIAL) input_error("System task `%s' outside initial block is unsupported.\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) 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, stage, width_hint, sign_hint)) { } if (node_filename->type != AST_CONSTANT) 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, stage, width_hint, sign_hint)) { } if (node_memory->type != AST_IDENTIFIER || node_memory->id2ast == nullptr || node_memory->id2ast->type != AST_MEMORY) 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, stage, width_hint, sign_hint)) { } if (node_addr->type != AST_CONSTANT) 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, stage, width_hint, sign_hint)) { } if (node_addr->type != AST_CONSTANT) input_error("Failed to evaluate system function `%s' with non-constant 4th argument.\n", str.c_str()); finish_addr = int(node_addr->asInt(false)); } bool unconditional_init = false; if (current_always->type == AST_INITIAL) { pool queue; log_assert(current_always->children[0]->type == AST_BLOCK); queue.insert(current_always->children[0]); while (!unconditional_init && !queue.empty()) { pool next_queue; for (auto n : queue) for (auto c : n->children) { if (c == this) unconditional_init = true; next_queue.insert(c); } next_queue.swap(queue); } } newNode = readmem(str == "\\$readmemh", node_filename->bitsAsConst().decode_string(), node_memory->id2ast, start_addr, finish_addr, unconditional_init); delete node_filename; delete node_memory; 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) input_error("Can't resolve task name `%s'.\n", str.c_str()); } std::stringstream sstr; sstr << str << "$func$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); 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, 1, -1, false)) { } if (child->type != AST_CONSTANT && child->type != AST_REALVALUE) all_args_const = false; } if (all_args_const) { 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; if (newNode) { delete decl; goto apply_newNode; } } if (in_param) input_error("Non-constant function call in constant expression.\n"); if (require_const_eval) input_error("Function %s can only be called with constant arguments.\n", str.c_str()); } size_t arg_count = 0; dict wire_cache; vector new_stmts; vector output_assignments; if (current_block == NULL) { 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 == res_name) wire = child->clone(); log_assert(wire != NULL); 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, 1, -1, false)) { } AstNode *lvalue = new AstNode(AST_IDENTIFIER); lvalue->str = wire->str; AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_EQ, lvalue, clone()))); always->children[0]->children[0]->was_checked = true; current_ast_mod->children.push_back(always); goto replace_fcall_with_id; } if (decl->attributes.count(ID::via_celltype)) { std::string celltype = decl->attributes.at(ID::via_celltype)->asAttrConst().decode_string(); std::string outport = str; if (celltype.find(' ') != std::string::npos) { int pos = celltype.find(' '); outport = RTLIL::escape_id(celltype.substr(pos+1)); celltype = RTLIL::escape_id(celltype.substr(0, pos)); } else celltype = RTLIL::escape_id(celltype); AstNode *cell = new AstNode(AST_CELL, new AstNode(AST_CELLTYPE)); cell->str = prefix.substr(0, GetSize(prefix)-1); cell->children[0]->str = celltype; for (auto attr : decl->attributes) if (attr.first.str().rfind("\\via_celltype_defparam_", 0) == 0) { AstNode *cell_arg = new AstNode(AST_PARASET, attr.second->clone()); cell_arg->str = RTLIL::escape_id(attr.first.substr(strlen("\\via_celltype_defparam_"))); cell->children.push_back(cell_arg); } for (auto child : decl->children) if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) { AstNode *wire = child->clone(); wire->port_id = 0; wire->is_input = false; wire->is_output = false; current_ast_mod->children.push_back(wire); while (wire->simplify(true, 1, -1, false)) { } AstNode *wire_id = new AstNode(AST_IDENTIFIER); wire_id->str = wire->str; if ((child->is_input || child->is_output) && arg_count < children.size()) { AstNode *arg = children[arg_count++]->clone(); AstNode *assign = child->is_input ? new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) : new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone()); assign->children[0]->was_checked = true; for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { if (*it != current_block_child) continue; current_block->children.insert(it, assign); break; } } AstNode *cell_arg = new AstNode(AST_ARGUMENT, wire_id); cell_arg->str = child->str == str ? outport : child->str; cell->children.push_back(cell_arg); } current_ast_mod->children.push_back(cell); goto replace_fcall_with_id; } for (auto child : decl->children) if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_ENUM_ITEM) { AstNode *wire = nullptr; if (wire_cache.count(child->str)) { wire = wire_cache.at(child->str); bool contains_value = wire->type == AST_LOCALPARAM; if (wire->children.size() == contains_value) { for (auto c : child->children) wire->children.push_back(c->clone()); } else if (!child->children.empty()) { 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: input_error("Incompatible re-declaration of wire %s.\n", child->str.c_str()); } } } else { wire = child->clone(); wire->port_id = 0; wire->is_input = false; wire->is_output = false; wire->is_reg = true; wire->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); if (child->type == AST_ENUM_ITEM) 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); } while (wire->simplify(true, 1, -1, false)) { } if ((child->is_input || child->is_output) && arg_count < children.size()) { AstNode *arg = children[arg_count++]->clone(); // 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); wire_id->str = wire->str; AstNode *assign = child->is_input ? new AstNode(AST_ASSIGN_EQ, wire_id, arg) : new AstNode(AST_ASSIGN_EQ, arg, wire_id); assign->children[0]->was_checked = true; if (child->is_input) new_stmts.push_back(assign); else output_assignments.push_back(assign); } } for (auto child : decl->children) if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) new_stmts.push_back(child->clone()); new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); for (auto it = current_block->children.begin(); ; it++) { log_assert(it != current_block->children.end()); if (*it == current_block_child) { current_block->children.insert(it, new_stmts.begin(), new_stmts.end()); break; } } replace_fcall_with_id: delete decl; if (type == AST_FCALL) { delete_children(); type = AST_IDENTIFIER; str = prefix_id(prefix, "$result"); } if (type == AST_TCALL) str = ""; did_something = true; } replace_fcall_later:; // perform const folding when activated if (const_fold) { bool string_op; std::vector tmp_bits; RTLIL::Const (*const_func)(const RTLIL::Const&, const RTLIL::Const&, bool, bool, int); RTLIL::Const dummy_arg; switch (type) { case AST_IDENTIFIER: if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM)) { if (current_scope[str]->children[0]->type == AST_CONSTANT) { if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) { std::vector data; bool param_upto = current_scope[str]->range_valid && current_scope[str]->range_swapped; int param_offset = current_scope[str]->range_valid ? current_scope[str]->range_right : 0; int param_width = current_scope[str]->range_valid ? current_scope[str]->range_left - current_scope[str]->range_right + 1 : GetSize(current_scope[str]->children[0]->bits); int tmp_range_left = children[0]->range_left, tmp_range_right = children[0]->range_right; if (param_upto) { 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[chunk_offset + index]); else data.push_back(RTLIL::State::Sx); } newNode = mkconst_bits(data, false); } else if (children.size() == 0) newNode = current_scope[str]->children[0]->clone(); } else if (current_scope[str]->children[0]->isConst()) newNode = current_scope[str]->children[0]->clone(); } break; case AST_BIT_NOT: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = RTLIL::const_not(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); newNode = mkconst_bits(y.to_bits(), sign_hint); } break; case AST_TO_SIGNED: case AST_TO_UNSIGNED: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = children[0]->bitsAsConst(width_hint, sign_hint); newNode = mkconst_bits(y.to_bits(), type == AST_TO_SIGNED); } break; if (0) { case AST_BIT_AND: const_func = RTLIL::const_and; } if (0) { case AST_BIT_OR: const_func = RTLIL::const_or; } if (0) { case AST_BIT_XOR: const_func = RTLIL::const_xor; } if (0) { case AST_BIT_XNOR: const_func = RTLIL::const_xnor; } if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); newNode = mkconst_bits(y.to_bits(), sign_hint); } break; if (0) { case AST_REDUCE_AND: const_func = RTLIL::const_reduce_and; } if (0) { case AST_REDUCE_OR: const_func = RTLIL::const_reduce_or; } if (0) { case AST_REDUCE_XOR: const_func = RTLIL::const_reduce_xor; } if (0) { case AST_REDUCE_XNOR: const_func = RTLIL::const_reduce_xnor; } if (0) { case AST_REDUCE_BOOL: const_func = RTLIL::const_reduce_bool; } if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), dummy_arg, false, false, -1); newNode = mkconst_bits(y.to_bits(), false); } break; case AST_LOGIC_NOT: if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = RTLIL::const_logic_not(RTLIL::Const(children[0]->bits), dummy_arg, children[0]->is_signed, false, -1); newNode = mkconst_bits(y.to_bits(), false); } else if (children[0]->isConst()) { newNode = mkconst_int(children[0]->asReal(sign_hint) == 0, false, 1); } break; if (0) { case AST_LOGIC_AND: const_func = RTLIL::const_logic_and; } if (0) { case AST_LOGIC_OR: const_func = RTLIL::const_logic_or; } if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(RTLIL::Const(children[0]->bits), RTLIL::Const(children[1]->bits), children[0]->is_signed, children[1]->is_signed, -1); newNode = mkconst_bits(y.to_bits(), false); } else if (children[0]->isConst() && children[1]->isConst()) { if (type == AST_LOGIC_AND) newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) && (children[1]->asReal(sign_hint) != 0), false, 1); else newNode = mkconst_int((children[0]->asReal(sign_hint) != 0) || (children[1]->asReal(sign_hint) != 0), false, 1); } break; if (0) { case AST_SHIFT_LEFT: const_func = RTLIL::const_shl; } if (0) { case AST_SHIFT_RIGHT: const_func = RTLIL::const_shr; } if (0) { case AST_SHIFT_SLEFT: const_func = RTLIL::const_sshl; } if (0) { case AST_SHIFT_SRIGHT: const_func = RTLIL::const_sshr; } if (0) { case AST_POW: const_func = RTLIL::const_pow; } if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? children[1]->is_signed : false, width_hint); newNode = mkconst_bits(y.to_bits(), sign_hint); } else if (type == AST_POW && children[0]->isConst() && children[1]->isConst()) { newNode = new AstNode(AST_REALVALUE); newNode->realvalue = pow(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); } break; if (0) { case AST_LT: const_func = RTLIL::const_lt; } if (0) { case AST_LE: const_func = RTLIL::const_le; } if (0) { case AST_EQ: const_func = RTLIL::const_eq; } if (0) { case AST_NE: const_func = RTLIL::const_ne; } if (0) { case AST_EQX: const_func = RTLIL::const_eqx; } if (0) { case AST_NEX: const_func = RTLIL::const_nex; } if (0) { case AST_GE: const_func = RTLIL::const_ge; } if (0) { case AST_GT: const_func = RTLIL::const_gt; } if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { int cmp_width = max(children[0]->bits.size(), children[1]->bits.size()); bool cmp_signed = children[0]->is_signed && children[1]->is_signed; RTLIL::Const y = const_func(children[0]->bitsAsConst(cmp_width, cmp_signed), children[1]->bitsAsConst(cmp_width, cmp_signed), cmp_signed, cmp_signed, 1); newNode = mkconst_bits(y.to_bits(), false); } else if (children[0]->isConst() && children[1]->isConst()) { bool cmp_signed = (children[0]->type == AST_REALVALUE || children[0]->is_signed) && (children[1]->type == AST_REALVALUE || children[1]->is_signed); switch (type) { case AST_LT: newNode = mkconst_int(children[0]->asReal(cmp_signed) < children[1]->asReal(cmp_signed), false, 1); break; case AST_LE: newNode = mkconst_int(children[0]->asReal(cmp_signed) <= children[1]->asReal(cmp_signed), false, 1); break; case AST_EQ: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; case AST_NE: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; case AST_EQX: newNode = mkconst_int(children[0]->asReal(cmp_signed) == children[1]->asReal(cmp_signed), false, 1); break; case AST_NEX: newNode = mkconst_int(children[0]->asReal(cmp_signed) != children[1]->asReal(cmp_signed), false, 1); break; case AST_GE: newNode = mkconst_int(children[0]->asReal(cmp_signed) >= children[1]->asReal(cmp_signed), false, 1); break; case AST_GT: newNode = mkconst_int(children[0]->asReal(cmp_signed) > children[1]->asReal(cmp_signed), false, 1); break; default: log_abort(); } } break; if (0) { case AST_ADD: const_func = RTLIL::const_add; } if (0) { case AST_SUB: const_func = RTLIL::const_sub; } if (0) { case AST_MUL: const_func = RTLIL::const_mul; } if (0) { case AST_DIV: const_func = RTLIL::const_div; } if (0) { case AST_MOD: const_func = RTLIL::const_mod; } if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); newNode = mkconst_bits(y.to_bits(), sign_hint); } else if (children[0]->isConst() && children[1]->isConst()) { newNode = new AstNode(AST_REALVALUE); switch (type) { case AST_ADD: newNode->realvalue = children[0]->asReal(sign_hint) + children[1]->asReal(sign_hint); break; case AST_SUB: newNode->realvalue = children[0]->asReal(sign_hint) - children[1]->asReal(sign_hint); break; case AST_MUL: newNode->realvalue = children[0]->asReal(sign_hint) * children[1]->asReal(sign_hint); break; case AST_DIV: newNode->realvalue = children[0]->asReal(sign_hint) / children[1]->asReal(sign_hint); break; case AST_MOD: newNode->realvalue = fmod(children[0]->asReal(sign_hint), children[1]->asReal(sign_hint)); break; default: log_abort(); } } break; if (0) { case AST_SELFSZ: const_func = RTLIL::const_pos; } if (0) { case AST_POS: const_func = RTLIL::const_pos; } if (0) { case AST_NEG: const_func = RTLIL::const_neg; } if (children[0]->type == AST_CONSTANT) { RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint), dummy_arg, sign_hint, false, width_hint); newNode = mkconst_bits(y.to_bits(), sign_hint); } else if (children[0]->isConst()) { newNode = new AstNode(AST_REALVALUE); if (type == AST_NEG) newNode->realvalue = -children[0]->asReal(sign_hint); else newNode->realvalue = +children[0]->asReal(sign_hint); } break; case AST_TERNARY: if (children[0]->isConst()) { auto pair = get_tern_choice(); AstNode *choice = pair.first; AstNode *not_choice = pair.second; if (choice != NULL) { if (choice->type == AST_CONSTANT) { int other_width_hint = width_hint; bool other_sign_hint = sign_hint, other_real = false; not_choice->detectSignWidth(other_width_hint, other_sign_hint, &other_real); if (other_real) { newNode = new AstNode(AST_REALVALUE); choice->detectSignWidth(width_hint, sign_hint); newNode->realvalue = choice->asReal(sign_hint); } else { RTLIL::Const y = choice->bitsAsConst(width_hint, sign_hint); if (choice->is_string && y.size() % 8 == 0 && sign_hint == false) newNode = mkconst_str(y.to_bits()); else newNode = mkconst_bits(y.to_bits(), sign_hint); } } else if (choice->isConst()) { newNode = choice->clone(); } } else if (children[1]->type == AST_CONSTANT && children[2]->type == AST_CONSTANT) { RTLIL::Const a = children[1]->bitsAsConst(width_hint, sign_hint); RTLIL::Const b = children[2]->bitsAsConst(width_hint, sign_hint); log_assert(a.size() == b.size()); for (auto i = 0; i < a.size(); i++) if (a[i] != b[i]) a.bits()[i] = RTLIL::State::Sx; newNode = mkconst_bits(a.to_bits(), sign_hint); } else if (children[1]->isConst() && children[2]->isConst()) { newNode = new AstNode(AST_REALVALUE); if (children[1]->asReal(sign_hint) == children[2]->asReal(sign_hint)) newNode->realvalue = children[1]->asReal(sign_hint); else // IEEE Std 1800-2012 Sec. 11.4.11 states that the entry in Table 7-1 for // the data type in question should be returned if the ?: is ambiguous. The // value in Table 7-1 for the 'real' type is 0.0. newNode->realvalue = 0.0; } } break; 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; if (children[1]->is_unsized) val = children[1]->bitsAsUnsizedConst(width); else val = children[1]->bitsAsConst(width); newNode = mkconst_bits(val.to_bits(), children[1]->is_signed); } break; case AST_CONCAT: string_op = !children.empty(); for (auto it = children.begin(); it != children.end(); it++) { if ((*it)->type != AST_CONSTANT) goto not_const; if (!(*it)->is_string) string_op = false; tmp_bits.insert(tmp_bits.end(), (*it)->bits.begin(), (*it)->bits.end()); } newNode = string_op ? mkconst_str(tmp_bits) : mkconst_bits(tmp_bits, false); break; case AST_REPLICATE: if (children.at(0)->type != AST_CONSTANT || children.at(1)->type != AST_CONSTANT) goto not_const; for (int i = 0; i < children[0]->bitsAsConst().as_int(); i++) tmp_bits.insert(tmp_bits.end(), children.at(1)->bits.begin(), children.at(1)->bits.end()); newNode = children.at(1)->is_string ? mkconst_str(tmp_bits) : mkconst_bits(tmp_bits, false); break; default: not_const: break; } } // if any of the above set 'newNode' -> use 'newNode' as template to update 'this' if (newNode) { apply_newNode: // fprintf(stderr, "----\n"); // dumpAst(stderr, "- "); // newNode->dumpAst(stderr, "+ "); log_assert(newNode != NULL); newNode->filename = filename; newNode->location = location; newNode->cloneInto(this); fixup_hierarchy_flags(); delete newNode; did_something = true; } if (!did_something) basic_prep = true; recursion_counter--; return did_something; } void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &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 AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init) { int mem_width, mem_size, addr_bits; memory->meminfo(mem_width, mem_size, addr_bits); AstNode *block = new AstNode(AST_BLOCK); 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()) { #ifdef _WIN32 char slash = '\\'; #else char slash = '/'; #endif std::string path = filename.substr(0, filename.find_last_of(slash)+1); f.open(path + mem_filename.c_str()); yosys_input_files.insert(path + mem_filename); } else { yosys_input_files.insert(mem_filename); } if (f.fail() || GetSize(mem_filename) == 0) 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; int range_min = min(range_left, range_right), range_max = max(range_left, range_right); if (start_addr < 0) start_addr = range_min; if (finish_addr < 0) finish_addr = range_max + 1; bool in_comment = false; int increment = start_addr <= finish_addr ? +1 : -1; int cursor = start_addr; while (!f.eof()) { std::string line, token; std::getline(f, line); for (int i = 0; i < GetSize(line); i++) { if (in_comment && line.compare(i, 2, "*/") == 0) { line[i] = ' '; line[i+1] = ' '; in_comment = false; continue; } if (!in_comment && line.compare(i, 2, "/*") == 0) in_comment = true; if (in_comment) line[i] = ' '; } while (1) { token = next_token(line, " \t\r\n"); if (token.empty() || token.compare(0, 2, "//") == 0) break; if (token[0] == '@') { token = token.substr(1); const char *nptr = token.c_str(); char *endptr; cursor = strtol(nptr, &endptr, 16); if (!*nptr || *endptr) input_error("Can not parse address `%s` for %s.\n", nptr, str.c_str()); continue; } AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token); if (unconditional_init) { if (meminit == nullptr || cursor != next_meminit_cursor) { if (meminit != nullptr) { meminit->children[1] = AstNode::mkconst_bits(meminit_bits, 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; meminit_bits.clear(); meminit_size = 0; current_ast_mod->children.push_back(meminit); next_meminit_cursor = cursor; } meminit_size++; next_meminit_cursor++; meminit_bits.insert(meminit_bits.end(), value->bits.begin(), value->bits.end()); delete value; } else { block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); block->children.back()->children[0]->str = memory->str; block->children.back()->children[0]->id2ast = memory; block->children.back()->children[0]->was_checked = true; } cursor += increment; if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) break; } if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) break; } if (meminit != nullptr) { meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); meminit->children[3] = AstNode::mkconst_int(meminit_size, false); } return block; } // 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) { 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; } // 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; } } } } 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) child->replace_result_wire_name_in_function(child->str, new_name); else child->str = new_name; current_scope[new_name] = child; }; for (size_t i = 0; i < children.size(); i++) { AstNode *child = children[i]; switch (child->type) { case AST_WIRE: case AST_MEMORY: case AST_STRUCT: case AST_UNION: case AST_PARAMETER: case AST_LOCALPARAM: case AST_FUNCTION: case AST_TASK: case AST_CELL: case AST_TYPEDEF: case AST_ENUM_ITEM: case AST_GENVAR: prefix_node(child); break; case AST_BLOCK: case AST_GENBLOCK: if (!child->str.empty()) prefix_node(child); break; case AST_ENUM: current_scope[child->str] = child; for (auto enode : child->children){ log_assert(enode->type == AST_ENUM_ITEM); prefix_node(enode); } break; default: break; } } for (size_t i = 0; i < children.size(); i++) { AstNode *child = children[i]; // 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) 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; child->expand_genblock(prefix); } } // 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) { 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; 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; } 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; } } // helper function for mem2reg_as_needed_pass1 static void mark_memories_assign_lhs_complex(dict> &mem2reg_places, dict &mem2reg_candidates, AstNode *that) { for (auto &child : that->children) mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child); 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", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS; } } // find memories that should be replaced by registers void AstNode::mem2reg_as_needed_pass1(dict> &mem2reg_places, dict &mem2reg_candidates, dict &proc_flags, uint32_t &flags) { uint32_t children_flags = 0; int lhs_children_counter = 0; if (type == AST_TYPEDEF) return; // don't touch content of typedefs if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) { // mark all memories that are used in a complex expression on the left side of an assignment for (auto &lhs_child : children[0]->children) mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child); if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY) { AstNode *mem = children[0]->id2ast; // 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", 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", RTLIL::encode_filename(filename).c_str(), location.first_line)); proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1; } // for proper (non-init) writes: remember if this is a constant index or not if ((flags & MEM2REG_FL_INIT) == 0) { if (children[0]->children.size() && children[0]->children[0]->type == AST_RANGE && children[0]->children[0]->children.size()) { if (children[0]->children[0]->children[0]->type == AST_CONSTANT) mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CONST_LHS; else mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_VAR_LHS; } } // 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", 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", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE; } } lhs_children_counter = 1; } if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY) { 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", RTLIL::encode_filename(filename).c_str(), location.first_line)); mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2; } } // also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg' or 'logic' 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 || type == AST_INTERFACE) && get_bool_attribute(ID::mem2reg)) children_flags |= AstNode::MEM2REG_FL_ALL; dict *proc_flags_p = NULL; if (type == AST_ALWAYS) { int count_edge_events = 0; for (auto child : children) if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) count_edge_events++; if (count_edge_events != 1) children_flags |= AstNode::MEM2REG_FL_ASYNC; proc_flags_p = new dict; } else if (type == AST_INITIAL) { children_flags |= AstNode::MEM2REG_FL_INIT; proc_flags_p = new dict; } uint32_t backup_flags = flags; flags |= children_flags; log_assert((flags & ~0x000000ff) == 0); for (auto child : children) { if (lhs_children_counter > 0) { lhs_children_counter--; if (child->children.size() && child->children[0]->type == AST_RANGE && child->children[0]->children.size()) { for (auto c : child->children[0]->children) { if (proc_flags_p) c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags); else c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags); } } } else if (proc_flags_p) child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags); else child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags); } flags &= ~children_flags | backup_flags; if (proc_flags_p) { #ifndef NDEBUG for (auto it : *proc_flags_p) log_assert((it.second & ~0xff000000) == 0); #endif delete proc_flags_p; } } bool AstNode::mem2reg_check(pool &mem2reg_set) { if (type != AST_IDENTIFIER || !id2ast || !mem2reg_set.count(id2ast)) return false; if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1) input_error("Invalid array access.\n"); return true; } void AstNode::mem2reg_remove(pool &mem2reg_set, vector &delnodes) { log_assert(mem2reg_set.count(this) == 0); if (mem2reg_set.count(id2ast)) id2ast = nullptr; for (size_t i = 0; i < children.size(); i++) { if (mem2reg_set.count(children[i]) > 0) { delnodes.push_back(children[i]); children.erase(children.begin() + (i--)); } else { children[i]->mem2reg_remove(mem2reg_set, delnodes); } } } // actually replace memories with registers bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block) { bool did_something = false; if (type == AST_BLOCK) block = this; if (type == AST_FUNCTION || type == AST_TASK) return false; if (type == AST_TYPEDEF) return false; if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast)) { 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(); Const en = children[2]->bitsAsConst(); int length = children[3]->asInt(false); if (length != 0) { AstNode *block = new AstNode(AST_INITIAL, new AstNode(AST_BLOCK)); mod->children.push_back(block); block = block->children[0]; int wordsz = GetSize(data) / length; for (int i = 0; i < length; i++) { 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).to_bits(), false))); pos = epos; } } } } AstNode *newNode = new AstNode(AST_NONE); newNode->cloneInto(this); delete newNode; did_something = true; } if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set)) { if (async_block == NULL) { async_block = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); mod->children.push_back(async_block); } AstNode *newNode = clone(); newNode->type = AST_ASSIGN_EQ; newNode->children[0]->was_checked = true; async_block->children[0]->children.push_back(newNode); newNode = new AstNode(AST_NONE); newNode->cloneInto(this); delete newNode; did_something = true; } if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->mem2reg_check(mem2reg_set) && children[0]->children[0]->children[0]->type != AST_CONSTANT) { std::stringstream sstr; 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; bool mem_signed = children[0]->id2ast->is_signed; children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); wire_addr->str = id_addr; wire_addr->is_reg = true; wire_addr->was_checked = true; wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); mod->children.push_back(wire_addr); 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->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); mod->children.push_back(wire_data); while (wire_data->simplify(true, 1, -1, false)) { } log_assert(block != NULL); size_t assign_idx = 0; while (assign_idx < block->children.size() && block->children[assign_idx] != this) assign_idx++; log_assert(assign_idx < block->children.size()); AstNode *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; block->children.insert(block->children.begin()+assign_idx+1, assign_addr); AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); case_node->children[0]->str = id_addr; for (int i = 0; i < mem_size; i++) { if (children[0]->children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->children[0]->integer) != i) continue; AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); AstNode *assign_reg = new AstNode(type, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); if (children[0]->children.size() == 2) assign_reg->children[0]->children.push_back(children[0]->children[1]->clone()); assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i); assign_reg->children[1]->str = id_data; 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(); children[0]->range_valid = false; children[0]->id2ast = NULL; children[0]->str = id_data; type = AST_ASSIGN_EQ; children[0]->was_checked = true; fixup_hierarchy_flags(); did_something = true; } if (mem2reg_check(mem2reg_set)) { AstNode *bit_part_sel = NULL; if (children.size() == 2) bit_part_sel = children[1]->clone(); if (children[0]->children[0]->type == AST_CONSTANT) { int id = children[0]->children[0]->integer; 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(); 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 << "$" << 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; bool mem_signed = id2ast->is_signed; id2ast->meminfo(mem_width, mem_size, addr_bits); AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); wire_addr->str = id_addr; wire_addr->is_reg = true; wire_addr->was_checked = true; if (block) wire_addr->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); mod->children.push_back(wire_addr); 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; if (block) wire_data->set_attribute(ID::nosync, AstNode::mkconst_int(1, false)); mod->children.push_back(wire_data); 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; assign_addr->children[0]->was_checked = true; AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); case_node->children[0]->str = id_addr; for (int i = 0; i < mem_size; i++) { if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i) continue; AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); assign_reg->children[0]->str = id_data; assign_reg->children[0]->was_checked = true; assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i); cond_node->children[1]->children.push_back(assign_reg); case_node->children.push_back(cond_node); } std::vector x_bits; for (int i = 0; i < mem_width; i++) x_bits.push_back(RTLIL::State::Sx); AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK)); AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false)); assign_reg->children[0]->str = id_data; assign_reg->children[0]->was_checked = true; 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; while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this)) assign_idx++; log_assert(assign_idx < block->children.size()); block->children.insert(block->children.begin()+assign_idx, case_node); block->children.insert(block->children.begin()+assign_idx, assign_addr); } else { 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(); range_valid = false; id2ast = NULL; str = id_data; } if (bit_part_sel) { children.push_back(bit_part_sel); fixup_hierarchy_flags(); } did_something = true; } log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); auto children_list = children; for (size_t i = 0; i < children_list.size(); i++) if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block, async_block)) did_something = true; return did_something; } // calculate memory dimensions void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) { log_assert(type == AST_MEMORY); mem_width = children[0]->range_left - children[0]->range_right + 1; mem_size = children[1]->range_left - children[1]->range_right; if (mem_size < 0) mem_size *= -1; mem_size += min(children[1]->range_left, children[1]->range_right) + 1; addr_bits = 1; while ((1 << addr_bits) < mem_size) addr_bits++; } bool AstNode::detect_latch(const std::string &var) { switch (type) { case AST_ALWAYS: for (auto &c : children) { switch (c->type) { case AST_POSEDGE: case AST_NEGEDGE: return false; case AST_EDGE: break; case AST_BLOCK: if (!c->detect_latch(var)) return false; break; default: log_abort(); } } return true; case AST_BLOCK: for (auto &c : children) if (!c->detect_latch(var)) return false; return true; case AST_CASE: { bool r = true; for (auto &c : children) { if (c->type == AST_COND) { if (c->children.at(1)->detect_latch(var)) return true; r = false; } if (c->type == AST_DEFAULT) { if (c->children.at(0)->detect_latch(var)) return true; r = false; } } return r; } case AST_ASSIGN_EQ: case AST_ASSIGN_LE: if (children.at(0)->type == AST_IDENTIFIER && children.at(0)->children.empty() && children.at(0)->str == var) return false; return true; default: return true; } } bool AstNode::has_const_only_constructs() { if (type == AST_WHILE || type == AST_REPEAT) return true; for (auto child : children) if (child->has_const_only_constructs()) return true; return false; } bool AstNode::is_simple_const_expr() { if (type == AST_IDENTIFIER) return false; for (auto child : children) if (!child->is_simple_const_expr()) return false; return true; } // helper function for AstNode::eval_const_function() 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.size(); if (!children.empty()) { 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 true; } for (auto &child : children) if (!child->replace_variables(variables, fcall, must_succeed)) return false; return true; } // attempt to statically evaluate a functions with all-const arguments AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) { 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()) { AstNode *stmt = block->children.front(); #if 0 log("-----------------------------------\n"); for (auto &it : variables) log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val)); stmt->dumpAst(NULL, "stmt> "); #endif if (stmt->type == AST_WIRE) { 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()) { 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(variable.arg->type == AST_REALVALUE); variable.val = variable.arg->realAsConst(width); } } current_scope[stmt->str] = stmt; block->children.erase(block->children.begin()); to_delete.push_back(stmt); continue; } log_assert(variables.count(str) != 0); if (stmt->type == AST_LOCALPARAM) { while (stmt->simplify(true, 1, -1, false)) { } current_scope[stmt->str] = stmt; block->children.erase(block->children.begin()); to_delete.push_back(stmt); continue; } if (stmt->type == AST_ASSIGN_EQ) { 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) 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) { 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) { 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)) { 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.size()); } else { AstNode *range = stmt->children.at(0)->children.at(0); 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.size()); 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.at(i); } } delete block->children.front(); block->children.erase(block->children.begin()); continue; } if (stmt->type == AST_FOR) { block->children.insert(block->children.begin(), stmt->children.at(0)); stmt->children.at(3)->children.push_back(stmt->children.at(2)); stmt->children.erase(stmt->children.begin() + 2); stmt->children.erase(stmt->children.begin()); stmt->type = AST_WHILE; continue; } if (stmt->type == AST_WHILE) { AstNode *cond = stmt->children.at(0)->clone(); 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()); } else { delete block->children.front(); block->children.erase(block->children.begin()); } delete cond; continue; } if (stmt->type == AST_REPEAT) { AstNode *num = stmt->children.at(0)->clone(); 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++) block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); delete stmt; delete num; continue; } if (stmt->type == AST_CASE) { AstNode *expr = stmt->children.at(0)->clone(); 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++) { bool found_match = false; log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ); if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) { sel_case = stmt->children.at(i)->children.back(); continue; } 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(); if (!cond->replace_variables(variables, fcall, must_succeed)) goto finished; cond = new AstNode(AST_EQ, expr->clone(), cond); 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; } if (found_match) { sel_case = stmt->children.at(i)->children.back(); break; } } block->children.erase(block->children.begin()); if (sel_case) block->children.insert(block->children.begin(), sel_case->clone()); delete stmt; delete expr; continue; } 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; } 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.to_bits(), variables.at(str).is_signed); finished: delete block; current_scope = backup_scope; for (auto it : to_delete) { delete it; } to_delete.clear(); 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->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: // replace with auto-incremented constant delete node->children[i]; node->children[i] = AstNode::mkconst_int(++last_enum_int, true); break; case AST_CONSTANT: // explicit constant (or folded expression) // TODO: can't extend 'x or 'z item last_enum_int = node->children[i]->integer; break; default: // ignore ranges break; } // TODO: range check } } } 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 yosys-0.52/frontends/blif/000077500000000000000000000000001477540374200155615ustar00rootroot00000000000000yosys-0.52/frontends/blif/Makefile.inc000066400000000000000000000000451477540374200177700ustar00rootroot00000000000000 OBJS += frontends/blif/blifparse.o yosys-0.52/frontends/blif/blifparse.cc000066400000000000000000000446241477540374200200510ustar00rootroot00000000000000/* * 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 "blifparse.h" 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; int buffer_len = 0; buffer[0] = 0; while (1) { buffer_len += strlen(buffer + buffer_len); while (buffer_len > 0 && (buffer[buffer_len-1] == ' ' || buffer[buffer_len-1] == '\t' || buffer[buffer_len-1] == '\r' || buffer[buffer_len-1] == '\n')) buffer[--buffer_len] = 0; if (buffer_size-buffer_len < 4096) { buffer_size *= 2; buffer = (char*)realloc(buffer, buffer_size); } if (buffer_len == 0 || buffer[buffer_len-1] == '\\') { if (buffer_len > 0 && buffer[buffer_len-1] == '\\') buffer[--buffer_len] = 0; line_count++; if (!std::getline(f, strbuf)) return false; while (buffer_size-buffer_len < strbuf.size()+1) { buffer_size *= 2; buffer = (char*)realloc(buffer, buffer_size); } strcpy(buffer+buffer_len, strbuf.c_str()); } else return true; } } static std::pair wideports_split(std::string name) { int pos = -1; if (name.empty() || name.back() != ']') goto failed; for (int i = 0; i+1 < GetSize(name); i++) { if (name[i] == '[') pos = i; 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)); failed: 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) { RTLIL::Module *module = nullptr; RTLIL::Const *lutptr = NULL; RTLIL::Cell *sopcell = NULL; RTLIL::Cell *lastcell = nullptr; RTLIL::State lut_default_state = RTLIL::State::Sx; std::string err_reason; int blif_maxnum = 0, sopmode = -1; auto blif_wire = [&](const std::string &wire_name) -> Wire* { if (wire_name[0] == '$') { for (int i = 0; i+1 < GetSize(wire_name); i++) { if (wire_name[i] != '$') continue; int len = 0; while (i+len+1 < GetSize(wire_name) && '0' <= wire_name[i+len+1] && wire_name[i+len+1] <= '9') len++; if (len > 0) { string num_str = wire_name.substr(i+1, len); int num = atoi(num_str.c_str()) & 0x0fffffff; blif_maxnum = std::max(blif_maxnum, num); } } } IdString wire_id = RTLIL::escape_id(wire_name); Wire *wire = module->wire(wire_id); if (wire == nullptr) wire = module->addWire(wire_id); return wire; }; dict *obj_attributes = nullptr; dict *obj_parameters = nullptr; dict> wideports_cache; size_t buffer_size = 4096; char *buffer = (char*)malloc(buffer_size); int line_count = 0; while (1) { if (!read_next_line(buffer, buffer_size, line_count, f)) { if (module != nullptr) goto error; free(buffer); return; } continue_without_read: if (buffer[0] == '#') continue; if (buffer[0] == '.') { if (lutptr) { for (auto &bit : lutptr->bits()) if (bit == RTLIL::State::Sx) bit = lut_default_state; lutptr = NULL; lut_default_state = RTLIL::State::Sx; } if (sopcell) { sopcell = NULL; sopmode = -1; } char *cmd = strtok(buffer, " \t\r\n"); if (!strcmp(cmd, ".model")) { if (module != nullptr) goto error; module = new RTLIL::Module; lastcell = nullptr; 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)) log_error("Duplicate definition of module %s in line %d!\n", log_id(module->name), line_count); design->add(module); continue; } if (module == nullptr) goto error; if (!strcmp(cmd, ".blackbox")) { module->attributes[ID::blackbox] = RTLIL::Const(1); continue; } if (!strcmp(cmd, ".end")) { for (auto &wp : wideports_cache) { auto name = wp.first; int width = wp.second.first; bool isinput = wp.second.second; RTLIL::Wire *wire = module->addWire(name, width); wire->port_input = isinput; wire->port_output = !isinput; for (int i = 0; i < width; i++) { RTLIL::IdString other_name = name.str() + stringf("[%d]", i); RTLIL::Wire *other_wire = module->wire(other_name); if (other_wire) { other_wire->port_input = false; other_wire->port_output = false; if (isinput) module->connect(other_wire, SigSpec(wire, i)); else module->connect(SigSpec(wire, i), other_wire); } } } module->fixup_ports(); wideports_cache.clear(); if (run_clean) { Const buffer_lut(vector({State::S0, State::S1})); vector remove_cells; for (auto cell : module->cells()) if (cell->type == ID($lut) && cell->getParam(ID::LUT) == buffer_lut) { module->connect(cell->getPort(ID::Y), cell->getPort(ID::A)); remove_cells.push_back(cell); } for (auto cell : remove_cells) module->remove(cell); Wire *true_wire = module->wire(ID($true)); Wire *false_wire = module->wire(ID($false)); Wire *undef_wire = module->wire(ID($undef)); if (true_wire != nullptr) module->rename(true_wire, stringf("$true$%d", ++blif_maxnum)); if (false_wire != nullptr) module->rename(false_wire, stringf("$false$%d", ++blif_maxnum)); if (undef_wire != nullptr) module->rename(undef_wire, stringf("$undef$%d", ++blif_maxnum)); autoidx = std::max(autoidx, blif_maxnum+1); blif_maxnum = 0; } module = nullptr; lastcell = nullptr; obj_attributes = nullptr; obj_parameters = nullptr; 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; while ((p = strtok(NULL, " \t\r\n")) != NULL) { RTLIL::IdString wire_name(stringf("\\%s", p)); RTLIL::Wire *wire = module->wire(wire_name); if (wire == nullptr) wire = module->addWire(wire_name); if (!strcmp(cmd, ".inputs")) wire->port_input = true; else wire->port_output = true; if (wideports) { std::pair wp = wideports_split(p); 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"); } } } obj_attributes = nullptr; obj_parameters = nullptr; continue; } if (!strcmp(cmd, ".cname")) { char *p = strtok(NULL, " \t\r\n"); if (p == NULL) goto error; if(lastcell == nullptr || module == nullptr) { err_reason = stringf("No primitive object to attach .cname %s.", p); goto error_with_reason; } module->rename(lastcell, RTLIL::escape_id(p)); continue; } if (!strcmp(cmd, ".attr") || !strcmp(cmd, ".param")) { char *n = strtok(NULL, " \t\r\n"); char *v = strtok(NULL, "\r\n"); IdString id_n = RTLIL::escape_id(n); Const const_v; if (v[0] == '"') { std::string str(v+1); if (str.back() == '"') str.resize(str.size()-1); const_v = Const(str); } else { int n = strlen(v); const_v.bits().resize(n); for (int i = 0; i < n; i++) const_v.bits()[i] = v[n-i-1] != '0' ? State::S1 : State::S0; } if (!strcmp(cmd, ".attr")) { if (obj_attributes == nullptr) { err_reason = stringf("No object to attach .attr too."); goto error_with_reason; } (*obj_attributes)[id_n] = const_v; } else { if (obj_parameters == nullptr) { err_reason = stringf("No object to attach .param too."); goto error_with_reason; } (*obj_parameters)[id_n] = const_v; } continue; } if (!strcmp(cmd, ".latch")) { char *d = strtok(NULL, " \t\r\n"); char *q = strtok(NULL, " \t\r\n"); char *edge = strtok(NULL, " \t\r\n"); char *clock = strtok(NULL, " \t\r\n"); char *init = strtok(NULL, " \t\r\n"); RTLIL::Cell *cell = nullptr; if (clock == nullptr && edge != nullptr) { init = edge; edge = nullptr; } if (init != nullptr && (init[0] == '0' || init[0] == '1')) blif_wire(q)->attributes[ID::init] = Const(init[0] == '1' ? 1 : 0, 1); if (clock == nullptr) goto no_latch_clock; if (!strcmp(edge, "re")) cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q)); else if (!strcmp(edge, "fe")) cell = module->addDff(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false); else if (!strcmp(edge, "ah")) cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q)); else if (!strcmp(edge, "al")) cell = module->addDlatch(NEW_ID, blif_wire(clock), blif_wire(d), blif_wire(q), false); else { no_latch_clock: if (dff_name.empty()) { cell = module->addFf(NEW_ID, blif_wire(d), blif_wire(q)); } else { cell = module->addCell(NEW_ID, dff_name); cell->setPort(ID::D, blif_wire(d)); cell->setPort(ID::Q, blif_wire(q)); } } lastcell = cell; obj_attributes = &cell->attributes; obj_parameters = &cell->parameters; continue; } if (!strcmp(cmd, ".gate") || !strcmp(cmd, ".subckt")) { char *p = strtok(NULL, " \t\r\n"); if (p == NULL) goto error; 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; while ((p = strtok(NULL, " \t\r\n")) != NULL) { char *q = strchr(p, '='); if (q == NULL || !q[0]) goto error; *(q++) = 0; if (wideports) { std::pair wp = wideports_split(p); 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()); } } 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++) { 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)); } cell->setPort(it.first, sig); } lastcell = cell; obj_attributes = &cell->attributes; obj_parameters = &cell->parameters; continue; } obj_attributes = nullptr; obj_parameters = nullptr; if (!strcmp(cmd, ".barbuf") || !strcmp(cmd, ".conn")) { char *p = strtok(NULL, " \t\r\n"); if (p == NULL) goto error; char *q = strtok(NULL, " \t\r\n"); if (q == NULL) goto error; module->connect(blif_wire(q), blif_wire(p)); continue; } if (!strcmp(cmd, ".names")) { char *p; RTLIL::SigSpec input_sig, output_sig; while ((p = strtok(NULL, " \t\r\n")) != NULL) input_sig.append(blif_wire(p)); output_sig = input_sig.extract(input_sig.size()-1, 1); input_sig = input_sig.extract(0, input_sig.size()-1); if (input_sig.size() == 0) { RTLIL::State state = RTLIL::State::Sa; while (1) { if (!read_next_line(buffer, buffer_size, line_count, f)) goto error; for (int i = 0; buffer[i]; i++) { if (buffer[i] == ' ' || buffer[i] == '\t') continue; if (i == 0 && buffer[i] == '.') goto finished_parsing_constval; if (buffer[i] == '0') { if (state == RTLIL::State::S1) goto error; state = RTLIL::State::S0; continue; } if (buffer[i] == '1') { if (state == RTLIL::State::S0) goto error; state = RTLIL::State::S1; continue; } goto error; } } finished_parsing_constval: if (state == RTLIL::State::Sa) state = RTLIL::State::S0; if (output_sig.as_wire()->name == ID($undef)) state = RTLIL::State::Sx; module->connect(RTLIL::SigSig(output_sig, state)); goto continue_without_read; } if (sop_mode) { sopcell = module->addCell(NEW_ID, ID($sop)); sopcell->parameters[ID::WIDTH] = RTLIL::Const(input_sig.size()); sopcell->parameters[ID::DEPTH] = 0; sopcell->parameters[ID::TABLE] = RTLIL::Const(); sopcell->setPort(ID::A, input_sig); sopcell->setPort(ID::Y, output_sig); 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)); cell->parameters[ID::WIDTH] = RTLIL::Const(input_sig.size()); cell->parameters[ID::LUT] = RTLIL::Const(RTLIL::State::Sx, 1 << input_sig.size()); cell->setPort(ID::A, input_sig); cell->setPort(ID::Y, output_sig); lutptr = &cell->parameters.at(ID::LUT); lut_default_state = RTLIL::State::Sx; lastcell = cell; } continue; } goto error; } if (lutptr == NULL && sopcell == NULL) goto error; char *input = strtok(buffer, " \t\r\n"); char *output = strtok(NULL, " \t\r\n"); if (input == NULL || output == NULL || (strcmp(output, "0") && strcmp(output, "1"))) goto error; int input_len = strlen(input); if (sopcell) { log_assert(sopcell->parameters[ID::WIDTH].as_int() == input_len); sopcell->parameters[ID::DEPTH] = sopcell->parameters[ID::DEPTH].as_int() + 1; for (int i = 0; i < input_len; i++) switch (input[i]) { case '0': sopcell->parameters[ID::TABLE].bits().push_back(State::S1); sopcell->parameters[ID::TABLE].bits().push_back(State::S0); break; case '1': sopcell->parameters[ID::TABLE].bits().push_back(State::S0); sopcell->parameters[ID::TABLE].bits().push_back(State::S1); break; default: sopcell->parameters[ID::TABLE].bits().push_back(State::S0); sopcell->parameters[ID::TABLE].bits().push_back(State::S0); break; } if (sopmode == -1) { sopmode = (*output == '1'); if (!sopmode) { SigSpec outnet = sopcell->getPort(ID::Y); SigSpec tempnet = module->addWire(NEW_ID); module->addNotGate(NEW_ID, tempnet, outnet); sopcell->setPort(ID::Y, tempnet); } } else log_assert(sopmode == (*output == '1')); } if (lutptr) { if (input_len > lut_input_plane_limit) goto error; for (int i = 0; i < (1 << input_len); i++) { for (int j = 0; j < input_len; j++) { char c1 = input[j]; if (c1 != '-') { char c2 = (i & (1 << j)) != 0 ? '1' : '0'; if (c1 != c2) goto try_next_value; } } lutptr->bits().at(i) = !strcmp(output, "0") ? RTLIL::State::S0 : RTLIL::State::S1; try_next_value:; } lut_default_state = !strcmp(output, "0") ? RTLIL::State::S1 : RTLIL::State::S0; } } return; error: log_error("Syntax error in line %d!\n", line_count); error_with_reason: log_error("Syntax error in line %d: %s\n", line_count, err_reason.c_str()); } struct BlifFrontend : public Frontend { BlifFrontend() : Frontend("blif", "read BLIF file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_blif [options] [filename]\n"); log("\n"); log("Load modules from a BLIF file into the current design.\n"); log("\n"); log(" -sop\n"); log(" Create $sop cells instead of $lut cells\n"); log("\n"); log(" -wideports\n"); log(" Merge ports that match the pattern 'name[int]' into a single\n"); log(" multi-bit port 'name'.\n"); log("\n"); } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { bool sop_mode = false; bool wideports = false; log_header(design, "Executing BLIF frontend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-sop") { sop_mode = true; continue; } if (arg == "-wideports") { wideports = true; continue; } break; } extra_args(f, filename, args, argidx); parse_blif(design, *f, "", true, sop_mode, wideports); } } BlifFrontend; YOSYS_NAMESPACE_END yosys-0.52/frontends/blif/blifparse.h000066400000000000000000000021631477540374200177030ustar00rootroot00000000000000/* * 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 ABC_BLIFPARSE #define ABC_BLIFPARSE #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN extern void parse_blif(RTLIL::Design *design, std::istream &f, IdString dff_name, bool run_clean = false, bool sop_mode = false, bool wideports = false); YOSYS_NAMESPACE_END #endif yosys-0.52/frontends/json/000077500000000000000000000000001477540374200156165ustar00rootroot00000000000000yosys-0.52/frontends/json/Makefile.inc000066400000000000000000000000451477540374200200250ustar00rootroot00000000000000 OBJS += frontends/json/jsonparse.o yosys-0.52/frontends/json/jsonparse.cc000066400000000000000000000446451477540374200201460ustar00rootroot00000000000000/* * 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 "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN struct JsonNode { char type; // S=String, N=Number, A=Array, D=Dict string data_string; int64_t data_number; vector data_array; dict data_dict; vector data_dict_keys; JsonNode(std::istream &f) { type = 0; data_number = 0; while (1) { int ch = f.get(); if (ch == EOF) log_error("Unexpected EOF in JSON file.\n"); if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') continue; if (ch == '"') { type = 'S'; while (1) { ch = f.get(); if (ch == EOF) log_error("Unexpected EOF in JSON string.\n"); if (ch == '"') break; if (ch == '\\') { ch = f.get(); 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; } break; } if (('0' <= ch && ch <= '9') || ch == '-') { bool negative = false; type = 'N'; if (ch == '-') { data_number = 0; negative = true; } else { data_number = ch - '0'; } data_string += ch; while (1) { ch = f.get(); if (ch == EOF) break; if (ch == '.') goto parse_real; if (ch < '0' || '9' < ch) { f.unget(); break; } data_number = data_number*10 + (ch - '0'); data_string += ch; } data_number = negative ? -data_number : data_number; data_string = ""; break; parse_real: type = 'S'; data_number = 0; data_string += ch; while (1) { ch = f.get(); if (ch == EOF) break; if (ch < '0' || '9' < ch) { f.unget(); break; } data_string += ch; } break; } if (ch == '[') { type = 'A'; while (1) { ch = f.get(); if (ch == EOF) log_error("Unexpected EOF in JSON file.\n"); if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') continue; if (ch == ']') break; f.unget(); data_array.push_back(new JsonNode(f)); } break; } if (ch == '{') { type = 'D'; while (1) { ch = f.get(); if (ch == EOF) log_error("Unexpected EOF in JSON file.\n"); if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') continue; if (ch == '}') break; f.unget(); JsonNode key(f); while (1) { ch = f.get(); if (ch == EOF) log_error("Unexpected EOF in JSON file.\n"); if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ':') continue; f.unget(); break; } JsonNode *value = new JsonNode(f); if (key.type != 'S') log_error("Unexpected non-string key in JSON dict.\n"); data_dict[key.data_string] = value; data_dict_keys.push_back(key.data_string); } break; } log_error("Unexpected character in JSON file: '%c'\n", ch); } } ~JsonNode() { for (auto it : data_array) delete it; for (auto &it : data_dict) delete it.second; } }; Const json_parse_attr_param_value(JsonNode *node) { Const value; if (node->type == 'S') { string &s = node->data_string; size_t cursor = s.find_first_not_of("01xz"); if (cursor == string::npos) { value = Const::from_string(s); } else if (s.find_first_not_of(' ', cursor) == string::npos) { value = Const(s.substr(0, GetSize(s)-1)); } else { value = Const(s); } } else if (node->type == 'N') { value = Const(node->data_number, 32); if (node->data_number < 0) value.flags |= RTLIL::CONST_FLAG_SIGNED; } else if (node->type == 'A') { log_error("JSON attribute or parameter value is an array.\n"); } else if (node->type == 'D') { log_error("JSON attribute or parameter value is a dict.\n"); } else { log_abort(); } return value; } void json_parse_attr_param(dict &results, JsonNode *node) { if (node->type != 'D') log_error("JSON attributes or parameters node is not a dictionary.\n"); for (auto it : node->data_dict) { IdString key = RTLIL::escape_id(it.first.c_str()); Const value = json_parse_attr_param_value(it.second); results[key] = value; } } void json_import(Design *design, string &modname, JsonNode *node) { log("Importing module %s from JSON tree.\n", modname.c_str()); Module *module = new RTLIL::Module; module->name = RTLIL::escape_id(modname.c_str()); if (design->module(module->name)) log_error("Re-definition of module %s.\n", log_id(module->name)); design->add(module); if (node->data_dict.count("attributes")) json_parse_attr_param(module->attributes, node->data_dict.at("attributes")); dict signal_bits; if (node->data_dict.count("ports")) { JsonNode *ports_node = node->data_dict.at("ports"); if (ports_node->type != 'D') log_error("JSON ports node is not a dictionary.\n"); for (int port_id = 1; port_id <= GetSize(ports_node->data_dict_keys); port_id++) { IdString port_name = RTLIL::escape_id(ports_node->data_dict_keys[port_id-1].c_str()); JsonNode *port_node = ports_node->data_dict.at(ports_node->data_dict_keys[port_id-1]); if (port_node->type != 'D') log_error("JSON port node '%s' is not a dictionary.\n", log_id(port_name)); if (port_node->data_dict.count("direction") == 0) log_error("JSON port node '%s' has no direction attribute.\n", log_id(port_name)); if (port_node->data_dict.count("bits") == 0) log_error("JSON port node '%s' has no bits attribute.\n", log_id(port_name)); JsonNode *port_direction_node = port_node->data_dict.at("direction"); JsonNode *port_bits_node = port_node->data_dict.at("bits"); if (port_direction_node->type != 'S') log_error("JSON port node '%s' has non-string direction attribute.\n", log_id(port_name)); if (port_bits_node->type != 'A') log_error("JSON port node '%s' has non-array bits attribute.\n", log_id(port_name)); Wire *port_wire = module->wire(port_name); if (port_wire == nullptr) port_wire = module->addWire(port_name, GetSize(port_bits_node->data_array)); if (port_node->data_dict.count("upto") != 0) { JsonNode *val = port_node->data_dict.at("upto"); if (val->type == 'N') port_wire->upto = val->data_number != 0; } if (port_node->data_dict.count("signed") != 0) { JsonNode *val = port_node->data_dict.at("signed"); if (val->type == 'N') port_wire->is_signed = val->data_number != 0; } if (port_node->data_dict.count("offset") != 0) { JsonNode *val = port_node->data_dict.at("offset"); if (val->type == 'N') port_wire->start_offset = val->data_number; } if (port_direction_node->data_string == "input") { port_wire->port_input = true; } else if (port_direction_node->data_string == "output") { port_wire->port_output = true; } else if (port_direction_node->data_string == "inout") { port_wire->port_input = true; port_wire->port_output = true; } else log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", log_id(port_name), port_direction_node->data_string.c_str()); port_wire->port_id = port_id; for (int i = 0; i < GetSize(port_bits_node->data_array); i++) { JsonNode *bitval_node = port_bits_node->data_array.at(i); SigBit sigbit(port_wire, i); if (bitval_node->type == 'S') { if (bitval_node->data_string == "0") module->connect(sigbit, State::S0); else if (bitval_node->data_string == "1") module->connect(sigbit, State::S1); else if (bitval_node->data_string == "x") module->connect(sigbit, State::Sx); else if (bitval_node->data_string == "z") module->connect(sigbit, State::Sz); else log_error("JSON port node '%s' has invalid '%s' bit string value on bit %d.\n", log_id(port_name), bitval_node->data_string.c_str(), i); } else if (bitval_node->type == 'N') { int bitidx = bitval_node->data_number; if (signal_bits.count(bitidx)) { if (port_wire->port_output) { module->connect(sigbit, signal_bits.at(bitidx)); } else { module->connect(signal_bits.at(bitidx), sigbit); signal_bits[bitidx] = sigbit; } } else { signal_bits[bitidx] = sigbit; } } else log_error("JSON port node '%s' has invalid bit value on bit %d.\n", log_id(port_name), i); } } module->fixup_ports(); } if (node->data_dict.count("netnames")) { JsonNode *netnames_node = node->data_dict.at("netnames"); if (netnames_node->type != 'D') log_error("JSON netnames node is not a dictionary.\n"); for (auto &net : netnames_node->data_dict) { IdString net_name = RTLIL::escape_id(net.first.c_str()); JsonNode *net_node = net.second; if (net_node->type != 'D') log_error("JSON netname node '%s' is not a dictionary.\n", log_id(net_name)); if (net_node->data_dict.count("bits") == 0) log_error("JSON netname node '%s' has no bits attribute.\n", log_id(net_name)); JsonNode *bits_node = net_node->data_dict.at("bits"); if (bits_node->type != 'A') log_error("JSON netname node '%s' has non-array bits attribute.\n", log_id(net_name)); Wire *wire = module->wire(net_name); if (wire == nullptr) wire = module->addWire(net_name, GetSize(bits_node->data_array)); if (net_node->data_dict.count("upto") != 0) { JsonNode *val = net_node->data_dict.at("upto"); if (val->type == 'N') wire->upto = val->data_number != 0; } if (net_node->data_dict.count("offset") != 0) { JsonNode *val = net_node->data_dict.at("offset"); if (val->type == 'N') wire->start_offset = val->data_number; } for (int i = 0; i < GetSize(bits_node->data_array); i++) { JsonNode *bitval_node = bits_node->data_array.at(i); SigBit sigbit(wire, i); if (bitval_node->type == 'S') { if (bitval_node->data_string == "0") module->connect(sigbit, State::S0); else if (bitval_node->data_string == "1") module->connect(sigbit, State::S1); else if (bitval_node->data_string == "x") module->connect(sigbit, State::Sx); else if (bitval_node->data_string == "z") module->connect(sigbit, State::Sz); else log_error("JSON netname node '%s' has invalid '%s' bit string value on bit %d.\n", log_id(net_name), bitval_node->data_string.c_str(), i); } else if (bitval_node->type == 'N') { int bitidx = bitval_node->data_number; if (signal_bits.count(bitidx)) { if (sigbit != signal_bits.at(bitidx)) module->connect(sigbit, signal_bits.at(bitidx)); } else { signal_bits[bitidx] = sigbit; } } else log_error("JSON netname node '%s' has invalid bit value on bit %d.\n", log_id(net_name), i); } if (net_node->data_dict.count("attributes")) json_parse_attr_param(wire->attributes, net_node->data_dict.at("attributes")); } } if (node->data_dict.count("cells")) { JsonNode *cells_node = node->data_dict.at("cells"); if (cells_node->type != 'D') log_error("JSON cells node is not a dictionary.\n"); for (auto &cell_node_it : cells_node->data_dict) { IdString cell_name = RTLIL::escape_id(cell_node_it.first.c_str()); JsonNode *cell_node = cell_node_it.second; if (cell_node->type != 'D') log_error("JSON cells node '%s' is not a dictionary.\n", log_id(cell_name)); if (cell_node->data_dict.count("type") == 0) log_error("JSON cells node '%s' has no type attribute.\n", log_id(cell_name)); JsonNode *type_node = cell_node->data_dict.at("type"); if (type_node->type != 'S') log_error("JSON cells node '%s' has a non-string type.\n", log_id(cell_name)); IdString cell_type = RTLIL::escape_id(type_node->data_string.c_str()); Cell *cell = module->addCell(cell_name, cell_type); if (cell_node->data_dict.count("connections") == 0) log_error("JSON cells node '%s' has no connections attribute.\n", log_id(cell_name)); JsonNode *connections_node = cell_node->data_dict.at("connections"); if (connections_node->type != 'D') log_error("JSON cells node '%s' has non-dictionary connections attribute.\n", log_id(cell_name)); for (auto &conn_it : connections_node->data_dict) { IdString conn_name = RTLIL::escape_id(conn_it.first.c_str()); JsonNode *conn_node = conn_it.second; if (conn_node->type != 'A') log_error("JSON cells node '%s' connection '%s' is not an array.\n", log_id(cell_name), log_id(conn_name)); SigSpec sig; for (int i = 0; i < GetSize(conn_node->data_array); i++) { JsonNode *bitval_node = conn_node->data_array.at(i); if (bitval_node->type == 'S') { if (bitval_node->data_string == "0") sig.append(State::S0); else if (bitval_node->data_string == "1") sig.append(State::S1); else if (bitval_node->data_string == "x") sig.append(State::Sx); else if (bitval_node->data_string == "z") sig.append(State::Sz); else log_error("JSON cells node '%s' connection '%s' has invalid '%s' bit string value on bit %d.\n", log_id(cell_name), log_id(conn_name), bitval_node->data_string.c_str(), i); } else if (bitval_node->type == 'N') { int bitidx = bitval_node->data_number; if (signal_bits.count(bitidx) == 0) signal_bits[bitidx] = module->addWire(NEW_ID); sig.append(signal_bits.at(bitidx)); } else log_error("JSON cells node '%s' connection '%s' has invalid bit value on bit %d.\n", log_id(cell_name), log_id(conn_name), i); } cell->setPort(conn_name, sig); } if (cell_node->data_dict.count("attributes")) json_parse_attr_param(cell->attributes, cell_node->data_dict.at("attributes")); if (cell_node->data_dict.count("parameters")) 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 { JsonFrontend() : Frontend("json", "read JSON file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_json [filename]\n"); log("\n"); log("Load modules from a JSON file into the current design See \"help write_json\"\n"); log("for a description of the file format.\n"); log("\n"); } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { log_header(design, "Executing JSON frontend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { // std::string arg = args[argidx]; // if (arg == "-sop") { // sop_mode = true; // continue; // } break; } extra_args(f, filename, args, argidx); JsonNode root(*f); if (root.type != 'D') log_error("JSON root node is not a dictionary.\n"); if (root.data_dict.count("modules") != 0) { JsonNode *modules = root.data_dict.at("modules"); if (modules->type != 'D') log_error("JSON modules node is not a dictionary.\n"); for (auto &it : modules->data_dict) json_import(design, it.first, it.second); } } } JsonFrontend; YOSYS_NAMESPACE_END yosys-0.52/frontends/liberty/000077500000000000000000000000001477540374200163175ustar00rootroot00000000000000yosys-0.52/frontends/liberty/Makefile.inc000066400000000000000000000000461477540374200205270ustar00rootroot00000000000000 OBJS += frontends/liberty/liberty.o yosys-0.52/frontends/liberty/liberty.cc000066400000000000000000000613241477540374200203060ustar00rootroot00000000000000/* * 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 "passes/techmap/libparse.h" #include "kernel/register.h" #include "kernel/log.h" YOSYS_NAMESPACE_BEGIN struct token_t { char type; RTLIL::SigSpec sig; token_t (char t) : type(t) { } token_t (char t, RTLIL::SigSpec s) : type(t), sig(s) { } }; static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *&expr) { log_assert(*expr != 0); int id_len = 0; while (('a' <= expr[id_len] && expr[id_len] <= 'z') || ('A' <= expr[id_len] && expr[id_len] <= 'Z') || ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++; if (id_len == 0) log_error("Expected identifier at `%s'.\n", expr); if (id_len == 1 && (*expr == '0' || *expr == '1')) return *(expr++) == '0' ? RTLIL::State::S0 : RTLIL::State::S1; std::string id = RTLIL::escape_id(std::string(expr, id_len)); if (!module->wires_.count(id)) log_error("Can't resolve wire name %s.\n", RTLIL::unescape_id(id).c_str()); expr += id_len; return module->wires_.at(id); } static bool parse_func_reduce(RTLIL::Module *module, std::vector &stack, token_t next_token) { int top = int(stack.size())-1; if (0 <= top-1 && stack[top].type == 0 && stack[top-1].type == '!') { token_t t = token_t(0, module->NotGate(NEW_ID, stack[top].sig)); stack.pop_back(); stack.pop_back(); stack.push_back(t); return true; } if (0 <= top-1 && stack[top].type == '\'' && stack[top-1].type == 0) { token_t t = token_t(0, module->NotGate(NEW_ID, stack[top-1].sig)); stack.pop_back(); stack.pop_back(); stack.push_back(t); return true; } if (0 <= top && stack[top].type == 0) { if (next_token.type == '\'') return false; stack[top].type = 1; return true; } if (0 <= top-2 && stack[top-2].type == 1 && stack[top-1].type == '^' && stack[top].type == 1) { token_t t = token_t(1, module->XorGate(NEW_ID, stack[top-2].sig, stack[top].sig)); stack.pop_back(); stack.pop_back(); stack.pop_back(); stack.push_back(t); return true; } if (0 <= top && stack[top].type == 1) { if (next_token.type == '^') return false; stack[top].type = 2; return true; } if (0 <= top-1 && stack[top-1].type == 2 && stack[top].type == 2) { token_t t = token_t(2, module->AndGate(NEW_ID, stack[top-1].sig, stack[top].sig)); stack.pop_back(); stack.pop_back(); stack.push_back(t); return true; } if (0 <= top-2 && stack[top-2].type == 2 && (stack[top-1].type == '*' || stack[top-1].type == '&') && stack[top].type == 2) { token_t t = token_t(2, module->AndGate(NEW_ID, stack[top-2].sig, stack[top].sig)); stack.pop_back(); stack.pop_back(); stack.pop_back(); stack.push_back(t); return true; } if (0 <= top && stack[top].type == 2) { if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(' || next_token.type == '!') return false; stack[top].type = 3; return true; } if (0 <= top-2 && stack[top-2].type == 3 && (stack[top-1].type == '+' || stack[top-1].type == '|') && stack[top].type == 3) { token_t t = token_t(3, module->OrGate(NEW_ID, stack[top-2].sig, stack[top].sig)); stack.pop_back(); stack.pop_back(); stack.pop_back(); stack.push_back(t); return true; } if (0 <= top-2 && stack[top-2].type == '(' && stack[top-1].type == 3 && stack[top].type == ')') { token_t t = token_t(0, stack[top-1].sig); stack.pop_back(); stack.pop_back(); stack.pop_back(); stack.push_back(t); return true; } return false; } static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr) { const char *orig_expr = expr; std::vector stack; while (*expr) { if (*expr == ' ' || *expr == '\t' || *expr == '\r' || *expr == '\n' || *expr == '"') { expr++; continue; } token_t next_token(0); if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|' || *expr == '&') next_token = token_t(*(expr++)); else next_token = token_t(0, parse_func_identifier(module, expr)); while (parse_func_reduce(module, stack, next_token)) {} stack.push_back(next_token); } while (parse_func_reduce(module, stack, token_t('.'))) {} #if 0 for (size_t i = 0; i < stack.size(); i++) if (stack[i].type < 16) log("%3d: %d %s\n", int(i), stack[i].type, log_signal(stack[i].sig)); else log("%3d: %c\n", int(i), stack[i].type); #endif if (stack.size() != 1 || stack.back().type != 3) log_error("Parser error in function expr `%s'.\n", orig_expr); return stack.back().sig; } static RTLIL::SigSpec create_tristate(RTLIL::Module *module, RTLIL::SigSpec func, const char *three_state_expr) { RTLIL::SigSpec three_state = parse_func_expr(module, three_state_expr); RTLIL::Cell *cell = module->addCell(NEW_ID, ID($tribuf)); cell->setParam(ID::WIDTH, GetSize(func)); cell->setPort(ID::A, func); cell->setPort(ID::EN, module->NotGate(NEW_ID, three_state)); cell->setPort(ID::Y, module->addWire(NEW_ID)); return cell->getPort(ID::Y); } static void create_ff(RTLIL::Module *module, const LibertyAst *node) { RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0)))); RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1)))); RTLIL::SigSpec clk_sig, data_sig, clear_sig, preset_sig; bool clk_polarity = true, clear_polarity = true, preset_polarity = true; for (auto child : node->children) { if (child->id == "clocked_on") clk_sig = parse_func_expr(module, child->value.c_str()); if (child->id == "next_state") data_sig = parse_func_expr(module, child->value.c_str()); if (child->id == "clear") clear_sig = parse_func_expr(module, child->value.c_str()); if (child->id == "preset") preset_sig = parse_func_expr(module, child->value.c_str()); } if (clk_sig.size() == 0 || data_sig.size() == 0) log_error("FF cell %s has no next_state and/or clocked_on attribute.\n", log_id(module->name)); for (bool rerun_invert_rollback = true; rerun_invert_rollback;) { rerun_invert_rollback = false; for (auto &it : module->cells_) { if (it.second->type == ID($_NOT_) && it.second->getPort(ID::Y) == clk_sig) { clk_sig = it.second->getPort(ID::A); clk_polarity = !clk_polarity; rerun_invert_rollback = true; } if (it.second->type == ID($_NOT_) && it.second->getPort(ID::Y) == clear_sig) { clear_sig = it.second->getPort(ID::A); clear_polarity = !clear_polarity; rerun_invert_rollback = true; } if (it.second->type == ID($_NOT_) && it.second->getPort(ID::Y) == preset_sig) { preset_sig = it.second->getPort(ID::A); preset_polarity = !preset_polarity; rerun_invert_rollback = true; } } } RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_NOT_)); cell->setPort(ID::A, iq_sig); cell->setPort(ID::Y, iqn_sig); cell = module->addCell(NEW_ID, ""); cell->setPort(ID::D, data_sig); cell->setPort(ID::Q, iq_sig); cell->setPort(ID::C, clk_sig); if (clear_sig.size() == 0 && preset_sig.size() == 0) { cell->type = stringf("$_DFF_%c_", clk_polarity ? 'P' : 'N'); } if (clear_sig.size() == 1 && preset_sig.size() == 0) { cell->type = stringf("$_DFF_%c%c0_", clk_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N'); cell->setPort(ID::R, clear_sig); } if (clear_sig.size() == 0 && preset_sig.size() == 1) { cell->type = stringf("$_DFF_%c%c1_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N'); cell->setPort(ID::R, preset_sig); } if (clear_sig.size() == 1 && preset_sig.size() == 1) { cell->type = stringf("$_DFFSR_%c%c%c_", clk_polarity ? 'P' : 'N', preset_polarity ? 'P' : 'N', clear_polarity ? 'P' : 'N'); cell->setPort(ID::S, preset_sig); cell->setPort(ID::R, clear_sig); } log_assert(!cell->type.empty()); } static bool create_latch(RTLIL::Module *module, const LibertyAst *node, bool flag_ignore_miss_data_latch) { RTLIL::SigSpec iq_sig(module->addWire(RTLIL::escape_id(node->args.at(0)))); RTLIL::SigSpec iqn_sig(module->addWire(RTLIL::escape_id(node->args.at(1)))); RTLIL::SigSpec enable_sig, data_sig, clear_sig, preset_sig; bool enable_polarity = true, clear_polarity = true, preset_polarity = true; for (auto child : node->children) { if (child->id == "enable") enable_sig = parse_func_expr(module, child->value.c_str()); if (child->id == "data_in") data_sig = parse_func_expr(module, child->value.c_str()); if (child->id == "clear") clear_sig = parse_func_expr(module, child->value.c_str()); if (child->id == "preset") preset_sig = parse_func_expr(module, child->value.c_str()); } if (enable_sig.size() == 0 || data_sig.size() == 0) { if (!flag_ignore_miss_data_latch) log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name)); else log("Ignored latch cell %s with no data_in and/or enable attribute.\n", log_id(module->name)); return false; } for (bool rerun_invert_rollback = true; rerun_invert_rollback;) { rerun_invert_rollback = false; for (auto &it : module->cells_) { if (it.second->type == ID($_NOT_) && it.second->getPort(ID::Y) == enable_sig) { enable_sig = it.second->getPort(ID::A); enable_polarity = !enable_polarity; rerun_invert_rollback = true; } if (it.second->type == ID($_NOT_) && it.second->getPort(ID::Y) == clear_sig) { clear_sig = it.second->getPort(ID::A); clear_polarity = !clear_polarity; rerun_invert_rollback = true; } if (it.second->type == ID($_NOT_) && it.second->getPort(ID::Y) == preset_sig) { preset_sig = it.second->getPort(ID::A); preset_polarity = !preset_polarity; rerun_invert_rollback = true; } } } RTLIL::Cell *cell = module->addCell(NEW_ID, ID($_NOT_)); cell->setPort(ID::A, iq_sig); cell->setPort(ID::Y, iqn_sig); if (clear_sig.size() == 1) { RTLIL::SigSpec clear_negative = clear_sig; RTLIL::SigSpec clear_enable = clear_sig; if (clear_polarity == true || clear_polarity != enable_polarity) { RTLIL::Cell *inv = module->addCell(NEW_ID, ID($_NOT_)); inv->setPort(ID::A, clear_sig); inv->setPort(ID::Y, module->addWire(NEW_ID)); if (clear_polarity == true) clear_negative = inv->getPort(ID::Y); if (clear_polarity != enable_polarity) clear_enable = inv->getPort(ID::Y); } RTLIL::Cell *data_gate = module->addCell(NEW_ID, ID($_AND_)); data_gate->setPort(ID::A, data_sig); data_gate->setPort(ID::B, clear_negative); data_gate->setPort(ID::Y, data_sig = module->addWire(NEW_ID)); RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? ID($_OR_) : ID($_AND_)); enable_gate->setPort(ID::A, enable_sig); enable_gate->setPort(ID::B, clear_enable); enable_gate->setPort(ID::Y, enable_sig = module->addWire(NEW_ID)); } if (preset_sig.size() == 1) { RTLIL::SigSpec preset_positive = preset_sig; RTLIL::SigSpec preset_enable = preset_sig; if (preset_polarity == false || preset_polarity != enable_polarity) { RTLIL::Cell *inv = module->addCell(NEW_ID, ID($_NOT_)); inv->setPort(ID::A, preset_sig); inv->setPort(ID::Y, module->addWire(NEW_ID)); if (preset_polarity == false) preset_positive = inv->getPort(ID::Y); if (preset_polarity != enable_polarity) preset_enable = inv->getPort(ID::Y); } RTLIL::Cell *data_gate = module->addCell(NEW_ID, ID($_OR_)); data_gate->setPort(ID::A, data_sig); data_gate->setPort(ID::B, preset_positive); data_gate->setPort(ID::Y, data_sig = module->addWire(NEW_ID)); RTLIL::Cell *enable_gate = module->addCell(NEW_ID, enable_polarity ? ID($_OR_) : ID($_AND_)); enable_gate->setPort(ID::A, enable_sig); enable_gate->setPort(ID::B, preset_enable); enable_gate->setPort(ID::Y, enable_sig = module->addWire(NEW_ID)); } cell = module->addCell(NEW_ID, stringf("$_DLATCH_%c_", enable_polarity ? 'P' : 'N')); cell->setPort(ID::D, data_sig); cell->setPort(ID::Q, iq_sig); cell->setPort(ID::E, enable_sig); return true; } void parse_type_map(std::map> &type_map, const LibertyAst *ast) { for (auto type_node : ast->children) { if (type_node->id != "type" || type_node->args.size() != 1) continue; std::string type_name = type_node->args.at(0); int bit_width = -1, bit_from = -1, bit_to = -1; bool upto = false; for (auto child : type_node->children) { if (child->id == "base_type" && child->value != "array") goto next_type; if (child->id == "data_type" && child->value != "bit") goto next_type; if (child->id == "bit_width") bit_width = atoi(child->value.c_str()); if (child->id == "bit_from") bit_from = atoi(child->value.c_str()); if (child->id == "bit_to") bit_to = atoi(child->value.c_str()); if (child->id == "downto" && (child->value == "0" || child->value == "false" || child->value == "FALSE")) upto = true; } if (bit_width != (std::max(bit_from, bit_to) - std::min(bit_from, bit_to) + 1)) log_error("Incompatible array type '%s': bit_width=%d, bit_from=%d, bit_to=%d.\n", type_name.c_str(), bit_width, bit_from, bit_to); type_map[type_name] = std::tuple(bit_width, std::min(bit_from, bit_to), upto); next_type:; } } struct LibertyFrontend : public Frontend { LibertyFrontend() : Frontend("liberty", "read cells from liberty file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_liberty [filename]\n"); log("\n"); log("Read cells from liberty file as modules into current design.\n"); log("\n"); 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"); log(" module, and overwrite the existing module if it is a blackbox module.)\n"); log("\n"); log(" -overwrite\n"); log(" overwrite existing modules with the same name\n"); log("\n"); log(" -ignore_miss_func\n"); log(" ignore cells with missing function specification of outputs\n"); log("\n"); log(" -ignore_miss_dir\n"); log(" ignore cells with a missing or invalid direction\n"); log(" specification on a pin\n"); log("\n"); log(" -ignore_miss_data_latch\n"); log(" ignore latches with missing data and/or enable pins\n"); log("\n"); log(" -ignore_buses\n"); log(" ignore cells with bus interfaces (wide ports)\n"); log("\n"); log(" -setattr \n"); log(" set the specified attribute (to the value 1) on all loaded modules\n"); log("\n"); log(" -unit_delay\n"); log(" import combinational timing arcs under the unit delay model\n"); log("\n"); } 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; bool flag_ignore_miss_dir = false; bool flag_ignore_miss_data_latch = false; bool flag_ignore_buses = false; bool flag_unit_delay = false; std::vector attributes; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-lib") { flag_lib = true; continue; } if (arg == "-wb") { flag_wb = true; continue; } if (arg == "-ignore_redef" || arg == "-nooverwrite") { flag_nooverwrite = true; flag_overwrite = false; continue; } if (arg == "-overwrite") { flag_nooverwrite = false; flag_overwrite = true; continue; } if (arg == "-ignore_miss_func") { flag_ignore_miss_func = true; continue; } if (arg == "-ignore_miss_dir") { flag_ignore_miss_dir = true; continue; } if (arg == "-ignore_miss_data_latch") { flag_ignore_miss_data_latch = true; continue; } if (arg == "-ignore_buses") { flag_ignore_buses = true; continue; } if (arg == "-setattr" && argidx+1 < args.size()) { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; } if (arg == "-unit_delay") { flag_unit_delay = true; continue; } break; } 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, filename); int cell_count = 0; std::map> global_type_map; parse_type_map(global_type_map, parser.ast); for (auto cell : parser.ast->children) { if (cell->id != "cell" || cell->args.size() != 1) continue; // log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str()); std::map> type_map = global_type_map; parse_type_map(type_map, cell); RTLIL::Module *module = new RTLIL::Module; std::string cell_name = RTLIL::escape_id(cell->args.at(0)); module->name = cell_name; if (flag_lib) module->set_bool_attribute(ID::blackbox); if (flag_wb) module->set_bool_attribute(ID::whitebox); const LibertyAst *area = cell->find("area"); if (area) module->attributes[ID::area] = area->value; for (auto &attr : attributes) module->attributes[attr] = 1; bool simple_comb_cell = true, has_outputs = false; for (auto node : cell->children) { if (node->id == "pin" && node->args.size() == 1) { const LibertyAst *dir = node->find("direction"); if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal")) { if (!flag_ignore_miss_dir) { log_error("Missing or invalid direction for pin %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name)); } else { log("Ignoring cell %s with missing or invalid direction for pin %s.\n", log_id(module->name), node->args.at(0).c_str()); delete module; goto skip_cell; } } if (!flag_lib || dir->value != "internal") module->addWire(RTLIL::escape_id(node->args.at(0))); } if (node->id == "bus" && node->args.size() == 1) { if (flag_ignore_buses) { log("Ignoring cell %s with a bus interface %s.\n", log_id(module->name), node->args.at(0).c_str()); delete module; goto skip_cell; } if (!flag_lib) log_error("Error in cell %s: bus interfaces are only supported in -lib mode.\n", log_id(cell_name)); const LibertyAst *dir = node->find("direction"); if (dir == nullptr) { const LibertyAst *pin = node->find("pin"); if (pin != nullptr) dir = pin->find("direction"); } if (!dir || (dir->value != "input" && dir->value != "output" && dir->value != "inout" && dir->value != "internal")) log_error("Missing or invalid direction for bus %s on cell %s.\n", node->args.at(0).c_str(), log_id(module->name)); simple_comb_cell = false; if (dir->value == "internal") continue; const LibertyAst *bus_type_node = node->find("bus_type"); if (!bus_type_node || !type_map.count(bus_type_node->value)) log_error("Unknown or unsupported type for bus interface %s on cell %s.\n", node->args.at(0).c_str(), log_id(cell_name)); int bus_type_width = std::get<0>(type_map.at(bus_type_node->value)); int bus_type_offset = std::get<1>(type_map.at(bus_type_node->value)); bool bus_type_upto = std::get<2>(type_map.at(bus_type_node->value)); Wire *wire = module->addWire(RTLIL::escape_id(node->args.at(0)), bus_type_width); wire->start_offset = bus_type_offset; wire->upto = bus_type_upto; if (dir->value == "input" || dir->value == "inout") wire->port_input = true; if (dir->value == "output" || dir->value == "inout") wire->port_output = true; } } if (!flag_lib) { // some liberty files do not put ff/latch at the beginning of a cell // try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes for (auto node : cell->children) { if (node->id == "ff" && node->args.size() == 2) create_ff(module, node); if (node->id == "latch" && node->args.size() == 2) if (!create_latch(module, node, flag_ignore_miss_data_latch)) { delete module; goto skip_cell; } } } for (auto node : cell->children) { if (node->id == "pin" && node->args.size() == 1) { const LibertyAst *dir = node->find("direction"); if (dir->value == "internal" || dir->value == "inout") simple_comb_cell = false; if (flag_lib && dir->value == "internal") continue; RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0))); log_assert(wire); const LibertyAst *capacitance = node->find("capacitance"); if (capacitance) wire->attributes[ID::capacitance] = capacitance->value; if (dir && dir->value == "inout") { wire->port_input = true; wire->port_output = true; } if (dir && dir->value == "input") { wire->port_input = true; continue; } if (dir && dir->value == "output") { has_outputs = true; wire->port_output = true; } if (flag_lib) continue; const LibertyAst *func = node->find("function"); if (func == NULL) { if (dir->value != "inout") { // allow inout with missing function, can be used for power pins if (!flag_ignore_miss_func) { log_error("Missing function on output %s of cell %s.\n", log_id(wire->name), log_id(module->name)); } else { log("Ignoring cell %s with missing function on output %s.\n", log_id(module->name), log_id(wire->name)); delete module; goto skip_cell; } } simple_comb_cell = false; } else { RTLIL::SigSpec out_sig = parse_func_expr(module, func->value.c_str()); const LibertyAst *three_state = node->find("three_state"); if (three_state) { out_sig = create_tristate(module, out_sig, three_state->value.c_str()); simple_comb_cell = false; } module->connect(RTLIL::SigSig(wire, out_sig)); } } if (node->id == "ff" || node->id == "ff_bank" || node->id == "latch" || node->id == "latch_bank" || node->id == "statetable") simple_comb_cell = false; } if (simple_comb_cell && has_outputs) { module->set_bool_attribute(ID::abc9_box); if (flag_unit_delay) { for (auto wi : module->wires()) if (wi->port_input) { for (auto wo : module->wires()) if (wo->port_output) { RTLIL::Cell *spec = module->addCell(NEW_ID, ID($specify2)); spec->setParam(ID::SRC_WIDTH, wi->width); spec->setParam(ID::DST_WIDTH, wo->width); spec->setParam(ID::T_FALL_MAX, 1000); spec->setParam(ID::T_FALL_TYP, 1000); spec->setParam(ID::T_FALL_MIN, 1000); spec->setParam(ID::T_RISE_MAX, 1000); spec->setParam(ID::T_RISE_TYP, 1000); spec->setParam(ID::T_RISE_MIN, 1000); spec->setParam(ID::SRC_DST_POL, false); spec->setParam(ID::SRC_DST_PEN, false); spec->setParam(ID::FULL, true); spec->setPort(ID::EN, Const(1, 1)); spec->setPort(ID::SRC, wi); spec->setPort(ID::DST, wo); } } } } if (design->has(cell_name)) { Module *existing_mod = design->module(cell_name); if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) { log_error("Re-definition of cell/module %s!\n", log_id(cell_name)); } else if (flag_nooverwrite) { log("Ignoring re-definition of module %s.\n", log_id(cell_name)); delete module; goto skip_cell; } else { log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", log_id(cell_name)); design->remove(existing_mod); } } module->fixup_ports(); design->add(module); cell_count++; skip_cell:; } log("Imported %d cell types from liberty file.\n", cell_count); } } LibertyFrontend; YOSYS_NAMESPACE_END yosys-0.52/frontends/rpc/000077500000000000000000000000001477540374200154315ustar00rootroot00000000000000yosys-0.52/frontends/rpc/Makefile.inc000066400000000000000000000001051477540374200176350ustar00rootroot00000000000000ifeq ($(DISABLE_SPAWN),0) OBJS += frontends/rpc/rpc_frontend.o endif yosys-0.52/frontends/rpc/rpc_frontend.cc000066400000000000000000000532731477540374200204350ustar00rootroot00000000000000/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2019 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. * */ // The reason the -path mode of connect_rpc uses byte-oriented and not message-oriented sockets, even though // it is a message-oriented interface, is that the system can place various limits on the message size, which // are not always transparent or easy to change. Given that generated HDL code get be extremely large, it is // unwise to rely on those limits being large enough, and using byte-oriented sockets is guaranteed to work. #ifndef _WIN32 #include #include #include #include #include extern char **environ; #endif #include "libs/json11/json11.hpp" #include "libs/sha1/sha1.h" #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN #if defined(_WIN32) static std::wstring str2wstr(const std::string &in) { if(in == "") return L""; std::wstring out; out.resize(MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/NULL, /*cchWideChar=*/0)); int written = MultiByteToWideChar(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpMultiByteStr=*/&in[0], /*cbMultiByte=*/(int)in.length(), /*lpWideCharStr=*/&out[0], /*cchWideChar=*/(int)out.length()); log_assert(written == (int)out.length()); return out; } static std::string wstr2str(const std::wstring &in) { if(in == L"") return ""; std::string out; out.resize(WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/NULL, /*cbMultiByte=*/0, /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL)); int written = WideCharToMultiByte(/*CodePage=*/CP_UTF8, /*dwFlags=*/0, /*lpWideCharStr=*/&in[0], /*cchWideChar=*/(int)in.length(), /*lpMultiByteStr=*/&out[0], /*cbMultiByte=*/(int)out.length(), /*lpDefaultChar=*/NULL, /*lpUsedDefaultChar=*/NULL); log_assert(written == (int)out.length()); return out; } static std::string get_last_error_str() { DWORD last_error = GetLastError(); LPWSTR out_w; DWORD size_w = FormatMessageW(/*dwFlags=*/FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS, /*lpSource=*/NULL, /*dwMessageId=*/last_error, /*dwLanguageId=*/0, /*lpBuffer=*/(LPWSTR)&out_w, /*nSize=*/0, /*Arguments=*/NULL); if (size_w == 0) return std::to_string(last_error); std::string out = wstr2str(std::wstring(out_w, size_w)); LocalFree(out_w); return out; } #endif using json11::Json; struct RpcServer { std::string name; RpcServer(const std::string &name) : name(name) { } virtual ~RpcServer() { } virtual void write(const std::string &data) = 0; virtual std::string read() = 0; Json call(const Json &json_request) { std::string request; json_request.dump(request); request += '\n'; log_debug("RPC frontend request: %s", request.c_str()); write(request); std::string response = read(); log_debug("RPC frontend response: %s", response.c_str()); std::string error; Json json_response = Json::parse(response, error); if (json_response.is_null()) log_cmd_error("parsing JSON failed: %s\n", error.c_str()); if (json_response["error"].is_string()) log_cmd_error("RPC frontend returned an error: %s\n", json_response["error"].string_value().c_str()); return json_response; } std::vector get_module_names() { Json response = call(Json::object { { "method", "modules" }, }); bool is_valid = true; std::vector modules; if (response["modules"].is_array()) { for (auto &json_module : response["modules"].array_items()) { if (json_module.is_string()) modules.push_back(json_module.string_value()); else is_valid = false; } } else is_valid = false; if (!is_valid) log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str()); return modules; } std::pair derive_module(const std::string &module, const dict ¶meters) { Json::object json_parameters; for (auto ¶m : parameters) { std::string type, value; if (param.second.flags & RTLIL::CONST_FLAG_REAL) { type = "real"; value = param.second.decode_string(); } else if (param.second.flags & RTLIL::CONST_FLAG_STRING) { type = "string"; value = param.second.decode_string(); } else if ((param.second.flags & ~RTLIL::CONST_FLAG_SIGNED) == RTLIL::CONST_FLAG_NONE) { type = (param.second.flags & RTLIL::CONST_FLAG_SIGNED) ? "signed" : "unsigned"; value = param.second.as_string(); } else log_cmd_error("Unserializable constant flags 0x%x\n", param.second.flags); json_parameters[param.first.str()] = Json::object { { "type", type }, { "value", value }, }; } Json response = call(Json::object { { "method", "derive" }, { "module", module }, { "parameters", json_parameters }, }); bool is_valid = true; std::string frontend, source; if (response["frontend"].is_string()) frontend = response["frontend"].string_value(); else is_valid = false; if (response["source"].is_string()) source = response["source"].string_value(); else is_valid = false; if (!is_valid) log_cmd_error("RPC frontend returned malformed response: %s\n", response.dump().c_str()); return std::make_pair(frontend, source); } }; struct RpcModule : RTLIL::Module { std::shared_ptr server; RTLIL::IdString derive(RTLIL::Design *design, const dict ¶meters, bool /*mayfail*/) override { std::string stripped_name = name.str(); if (stripped_name.compare(0, 9, "$abstract") == 0) stripped_name = stripped_name.substr(9); log_assert(stripped_name[0] == '\\'); log_header(design, "Executing RPC frontend `%s' for module `%s'.\n", server->name.c_str(), stripped_name.c_str()); std::string parameter_info; for (auto ¶m : parameters) { log("Parameter %s = %s\n", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second))); parameter_info += stringf("%s=%s", param.first.c_str(), log_signal(RTLIL::SigSpec(param.second))); } std::string derived_name; if (parameters.empty()) derived_name = stripped_name; else if (parameter_info.size() > 60) derived_name = "$paramod$" + sha1(parameter_info) + stripped_name; else derived_name = "$paramod" + stripped_name + parameter_info; if (design->has(derived_name)) { log("Found cached RTLIL representation for module `%s'.\n", derived_name.c_str()); } else { std::string command, input; std::tie(command, input) = server->derive_module(stripped_name.substr(1), parameters); std::istringstream input_stream(input); RTLIL::Design *derived_design = new RTLIL::Design; Frontend::frontend_call(derived_design, &input_stream, "" + derived_name.substr(8), command); derived_design->check(); dict name_mangling; bool found_derived_top = false; for (auto module : derived_design->modules()) { std::string original_name = module->name.str(); if (original_name == stripped_name) { found_derived_top = true; name_mangling[original_name] = derived_name; } else { name_mangling[original_name] = derived_name + module->name.str(); } } if (!found_derived_top) log_cmd_error("RPC frontend did not return requested module `%s`!\n", stripped_name.c_str()); for (auto module : derived_design->modules()) for (auto cell : module->cells()) if (name_mangling.count(cell->type.str())) cell->type = name_mangling[cell->type.str()]; for (auto module : derived_design->modules_) { std::string mangled_name = name_mangling[module.first.str()]; log("Importing `%s' as `%s'.\n", log_id(module.first), log_id(mangled_name)); module.second->name = mangled_name; module.second->design = design; module.second->attributes.erase(ID::top); if (!module.second->has_attribute(ID::hdlname)) module.second->set_string_attribute(ID::hdlname, module.first.str()); design->modules_[mangled_name] = module.second; derived_design->modules_.erase(module.first); } delete derived_design; } return derived_name; } RTLIL::Module *clone() const override { RpcModule *new_mod = new RpcModule; new_mod->server = server; cloneInto(new_mod); return new_mod; } }; #if defined(_WIN32) #if defined(_MSC_VER) #include typedef SSIZE_T ssize_t; #endif struct HandleRpcServer : RpcServer { HANDLE hsend, hrecv; HandleRpcServer(const std::string &name, HANDLE hsend, HANDLE hrecv) : RpcServer(name), hsend(hsend), hrecv(hrecv) { } void write(const std::string &data) override { log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1); ssize_t offset = 0; do { DWORD data_written; if (!WriteFile(hsend, &data[offset], data.length() - offset, &data_written, /*lpOverlapped=*/NULL)) log_cmd_error("WriteFile failed: %s\n", get_last_error_str().c_str()); offset += data_written; } while(offset < (ssize_t)data.length()); } std::string read() override { std::string data; ssize_t offset = 0; while (data.length() == 0 || data[data.length() - 1] != '\n') { data.resize(data.length() + 1024); DWORD data_read; if (!ReadFile(hrecv, &data[offset], data.length() - offset, &data_read, /*lpOverlapped=*/NULL)) log_cmd_error("ReadFile failed: %s\n", get_last_error_str().c_str()); offset += data_read; data.resize(offset); size_t term_pos = data.find('\n', offset); if (term_pos != data.length() - 1 && term_pos != std::string::npos) log_cmd_error("read failed: more than one response\n"); } return data; } ~HandleRpcServer() { CloseHandle(hsend); if (hrecv != hsend) CloseHandle(hrecv); } }; #else struct FdRpcServer : RpcServer { int fdsend, fdrecv; pid_t pid; FdRpcServer(const std::string &name, int fdsend, int fdrecv, pid_t pid = -1) : RpcServer(name), fdsend(fdsend), fdrecv(fdrecv), pid(pid) { } void check_pid() { if (pid == -1) return; // If we're communicating with a process, check that it's still running, or we may get killed with SIGPIPE. pid_t wait_result = ::waitpid(pid, NULL, WNOHANG); if (wait_result == -1) log_cmd_error("waitpid failed: %s\n", strerror(errno)); if (wait_result == pid) log_cmd_error("RPC frontend terminated unexpectedly\n"); } void write(const std::string &data) override { log_assert(data.length() >= 1 && data.find('\n') == data.length() - 1); ssize_t offset = 0; do { check_pid(); ssize_t result = ::write(fdsend, &data[offset], data.length() - offset); if (result == -1) log_cmd_error("write failed: %s\n", strerror(errno)); offset += result; } while(offset < (ssize_t)data.length()); } std::string read() override { std::string data; ssize_t offset = 0; while (data.length() == 0 || data[data.length() - 1] != '\n') { data.resize(data.length() + 1024); check_pid(); ssize_t result = ::read(fdrecv, &data[offset], data.length() - offset); if (result == -1) log_cmd_error("read failed: %s\n", strerror(errno)); offset += result; data.resize(offset); size_t term_pos = data.find('\n', offset); if (term_pos != data.length() - 1 && term_pos != std::string::npos) log_cmd_error("read failed: more than one response\n"); } return data; } ~FdRpcServer() { close(fdsend); if (fdrecv != fdsend) close(fdrecv); } }; #endif // RpcFrontend does not inherit from Frontend since it does not read files. struct RpcFrontend : public Pass { RpcFrontend() : Pass("connect_rpc", "connect to RPC frontend") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" connect_rpc -exec [args...]\n"); log(" connect_rpc -path \n"); log("\n"); log("Load modules using an out-of-process frontend.\n"); log("\n"); log(" -exec [args...]\n"); log(" run with arguments [args...]. send requests on stdin, read\n"); log(" responses from stdout.\n"); log("\n"); log(" -path \n"); log(" connect to Unix domain socket at . (Unix)\n"); log(" connect to bidirectional byte-type named pipe at . (Windows)\n"); log("\n"); log("A simple JSON-based, newline-delimited protocol is used for communicating with\n"); log("the frontend. Yosys requests data from the frontend by sending exactly 1 line\n"); log("of JSON. Frontend responds with data or error message by replying with exactly\n"); log("1 line of JSON as well.\n"); log("\n"); log(" -> {\"method\": \"modules\"}\n"); log(" <- {\"modules\": [\"\", ...]}\n"); log(" <- {\"error\": \"\"}\n"); log(" request for the list of modules that can be derived by this frontend.\n"); log(" the 'hierarchy' command will call back into this frontend if a cell\n"); log(" with type is instantiated in the design.\n"); log("\n"); log(" -> {\"method\": \"derive\", \"module\": \", \"parameters\": {\n"); log(" \"\": {\"type\": \"[unsigned|signed|string|real]\",\n"); log(" \"value\": \"\"}, ...}}\n"); log(" <- {\"frontend\": \"[rtlil|verilog|...]\",\"source\": \"\"}}\n"); log(" <- {\"error\": \"\"}\n"); 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\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 { log_header(design, "Connecting to RPC frontend.\n"); std::vector command; std::string path; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-exec" && argidx+1 < args.size()) { command.insert(command.begin(), args.begin() + argidx + 1, args.end()); argidx = args.size()-1; continue; } if (arg == "-path" && argidx+1 < args.size()) { path = args[++argidx]; continue; } break; } extra_args(args, argidx, design); if ((!command.empty()) + (!path.empty()) != 1) log_cmd_error("Exactly one of -exec, -unix must be specified.\n"); std::shared_ptr server; if (!command.empty()) { std::string command_line; bool first = true; for (auto &arg : command) { if (!first) command_line += ' '; command_line += arg; first = false; } #ifdef _WIN32 std::wstring command_w = str2wstr(command[0]); std::wstring command_path_w; std::wstring command_line_w = str2wstr(command_line); DWORD command_path_len_w; SECURITY_ATTRIBUTES pipe_attr = {}; HANDLE send_r = NULL, send_w = NULL, recv_r = NULL, recv_w = NULL; STARTUPINFOW startup_info = {}; PROCESS_INFORMATION proc_info = {}; command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/0, /*lpBuffer=*/NULL, /*lpFilePart=*/NULL); if (command_path_len_w == 0) { log_error("SearchPathW failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } command_path_w.resize(command_path_len_w - 1); command_path_len_w = SearchPathW(/*lpPath=*/NULL, /*lpFileName=*/command_w.c_str(), /*lpExtension=*/L".exe", /*nBufferLength=*/command_path_len_w, /*lpBuffer=*/&command_path_w[0], /*lpFilePart=*/NULL); log_assert(command_path_len_w == command_path_w.length()); pipe_attr.nLength = sizeof(pipe_attr); pipe_attr.bInheritHandle = TRUE; pipe_attr.lpSecurityDescriptor = NULL; if (!CreatePipe(&send_r, &send_w, &pipe_attr, /*nSize=*/0)) { log_error("CreatePipe failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } if (!SetHandleInformation(send_w, HANDLE_FLAG_INHERIT, 0)) { log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } if (!CreatePipe(&recv_r, &recv_w, &pipe_attr, /*nSize=*/0)) { log_error("CreatePipe failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } if (!SetHandleInformation(recv_r, HANDLE_FLAG_INHERIT, 0)) { log_error("SetHandleInformation failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } startup_info.cb = sizeof(startup_info); startup_info.hStdInput = send_r; startup_info.hStdOutput = recv_w; startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); startup_info.dwFlags |= STARTF_USESTDHANDLES; if (!CreateProcessW(/*lpApplicationName=*/command_path_w.c_str(), /*lpCommandLine=*/&command_line_w[0], /*lpProcessAttributes=*/NULL, /*lpThreadAttributes=*/NULL, /*bInheritHandles=*/TRUE, /*dwCreationFlags=*/0, /*lpEnvironment=*/NULL, /*lpCurrentDirectory=*/NULL, &startup_info, &proc_info)) { log_error("CreateProcessW failed: %s\n", get_last_error_str().c_str()); goto cleanup_exec; } CloseHandle(proc_info.hProcess); CloseHandle(proc_info.hThread); server = std::make_shared(path, send_w, recv_r); send_w = NULL; recv_r = NULL; cleanup_exec: if (send_r != NULL) CloseHandle(send_r); if (send_w != NULL) CloseHandle(send_w); if (recv_r != NULL) CloseHandle(recv_r); if (recv_w != NULL) CloseHandle(recv_w); #else std::vector argv; int send[2] = {-1,-1}, recv[2] = {-1,-1}; posix_spawn_file_actions_t file_actions, *file_actions_p = NULL; pid_t pid; for (auto &arg : command) argv.push_back(&arg[0]); argv.push_back(nullptr); if (pipe(send) != 0) { log_error("pipe failed: %s\n", strerror(errno)); goto cleanup_exec; } if (pipe(recv) != 0) { log_error("pipe failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawn_file_actions_init(&file_actions) != 0) { log_error("posix_spawn_file_actions_init failed: %s\n", strerror(errno)); goto cleanup_exec; } file_actions_p = &file_actions; if (posix_spawn_file_actions_adddup2(file_actions_p, send[0], STDIN_FILENO) != 0) { log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawn_file_actions_addclose(file_actions_p, send[1]) != 0) { log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawn_file_actions_adddup2(file_actions_p, recv[1], STDOUT_FILENO) != 0) { log_error("posix_spawn_file_actions_adddup2 failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawn_file_actions_addclose(file_actions_p, recv[0]) != 0) { log_error("posix_spawn_file_actions_addclose failed: %s\n", strerror(errno)); goto cleanup_exec; } if (posix_spawnp(&pid, argv[0], file_actions_p, /*attrp=*/NULL, argv.data(), environ) != 0) { log_error("posix_spawnp failed: %s\n", strerror(errno)); goto cleanup_exec; } server = std::make_shared(command_line, send[1], recv[0], pid); send[1] = -1; recv[0] = -1; cleanup_exec: if (send[0] != -1) close(send[0]); if (send[1] != -1) close(send[1]); if (recv[0] != -1) close(recv[0]); if (recv[1] != -1) close(recv[1]); if (file_actions_p != NULL) posix_spawn_file_actions_destroy(file_actions_p); #endif } else if (!path.empty()) { #ifdef _WIN32 std::wstring path_w = str2wstr(path); HANDLE h; h = CreateFileW(path_w.c_str(), GENERIC_READ|GENERIC_WRITE, /*dwShareMode=*/0, /*lpSecurityAttributes=*/NULL, /*dwCreationDisposition=*/OPEN_EXISTING, /*dwFlagsAndAttributes=*/0, /*hTemplateFile=*/NULL); if (h == INVALID_HANDLE_VALUE) { log_error("CreateFileW failed: %s\n", get_last_error_str().c_str()); goto cleanup_path; } server = std::make_shared(path, h, h); cleanup_path: ; #else struct sockaddr_un addr; addr.sun_family = AF_UNIX; strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1); int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { log_error("socket failed: %s\n", strerror(errno)); goto cleanup_path; } if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { log_error("connect failed: %s\n", strerror(errno)); goto cleanup_path; } server = std::make_shared(path, fd, fd); fd = -1; cleanup_path: if (fd != -1) close(fd); #endif } if (!server) log_cmd_error("Failed to connect to RPC frontend.\n"); for (auto &module_name : server->get_module_names()) { log("Linking module `%s'.\n", module_name.c_str()); RpcModule *module = new RpcModule; module->name = "$abstract\\" + module_name; module->server = server; design->add(module); } } } RpcFrontend; YOSYS_NAMESPACE_END yosys-0.52/frontends/rtlil/000077500000000000000000000000001477540374200157735ustar00rootroot00000000000000yosys-0.52/frontends/rtlil/.gitignore000066400000000000000000000001131477540374200177560ustar00rootroot00000000000000rtlil_lexer.cc rtlil_parser.output rtlil_parser.tab.cc rtlil_parser.tab.hh yosys-0.52/frontends/rtlil/Makefile.inc000066400000000000000000000012421477540374200202020ustar00rootroot00000000000000 GENFILES += frontends/rtlil/rtlil_parser.tab.cc GENFILES += frontends/rtlil/rtlil_parser.tab.hh GENFILES += frontends/rtlil/rtlil_parser.output GENFILES += frontends/rtlil/rtlil_lexer.cc frontends/rtlil/rtlil_parser.tab.cc: frontends/rtlil/rtlil_parser.y $(Q) mkdir -p $(dir $@) $(P) $(BISON) -o $@ -d -r all -b frontends/rtlil/rtlil_parser $< frontends/rtlil/rtlil_parser.tab.hh: frontends/rtlil/rtlil_parser.tab.cc frontends/rtlil/rtlil_lexer.cc: frontends/rtlil/rtlil_lexer.l $(Q) mkdir -p $(dir $@) $(P) flex -o frontends/rtlil/rtlil_lexer.cc $< OBJS += frontends/rtlil/rtlil_parser.tab.o frontends/rtlil/rtlil_lexer.o OBJS += frontends/rtlil/rtlil_frontend.o yosys-0.52/frontends/rtlil/rtlil_frontend.cc000066400000000000000000000065341477540374200213370ustar00rootroot00000000000000/* * 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. * * --- * * A very simple and straightforward frontend for the RTLIL text * representation. * */ #include "rtlil_frontend.h" #include "kernel/register.h" #include "kernel/log.h" void rtlil_frontend_yyerror(char const *s) { YOSYS_NAMESPACE_PREFIX log_error("Parser error in line %d: %s\n", rtlil_frontend_yyget_lineno(), s); } void rtlil_frontend_yywarning(char const *s) { YOSYS_NAMESPACE_PREFIX log_warning("In line %d: %s\n", rtlil_frontend_yyget_lineno(), s); } YOSYS_NAMESPACE_BEGIN struct RTLILFrontend : public Frontend { RTLILFrontend() : Frontend("rtlil", "read modules from RTLIL file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_rtlil [filename]\n"); log("\n"); log("Load modules from an RTLIL file to the current design. (RTLIL is a text\n"); log("representation of a design in yosys's internal format.)\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"); log(" module, and overwrite the existing module if it is a blackbox module.)\n"); log("\n"); log(" -overwrite\n"); log(" overwrite existing modules with the same name\n"); log("\n"); log(" -lib\n"); log(" only create empty blackbox modules\n"); log("\n"); } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { RTLIL_FRONTEND::flag_nooverwrite = false; RTLIL_FRONTEND::flag_overwrite = false; RTLIL_FRONTEND::flag_lib = false; log_header(design, "Executing RTLIL frontend.\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-nooverwrite") { RTLIL_FRONTEND::flag_nooverwrite = true; RTLIL_FRONTEND::flag_overwrite = false; continue; } if (arg == "-overwrite") { RTLIL_FRONTEND::flag_nooverwrite = false; RTLIL_FRONTEND::flag_overwrite = true; continue; } if (arg == "-lib") { RTLIL_FRONTEND::flag_lib = true; continue; } break; } extra_args(f, filename, args, argidx); log("Input filename: %s\n", filename.c_str()); RTLIL_FRONTEND::lexin = f; RTLIL_FRONTEND::current_design = design; rtlil_frontend_yydebug = false; rtlil_frontend_yyrestart(NULL); rtlil_frontend_yyparse(); rtlil_frontend_yylex_destroy(); } } RTLILFrontend; YOSYS_NAMESPACE_END yosys-0.52/frontends/rtlil/rtlil_frontend.h000066400000000000000000000030551477540374200211740ustar00rootroot00000000000000/* * 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. * * --- * * A very simple and straightforward frontend for the RTLIL text * representation. * */ #ifndef RTLIL_FRONTEND_H #define RTLIL_FRONTEND_H #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN namespace RTLIL_FRONTEND { extern std::istream *lexin; extern RTLIL::Design *current_design; extern bool flag_nooverwrite; extern bool flag_overwrite; extern bool flag_lib; } YOSYS_NAMESPACE_END extern int rtlil_frontend_yydebug; int rtlil_frontend_yylex(void); void rtlil_frontend_yyerror(char const *s); void rtlil_frontend_yywarning(char const *s); void rtlil_frontend_yyrestart(FILE *f); int rtlil_frontend_yyparse(void); int rtlil_frontend_yylex_destroy(void); int rtlil_frontend_yyget_lineno(void); #endif yosys-0.52/frontends/rtlil/rtlil_lexer.l000066400000000000000000000102201477540374200204700ustar00rootroot00000000000000/* * 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. * * --- * * A very simple and straightforward frontend for the RTLIL text * representation. * */ %{ #ifdef __clang__ // bison generates code using the 'register' storage class specifier #pragma clang diagnostic ignored "-Wdeprecated-register" #endif #include #include "frontends/rtlil/rtlil_frontend.h" #include "rtlil_parser.tab.hh" USING_YOSYS_NAMESPACE #define YY_INPUT(buf,result,max_size) \ result = readsome(*RTLIL_FRONTEND::lexin, buf, max_size) %} %option yylineno %option noyywrap %option nounput %option prefix="rtlil_frontend_yy" %x STRING %% "autoidx" { return TOK_AUTOIDX; } "module" { return TOK_MODULE; } "attribute" { return TOK_ATTRIBUTE; } "parameter" { return TOK_PARAMETER; } "signed" { return TOK_SIGNED; } "real" { return TOK_REAL; } "wire" { return TOK_WIRE; } "memory" { return TOK_MEMORY; } "width" { return TOK_WIDTH; } "upto" { return TOK_UPTO; } "offset" { return TOK_OFFSET; } "size" { return TOK_SIZE; } "input" { return TOK_INPUT; } "output" { return TOK_OUTPUT; } "inout" { return TOK_INOUT; } "cell" { return TOK_CELL; } "connect" { return TOK_CONNECT; } "switch" { return TOK_SWITCH; } "case" { return TOK_CASE; } "assign" { return TOK_ASSIGN; } "sync" { return TOK_SYNC; } "low" { return TOK_LOW; } "high" { return TOK_HIGH; } "posedge" { return TOK_POSEDGE; } "negedge" { return TOK_NEGEDGE; } "edge" { return TOK_EDGE; } "always" { return TOK_ALWAYS; } "global" { return TOK_GLOBAL; } "init" { return TOK_INIT; } "update" { return TOK_UPDATE; } "memwr" { return TOK_MEMWR; } "process" { return TOK_PROCESS; } "end" { return TOK_END; } [a-z]+ { return TOK_INVALID; } "\\"[^ \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]+'s?[01xzm-]* { rtlil_frontend_yylval.string = strdup(yytext); return TOK_VALUE; } -?[0-9]+ { char *end = nullptr; errno = 0; long value = strtol(yytext, &end, 10); log_assert(end == yytext + strlen(yytext)); if (errno == ERANGE) return TOK_INVALID; // literal out of range of long if (value < INT_MIN || value > INT_MAX) return TOK_INVALID; // literal out of range of int (relevant mostly for LP64 platforms) rtlil_frontend_yylval.integer = value; return TOK_INT; } \" { BEGIN(STRING); } \\. { yymore(); } \" { BEGIN(0); char *yystr = strdup(yytext); yystr[strlen(yytext) - 1] = 0; int i = 0, j = 0; while (yystr[i]) { if (yystr[i] == '\\' && yystr[i + 1]) { i++; if (yystr[i] == 'n') yystr[i] = '\n'; else if (yystr[i] == 't') yystr[i] = '\t'; else if ('0' <= yystr[i] && yystr[i] <= '7') { yystr[i] = yystr[i] - '0'; if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; i++; } if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; i++; } } } yystr[j++] = yystr[i++]; } yystr[j] = 0; rtlil_frontend_yylval.string = yystr; return TOK_STRING; } . { yymore(); } "#"[^\n]* /* ignore comments */ [ \t] /* ignore non-newline whitespaces */ [\r\n]+ { return TOK_EOL; } . { return *yytext; } %% // this is a hack to avoid the 'yyinput defined but not used' error msgs void *rtlil_frontend_avoid_input_warnings() { return (void*)&yyinput; } yosys-0.52/frontends/rtlil/rtlil_parser.y000066400000000000000000000330221477540374200206670ustar00rootroot00000000000000/* * 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. * * --- * * A very simple and straightforward frontend for the RTLIL text * representation. * */ %require "3.0" %{ #include #include "frontends/rtlil/rtlil_frontend.h" YOSYS_NAMESPACE_BEGIN namespace RTLIL_FRONTEND { std::istream *lexin; RTLIL::Design *current_design; RTLIL::Module *current_module; RTLIL::Wire *current_wire; RTLIL::Memory *current_memory; RTLIL::Cell *current_cell; RTLIL::Process *current_process; std::vector*> switch_stack; std::vector case_stack; dict attrbuf; bool flag_nooverwrite, flag_overwrite, flag_lib; bool delete_current_module; } using namespace RTLIL_FRONTEND; YOSYS_NAMESPACE_END USING_YOSYS_NAMESPACE %} %define api.prefix {rtlil_frontend_yy} /* The union is defined in the header, so we need to provide all the * includes it requires */ %code requires { #include #include #include "frontends/rtlil/rtlil_frontend.h" } %union { char *string; int integer; YOSYS_NAMESPACE_PREFIX RTLIL::Const *data; YOSYS_NAMESPACE_PREFIX RTLIL::SigSpec *sigspec; std::vector *rsigspec; } %token TOK_ID TOK_VALUE TOK_STRING %token TOK_INT %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_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 %type sigspec sigspec_list %type sync_type %type constant %expect 0 %debug %% input: optional_eol { attrbuf.clear(); } design { if (attrbuf.size() != 0) rtlil_frontend_yyerror("dangling attribute"); }; EOL: optional_eol TOK_EOL; optional_eol: optional_eol TOK_EOL | /* empty */; design: design module | design attr_stmt | design autoidx_stmt | /* empty */; module: TOK_MODULE TOK_ID EOL { delete_current_module = false; if (current_design->has($2)) { RTLIL::Module *existing_mod = current_design->module($2); if (!flag_overwrite && (flag_lib || (attrbuf.count(ID::blackbox) && attrbuf.at(ID::blackbox).as_bool()))) { log("Ignoring blackbox re-definition of module %s.\n", $2); delete_current_module = true; } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) { rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of module %s.", $2).c_str()); } else if (flag_nooverwrite) { log("Ignoring re-definition of module %s.\n", $2); delete_current_module = true; } else { log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", $2); current_design->remove(existing_mod); } } current_module = new RTLIL::Module; current_module->name = $2; current_module->attributes = attrbuf; if (!delete_current_module) current_design->add(current_module); attrbuf.clear(); free($2); } module_body TOK_END { if (attrbuf.size() != 0) rtlil_frontend_yyerror("dangling attribute"); current_module->fixup_ports(); if (delete_current_module) delete current_module; else if (flag_lib) current_module->makeblackbox(); current_module = nullptr; } EOL; module_body: module_body module_stmt | /* empty */; module_stmt: param_stmt | param_defval_stmt | attr_stmt | wire_stmt | memory_stmt | cell_stmt | proc_stmt | conn_stmt; param_stmt: TOK_PARAMETER TOK_ID EOL { current_module->avail_parameters($2); free($2); }; 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); }; attr_stmt: TOK_ATTRIBUTE TOK_ID constant EOL { attrbuf[$2] = *$3; delete $3; free($2); }; autoidx_stmt: TOK_AUTOIDX TOK_INT EOL { autoidx = max(autoidx, $2); }; wire_stmt: TOK_WIRE { current_wire = current_module->addWire("$__rtlil_frontend_tmp__"); current_wire->attributes = attrbuf; attrbuf.clear(); } wire_options TOK_ID EOL { if (current_module->wire($4) != nullptr) rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of wire %s.", $4).c_str()); current_module->rename(current_wire, $4); free($4); }; wire_options: wire_options TOK_WIDTH TOK_INT { current_wire->width = $3; } | wire_options TOK_WIDTH TOK_INVALID { rtlil_frontend_yyerror("RTLIL error: invalid wire width"); } | wire_options TOK_UPTO { current_wire->upto = true; } | wire_options TOK_SIGNED { current_wire->is_signed = true; } | wire_options TOK_OFFSET TOK_INT { current_wire->start_offset = $3; } | wire_options TOK_INPUT TOK_INT { current_wire->port_id = $3; current_wire->port_input = true; current_wire->port_output = false; } | wire_options TOK_OUTPUT TOK_INT { current_wire->port_id = $3; current_wire->port_input = false; current_wire->port_output = true; } | wire_options TOK_INOUT TOK_INT { current_wire->port_id = $3; current_wire->port_input = true; current_wire->port_output = true; } | /* empty */; memory_stmt: TOK_MEMORY { current_memory = new RTLIL::Memory; current_memory->attributes = attrbuf; attrbuf.clear(); } memory_options TOK_ID EOL { if (current_module->memories.count($4) != 0) rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of memory %s.", $4).c_str()); current_memory->name = $4; current_module->memories[$4] = current_memory; free($4); }; memory_options: memory_options TOK_WIDTH TOK_INT { current_memory->width = $3; } | memory_options TOK_SIZE TOK_INT { current_memory->size = $3; } | memory_options TOK_OFFSET TOK_INT { current_memory->start_offset = $3; } | /* empty */; cell_stmt: TOK_CELL TOK_ID TOK_ID EOL { if (current_module->cell($3) != nullptr) rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of cell %s.", $3).c_str()); current_cell = current_module->addCell($3, $2); current_cell->attributes = attrbuf; attrbuf.clear(); free($2); free($3); } cell_body TOK_END EOL; cell_body: cell_body TOK_PARAMETER TOK_ID constant EOL { current_cell->parameters[$3] = *$4; free($3); delete $4; } | cell_body TOK_PARAMETER TOK_SIGNED TOK_ID constant EOL { current_cell->parameters[$4] = *$5; current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_SIGNED; free($4); delete $5; } | cell_body TOK_PARAMETER TOK_REAL TOK_ID constant EOL { current_cell->parameters[$4] = *$5; current_cell->parameters[$4].flags |= RTLIL::CONST_FLAG_REAL; free($4); delete $5; } | cell_body TOK_CONNECT TOK_ID sigspec EOL { if (current_cell->hasPort($3)) rtlil_frontend_yyerror(stringf("RTLIL error: redefinition of cell port %s.", $3).c_str()); current_cell->setPort($3, *$4); delete $4; free($3); } | /* empty */; 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 = current_module->addProcess($2); current_process->attributes = attrbuf; switch_stack.clear(); switch_stack.push_back(¤t_process->root_case.switches); case_stack.clear(); case_stack.push_back(¤t_process->root_case); attrbuf.clear(); free($2); } case_body sync_list TOK_END EOL; switch_stmt: TOK_SWITCH sigspec EOL { RTLIL::SwitchRule *rule = new RTLIL::SwitchRule; rule->signal = *$2; rule->attributes = attrbuf; switch_stack.back()->push_back(rule); attrbuf.clear(); delete $2; } attr_list switch_body TOK_END EOL; attr_list: /* empty */ | attr_list attr_stmt; switch_body: switch_body TOK_CASE { RTLIL::CaseRule *rule = new RTLIL::CaseRule; rule->attributes = attrbuf; switch_stack.back()->back()->cases.push_back(rule); switch_stack.push_back(&rule->switches); case_stack.push_back(rule); attrbuf.clear(); } compare_list EOL case_body { switch_stack.pop_back(); case_stack.pop_back(); } | /* empty */; compare_list: sigspec { case_stack.back()->compare.push_back(*$1); delete $1; } | compare_list ',' sigspec { case_stack.back()->compare.push_back(*$3); delete $3; } | /* empty */; case_body: case_body attr_stmt | case_body switch_stmt | case_body assign_stmt | /* empty */; assign_stmt: TOK_ASSIGN sigspec sigspec EOL { if (attrbuf.size() != 0) rtlil_frontend_yyerror("dangling attribute"); // See https://github.com/YosysHQ/yosys/pull/4765 for discussion on this // warning if (!switch_stack.back()->empty()) { rtlil_frontend_yywarning( "case rule assign statements after switch statements may cause unexpected behaviour. " "The assign statement is reordered to come before all switch statements." ); } case_stack.back()->actions.push_back(RTLIL::SigSig(*$2, *$3)); delete $2; delete $3; }; sync_list: sync_list TOK_SYNC sync_type sigspec EOL { RTLIL::SyncRule *rule = new RTLIL::SyncRule; rule->type = RTLIL::SyncType($3); rule->signal = *$4; current_process->syncs.push_back(rule); delete $4; } update_list | sync_list TOK_SYNC TOK_ALWAYS EOL { RTLIL::SyncRule *rule = new RTLIL::SyncRule; rule->type = RTLIL::SyncType::STa; rule->signal = RTLIL::SigSpec(); current_process->syncs.push_back(rule); } update_list | sync_list TOK_SYNC TOK_GLOBAL EOL { RTLIL::SyncRule *rule = new RTLIL::SyncRule; rule->type = RTLIL::SyncType::STg; rule->signal = RTLIL::SigSpec(); current_process->syncs.push_back(rule); } update_list | sync_list TOK_SYNC TOK_INIT EOL { RTLIL::SyncRule *rule = new RTLIL::SyncRule; rule->type = RTLIL::SyncType::STi; rule->signal = RTLIL::SigSpec(); current_process->syncs.push_back(rule); } update_list | /* empty */; sync_type: TOK_LOW { $$ = RTLIL::ST0; } | TOK_HIGH { $$ = RTLIL::ST1; } | TOK_POSEDGE { $$ = RTLIL::STp; } | TOK_NEGEDGE { $$ = RTLIL::STn; } | TOK_EDGE { $$ = RTLIL::STe; }; update_list: update_list TOK_UPDATE sigspec sigspec EOL { current_process->syncs.back()->actions.push_back(RTLIL::SigSig(*$3, *$4)); 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: TOK_VALUE { char *ep; int width = strtol($1, &ep, 10); bool is_signed = false; if (*ep == '\'') { ep++; } if (*ep == 's') { is_signed = true; ep++; } std::list bits; while (*ep != 0) { RTLIL::State bit = RTLIL::Sx; switch (*ep) { case '0': bit = RTLIL::S0; break; case '1': bit = RTLIL::S1; break; case 'x': bit = RTLIL::Sx; break; case 'z': bit = RTLIL::Sz; break; case '-': bit = RTLIL::Sa; break; case 'm': bit = RTLIL::Sm; break; } bits.push_front(bit); ep++; } if (bits.size() == 0) bits.push_back(RTLIL::Sx); while ((int)bits.size() < width) { RTLIL::State bit = bits.back(); if (bit == RTLIL::S1) bit = RTLIL::S0; bits.push_back(bit); } while ((int)bits.size() > width) bits.pop_back(); $$ = new RTLIL::Const; for (auto it = bits.begin(); it != bits.end(); it++) $$->bits().push_back(*it); if (is_signed) { $$->flags |= RTLIL::CONST_FLAG_SIGNED; } free($1); } | TOK_INT { $$ = new RTLIL::Const($1, 32); } | TOK_STRING { $$ = new RTLIL::Const($1); free($1); }; sigspec: constant { $$ = new RTLIL::SigSpec(*$1); delete $1; } | TOK_ID { if (current_module->wire($1) == nullptr) rtlil_frontend_yyerror(stringf("RTLIL error: wire %s not found", $1).c_str()); $$ = new RTLIL::SigSpec(current_module->wire($1)); free($1); } | sigspec '[' TOK_INT ']' { if ($3 >= $1->size() || $3 < 0) rtlil_frontend_yyerror("bit index out of range"); $$ = new RTLIL::SigSpec($1->extract($3)); delete $1; } | sigspec '[' TOK_INT ':' TOK_INT ']' { if ($3 >= $1->size() || $3 < 0 || $3 < $5) rtlil_frontend_yyerror("invalid slice"); $$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1)); delete $1; } | '{' sigspec_list '}' { $$ = $2; }; sigspec_list_reversed: sigspec_list_reversed sigspec { $$->push_back(*$2); delete $2; } | /* empty */ { $$ = new std::vector; }; sigspec_list: sigspec_list_reversed { $$ = new RTLIL::SigSpec; for (auto it = $1->rbegin(); it != $1->rend(); it++) $$->append(*it); delete $1; }; conn_stmt: TOK_CONNECT sigspec sigspec EOL { if (attrbuf.size() != 0) rtlil_frontend_yyerror("dangling attribute"); current_module->connect(*$2, *$3); delete $2; delete $3; }; yosys-0.52/frontends/verific/000077500000000000000000000000001477540374200162745ustar00rootroot00000000000000yosys-0.52/frontends/verific/Makefile.inc000066400000000000000000000012561477540374200205100ustar00rootroot00000000000000 OBJS += frontends/verific/verific.o ifeq ($(ENABLE_VERIFIC),1) OBJS += frontends/verific/verificsva.o EXTRA_TARGETS += share/verific share/verific: $(P) rm -rf share/verific.new $(Q) mkdir -p share/verific.new ifeq ($(ENABLE_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 $(Q) cp -r $(VERIFIC_DIR)/vhdl_packages/vdbs_2019/. share/verific.new/vhdl_vdbs_2019 endif $(Q) chmod -R a+rX share/verific.new $(Q) mv share/verific.new share/verific endif yosys-0.52/frontends/verific/README000066400000000000000000000023301477540374200171520ustar00rootroot00000000000000 This directory contains Verific bindings for Yosys. Use Tabby CAD Suite from YosysHQ if you need Yosys+Verific. https://www.yosyshq.com/ Contact YosysHQ at contact@yosyshq.com for free evaluation binaries of Tabby CAD Suite. Verific Features that should be enabled in your Verific library =============================================================== database/DBCompileFlags.h: DB_PRESERVE_INITIAL_VALUE Testing Verific+Yosys+SymbiYosys for formal verification ======================================================== Install Yosys+Verific, SymbiYosys, and Yices2. Install instructions: http://symbiyosys.readthedocs.io/en/latest/quickstart.html#installing Then run in the following command in this directory: sby -f example.sby This will generate approximately one page of text output. The last lines should be something like this: SBY [example] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:00 (0) SBY [example] summary: Elapsed process time [H:MM:SS (secs)]: 0:00:00 (0) SBY [example] summary: engine_0 (smtbmc yices) returned PASS for basecase SBY [example] summary: engine_0 (smtbmc yices) returned PASS for induction SBY [example] summary: successful proof by k-induction. SBY [example] DONE (PASS, rc=0) yosys-0.52/frontends/verific/example.sby000066400000000000000000000003001477540374200204370ustar00rootroot00000000000000# Simple SymbiYosys example job utilizing Verific [options] mode prove depth 10 [engines] smtbmc yices [script] verific -sv example.sv verific -import top prep -top top [files] example.sv yosys-0.52/frontends/verific/example.sv000066400000000000000000000004201477540374200202750ustar00rootroot00000000000000module top ( input clk, rst, output reg [3:0] cnt ); initial cnt = 0; always @(posedge clk) begin if (rst) cnt <= 0; else cnt <= cnt + 4'd 1; end always @(posedge clk) begin assume (cnt != 10); assert (cnt != 15); end endmodule yosys-0.52/frontends/verific/verific.cc000066400000000000000000004264351477540374200202500ustar00rootroot00000000000000/* * 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 "kernel/yosys.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/log.h" #include "kernel/utils.h" #include "libs/sha1/sha1.h" #include #include #include #ifndef _WIN32 # include # include #endif #include "frontends/verific/verific.h" USING_YOSYS_NAMESPACE #ifdef YOSYS_ENABLE_VERIFIC #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Woverloaded-virtual" #endif #include "Array.h" #include "RuntimeFlags.h" #ifdef VERIFIC_HIER_TREE_SUPPORT #include "hier_tree.h" #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT #include "veri_file.h" #include "VeriModule.h" #include "VeriWrite.h" #include "VeriLibrary.h" #include "VeriExpression.h" #endif #ifdef VERIFIC_VHDL_SUPPORT #include "vhdl_file.h" #include "VhdlIdDef.h" #include "VhdlUnits.h" #include "NameSpace.h" #endif #ifdef VERIFIC_EDIF_SUPPORT #include "edif_file.h" #endif #ifdef VERIFIC_LIBERTY_SUPPORT #include "synlib_file.h" #include "SynlibGroup.h" #endif #include "VerificStream.h" #include "FileSystem.h" #ifdef YOSYSHQ_VERIFIC_EXTENSIONS #include "VerificExtensions.h" #endif #ifndef YOSYSHQ_VERIFIC_API_VERSION #warning "Only YosysHQ flavored Verific is fully supported. Please contact office@yosyshq.com for commercial support for Yosys+Verific." #else #if YOSYSHQ_VERIFIC_API_VERSION < 20230901 # error "Please update your version of YosysHQ flavored Verific." #endif #endif #if !defined(VERIFIC_VHDL_SUPPORT) && !defined(VERIFIC_SYSTEMVERILOG_SUPPORT) #error "At least one of HDL languages must be enabled." #endif #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef VERIFIC_NAMESPACE using namespace Verific; #endif #endif #ifdef YOSYS_ENABLE_VERIFIC YOSYS_NAMESPACE_BEGIN int verific_verbose; bool verific_import_pending; string verific_error_msg; int verific_sva_fsm_limit; #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT vector verific_incdirs, verific_libdirs, verific_libexts; #endif void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefile, const char *msg, va_list args) { string message_prefix = stringf("VERIFIC-%s [%s] ", msg_type == VERIFIC_NONE ? "NONE" : msg_type == VERIFIC_ERROR ? "ERROR" : msg_type == VERIFIC_WARNING ? "WARNING" : msg_type == VERIFIC_IGNORE ? "IGNORE" : msg_type == VERIFIC_INFO ? "INFO" : msg_type == VERIFIC_COMMENT ? "COMMENT" : msg_type == VERIFIC_PROGRAM_ERROR ? "PROGRAM_ERROR" : "UNKNOWN", message_id); string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : ""; message += vstringf(msg, args); if (log_verific_callback) { string full_message = stringf("%s%s\n", message_prefix.c_str(), message.c_str()); #ifdef VERIFIC_LINEFILE_INCLUDES_COLUMNS 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 log_verific_callback(int(msg_type), message_id, LineFile::GetFileName(linefile), linefile ? LineFile::GetLineNo(linefile) : 0, 0, linefile ? LineFile::GetLineNo(linefile) : 0, 0, full_message.c_str()); #endif } 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) { Instance *inst = (Instance*)nl->GetReferences()->GetLast(); return get_full_netlist_name(inst->Owner()) + "." + inst->Name(); } return nl->CellBaseName(); } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT 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; #endif // ================================================================== VerificImporter::VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover, bool mode_fullinit) : mode_gates(mode_gates), mode_keep(mode_keep), mode_nosva(mode_nosva), mode_names(mode_names), mode_verific(mode_verific), mode_autocover(mode_autocover), mode_fullinit(mode_fullinit) { } RTLIL::SigBit VerificImporter::net_map_at(Net *net) { if (net->IsExternalTo(netlist)) log_error("Found external reference to '%s.%s' in netlist '%s', please use -flatten or -extnets.\n", get_full_netlist_name(net->Owner()).c_str(), net->Name(), get_full_netlist_name(netlist).c_str()); return net_map.at(net); } bool is_blackbox(Netlist *nl) { if (nl->IsBlackBox() || nl->IsEmptyBox()) return true; const char *attr = nl->GetAttValue("blackbox"); if (attr != nullptr && strcmp(attr, "0")) return true; return false; } RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj) { std::string s = stringf("$verific$%s", obj->Name()); if (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; } static const RTLIL::Const extract_vhdl_boolean(std::string &val) { if (val == "false") return RTLIL::Const::from_string("0"); if (val == "true") return RTLIL::Const::from_string("1"); log_error("Expecting VHDL boolean value.\n"); } static const RTLIL::Const extract_vhdl_bit(std::string &val, std::string &typ) { if (val.size()==3 && val[0]=='\'' && val.back()=='\'') return RTLIL::Const::from_string(val.substr(1,val.size()-2)); log_error("Error parsing VHDL %s.\n", typ.c_str()); } static const RTLIL::Const extract_vhdl_bit_vector(std::string &val, std::string &typ) { if (val.size()>1 && val[0]=='\"' && val.back()=='\"') { RTLIL::Const c = RTLIL::Const::from_string(val.substr(1,val.size()-2)); if (typ == "signed") c.flags |= RTLIL::CONST_FLAG_SIGNED; return c; } log_error("Error parsing VHDL %s.\n", typ.c_str()); } static const RTLIL::Const extract_vhdl_integer(std::string &val) { char *end; return RTLIL::Const((int)std::strtol(val.c_str(), &end, 10), 32); } static const RTLIL::Const extract_vhdl_char(std::string &val) { if (val.size()==3 && val[0]=='\"' && val.back()=='\"') return RTLIL::Const((int)val[1], 32); log_error("Error parsing VHDL character.\n"); } static const RTLIL::Const extract_real_value(std::string &val) { RTLIL::Const c(val); c.flags |= RTLIL::CONST_FLAG_REAL; return c; } static const RTLIL::Const extract_vhdl_string(std::string &val) { if (!(val.size()>1 && val[0]=='\"' && val.back()=='\"')) log_error("Error parsing VHDL string.\n"); return RTLIL::Const(val.substr(1,val.size()-2)); } static const RTLIL::Const extract_vhdl_const(const char *value, bool output_signed) { RTLIL::Const c; char *end; int decimal; bool is_signed = false; std::string val = std::string(value); if (val.size()>1 && val[0]=='\"' && val.back()=='\"') { std::string data = val.substr(1,val.size()-2); bool isBinary = std::all_of(data.begin(), data.end(), [](char c) {return c=='1' || c=='0'; }); if (isBinary) c = RTLIL::Const::from_string(data); else c = RTLIL::Const(data); } else if (val.size()==3 && val[0]=='\'' && val.back()=='\'') { c = RTLIL::Const::from_string(val.substr(1,val.size()-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 (val == "false") { c = RTLIL::Const::from_string("0"); } else if (val == "true") { c = RTLIL::Const::from_string("1"); } else { c = RTLIL::Const(val); log_warning("encoding value '%s' as string.\n", value); } if (is_signed) c.flags |= RTLIL::CONST_FLAG_SIGNED; return c; } static const RTLIL::Const extract_verilog_const(const char *value, bool allow_string, bool output_signed) { RTLIL::Const c; char *end; int decimal; bool is_signed = false; size_t found; 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 { c = RTLIL::Const(val); log_warning("encoding value '%s' as string.\n", value); } if (is_signed) c.flags |= RTLIL::CONST_FLAG_SIGNED; return c; } // 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* type_name, const char *value, DesignObj *obj, bool allow_string = true, bool output_signed = false) { std::string val = std::string(value); // VHDL if (obj->IsFromVhdl()) { if (type_name) { std::string typ = std::string(type_name); transform(typ.begin(), typ.end(), typ.begin(), ::tolower); if (typ == "integer" || typ == "natural" || typ=="positive") return extract_vhdl_integer(val); else if (typ =="boolean") return extract_vhdl_boolean(val); else if (typ == "bit" || typ =="std_logic" || typ == "std_ulogic") return extract_vhdl_bit(val,typ); else if (typ == "character") return extract_vhdl_char(val); else if (typ == "bit_vector" || typ == "std_logic_vector" || typ == "std_ulogic_vector" || typ == "unsigned" || typ == "signed") return extract_vhdl_bit_vector(val,typ); else if (typ == "real") return extract_real_value(val); else if (typ == "string") return extract_vhdl_string(val); else { if (val.size()>1 && val[0]=='\"' && val.back()=='\"') return RTLIL::Const(val.substr(1,val.size()-2)); else if (val.size()==3 && val[0]=='\'' && val.back()=='\'') return RTLIL::Const(val.substr(1,val.size()-2)); else return RTLIL::Const(val); } } else extract_vhdl_const(value, output_signed); } // SystemVerilog if (type_name && strcmp(type_name, "real")==0) { return extract_real_value(val); } else return extract_verilog_const(value, allow_string, output_signed); } #ifdef YOSYSHQ_VERIFIC_API_VERSION 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; } #endif void VerificImporter::import_attributes(dict &attributes, DesignObj *obj, Netlist *nl, int wire_width_hint) { if (!obj) return; MapIter mi; Att *attr; #ifdef VERIFIC_LINEFILE_INCLUDES_COLUMNS if (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()); #else if (obj->Linefile()) attributes[ID::src] = stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile())); #endif FOREACH_ATTRIBUTE(obj, mi, attr) { if (attr->Key()[0] == ' ' || attr->Value() == nullptr) continue; attributes[RTLIL::escape_id(attr->Key())] = verific_const(nullptr, attr->Value(), obj); } if (nl) { auto type_range = nl->GetTypeRange(obj->Name()); if (!type_range) return; if (nl->IsFromVhdl() && type_range->IsTypeScalar()) { const long long bottom_bound = type_range->GetScalarRangeLeftBound(); const long long top_bound = type_range->GetScalarRangeRightBound(); int bit_width = type_range->LeftRangeBound()+1; if (bit_width <= 0) { // VHDL null range if (wire_width_hint >= 0) bit_width = wire_width_hint; else bit_width = 64; //fallback, currently largest integer width that verific will allow (in vhdl2019 mode) } else { if (wire_width_hint >= 0) log_assert(bit_width == wire_width_hint); } RTLIL::Const bottom_const(bottom_bound, bit_width); RTLIL::Const top_const(top_bound, bit_width); if (bottom_bound < 0 || top_bound < 0) { bottom_const.flags |= RTLIL::CONST_FLAG_SIGNED; top_const.flags |= RTLIL::CONST_FLAG_SIGNED; } attributes.emplace(ID(bottom_bound), bottom_const); attributes.emplace(ID(top_bound), top_const); } 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; attributes.emplace(ID::wiretype, RTLIL::escape_id(type_name)); MapIter mi; const char *k, *v; FOREACH_MAP_ITEM(type_range->GetEnumIdMap(), mi, &k, &v) { if (nl->IsFromVerilog()) { auto const value = verific_const(type_name, v, nl, 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; if (p) { if (*p != '"') { auto l = strlen(p); auto q = (char*)malloc(l+1); strncpy(q, p, l); q[l] = '\0'; for(char *ptr = q; *ptr; ++ptr )*ptr = tolower(*ptr); attributes.emplace(stringf("\\enum_value_%s", q), RTLIL::escape_id(k)); } else { auto *q = p+1; for (; *q != '"'; q++) if (*q != '0' && *q != '1') { p = nullptr; break; } if (p && *(q+1) != '\0') p = nullptr; if (p != nullptr) { auto l = strlen(p); auto q = (char*)malloc(l+1-2); strncpy(q, p+1, l-2); q[l-2] = '\0'; attributes.emplace(stringf("\\enum_value_%s", q), RTLIL::escape_id(k)); free(q); } } } 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--) { 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--) { 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--) { Net *net = inst->GetInput2Bit(i); sig.append(netToSigBit(net)); } return sig; } RTLIL::SigSpec VerificImporter::operatorInport(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 if (net->IsX()) sig.append(RTLIL::State::Sx); else sig.append(RTLIL::State::Sz); } else sig.append(net_map_at(net)); } else sig.append(RTLIL::State::Sz); } 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::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; RTLIL::Wire *dummy_wire = NULL; for (int i = int(inst->OutputSize())-1; i >= 0; i--) if (inst->GetOutputBit(i) && (!any_all_nets || !any_all_nets->count(inst->GetOutputBit(i)))) { sig.append(net_map_at(inst->GetOutputBit(i))); dummy_wire = NULL; } else { if (dummy_wire == NULL) dummy_wire = module->addWire(new_verific_id(inst)); else dummy_wire->width++; sig.append(RTLIL::SigSpec(dummy_wire, dummy_wire->width - 1)); } return sig; } bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdString inst_name) { if (inst->Type() == PRIM_AND) { module->addAndGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); return true; } if (inst->Type() == PRIM_NAND) { RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); module->addAndGate(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput())); return true; } if (inst->Type() == PRIM_OR) { module->addOrGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); return true; } if (inst->Type() == PRIM_NOR) { RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); module->addOrGate(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput())); return true; } if (inst->Type() == PRIM_XOR) { module->addXorGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); return true; } if (inst->Type() == PRIM_XNOR) { module->addXnorGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); return true; } if (inst->Type() == PRIM_BUF) { auto outnet = inst->GetOutput(); if (!any_all_nets.count(outnet)) module->addBufGate(inst_name, net_map_at(inst->GetInput()), net_map_at(outnet)); return true; } if (inst->Type() == PRIM_INV) { module->addNotGate(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); return true; } if (inst->Type() == PRIM_MUX) { module->addMuxGate(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); return true; } 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; } if (inst->Type() == PRIM_FADD) { RTLIL::SigSpec a = net_map_at(inst->GetInput1()), b = net_map_at(inst->GetInput2()), c = net_map_at(inst->GetCin()); RTLIL::SigSpec x = inst->GetCout() ? net_map_at(inst->GetCout()) : module->addWire(new_verific_id(inst)); RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(new_verific_id(inst)); RTLIL::SigSpec tmp1 = module->addWire(new_verific_id(inst)); RTLIL::SigSpec tmp2 = module->addWire(new_verific_id(inst)); RTLIL::SigSpec tmp3 = module->addWire(new_verific_id(inst)); module->addXorGate(new_verific_id(inst), a, b, tmp1); module->addXorGate(inst_name, tmp1, c, y); module->addAndGate(new_verific_id(inst), tmp1, c, tmp2); module->addAndGate(new_verific_id(inst), a, b, tmp3); module->addOrGate(new_verific_id(inst), tmp2, tmp3, x); return true; } if (inst->Type() == PRIM_DFFRS) { VerificClocking clocking(this, inst->GetClock()); log_assert(clocking.disable_sig == State::S0); log_assert(clocking.body_net == nullptr); if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); else if (inst->GetSet()->IsGnd()) clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), State::S0); else if (inst->GetReset()->IsGnd()) clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), State::S1); else clocking.addDffsr(inst_name, 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_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; } bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdString inst_name) { RTLIL::Cell *cell = nullptr; if (inst->Type() == PRIM_AND) { cell = module->addAnd(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_NAND) { RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); cell = module->addAnd(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); import_attributes(cell->attributes, inst); cell = module->addNot(inst_name, tmp, net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_OR) { cell = module->addOr(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_NOR) { RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); cell = module->addOr(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); import_attributes(cell->attributes, inst); cell = module->addNot(inst_name, tmp, net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_XOR) { cell = module->addXor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_XNOR) { cell = module->addXnor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_INV) { cell = module->addNot(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_MUX) { cell = module->addMux(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } 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; } if (inst->Type() == PRIM_FADD) { RTLIL::SigSpec a_plus_b = module->addWire(new_verific_id(inst), 2); RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(new_verific_id(inst)); if (inst->GetCout()) y.append(net_map_at(inst->GetCout())); cell = module->addAdd(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), a_plus_b); import_attributes(cell->attributes, inst); cell = module->addAdd(inst_name, a_plus_b, net_map_at(inst->GetCin()), y); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_DFFRS) { VerificClocking clocking(this, inst->GetClock()); log_assert(clocking.disable_sig == State::S0); log_assert(clocking.body_net == nullptr); if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) cell = clocking.addDff(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); else if (inst->GetSet()->IsGnd()) cell = clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S0); else if (inst->GetReset()->IsGnd()) cell = clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S1); else cell = clocking.addDffsr(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_DLATCHRS) { if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); else cell = 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())); import_attributes(cell->attributes, inst); 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) #define OUT operatorOutput(inst) #define FILTERED_OUT operatorOutput(inst, &any_all_nets) #define SIGNED inst->View()->IsSigned() if (inst->Type() == OPER_ADDER) { RTLIL::SigSpec out = OUT; if (inst->GetCout() != NULL) out.append(net_map_at(inst->GetCout())); if (inst->GetCin()->IsGnd()) { cell = module->addAdd(inst_name, IN1, IN2, out, SIGNED); import_attributes(cell->attributes, inst); } else { RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst), GetSize(out)); cell = module->addAdd(new_verific_id(inst), IN1, IN2, tmp, SIGNED); import_attributes(cell->attributes, inst); cell = module->addAdd(inst_name, tmp, net_map_at(inst->GetCin()), out, false); import_attributes(cell->attributes, inst); } return true; } if (inst->Type() == OPER_MULTIPLIER) { cell = module->addMul(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_DIVIDER) { cell = module->addDiv(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_MODULO) { cell = module->addMod(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REMAINDER) { cell = module->addMod(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_SHIFT_LEFT) { cell = module->addShl(inst_name, IN1, IN2, OUT, false); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_ENABLED_DECODER) { RTLIL::SigSpec vec; vec.append(net_map_at(inst->GetControl())); for (unsigned i = 1; i < inst->OutputSize(); i++) { vec.append(RTLIL::State::S0); } cell = module->addShl(inst_name, vec, IN, OUT, false); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_DECODER) { RTLIL::SigSpec vec; vec.append(RTLIL::State::S1); for (unsigned i = 1; i < inst->OutputSize(); i++) { vec.append(RTLIL::State::S0); } cell = module->addShl(inst_name, vec, IN, OUT, false); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_SHIFT_RIGHT) { Net *net_cin = inst->GetCin(); Net *net_a_msb = inst->GetInput1Bit(0); if (net_cin->IsGnd()) cell = module->addShr(inst_name, IN1, IN2, OUT, false); else if (net_cin == net_a_msb) cell = module->addSshr(inst_name, IN1, IN2, OUT, true); else log_error("Can't import Verific OPER_SHIFT_RIGHT instance %s: carry_in is neither 0 nor msb of left input\n", inst->Name()); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_AND) { cell = module->addReduceAnd(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_NAND) { Wire *tmp = module->addWire(NEW_ID); cell = module->addReduceAnd(inst_name, IN, tmp, SIGNED); module->addNot(NEW_ID, tmp, net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_OR) { cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_XOR) { cell = module->addReduceXor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_XNOR) { cell = module->addReduceXnor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_NOR) { SigSpec t = module->ReduceOr(new_verific_id(inst), IN, SIGNED); cell = module->addNot(inst_name, t, net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_LESSTHAN) { Net *net_cin = inst->GetCin(); if (net_cin->IsGnd()) cell = module->addLt(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); else if (net_cin->IsPwr()) cell = module->addLe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); else log_error("Can't import Verific OPER_LESSTHAN instance %s: carry_in is neither 0 nor 1\n", inst->Name()); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_AND) { cell = module->addAnd(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_OR) { cell = module->addOr(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_XOR) { cell = module->addXor(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_XNOR) { cell = module->addXnor(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_BUF) { cell = module->addPos(inst_name, IN, FILTERED_OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_INV) { cell = module->addNot(inst_name, IN, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_MINUS) { cell = module->addSub(inst_name, IN1, IN2, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_UMINUS) { cell = module->addNeg(inst_name, IN, OUT, SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_EQUAL) { cell = module->addEq(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_NEQUAL) { cell = module->addNe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_MUX) { cell = module->addMux(inst_name, IN1, IN2, net_map_at(inst->GetControl()), OUT); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_NTO1MUX) { 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) { cell = module->addBmux(inst_name, IN2, IN1, OUT); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_SELECTOR) { cell = module->addPmux(inst_name, State::S0, IN2, IN1, net_map_at(inst->GetOutput())); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_SELECTOR) { SigSpec out = OUT; cell = module->addPmux(inst_name, SigSpec(State::S0, GetSize(out)), IN2, IN1, out); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_TRI) { cell = module->addMux(inst_name, RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map_at(inst->GetControl()), OUT); import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_DFFRS) { VerificClocking clocking(this, inst->GetClock()); log_assert(clocking.disable_sig == State::S0); log_assert(clocking.body_net == nullptr); 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 = clocking.addDff(inst_name, IN, OUT); else cell = clocking.addDffsr(inst_name, sig_set, sig_reset, IN, OUT); import_attributes(cell->attributes, inst); 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; } #ifdef YOSYSHQ_VERIFIC_API_VERSION 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; } #endif #undef IN #undef IN1 #undef IN2 #undef OUT #undef SIGNED return false; } void VerificImporter::merge_past_ffs_clock(pool &candidates, SigBit clock, bool clock_pol) { bool keep_running = true; SigMap sigmap; while (keep_running) { keep_running = false; dict> dbits_db; SigSpec dbits; for (auto cell : candidates) { SigBit bit = sigmap(cell->getPort(ID::D)); dbits_db[bit].insert(cell); dbits.append(bit); } dbits.sort_and_unify(); for (auto chunk : dbits.chunks()) { SigSpec sig_d = chunk; if (chunk.wire == nullptr || GetSize(sig_d) == 1) continue; SigSpec sig_q = module->addWire(NEW_ID, GetSize(sig_d)); RTLIL::Cell *new_ff = module->addDff(NEW_ID, clock, sig_d, sig_q, clock_pol); if (verific_verbose) log(" merging single-bit past_ffs into new %d-bit ff %s.\n", GetSize(sig_d), log_id(new_ff)); for (int i = 0; i < GetSize(sig_d); i++) for (auto old_ff : dbits_db[sig_d[i]]) { if (verific_verbose) log(" replacing old ff %s on bit %d.\n", log_id(old_ff), i); SigBit old_q = old_ff->getPort(ID::Q); SigBit new_q = sig_q[i]; sigmap.add(old_q, new_q); module->connect(old_q, new_q); candidates.erase(old_ff); module->remove(old_ff); keep_running = true; } } } } void VerificImporter::merge_past_ffs(pool &candidates) { dict, pool> database; 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); } for (auto it : database) merge_past_ffs_clock(it.second, it.first.first, it.first.second); } static std::string sha1_if_contain_spaces(std::string str) { if(str.find_first_of(' ') != std::string::npos) { std::size_t open = str.find_first_of('('); std::size_t closed = str.find_last_of(')'); if (open != std::string::npos && closed != std::string::npos) { std::string content = str.substr(open + 1, closed - open - 1); return str.substr(0, open + 1) + sha1(content) + str.substr(closed); } else { return sha1(str); } } return str; } void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::map &nl_todo, bool norename) { 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() && !is_blackbox(nl)) { module_name += "("; module_name += nl->Name(); module_name += ")"; } module_name = "\\" + sha1_if_contain_spaces(module_name); } netlist = nl; if (design->has(module_name)) { if (!nl->IsOperator() && !is_blackbox(nl)) log_cmd_error("Re-definition of module `%s'.\n", netlist_name.c_str()); return; } module = new RTLIL::Module; module->name = module_name; design->add(module); if (is_blackbox(nl)) { log("Importing blackbox module %s.\n", RTLIL::id2cstr(module->name)); module->set_bool_attribute(ID::blackbox); } else { log("Importing module %s.\n", RTLIL::id2cstr(module->name)); } import_attributes(module->attributes, nl, nl); if (module->name.isPublic()) module->set_string_attribute(ID::hdlname, nl->CellBaseName()); module->set_string_attribute(ID(library), nl->Owner()->Owner()->Name()); #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)); const TypeRange *tr = nl->GetTypeRange(param_name) ; const char* type_name = (tr) ? tr->GetTypeName() : nullptr; module->parameter_default_values[RTLIL::escape_id(param_name)] = verific_const(type_name, param_value, nl); } SetIter si; 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) { if (port->Bus()) continue; if (verific_verbose) log(" importing port %s.\n", port->Name()); RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(port->Name())); import_attributes(wire->attributes, port, nl, 1); wire->port_id = nl->IndexOf(port) + 1; if (port->GetDir() == DIR_INOUT || port->GetDir() == DIR_IN) wire->port_input = true; if (port->GetDir() == DIR_INOUT || port->GetDir() == DIR_OUT) wire->port_output = true; if (port->GetNet()) { net = port->GetNet(); if (net_map.count(net) == 0) net_map[net] = wire; else if (wire->port_input) module->connect(net_map_at(net), wire); else module->connect(wire, net_map_at(net)); } } FOREACH_PORTBUS_OF_NETLIST(nl, mi, portbus) { if (verific_verbose) log(" importing portbus %s.\n", portbus->Name()); 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, portbus->Size()); SetIter si ; Port *port ; FOREACH_PORT_OF_PORTBUS(portbus, si, port) { import_attributes(wire->attributes, port->GetNet(), nl, portbus->Size()); 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(); 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 (bit_input) module->connect(net_map_at(net), bit); else module->connect(bit, net_map_at(net)); } if (i == portbus->RightIndex()) break; } } module->fixup_ports(); dict init_nets; pool anyconst_nets, anyseq_nets; pool allconst_nets, allseq_nets; any_all_nets.clear(); FOREACH_NET_OF_NETLIST(nl, mi, net) { if (net->IsRamNet()) { RTLIL::Memory *memory = new RTLIL::Memory; 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(); int bits_in_word = number_of_bits; FOREACH_PORTREF_OF_NET(net, si, pr) { if (pr->GetInst()->Type() == OPER_READ_PORT) { bits_in_word = min(bits_in_word, pr->GetInst()->OutputSize()); continue; } if (pr->GetInst()->Type() == OPER_WRITE_PORT || pr->GetInst()->Type() == OPER_CLOCKED_WRITE_PORT) { bits_in_word = min(bits_in_word, pr->GetInst()->Input2Size()); continue; } log_error("Verific RamNet %s is connected to unsupported instance type %s (%s).\n", net->Name(), pr->GetInst()->View()->Owner()->Name(), pr->GetInst()->Name()); } memory->width = bits_in_word; memory->size = number_of_bits / bits_in_word; const char *ascii_initdata = net->GetWideInitialValue(); if (ascii_initdata) { while (*ascii_initdata != 0 && *ascii_initdata != '\'') ascii_initdata++; if (*ascii_initdata == '\'') ascii_initdata++; if (*ascii_initdata != 0) { log_assert(*ascii_initdata == 'b'); ascii_initdata++; } for (int word_idx = 0; word_idx < memory->size; word_idx++) { Const initval = Const(State::Sx, memory->width); bool initval_valid = false; for (int bit_idx = memory->width-1; bit_idx >= 0; bit_idx--) { if (*ascii_initdata == 0) break; if (*ascii_initdata == '0' || *ascii_initdata == '1') { initval.bits()[bit_idx] = (*ascii_initdata == '0') ? State::S0 : State::S1; initval_valid = true; } ascii_initdata++; } if (initval_valid) { RTLIL::Cell *cell = module->addCell(new_verific_id(net), ID($meminit)); cell->parameters[ID::WORDS] = 1; if (net->GetOrigTypeRange()->LeftRangeBound() < net->GetOrigTypeRange()->RightRangeBound()) cell->setPort(ID::ADDR, word_idx); else cell->setPort(ID::ADDR, memory->size - word_idx - 1); cell->setPort(ID::DATA, initval); cell->parameters[ID::MEMID] = RTLIL::Const(memory->name.str()); cell->parameters[ID::ABITS] = 32; cell->parameters[ID::WIDTH] = memory->width; cell->parameters[ID::PRIORITY] = RTLIL::Const(autoidx-1); } } } continue; } if (net->GetInitialValue()) init_nets[net] = net->GetInitialValue(); const char *rand_const_attr = net->GetAttValue(" rand_const"); const char *rand_attr = net->GetAttValue(" rand"); const char *anyconst_attr = net->GetAttValue("anyconst"); const char *anyseq_attr = net->GetAttValue("anyseq"); const char *allconst_attr = net->GetAttValue("allconst"); const char *allseq_attr = net->GetAttValue("allseq"); if (rand_const_attr != nullptr && (!strcmp(rand_const_attr, "1") || !strcmp(rand_const_attr, "'1'"))) { anyconst_nets.insert(net); any_all_nets.insert(net); } else if (rand_attr != nullptr && (!strcmp(rand_attr, "1") || !strcmp(rand_attr, "'1'"))) { anyseq_nets.insert(net); any_all_nets.insert(net); } else if (anyconst_attr != nullptr && (!strcmp(anyconst_attr, "1") || !strcmp(anyconst_attr, "'1'"))) { anyconst_nets.insert(net); any_all_nets.insert(net); } else if (anyseq_attr != nullptr && (!strcmp(anyseq_attr, "1") || !strcmp(anyseq_attr, "'1'"))) { anyseq_nets.insert(net); any_all_nets.insert(net); } else if (allconst_attr != nullptr && (!strcmp(allconst_attr, "1") || !strcmp(allconst_attr, "'1'"))) { allconst_nets.insert(net); any_all_nets.insert(net); } else if (allseq_attr != nullptr && (!strcmp(allseq_attr, "1") || !strcmp(allseq_attr, "'1'"))) { allseq_nets.insert(net); any_all_nets.insert(net); } if (net_map.count(net)) { if (verific_verbose) log(" skipping net %s.\n", net->Name()); continue; } if (net->Bus()) continue; RTLIL::IdString wire_name = module->uniquify(mode_names || net->IsUserDeclared() ? RTLIL::escape_id(net->Name()) : new_verific_id(net)); if (verific_verbose) log(" importing net %s as %s.\n", net->Name(), log_id(wire_name)); RTLIL::Wire *wire = module->addWire(wire_name); import_attributes(wire->attributes, net, nl, 1); net_map[net] = wire; } FOREACH_NETBUS_OF_NETLIST(nl, mi, netbus) { bool found_new_net = false; for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) { net = netbus->ElementAtIndex(i); if (net_map.count(net) == 0) found_new_net = true; if (i == netbus->RightIndex()) break; } if (found_new_net) { RTLIL::IdString wire_name = module->uniquify(mode_names || netbus->IsUserDeclared() ? RTLIL::escape_id(netbus->Name()) : new_verific_id(netbus)); if (verific_verbose) log(" importing netbus %s as %s.\n", netbus->Name(), log_id(wire_name)); 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, netbus->Size()); break; } import_attributes(wire->attributes, netbus, nl, netbus->Size()); RTLIL::Const initval = Const(State::Sx, GetSize(wire)); bool initval_valid = false; for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) { if (netbus->ElementAtIndex(i)) { int bitidx = wire->upto ? (wire->width - 1 - (i - wire->start_offset)) : (i - wire->start_offset); net = netbus->ElementAtIndex(i); RTLIL::SigBit bit(wire, bitidx); if (init_nets.count(net)) { if (init_nets.at(net) == '0') initval.bits().at(bitidx) = State::S0; if (init_nets.at(net) == '1') initval.bits().at(bitidx) = State::S1; initval_valid = true; init_nets.erase(net); } if (net_map.count(net) == 0) net_map[net] = bit; else module->connect(bit, net_map_at(net)); } if (i == netbus->RightIndex()) break; } if (initval_valid) wire->attributes[ID::init] = initval; } else { if (verific_verbose) log(" skipping netbus %s.\n", netbus->Name()); } SigSpec anyconst_sig; SigSpec anyseq_sig; SigSpec allconst_sig; SigSpec allseq_sig; for (int i = netbus->RightIndex();; i += netbus->IsUp() ? -1 : +1) { net = netbus->ElementAtIndex(i); if (net != nullptr && anyconst_nets.count(net)) { anyconst_sig.append(net_map_at(net)); anyconst_nets.erase(net); } if (net != nullptr && anyseq_nets.count(net)) { anyseq_sig.append(net_map_at(net)); anyseq_nets.erase(net); } if (net != nullptr && allconst_nets.count(net)) { allconst_sig.append(net_map_at(net)); allconst_nets.erase(net); } if (net != nullptr && allseq_nets.count(net)) { allseq_sig.append(net_map_at(net)); allseq_nets.erase(net); } if (i == netbus->LeftIndex()) break; } if (GetSize(anyconst_sig)) module->connect(anyconst_sig, module->Anyconst(new_verific_id(netbus), GetSize(anyconst_sig))); if (GetSize(anyseq_sig)) module->connect(anyseq_sig, module->Anyseq(new_verific_id(netbus), GetSize(anyseq_sig))); if (GetSize(allconst_sig)) module->connect(allconst_sig, module->Allconst(new_verific_id(netbus), GetSize(allconst_sig))); if (GetSize(allseq_sig)) module->connect(allseq_sig, module->Allseq(new_verific_id(netbus), GetSize(allseq_sig))); } for (auto it : init_nets) { Const initval; SigBit bit = net_map_at(it.first); log_assert(bit.wire); if (bit.wire->attributes.count(ID::init)) initval = bit.wire->attributes.at(ID::init); while (GetSize(initval) < GetSize(bit.wire)) initval.bits().push_back(State::Sx); if (it.second == '0') initval.bits().at(bit.offset) = State::S0; if (it.second == '1') initval.bits().at(bit.offset) = State::S1; bit.wire->attributes[ID::init] = initval; } for (auto net : anyconst_nets) module->connect(net_map_at(net), module->Anyconst(new_verific_id(net))); for (auto net : anyseq_nets) module->connect(net_map_at(net), module->Anyseq(new_verific_id(net))); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT pool sva_asserts; pool sva_assumes; pool sva_covers; pool sva_triggers; #endif pool past_ffs; FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst) { RTLIL::IdString inst_name = module->uniquify(mode_names || inst->IsUserDeclared() ? RTLIL::escape_id(inst->Name()) : new_verific_id(inst)); if (verific_verbose) log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), log_id(inst_name)); if (mode_verific) goto import_verific_cells; if (inst->Type() == PRIM_PWR) { module->connect(net_map_at(inst->GetOutput()), RTLIL::State::S1); continue; } if (inst->Type() == PRIM_GND) { module->connect(net_map_at(inst->GetOutput()), RTLIL::State::S0); continue; } if (inst->Type() == PRIM_BUF) { auto outnet = inst->GetOutput(); if (!any_all_nets.count(outnet)) module->addBufGate(inst_name, net_map_at(inst->GetInput()), net_map_at(outnet)); continue; } if (inst->Type() == PRIM_X) { module->connect(net_map_at(inst->GetOutput()), RTLIL::State::Sx); continue; } if (inst->Type() == PRIM_Z) { module->connect(net_map_at(inst->GetOutput()), RTLIL::State::Sz); continue; } if (inst->Type() == OPER_READ_PORT) { RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetInput()->Name()), nullptr); if (!memory) log_error("Memory net '%s' missing, possibly no driver, use verific -flatten.\n", inst->GetInput()->Name()); int numchunks = int(inst->OutputSize()) / memory->width; int chunksbits = ceil_log2(numchunks); for (int i = 0; i < numchunks; i++) { RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)}; RTLIL::SigSpec data = operatorOutput(inst).extract(i * memory->width, memory->width); RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name : RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), ID($memrd)); cell->parameters[ID::MEMID] = memory->name.str(); cell->parameters[ID::CLK_ENABLE] = false; cell->parameters[ID::CLK_POLARITY] = true; 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); cell->setPort(ID::DATA, data); } continue; } if (inst->Type() == OPER_WRITE_PORT || inst->Type() == OPER_CLOCKED_WRITE_PORT) { RTLIL::Memory *memory = module->memories.at(RTLIL::escape_id(inst->GetOutput()->Name()), nullptr); if (!memory) log_error("Memory net '%s' missing, possibly no driver, use verific -flatten.\n", inst->GetInput()->Name()); int numchunks = int(inst->Input2Size()) / memory->width; int chunksbits = ceil_log2(numchunks); for (int i = 0; i < numchunks; i++) { RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)}; RTLIL::SigSpec data = operatorInput2(inst).extract(i * memory->width, memory->width); RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name : RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), ID($memwr)); cell->parameters[ID::MEMID] = memory->name.str(); cell->parameters[ID::CLK_ENABLE] = false; cell->parameters[ID::CLK_POLARITY] = true; 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); cell->setPort(ID::DATA, data); if (inst->Type() == OPER_CLOCKED_WRITE_PORT) { cell->parameters[ID::CLK_ENABLE] = true; cell->setPort(ID::CLK, net_map_at(inst->GetClock())); } } continue; } if (!mode_gates) { if (import_netlist_instance_cells(inst, inst_name)) continue; if (inst->IsOperator() && !verific_sva_prims.count(inst->Type())) log_warning("Unsupported Verific operator: %s (fallback to gate level implementation provided by verific)\n", inst->View()->Owner()->Name()); } else { if (import_netlist_instance_gates(inst, inst_name)) continue; } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (inst->Type() == PRIM_SVA_ASSERT || inst->Type() == PRIM_SVA_IMMEDIATE_ASSERT) sva_asserts.insert(inst); if (inst->Type() == PRIM_SVA_ASSUME || inst->Type() == PRIM_SVA_IMMEDIATE_ASSUME || inst->Type() == PRIM_SVA_RESTRICT) sva_assumes.insert(inst); if (inst->Type() == PRIM_SVA_COVER || inst->Type() == PRIM_SVA_IMMEDIATE_COVER) sva_covers.insert(inst); if (inst->Type() == PRIM_SVA_TRIGGERED) sva_triggers.insert(inst); if (inst->Type() == OPER_SVA_STABLE) { VerificClocking clocking(this, inst->GetInput2Bit(0)); log_assert(clocking.disable_sig == State::S0); log_assert(clocking.body_net == nullptr); log_assert(inst->Input1Size() == inst->OutputSize()); 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(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_dx), log_signal(sig_qx), log_signal(clocking.clock_sig)); log(" XNOR with A=%s, B=%s, Y=%s.\n", 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)); } 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; } if (inst->Type() == PRIM_SVA_STABLE) { VerificClocking clocking(this, inst->GetInput2()); log_assert(clocking.disable_sig == State::S0); log_assert(clocking.body_net == nullptr); SigSpec sig_d = net_map_at(inst->GetInput1()); SigSpec sig_o = net_map_at(inst->GetOutput()); 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_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)); } 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; } if (inst->Type() == PRIM_SVA_PAST) { VerificClocking clocking(this, inst->GetInput2()); log_assert(clocking.disable_sig == State::S0); log_assert(clocking.body_net == nullptr); SigBit sig_d = net_map_at(inst->GetInput1()); SigBit sig_q = net_map_at(inst->GetOutput()); if (verific_verbose) 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)); past_ffs.insert(clocking.addDff(new_verific_id(inst), sig_d, sig_q)); if (!mode_keep) continue; } if ((inst->Type() == PRIM_SVA_ROSE || inst->Type() == PRIM_SVA_FELL)) { VerificClocking clocking(this, inst->GetInput2()); log_assert(clocking.disable_sig == State::S0); log_assert(clocking.body_net == nullptr); 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) { 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_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)); } 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; } #endif #ifdef YOSYSHQ_VERIFIC_API_VERSION 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); if (!mode_keep) continue; } #endif if (!mode_keep && verific_sva_prims.count(inst->Type())) { if (verific_verbose) log(" skipping SVA cell in non k-mode\n"); continue; } if (inst->Type() == PRIM_HDL_ASSERTION) { SigBit cond = net_map_at(inst->GetInput()); if (verific_verbose) log(" assert condition %s.\n", log_signal(cond)); Cell *cell = module->addAssert(new_verific_id(inst), cond, State::S1); import_attributes(cell->attributes, inst); continue; } if (inst->IsPrimitive()) { if (!mode_keep) log_error("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name()); if (!verific_sva_prims.count(inst->Type())) log_warning("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name()); } import_verific_cells: std::string inst_type = is_blackbox(inst->View()) ? inst->View()->CellBaseName() : 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() && !is_blackbox(inst->View())) { inst_type += "("; inst_type += inst->View()->Name(); inst_type += ")"; } inst_type = "\\" + sha1_if_contain_spaces(inst_type); } RTLIL::Cell *cell = module->addCell(inst_name, inst_type); import_attributes(cell->attributes, inst); if (inst->IsPrimitive() && mode_keep) cell->attributes[ID::keep] = 1; dict> cell_port_conns; 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) { const TypeRange *tr = inst->View()->GetTypeRange(param_name) ; const char* type_name = (tr) ? tr->GetTypeName() : nullptr; cell->setParam(RTLIL::escape_id(param_name), verific_const(type_name, param_value, inst->View())); } } FOREACH_PORTREF_OF_INST(inst, mi2, pr) { if (verific_verbose) log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name()); const char *port_name = pr->GetPort()->Name(); int port_offset = 0; if (pr->GetPort()->Bus()) { port_name = pr->GetPort()->Bus()->Name(); int msb_index = pr->GetPort()->Bus()->LeftIndex(); int lsb_index = pr->GetPort()->Bus()->RightIndex(); int index_of_port = pr->GetPort()->Bus()->IndexOf(pr->GetPort()); port_offset = index_of_port - min(msb_index, lsb_index); // In cases where the msb order is flipped we need to make sure // that the indicies match LSB = 0 order to match the std::vector // to SigSpec LSB = 0 precondition. if (lsb_index > msb_index) { port_offset = abs(port_offset - (lsb_index - min(msb_index, lsb_index))); } } IdString port_name_id = RTLIL::escape_id(port_name); auto &sigvec = cell_port_conns[port_name_id]; if (GetSize(sigvec) <= port_offset) { SigSpec zwires = module->addWire(new_verific_id(inst), port_offset+1-GetSize(sigvec)); for (auto bit : zwires) sigvec.push_back(bit); } sigvec[port_offset] = net_map_at(pr->GetNet()); } if (verific_verbose) log(" ports in yosys db:\n"); for (auto &it : cell_port_conns) { if (verific_verbose) log(" .%s(%s)\n", log_id(it.first), log_signal(it.second)); cell->setPort(it.first, it.second); } } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (!mode_nosva) { for (auto inst : sva_asserts) { if (mode_autocover) verific_import_sva_cover(this, inst); verific_import_sva_assert(this, inst); } for (auto inst : sva_assumes) verific_import_sva_assume(this, inst); for (auto inst : sva_covers) verific_import_sva_cover(this, inst); for (auto inst : sva_triggers) verific_import_sva_trigger(this, inst); merge_past_ffs(past_ffs); } #endif if (!mode_fullinit) { pool non_ff_bits; CellTypes ff_types; ff_types.setup_internals_ff(); ff_types.setup_stdcells_mem(); for (auto cell : module->cells()) { if (ff_types.cell_known(cell->type)) continue; for (auto conn : cell->connections()) { if (!cell->output(conn.first)) continue; for (auto bit : conn.second) if (bit.wire != nullptr) non_ff_bits.insert(bit); } } for (auto wire : module->wires()) { if (!wire->attributes.count(ID::init)) continue; Const &initval = wire->attributes.at(ID::init); for (int i = 0; i < GetSize(initval); i++) { if (initval[i] != State::S0 && initval[i] != State::S1) continue; if (non_ff_bits.count(SigBit(wire, i))) initval.bits()[i] = State::Sx; } 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); } } } // ================================================================== VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_at_only YS_MAYBE_UNUSED) { module = importer->module; log_assert(importer != nullptr); log_assert(net != nullptr); Instance *inst = net->Driver(); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT // 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(); body_net = inst->GetInput2(); inst = net->Driver(); Instance *body_inst = body_net->Driver(); if (body_inst != nullptr && body_inst->Type() == PRIM_SVA_DISABLE_IFF) { disable_net = body_inst->GetInput1(); disable_sig = importer->net_map_at(disable_net); body_net = body_inst->GetInput2(); } } else { if (sva_at_only) return; } // Use while() instead of if() to work around VIPER #13453 while (inst != nullptr && inst->Type() == PRIM_SVA_POSEDGE) { net = inst->GetInput(); inst = net->Driver();; } #endif if (inst != nullptr && inst->Type() == PRIM_INV) { net = inst->GetInput(); inst = net->Driver();; posedge = false; } // Detect clock-enable circuit do { if (inst == nullptr || inst->Type() != PRIM_AND) break; Net *net_dlatch = inst->GetInput1(); Instance *inst_dlatch = net_dlatch->Driver(); if (inst_dlatch == nullptr || inst_dlatch->Type() != PRIM_DLATCHRS) break; if (!inst_dlatch->GetSet()->IsGnd() || !inst_dlatch->GetReset()->IsGnd()) break; Net *net_enable = inst_dlatch->GetInput(); Net *net_not_clock = inst_dlatch->GetControl(); if (net_enable == nullptr || net_not_clock == nullptr) break; Instance *inst_not_clock = net_not_clock->Driver(); if (inst_not_clock == nullptr || inst_not_clock->Type() != PRIM_INV) break; Net *net_clock1 = inst_not_clock->GetInput(); Net *net_clock2 = inst->GetInput2(); if (net_clock1 == nullptr || net_clock1 != net_clock2) break; enable_net = net_enable; enable_sig = importer->net_map_at(enable_net); net = net_clock1; inst = net->Driver();; } while (0); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT // Detect condition expression do { if (body_net == nullptr) break; Instance *inst_mux = body_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; body_net = sva_net; cond_net = inst_mux->GetControl(); cond_pol = pwr1; } while (0); #endif clock_net = net; clock_sig = importer->net_map_at(clock_net); const char *gclk_attr = clock_net->GetAttValue("gclk"); if (gclk_attr != nullptr && (!strcmp(gclk_attr, "1") || !strcmp(gclk_attr, "'1'"))) gclk = true; } Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const init_value) { log_assert(GetSize(sig_d) == GetSize(sig_q)); 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(s)); w->attributes[ID::init] = init_value; 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(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.bits()[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) { 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); } Cell *VerificClocking::addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec sig_d, SigSpec sig_q, Const arst_value) { 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); return module->addAdff(name, clock_sig, sig_arst, sig_d, sig_q, arst_value, posedge); } Cell *VerificClocking::addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, SigSpec sig_d, SigSpec sig_q) { 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.bits()[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 { int portname_cnt = 0; // a map from Net to the same Net one level up in the design hierarchy std::map net_level_up_drive_up; std::map net_level_up_drive_down; Net *route_up(Net *net, bool drive_up, Net *final_net = nullptr) { auto &net_level_up = drive_up ? net_level_up_drive_up : net_level_up_drive_down; if (net_level_up.count(net) == 0) { Netlist *nl = net->Owner(); // Simply return if Netlist is not unique log_assert(nl->NumOfRefs() == 1); Instance *up_inst = (Instance*)nl->GetReferences()->GetLast(); Netlist *up_nl = up_inst->Owner(); // create new Port 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); nl->Buf(net)->Connect(new_port); // create new Net in up Netlist Net *new_net = final_net; if (new_net == nullptr || new_net->Owner() != up_nl) { new_net = new Net(name.c_str()); up_nl->Add(new_net); } up_inst->Connect(new_port, new_net); net_level_up[net] = new_net; } return net_level_up.at(net); } Net *route_up(Net *net, bool drive_up, Netlist *dest, Net *final_net = nullptr) { while (net->Owner() != dest) net = route_up(net, drive_up, final_net); if (final_net != nullptr) log_assert(net == final_net); return net; } Netlist *find_common_ancestor(Netlist *A, Netlist *B) { std::set ancestors_of_A; Netlist *cursor = A; while (1) { ancestors_of_A.insert(cursor); if (cursor->NumOfRefs() != 1) break; cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner(); } cursor = B; while (1) { if (ancestors_of_A.count(cursor)) return cursor; if (cursor->NumOfRefs() != 1) break; cursor = ((Instance*)cursor->GetReferences()->GetLast())->Owner(); } log_error("No common ancestor found between %s and %s.\n", get_full_netlist_name(A).c_str(), get_full_netlist_name(B).c_str()); } void run(Netlist *nl) { MapIter mi, mi2; Instance *inst; PortRef *pr; vector> todo_connect; FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst) run(inst->View()); FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst) FOREACH_PORTREF_OF_INST(inst, mi2, pr) { Port *port = pr->GetPort(); Net *net = pr->GetNet(); if (!net->IsExternalTo(nl)) continue; if (verific_verbose) log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl).c_str(), inst->Name(), port->Name()); Netlist *ext_nl = net->Owner(); if (verific_verbose) log(" external net owner: %s\n", get_full_netlist_name(ext_nl).c_str()); Netlist *ca_nl = find_common_ancestor(nl, ext_nl); if (verific_verbose) log(" common ancestor: %s\n", get_full_netlist_name(ca_nl).c_str()); Net *ca_net = route_up(net, !port->IsOutput(), ca_nl); Net *new_net = ca_net; if (ca_nl != nl) { if (verific_verbose) log(" net in common ancestor: %s\n", ca_net->Name()); string name = stringf("___extnets_%d", portname_cnt++); new_net = new Net(name.c_str()); nl->Add(new_net); Net *n = route_up(new_net, port->IsOutput(), ca_nl, ca_net); log_assert(n == ca_net); } if (verific_verbose) log(" new local net: %s\n", new_net->Name()); log_assert(!new_net->IsExternalTo(nl)); todo_connect.push_back(tuple(inst, port, new_net)); } for (auto it : todo_connect) { get<0>(it)->Disconnect(get<1>(it)); get<0>(it)->Connect(get<1>(it), get<2>(it)); } } }; #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT static msg_type_t prev_1063; #endif #ifdef VERIFIC_VHDL_SUPPORT static msg_type_t prev_1240 ; static msg_type_t prev_1241 ; #endif void save_blackbox_msg_state() { #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT prev_1063 = Message::GetMessageType("VERI-1063") ; Message::SetMessageType("VERI-1063", VERIFIC_INFO); #endif #ifdef VERIFIC_VHDL_SUPPORT prev_1240 = Message::GetMessageType("VHDL-1240") ; prev_1241 = Message::GetMessageType("VHDL-1241") ; Message::SetMessageType("VHDL-1240", VERIFIC_INFO); Message::SetMessageType("VHDL-1241", VERIFIC_INFO); #endif } void restore_blackbox_msg_state() { #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT Message::ClearMessageType("VERI-1063") ; if (Message::GetMessageType("VERI-1063")!=prev_1063) Message::SetMessageType("VERI-1063", prev_1063); #endif #ifdef VERIFIC_VHDL_SUPPORT 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 } void import_all(const char* work, std::map *nl_todo, Map *parameters, bool show_message, std::string ppfile YS_MAYBE_UNUSED) { #ifdef YOSYSHQ_VERIFIC_EXTENSIONS save_blackbox_msg_state(); VerificExtensions::ElaborateAndRewrite(work, parameters); verific_error_msg.clear(); restore_blackbox_msg_state(); #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (!ppfile.empty()) veri_file::PrettyPrint(ppfile.c_str(), nullptr, work); #endif Array vhdl_libs; #ifdef VERIFIC_VHDL_SUPPORT VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work, 1); if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); #endif Array veri_libs; #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT VeriLibrary *veri_lib = veri_file::GetLibrary(work, 1); if (veri_lib) veri_libs.InsertLast(veri_lib); #endif #ifdef VERIFIC_HIER_TREE_SUPPORT if (show_message) log("Running hier_tree::ElaborateAll().\n"); Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs, parameters); Netlist *nl; int i; FOREACH_ARRAY_ITEM(netlists, i, nl) nl_todo->emplace(nl->CellBaseName(), nl); delete netlists; #else if (parameters->Size()) log_warning("Please note that parameters are not propagated during import.\n"); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (show_message) log("Running veri_file::ElaborateAll().\n"); veri_file::ElaborateAll(work); #endif #ifdef VERIFIC_VHDL_SUPPORT if (show_message) log("Running vhdl_file::ElaborateAll().\n"); vhdl_file::ElaborateAll(work); #endif MapIter mi ; Verific::Cell *c ; MapIter it ; Library *l ; FOREACH_LIBRARY_OF_LIBSET(Libset::Global(),it,l) { if (l == Library::Primitives() || l == Library::Operators()) continue; FOREACH_CELL_OF_LIBRARY(l,mi,c) { MapIter ni ; Netlist *nl; FOREACH_NETLIST_OF_CELL(c, ni, nl) { if (nl) nl_todo->emplace(nl->CellBaseName(), nl); } } } #endif } std::set import_tops(const char* work, std::map *nl_todo, Map *parameters, bool show_message, std::string ppfile YS_MAYBE_UNUSED, std::vector &tops, std::string *top = nullptr) { std::set top_mod_names; Array *netlists = nullptr; #ifdef VERIFIC_VHDL_SUPPORT VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work, 1); #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT VeriLibrary* veri_lib = veri_file::GetLibrary(work, 1); #endif #ifdef YOSYSHQ_VERIFIC_EXTENSIONS for (int static_elaborate = 1; static_elaborate >= 0; static_elaborate--) #endif { Array vhdl_units; Array veri_modules; for (std::string n : tops) { const char *name = n.c_str(); top_mod_names.insert(name); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT VeriModule *veri_module = veri_lib ? veri_lib->GetModule(name, 1) : nullptr; if (veri_module) { if (veri_module->IsConfiguration()) { if (show_message) 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 { if (show_message) log("Adding Verilog module '%s' to elaboration queue.\n", name); veri_modules.InsertLast(veri_module); } continue; } #endif #ifdef VERIFIC_VHDL_SUPPORT VhdlDesignUnit *vhdl_unit = vhdl_lib ? vhdl_lib->GetPrimUnit(name) : nullptr; if (vhdl_unit) { if (show_message) log("Adding VHDL unit '%s' to elaboration queue.\n", name); vhdl_units.InsertLast(vhdl_unit); if (strcmp(name, vhdl_unit->Id()->OrigName()) != 0) { top_mod_names.erase(name); top_mod_names.insert(vhdl_unit->Id()->OrigName()); if (top && *top == name) *top = vhdl_unit->Id()->OrigName(); } continue; } #endif log_error("Can't find module/unit '%s'.\n", name); } #ifdef YOSYSHQ_VERIFIC_EXTENSIONS if (static_elaborate) { save_blackbox_msg_state(); VerificExtensions::ElaborateAndRewrite(work, &veri_modules, &vhdl_units, parameters); verific_error_msg.clear(); restore_blackbox_msg_state(); #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (!ppfile.empty()) veri_file::PrettyPrint(ppfile.c_str(), nullptr, work); #endif #ifdef YOSYSHQ_VERIFIC_EXTENSIONS continue; } #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT 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); } } } #endif #ifdef VERIFIC_HIER_TREE_SUPPORT if (show_message) log("Running hier_tree::Elaborate().\n"); netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units, parameters); #else #if defined(VERIFIC_SYSTEMVERILOG_SUPPORT) && !defined(VERIFIC_VHDL_SUPPORT) if (show_message) log("Running veri_file::ElaborateMultipleTop().\n"); // SystemVerilog support only netlists = veri_file::ElaborateMultipleTop(&veri_modules, parameters); #elif defined(VERIFIC_VHDL_SUPPORT) && !defined(VERIFIC_SYSTEMVERILOG_SUPPORT) if (show_message) log("Running vhdl_file::Elaborate().\n"); // VHDL support only netlists = new Array(top_mod_names.size()); for (auto &name : top_mod_names) { vhdl_file::Elaborate(name.c_str(), work, 0, parameters); netlists->InsertLast(Netlist::PresentDesign()); } #elif defined(VERIFIC_SYSTEMVERILOG_SUPPORT) && defined(VERIFIC_VHDL_SUPPORT) // Both SystemVerilog and VHDL support if (veri_modules.Size()>0) { if (show_message) log("Running veri_file::ElaborateMultipleTop().\n"); netlists = veri_file::ElaborateMultipleTop(&veri_modules, parameters); } else netlists = new Array(1); if (vhdl_units.Size()>0) { if (show_message) log("Running vhdl_file::Elaborate().\n"); for (auto &name : top_mod_names) { vhdl_file::Elaborate(name.c_str(), work, 0, parameters); netlists->InsertLast(Netlist::PresentDesign()); } } #else #endif #endif } 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->emplace(nl->CellBaseName(), nl); } delete netlists; return top_mod_names; } void verific_cleanup() { #ifdef YOSYSHQ_VERIFIC_EXTENSIONS VerificExtensions::Reset(); #endif #ifdef VERIFIC_HIER_TREE_SUPPORT hier_tree::DeleteHierarchicalTree(); #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT veri_file::Reset(); #endif #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(); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT verific_incdirs.clear(); verific_libdirs.clear(); verific_libexts.clear(); #endif verific_import_pending = false; } std::string verific_import(Design *design, const std::map ¶meters, std::string top) { verific_sva_fsm_limit = 16; std::map nl_todo, nl_done; Map verific_params(STRING_HASH); for (const auto &i : parameters) verific_params.Insert(i.first.c_str(), i.second.c_str()); std::set top_mod_names; if (top.empty()) { import_all("work", &nl_todo, &verific_params, false, ""); } else { std::vector tops; tops.push_back(top); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT veri_file::RemoveAllLOptions(); veri_file::AddLOption("work"); #endif top_mod_names = import_tops("work", &nl_todo, &verific_params, false, "", tops, &top) ; } if (!verific_error_msg.empty()) log_error("%s\n", verific_error_msg.c_str()); for (auto nl : nl_todo) nl.second->ChangePortBusStructures(1 /* hierarchical */); VerificExtNets worker; for (auto nl : nl_todo) worker.run(nl.second); while (!nl_todo.empty()) { 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); nl_done[it->first] = it->second; importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->CellBaseName())); } nl_todo.erase(it); } verific_cleanup(); if (!verific_error_msg.empty()) log_error("%s\n", verific_error_msg.c_str()); return top; } YOSYS_NAMESPACE_END #endif /* YOSYS_ENABLE_VERIFIC */ PRIVATE_NAMESPACE_BEGIN #ifdef YOSYS_ENABLE_VERIFIC bool check_noverific_env() { const char *e = getenv("YOSYS_NOVERIFIC"); if (e == nullptr) return false; if (atoi(e) == 0) return false; return true; } #endif struct VerificPass : public Pass { VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { } #ifdef YOSYSHQ_VERIFIC_EXTENSIONS void on_register() override { VerificExtensions::Reset(); } #endif void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv} ..\n"); log("\n"); log("Load the specified Verilog/SystemVerilog files into Verific.\n"); log("\n"); log("All files specified in one call to this command are one compilation unit.\n"); log("Files passed to different calls to this command are treated as belonging to\n"); log("different compilation units.\n"); 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 YOSYS, SYNTHESIS, and VERIFIC are defined implicitly.\n"); log("\n"); log("\n"); log(" verific -formal ..\n"); log("\n"); log("Like -sv, but define FORMAL instead of SYNTHESIS.\n"); log("\n"); log("\n"); #endif #ifdef VERIFIC_VHDL_SUPPORT log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl2019|-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 #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT 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"); #endif log(" verific [-work ] {-sv|-vhdl|...} \n"); log("\n"); log("Load the specified Verilog/SystemVerilog/VHDL file into the specified library.\n"); log("(default library when -work is not present: \"work\")\n"); log("\n"); log("\n"); log(" verific [-L ] {-sv|-vhdl|...} \n"); log("\n"); log("Look up external definitions in the specified library.\n"); log("(-L may be used more than once)\n"); log("\n"); log("\n"); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT log(" verific -vlog-incdir ..\n"); log("\n"); log("Add Verilog include directories.\n"); log("\n"); log("\n"); log(" verific -vlog-libdir ..\n"); log("\n"); log("Add Verilog library directories. Verific will search in this directories to\n"); 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"); log("\n"); log("\n"); log(" verific -vlog-undef ..\n"); log("\n"); log("Remove Verilog defines previously set with -vlog-define.\n"); log("\n"); log("\n"); #endif log(" verific -set-error ..\n"); log(" verific -set-warning ..\n"); log(" verific -set-info ..\n"); log(" verific -set-ignore ..\n"); 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("\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"); log(" -all\n"); log(" Elaborate all modules, not just the hierarchy below the given top\n"); log(" modules. With this option the list of modules to import is optional.\n"); log("\n"); log(" -gates\n"); log(" Create a gate-level netlist.\n"); log("\n"); log(" -flatten\n"); log(" Flatten the design in Verific before importing.\n"); log("\n"); 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"); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT log(" -autocover\n"); log(" Generate automatic cover statements for all asserts\n"); log("\n"); #endif 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"); log(" cause Verific to produce a VERI-1928 or VHDL-1676 message. 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(" -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"); log(" -k\n"); log(" Keep going after an unsupported verific primitive is found. The\n"); log(" unsupported primitive is added as blockbox module to the design.\n"); log(" This will also add all SVA related cells to the design parallel to\n"); log(" the checker logic inferred by it.\n"); log("\n"); log(" -V\n"); log(" Import Verific netlist as-is without translating to Yosys cell types. \n"); log("\n"); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT log(" -nosva\n"); log(" Ignore SVA properties, do not infer checker logic.\n"); log("\n"); log(" -L \n"); log(" Maximum number of ctrl bits for SVA checker FSMs (default=16).\n"); log("\n"); #endif log(" -n\n"); log(" Keep all Verific names on instances and nets. By default only\n"); log(" user-declared names are preserved.\n"); log("\n"); log(" -d \n"); log(" Dump the Verific netlist as a verilog file.\n"); log("\n"); log("\n"); log(" verific [-work ] -pp [options] []..\n"); log("\n"); log("Pretty print design (or just module) to the specified file from the\n"); log("specified library. (default library when -work is not present: \"work\")\n"); log("\n"); log("Pretty print options:\n"); log("\n"); log(" -verilog\n"); log(" Save output for Verilog/SystemVerilog design modules (default).\n"); log("\n"); log(" -vhdl\n"); log(" Save output for VHDL design units.\n"); log("\n"); log("\n"); log(" verific -cfg [ []]\n"); log("\n"); log("Get/set Verific runtime flags.\n"); log("\n"); log("\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("Contact office@yosyshq.com for free evaluation\n"); log("binaries of YosysHQ Tabby CAD Suite.\n"); log("\n"); } #ifdef YOSYS_ENABLE_VERIFIC 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 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); } } save_blackbox_msg_state(); } 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(); } } restore_blackbox_msg_state(); } #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT 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); } } save_blackbox_msg_state(); } 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(); } } restore_blackbox_msg_state(); } #endif void execute(std::vector args, RTLIL::Design *design) override { static bool set_verific_global_flags = true; if (check_noverific_env()) log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" "Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" "https://www.yosyshq.com/\n" "\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"); if (set_verific_global_flags) { 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); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT RuntimeFlags::SetVar("veri_extract_dualport_rams", 0); RuntimeFlags::SetVar("veri_extract_multiport_rams", 1); RuntimeFlags::SetVar("veri_allow_any_ram_in_loop", 1); #endif #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("vhdl_preserve_assignments", 1); //RuntimeFlags::SetVar("vhdl_preserve_comments", 1); RuntimeFlags::SetVar("vhdl_preserve_drivers", 1); #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT 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); // WARNING: instantiating unknown module 'XYZ' (VERI-1063) Message::SetMessageType("VERI-1063", VERIFIC_ERROR); // https://github.com/YosysHQ/yosys/issues/1055 RuntimeFlags::SetVar("veri_elaborate_top_level_modules_having_interface_ports", 1) ; #endif RuntimeFlags::SetVar("verific_produce_verbose_syntax_error_message", 1); #ifndef DB_PRESERVE_INITIAL_VALUE # warning Verific was built without DB_PRESERVE_INITIAL_VALUE. #endif set_verific_global_flags = false; } verific_verbose = 0; verific_sva_fsm_limit = 16; 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)"; for (char *p = release_tmstr; *p; p++) if (*p == '\n') *p = 0; log("Built with Verific %s, released at %s.\n", release_str, release_tmstr); int argidx = 1; std::string work = "work"; bool is_work_set = false; (void)is_work_set; #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT veri_file::RegisterCallBackVerificStream(&verific_read_cb); #endif if (GetSize(args) > argidx && (args[argidx] == "-set-error" || args[argidx] == "-set-warning" || args[argidx] == "-set-info" || args[argidx] == "-set-ignore")) { msg_type_t new_type; if (args[argidx] == "-set-error") new_type = VERIFIC_ERROR; else if (args[argidx] == "-set-warning") new_type = VERIFIC_WARNING; else if (args[argidx] == "-set-info") new_type = VERIFIC_INFO; else if (args[argidx] == "-set-ignore") new_type = VERIFIC_IGNORE; else log_abort(); 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; } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (GetSize(args) > argidx && args[argidx] == "-vlog-incdir") { for (argidx++; argidx < GetSize(args); argidx++) verific_incdirs.push_back(args[argidx]); goto check_error; } if (GetSize(args) > argidx && args[argidx] == "-vlog-libdir") { for (argidx++; argidx < GetSize(args); argidx++) verific_libdirs.push_back(args[argidx]); 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]; size_t equal = name.find('='); if (equal != std::string::npos) { string value = name.substr(equal+1); name = name.substr(0, equal); veri_file::DefineCmdLineMacro(name.c_str(), value.c_str()); } else { veri_file::DefineCmdLineMacro(name.c_str()); } } goto check_error; } if (GetSize(args) > argidx && args[argidx] == "-vlog-undef") { for (argidx++; argidx < GetSize(args); argidx++) { string name = args[argidx]; veri_file::UndefineMacro(name.c_str()); } goto check_error; } veri_file::RemoveAllLOptions(); #endif for (int i = argidx; i < GetSize(args); i++) { if (args[i] == "-work" && i+1 < GetSize(args)) { work = args[++i]; is_work_set = true; continue; } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (args[i] == "-L" && i+1 < GetSize(args)) { ++i; continue; } #endif break; } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT veri_file::AddLOption(work.c_str()); #endif for (int i = argidx; i < GetSize(args); i++) { if (args[i] == "-work" && i+1 < GetSize(args)) { ++i; continue; } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (args[i] == "-L" && i+1 < GetSize(args)) { if (args[++i] == work) veri_file::RemoveAllLOptions(); continue; } #endif break; } for (; argidx < GetSize(args); argidx++) { if (args[argidx] == "-work" && argidx+1 < GetSize(args)) { work = args[++argidx]; is_work_set = true; continue; } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (args[argidx] == "-L" && argidx+1 < GetSize(args)) { veri_file::AddLOption(args[++argidx].c_str()); continue; } #endif break; } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT 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")) { Array file_names; unsigned verilog_mode; if (args[argidx] == "-vlog95") verilog_mode = veri_file::VERILOG_95; else if (args[argidx] == "-vlog2k") verilog_mode = veri_file::VERILOG_2K; else if (args[argidx] == "-sv2005") verilog_mode = veri_file::SYSTEM_VERILOG_2005; else if (args[argidx] == "-sv2009") verilog_mode = veri_file::SYSTEM_VERILOG_2009; else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal") verilog_mode = veri_file::SYSTEM_VERILOG; else log_abort(); veri_file::DefineMacro("YOSYS"); veri_file::DefineMacro("VERIFIC"); veri_file::DefineMacro(args[argidx] == "-formal" ? "FORMAL" : "SYNTHESIS"); for (argidx++; argidx < GetSize(args) && GetSize(args[argidx]) >= 2 && args[argidx].compare(0, 2, "-D") == 0; argidx++) { std::string name = args[argidx].substr(2); if (args[argidx] == "-D") { if (++argidx >= GetSize(args)) break; name = args[argidx]; } size_t equal = name.find('='); if (equal != std::string::npos) { string value = name.substr(equal+1); name = name.substr(0, equal); veri_file::DefineMacro(name.c_str(), value.c_str()); } else { veri_file::DefineMacro(name.c_str()); } } for (auto &dir : verific_incdirs) veri_file::AddIncludeDir(dir.c_str()); for (auto &dir : verific_libdirs) veri_file::AddYDir(dir.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"); } char* fn; int i = 0; FOREACH_ARRAY_ITEM(&file_names, i, fn) { free(fn); } set_modules_to_blackbox(map, work, flag_lib); verific_import_pending = true; goto check_error; } #endif #ifdef VERIFIC_VHDL_SUPPORT if (GetSize(args) > argidx && args[argidx] == "-vhdl87") { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_1987").c_str()); bool flag_lib = false; argidx++; 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; } 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()); bool flag_lib = false; argidx++; 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; } 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()); bool flag_lib = false; argidx++; 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; } 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] == "-vhdl2019")) { vhdl_file::SetDefaultLibraryPath((proc_share_dirname() + "verific/vhdl_vdbs_2019").c_str()); bool flag_lib = false; argidx++; 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; } 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_2019)) log_cmd_error("Reading `%s' in VHDL_2019 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()); bool flag_lib = false; argidx++; 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; } 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].compare(0, 1, "-") == 0) { cmd_error(args, argidx, "unknown option"); goto check_error; } 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; } if (args[argidx].compare(0, 1, "-") == 0) { cmd_error(args, argidx, "unknown option"); goto check_error; } if (!filename) { filename = args[argidx].c_str(); continue; } if (module) log_cmd_error("Only one module can be specified.\n"); module = args[argidx].c_str(); } if (argidx < GetSize(args)) cmd_error(args, argidx, "unknown option/parameter"); if (!filename) 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 #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT else veri_file::PrettyPrint(filename, module, work.c_str()); #endif goto check_error; } if (GetSize(args) > argidx && args[argidx] == "-import") { 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, mode_cells = false; bool split_complex_ports = true; string dumpfile; string ppfile; Map parameters(STRING_HASH); for (argidx++; argidx < GetSize(args); argidx++) { if (args[argidx] == "-all") { mode_all = true; continue; } if (args[argidx] == "-gates") { mode_gates = true; continue; } if (args[argidx] == "-flatten") { flatten = true; continue; } if (args[argidx] == "-no-split-complex-ports") { split_complex_ports = false; continue; } if (args[argidx] == "-extnets") { extnets = true; continue; } if (args[argidx] == "-k") { mode_keep = true; continue; } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (args[argidx] == "-nosva") { mode_nosva = true; continue; } if (args[argidx] == "-L" && argidx+1 < GetSize(args)) { verific_sva_fsm_limit = atoi(args[++argidx].c_str()); continue; } if (args[argidx] == "-autocover") { mode_autocover = true; continue; } #endif if (args[argidx] == "-n") { mode_names = true; continue; } if (args[argidx] == "-fullinit") { 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 */); if (!new_insertion) log_warning_noprefix("-chparam %s already specified: overwriting.\n", key.c_str()); continue; } if (args[argidx] == "-V") { mode_verific = true; continue; } if (args[argidx] == "-v") { verific_verbose = 1; continue; } if (args[argidx] == "-vv") { verific_verbose = 2; continue; } if (args[argidx] == "-d" && argidx+1 < GetSize(args)) { dumpfile = args[++argidx]; continue; } if (args[argidx] == "-pp" && argidx+1 < GetSize(args)) { ppfile = args[++argidx]; continue; } break; } if (argidx > GetSize(args) && args[argidx].compare(0, 1, "-") == 0) cmd_error(args, argidx, "unknown option"); std::set top_mod_names; if (mode_all) { import_all(work.c_str(), &nl_todo, ¶meters, true, ppfile); } else { if (argidx == GetSize(args)) cmd_error(args, argidx, "No top module specified.\n"); std::vector tops; for (int i = argidx; i < GetSize(args); i++) tops.push_back(args[i].c_str()); top_mod_names = import_tops(work.c_str(), &nl_todo, ¶meters, true, ppfile, tops) ; } 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.second->Flatten(); } if (extnets) { VerificExtNets worker; for (auto nl : nl_todo) worker.run(nl.second); } if (split_complex_ports) { for (auto nl : nl_todo) nl.second->ChangePortBusStructures(1 /* hierarchical */); } #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT if (!dumpfile.empty()) { VeriWrite veri_writer; veri_writer.WriteFile(dumpfile.c_str(), Netlist::PresentDesign()); } #endif while (!nl_todo.empty()) { 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); nl_done[it->first] = it->second; importer.import_netlist(design, nl, nl_todo, top_mod_names.count(nl->CellBaseName())); } nl_todo.erase(it); } verific_cleanup(); 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(), s.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()); } #else /* YOSYS_ENABLE_VERIFIC */ void execute(std::vector, RTLIL::Design *) override { log_cmd_error("This version of Yosys is built without Verific support.\n" "\n" "Use YosysHQ Tabby CAD Suite if you need Yosys+Verific.\n" "https://www.yosyshq.com/\n" "\n" "Contact office@yosyshq.com for free evaluation\n" "binaries of YosysHQ Tabby CAD Suite.\n"); } #endif } VerificPass; struct ReadPass : public Pass { ReadPass() : Pass("read", "load HDL designs") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv|-formal} ..\n"); log("\n"); log("Load the specified Verilog/SystemVerilog files. (Full SystemVerilog support\n"); log("is only available via Verific.)\n"); 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("\n"); log("\n"); #ifdef VERIFIC_VHDL_SUPPORT log(" read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl2019|-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.\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"); log("\n"); log("\n"); log(" read -undef ..\n"); log("\n"); log("Unset global Verilog/SystemVerilog defines.\n"); log("\n"); log("\n"); log(" read -incdir \n"); log("\n"); log("Add directory to global Verilog/SystemVerilog include directories.\n"); log("\n"); log("\n"); log(" read -verific\n"); log(" read -noverific\n"); log("\n"); log("Subsequent calls to 'read' will either use or not use Verific. Calling 'read'\n"); log("with -verific will result in an error on Yosys binaries that are built without\n"); log("Verific support. The default is to use Verific if it is available.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { #ifdef YOSYS_ENABLE_VERIFIC static bool verific_available = !check_noverific_env(); #else static bool verific_available = false; #endif static bool use_verific = verific_available; if (args.size() < 2 || args[1][0] != '-') cmd_error(args, 1, "Missing mode parameter.\n"); if (args[1] == "-verific" || args[1] == "-noverific") { if (args.size() != 2) cmd_error(args, 1, "Additional arguments to -verific/-noverific.\n"); if (args[1] == "-verific") { if (!verific_available) cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); use_verific = true; } else { use_verific = false; } return; } if (args.size() < 3) cmd_error(args, 3, "Missing file name parameter.\n"); if (args[1] == "-vlog95" || args[1] == "-vlog2k") { if (use_verific) { args[0] = "verific"; } else { args[0] = "read_verilog"; args[1] = "-defer"; } Pass::call(design, args); return; } if (args[1] == "-sv2005" || args[1] == "-sv2009" || args[1] == "-sv2012" || args[1] == "-sv" || args[1] == "-formal") { if (use_verific) { args[0] = "verific"; } else { args[0] = "read_verilog"; if (args[1] == "-formal") args.insert(args.begin()+1, std::string()); args[1] = "-sv"; args.insert(args.begin()+1, "-defer"); } Pass::call(design, args); return; } #ifdef VERIFIC_VHDL_SUPPORT if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl2019" || args[1] == "-vhdl") { 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 #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"; } else { #if !defined(__wasm) args[0] = "read_verilog_file_list"; #else cmd_error(args, 1, "Command files are not supported on this platform.\n"); #endif } Pass::call(design, args); return; } if (args[1] == "-define") { if (use_verific) { args[0] = "verific"; args[1] = "-vlog-define"; Pass::call(design, args); } args[0] = "verilog_defines"; args.erase(args.begin()+1, args.begin()+2); for (int i = 1; i < GetSize(args); i++) args[i] = "-D" + args[i]; Pass::call(design, args); return; } if (args[1] == "-undef") { if (use_verific) { args[0] = "verific"; args[1] = "-vlog-undef"; Pass::call(design, args); } args[0] = "verilog_defines"; args.erase(args.begin()+1, args.begin()+2); for (int i = 1; i < GetSize(args); i++) args[i] = "-U" + args[i]; Pass::call(design, args); return; } if (args[1] == "-incdir") { if (use_verific) { args[0] = "verific"; args[1] = "-vlog-incdir"; Pass::call(design, args); } args[0] = "verilog_defaults"; args[1] = "-add"; for (int i = 2; i < GetSize(args); i++) args[i] = "-I" + args[i]; Pass::call(design, args); return; } cmd_error(args, 1, "Missing or unsupported mode parameter.\n"); } } ReadPass; PRIVATE_NAMESPACE_END yosys-0.52/frontends/verific/verific.h000066400000000000000000000107221477540374200200760ustar00rootroot00000000000000/* * 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. * */ #ifdef YOSYS_ENABLE_VERIFIC #include "DataBase.h" YOSYS_NAMESPACE_BEGIN extern int verific_verbose; extern bool verific_import_pending; extern std::string verific_import(Design *design, const std::map ¶meters, std::string top = std::string()); extern pool verific_sva_prims; struct VerificImporter; struct VerificClocking { RTLIL::Module *module = nullptr; Verific::Net *clock_net = nullptr; Verific::Net *enable_net = nullptr; Verific::Net *disable_net = nullptr; Verific::Net *body_net = nullptr; Verific::Net *cond_net = nullptr; SigBit clock_sig = State::Sx; SigBit enable_sig = State::S1; 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) return false; if (enable_net != seq.enable_net) return false; if (posedge != seq.posedge) return false; return true; } }; struct VerificImporter { RTLIL::Module *module; Verific::Netlist *netlist; std::map net_map; std::map sva_posedge_map; pool any_all_nets; bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific; bool mode_autocover, mode_fullinit; VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover, bool mode_fullinit); RTLIL::SigBit net_map_at(Verific::Net *net); RTLIL::IdString new_verific_id(Verific::DesignObj *obj); void import_attributes(dict &attributes, Verific::DesignObj *obj, Verific::Netlist *nl = nullptr, int wire_width_hint = -1); 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); bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name); 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::map &nl_todo, bool norename = false); }; void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst); void verific_import_sva_assume(VerificImporter *importer, Verific::Instance *inst); void verific_import_sva_cover(VerificImporter *importer, Verific::Instance *inst); void verific_import_sva_trigger(VerificImporter *importer, Verific::Instance *inst); bool verific_is_sva_net(VerificImporter *importer, Verific::Net *net); extern int verific_sva_fsm_limit; YOSYS_NAMESPACE_END #endif yosys-0.52/frontends/verific/verificsva.cc000066400000000000000000001423771477540374200207620ustar00rootroot00000000000000/* * 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. * */ // Currently supported SVA sequence and property syntax: // http://symbiyosys.readthedocs.io/en/latest/verific.html // // Next gen property syntax: // basic_property // [antecedent_condition] property // [antecedent_condition] always.. property // [antecedent_condition] eventually.. basic_property // [antecedent_condition] property until.. expression // [antecedent_condition] basic_property until.. basic_property (assert/assume only) // // antecedent_condition: // sequence |-> // sequence |=> // // basic_property: // sequence // not basic_property // nexttime basic_property // nexttime[N] basic_property // sequence #-# basic_property // sequence #=# basic_property // basic_property or basic_property (cover only) // basic_property and basic_property (assert/assume only) // basic_property implies basic_property // basic_property iff basic_property // // sequence: // expression // sequence ##N sequence // sequence ##[*] sequence // sequence ##[+] sequence // sequence ##[N:M] sequence // sequence ##[N:$] sequence // expression [*] // expression [+] // expression [*N] // expression [*N:M] // expression [*N:$] // sequence or sequence // sequence and sequence // expression throughout sequence // sequence intersect sequence // sequence within sequence // first_match( sequence ) // expression [=N] // expression [=N:M] // expression [=N:$] // expression [->N] // expression [->N:M] // expression [->N:$] #include "kernel/yosys.h" #include "frontends/verific/verific.h" USING_YOSYS_NAMESPACE #ifdef VERIFIC_NAMESPACE using namespace Verific; #endif #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT PRIVATE_NAMESPACE_BEGIN // Non-deterministic FSM struct SvaNFsmNode { // Edge: Activate the target node if ctrl signal is true, consumes clock cycle // Link: Activate the target node if ctrl signal is true, doesn't consume clock cycle vector> edges, links; bool is_cond_node; }; // Non-deterministic FSM after resolving links struct SvaUFsmNode { // Edge: Activate the target node if all bits in ctrl signal are true, consumes clock cycle // Accept: This node functions as an accept node if all bits in ctrl signal are true vector> edges; vector accept, cond; bool reachable; }; // Deterministic FSM struct SvaDFsmNode { // A DFSM state corresponds to a set of NFSM states. We represent DFSM states as sorted vectors // of NFSM state node ids. Edge/accept controls are constants matched against the ctrl sigspec. SigSpec ctrl; vector, Const>> edges; vector accept, reject; // additional temp data for getReject() Wire *ffoutwire; SigBit statesig; SigSpec nextstate; // additional temp data for getDFsm() int outnode; }; struct SvaFsm { Module *module; VerificClocking clocking; SigBit trigger_sig = State::S1, disable_sig; SigBit throughout_sig = State::S1; bool in_cond_mode = false; vector disable_stack; vector throughout_stack; int startNode, acceptNode, condNode; vector nodes; vector unodes; dict, SvaDFsmNode> dnodes; dict, SigBit> cond_eq_cache; bool materialized = false; SigBit final_accept_sig = State::Sx; SigBit final_reject_sig = State::Sx; SvaFsm(const VerificClocking &clking, SigBit trig = State::S1) { module = clking.module; clocking = clking; trigger_sig = trig; startNode = createNode(); acceptNode = createNode(); in_cond_mode = true; condNode = createNode(); in_cond_mode = false; } void pushDisable(SigBit sig) { log_assert(!materialized); disable_stack.push_back(disable_sig); if (disable_sig == State::S0) disable_sig = sig; else disable_sig = module->Or(NEW_ID, disable_sig, sig); } void popDisable() { log_assert(!materialized); log_assert(!disable_stack.empty()); disable_sig = disable_stack.back(); disable_stack.pop_back(); } void pushThroughout(SigBit sig) { log_assert(!materialized); throughout_stack.push_back(throughout_sig); if (throughout_sig == State::S1) throughout_sig = sig; else throughout_sig = module->And(NEW_ID, throughout_sig, sig); } void popThroughout() { log_assert(!materialized); log_assert(!throughout_stack.empty()); throughout_sig = throughout_stack.back(); throughout_stack.pop_back(); } int createNode(int link_node = -1) { log_assert(!materialized); int idx = GetSize(nodes); nodes.push_back(SvaNFsmNode()); nodes.back().is_cond_node = in_cond_mode; if (link_node >= 0) createLink(link_node, idx); return idx; } int createStartNode() { return createNode(startNode); } void createEdge(int from_node, int to_node, SigBit ctrl = State::S1) { log_assert(!materialized); log_assert(0 <= from_node && from_node < GetSize(nodes)); log_assert(0 <= to_node && to_node < GetSize(nodes)); log_assert(from_node != acceptNode); log_assert(to_node != acceptNode); log_assert(from_node != condNode); log_assert(to_node != condNode); log_assert(to_node != startNode); if (from_node != startNode) log_assert(nodes.at(from_node).is_cond_node == nodes.at(to_node).is_cond_node); if (throughout_sig != State::S1) { if (ctrl != State::S1) ctrl = module->And(NEW_ID, throughout_sig, ctrl); else ctrl = throughout_sig; } nodes[from_node].edges.push_back(make_pair(to_node, ctrl)); } void createLink(int from_node, int to_node, SigBit ctrl = State::S1) { log_assert(!materialized); log_assert(0 <= from_node && from_node < GetSize(nodes)); log_assert(0 <= to_node && to_node < GetSize(nodes)); log_assert(from_node != acceptNode); log_assert(from_node != condNode); log_assert(to_node != startNode); if (from_node != startNode) log_assert(nodes.at(from_node).is_cond_node == nodes.at(to_node).is_cond_node); if (throughout_sig != State::S1) { if (ctrl != State::S1) ctrl = module->And(NEW_ID, throughout_sig, ctrl); else ctrl = throughout_sig; } nodes[from_node].links.push_back(make_pair(to_node, ctrl)); } void make_link_order(vector &order, int node, int min) { order[node] = std::max(order[node], min); for (auto &it : nodes[node].links) make_link_order(order, it.first, order[node]+1); } // ---------------------------------------------------- // Generating NFSM circuit to acquire accept signal SigBit getAccept() { log_assert(!materialized); materialized = true; vector state_wire(GetSize(nodes)); vector state_sig(GetSize(nodes)); vector next_state_sig(GetSize(nodes)); // Create state signals { SigBit not_disable = State::S1; if (disable_sig != State::S0) not_disable = module->Not(NEW_ID, disable_sig); for (int i = 0; i < GetSize(nodes); i++) { Wire *w = module->addWire(NEW_ID); state_wire[i] = w; state_sig[i] = w; if (i == startNode) state_sig[i] = module->Or(NEW_ID, state_sig[i], trigger_sig); if (disable_sig != State::S0) state_sig[i] = module->And(NEW_ID, state_sig[i], not_disable); } } // Follow Links { vector node_order(GetSize(nodes)); vector> order_to_nodes; for (int i = 0; i < GetSize(nodes); i++) make_link_order(node_order, i, 0); for (int i = 0; i < GetSize(nodes); i++) { if (node_order[i] >= GetSize(order_to_nodes)) order_to_nodes.resize(node_order[i]+1); order_to_nodes[node_order[i]].push_back(i); } for (int order = 0; order < GetSize(order_to_nodes); order++) for (int node : order_to_nodes[order]) { for (auto &it : nodes[node].links) { int target = it.first; SigBit ctrl = state_sig[node]; if (it.second != State::S1) ctrl = module->And(NEW_ID, ctrl, it.second); state_sig[target] = module->Or(NEW_ID, state_sig[target], ctrl); } } } // Construct activations { vector activate_sig(GetSize(nodes)); vector activate_bit(GetSize(nodes)); for (int i = 0; i < GetSize(nodes); i++) { for (auto &it : nodes[i].edges) activate_sig[it.first].append(module->And(NEW_ID, state_sig[i], it.second)); } for (int i = 0; i < GetSize(nodes); i++) { if (GetSize(activate_sig[i]) == 0) next_state_sig[i] = State::S0; else if (GetSize(activate_sig[i]) == 1) next_state_sig[i] = activate_sig[i]; else next_state_sig[i] = module->ReduceOr(NEW_ID, activate_sig[i]); } } // Create state FFs for (int i = 0; i < GetSize(nodes); i++) { if (next_state_sig[i] != State::S0) { clocking.addDff(NEW_ID, next_state_sig[i], state_wire[i], State::S0); } else { module->connect(state_wire[i], State::S0); } } final_accept_sig = state_sig[acceptNode]; return final_accept_sig; } // ---------------------------------------------------- // Generating quantifier-based NFSM circuit to acquire reject signal SigBit getAnyAllRejectWorker(bool /* allMode */) { // FIXME log_abort(); } SigBit getAnyReject() { return getAnyAllRejectWorker(false); } SigBit getAllReject() { return getAnyAllRejectWorker(true); } // ---------------------------------------------------- // Generating DFSM circuit to acquire reject signal void node_to_unode(int node, int unode, SigSpec ctrl) { if (node == acceptNode) unodes[unode].accept.push_back(ctrl); if (node == condNode) unodes[unode].cond.push_back(ctrl); for (auto &it : nodes[node].edges) { if (it.second != State::S1) { SigSpec s = {ctrl, it.second}; s.sort_and_unify(); unodes[unode].edges.push_back(make_pair(it.first, s)); } else { unodes[unode].edges.push_back(make_pair(it.first, ctrl)); } } for (auto &it : nodes[node].links) { if (it.second != State::S1) { SigSpec s = {ctrl, it.second}; s.sort_and_unify(); node_to_unode(it.first, unode, s); } else { node_to_unode(it.first, unode, ctrl); } } } void mark_reachable_unode(int unode) { if (unodes[unode].reachable) return; unodes[unode].reachable = true; for (auto &it : unodes[unode].edges) mark_reachable_unode(it.first); } void usortint(vector &vec) { vector newvec; std::sort(vec.begin(), vec.end()); for (int i = 0; i < GetSize(vec); i++) if (i == GetSize(vec)-1 || vec[i] != vec[i+1]) newvec.push_back(vec[i]); vec.swap(newvec); } bool cmp_ctrl(const pool &ctrl_bits, const SigSpec &ctrl) { for (int i = 0; i < GetSize(ctrl); i++) if (ctrl_bits.count(ctrl[i]) == 0) return false; return true; } void create_dnode(const vector &state, bool firstmatch, bool condaccept) { if (dnodes.count(state) != 0) return; SvaDFsmNode dnode; dnodes[state] = SvaDFsmNode(); for (int unode : state) { log_assert(unodes[unode].reachable); for (auto &it : unodes[unode].edges) dnode.ctrl.append(it.second); for (auto &it : unodes[unode].accept) dnode.ctrl.append(it); for (auto &it : unodes[unode].cond) dnode.ctrl.append(it); } dnode.ctrl.sort_and_unify(); if (GetSize(dnode.ctrl) > verific_sva_fsm_limit) { if (verific_verbose >= 2) { log(" detected state explosion in DFSM generation:\n"); dump(); log(" ctrl signal: %s\n", log_signal(dnode.ctrl)); } log_error("SVA DFSM state ctrl signal has %d (>%d) bits. Stopping to prevent exponential design size explosion.\n", GetSize(dnode.ctrl), verific_sva_fsm_limit); } for (int i = 0; i < (1 << GetSize(dnode.ctrl)); i++) { Const ctrl_val(i, GetSize(dnode.ctrl)); pool ctrl_bits; for (int i = 0; i < GetSize(dnode.ctrl); i++) if (ctrl_val[i] == State::S1) ctrl_bits.insert(dnode.ctrl[i]); vector new_state; bool accept = false, cond = false; for (int unode : state) { for (auto &it : unodes[unode].accept) if (cmp_ctrl(ctrl_bits, it)) accept = true; for (auto &it : unodes[unode].cond) if (cmp_ctrl(ctrl_bits, it)) cond = true; } bool new_state_cond = false; bool new_state_noncond = false; if (accept && condaccept) accept = cond; if (!accept || !firstmatch) { for (int unode : state) for (auto &it : unodes[unode].edges) if (cmp_ctrl(ctrl_bits, it.second)) { if (nodes.at(it.first).is_cond_node) new_state_cond = true; else new_state_noncond = true; new_state.push_back(it.first); } } if (accept) dnode.accept.push_back(ctrl_val); if (condaccept && (!new_state_cond || !new_state_noncond)) new_state.clear(); if (new_state.empty()) { if (!accept) dnode.reject.push_back(ctrl_val); } else { usortint(new_state); dnode.edges.push_back(make_pair(new_state, ctrl_val)); create_dnode(new_state, firstmatch, condaccept); } } dnodes[state] = dnode; } void optimize_cond(vector &values) { bool did_something = true; while (did_something) { did_something = false; for (int i = 0; i < GetSize(values); i++) for (int j = 0; j < GetSize(values); j++) { if (i == j) continue; log_assert(GetSize(values[i]) == GetSize(values[j])); int delta_pos = -1; bool i_within_j = true; bool j_within_i = true; for (int k = 0; k < GetSize(values[i]); k++) { if (values[i][k] == State::Sa && values[j][k] != State::Sa) { i_within_j = false; continue; } if (values[i][k] != State::Sa && values[j][k] == State::Sa) { j_within_i = false; continue; } if (values[i][k] == values[j][k]) continue; if (delta_pos >= 0) goto next_pair; delta_pos = k; } if (delta_pos >= 0 && i_within_j && j_within_i) { did_something = true; values[i].bits()[delta_pos] = State::Sa; values[j] = values.back(); values.pop_back(); goto next_pair; } if (delta_pos < 0 && i_within_j) { did_something = true; values[i] = values.back(); values.pop_back(); goto next_pair; } if (delta_pos < 0 && j_within_i) { did_something = true; values[j] = values.back(); values.pop_back(); goto next_pair; } next_pair:; } } } SigBit make_cond_eq(const SigSpec &ctrl, const Const &value, SigBit enable = State::S1) { SigSpec sig_a, sig_b; log_assert(GetSize(ctrl) == GetSize(value)); for (int i = 0; i < GetSize(ctrl); i++) if (value[i] != State::Sa) { sig_a.append(ctrl[i]); sig_b.append(value[i]); } if (GetSize(sig_a) == 0) return enable; if (enable != State::S1) { sig_a.append(enable); sig_b.append(State::S1); } auto key = make_pair(sig_a, sig_b); if (cond_eq_cache.count(key) == 0) { if (sig_b == State::S1) cond_eq_cache[key] = sig_a; else if (sig_b == State::S0) cond_eq_cache[key] = module->Not(NEW_ID, sig_a); else cond_eq_cache[key] = module->Eq(NEW_ID, sig_a, sig_b); if (verific_verbose >= 2) { log(" Cond: %s := %s == %s\n", log_signal(cond_eq_cache[key]), log_signal(sig_a), log_signal(sig_b)); } } return cond_eq_cache.at(key); } void getFirstAcceptReject(SigBit *accept_p, SigBit *reject_p) { log_assert(!materialized); materialized = true; // Create unlinked NFSM unodes.resize(GetSize(nodes)); for (int node = 0; node < GetSize(nodes); node++) node_to_unode(node, node, SigSpec()); mark_reachable_unode(startNode); // Create DFSM create_dnode(vector{startNode}, true, false); dnodes.sort(); // Create DFSM Circuit SigSpec accept_sig, reject_sig; for (auto &it : dnodes) { SvaDFsmNode &dnode = it.second; dnode.ffoutwire = module->addWire(NEW_ID); dnode.statesig = dnode.ffoutwire; if (it.first == vector{startNode}) dnode.statesig = module->Or(NEW_ID, dnode.statesig, trigger_sig); } for (auto &it : dnodes) { SvaDFsmNode &dnode = it.second; dict, vector> edge_cond; for (auto &edge : dnode.edges) edge_cond[edge.first].push_back(edge.second); for (auto &it : edge_cond) { optimize_cond(it.second); for (auto &value : it.second) dnodes.at(it.first).nextstate.append(make_cond_eq(dnode.ctrl, value, dnode.statesig)); } if (accept_p) { vector accept_cond = dnode.accept; optimize_cond(accept_cond); for (auto &value : accept_cond) accept_sig.append(make_cond_eq(dnode.ctrl, value, dnode.statesig)); } if (reject_p) { vector reject_cond = dnode.reject; optimize_cond(reject_cond); for (auto &value : reject_cond) reject_sig.append(make_cond_eq(dnode.ctrl, value, dnode.statesig)); } } for (auto &it : dnodes) { SvaDFsmNode &dnode = it.second; if (GetSize(dnode.nextstate) == 0) { module->connect(dnode.ffoutwire, State::S0); } else if (GetSize(dnode.nextstate) == 1) { clocking.addDff(NEW_ID, dnode.nextstate, dnode.ffoutwire, State::S0); } else { SigSpec nextstate = module->ReduceOr(NEW_ID, dnode.nextstate); clocking.addDff(NEW_ID, nextstate, dnode.ffoutwire, State::S0); } } if (accept_p) { if (GetSize(accept_sig) == 0) final_accept_sig = State::S0; else if (GetSize(accept_sig) == 1) final_accept_sig = accept_sig; else final_accept_sig = module->ReduceOr(NEW_ID, accept_sig); *accept_p = final_accept_sig; } if (reject_p) { if (GetSize(reject_sig) == 0) final_reject_sig = State::S0; else if (GetSize(reject_sig) == 1) final_reject_sig = reject_sig; else final_reject_sig = module->ReduceOr(NEW_ID, reject_sig); *reject_p = final_reject_sig; } } SigBit getFirstAccept() { SigBit accept; getFirstAcceptReject(&accept, nullptr); return accept; } SigBit getReject() { SigBit reject; getFirstAcceptReject(nullptr, &reject); return reject; } void getDFsm(SvaFsm &output_fsm, int output_start_node, int output_accept_node, int output_reject_node = -1, bool firstmatch = true, bool condaccept = false) { log_assert(!materialized); materialized = true; // Create unlinked NFSM unodes.resize(GetSize(nodes)); for (int node = 0; node < GetSize(nodes); node++) node_to_unode(node, node, SigSpec()); mark_reachable_unode(startNode); // Create DFSM create_dnode(vector{startNode}, firstmatch, condaccept); dnodes.sort(); // Create DFSM Graph for (auto &it : dnodes) { SvaDFsmNode &dnode = it.second; dnode.outnode = output_fsm.createNode(); if (it.first == vector{startNode}) output_fsm.createLink(output_start_node, dnode.outnode); if (output_accept_node >= 0) { vector accept_cond = dnode.accept; optimize_cond(accept_cond); for (auto &value : accept_cond) output_fsm.createLink(it.second.outnode, output_accept_node, make_cond_eq(dnode.ctrl, value)); } if (output_reject_node >= 0) { vector reject_cond = dnode.reject; optimize_cond(reject_cond); for (auto &value : reject_cond) output_fsm.createLink(it.second.outnode, output_reject_node, make_cond_eq(dnode.ctrl, value)); } } for (auto &it : dnodes) { SvaDFsmNode &dnode = it.second; dict, vector> edge_cond; for (auto &edge : dnode.edges) edge_cond[edge.first].push_back(edge.second); for (auto &it : edge_cond) { optimize_cond(it.second); for (auto &value : it.second) output_fsm.createEdge(dnode.outnode, dnodes.at(it.first).outnode, make_cond_eq(dnode.ctrl, value)); } } } // ---------------------------------------------------- // State dump for verbose log messages void dump_nodes() { if (nodes.empty()) return; log(" non-deterministic encoding:\n"); for (int i = 0; i < GetSize(nodes); i++) { log(" node %d:%s\n", i, i == startNode ? " [start]" : i == acceptNode ? " [accept]" : i == condNode ? " [cond]" : ""); for (auto &it : nodes[i].edges) { if (it.second != State::S1) log(" edge %s -> %d\n", log_signal(it.second), it.first); else log(" edge -> %d\n", it.first); } for (auto &it : nodes[i].links) { if (it.second != State::S1) log(" link %s -> %d\n", log_signal(it.second), it.first); else log(" link -> %d\n", it.first); } } } void dump_unodes() { if (unodes.empty()) return; log(" unlinked non-deterministic encoding:\n"); for (int i = 0; i < GetSize(unodes); i++) { if (!unodes[i].reachable) continue; log(" unode %d:%s\n", i, i == startNode ? " [start]" : ""); for (auto &it : unodes[i].edges) { if (!it.second.empty()) log(" edge %s -> %d\n", log_signal(it.second), it.first); else log(" edge -> %d\n", it.first); } for (auto &ctrl : unodes[i].accept) { if (!ctrl.empty()) log(" accept %s\n", log_signal(ctrl)); else log(" accept\n"); } for (auto &ctrl : unodes[i].cond) { if (!ctrl.empty()) log(" cond %s\n", log_signal(ctrl)); else log(" cond\n"); } } } void dump_dnodes() { if (dnodes.empty()) return; log(" deterministic encoding:\n"); for (auto &it : dnodes) { log(" dnode {"); for (int i = 0; i < GetSize(it.first); i++) log("%s%d", i ? "," : "", it.first[i]); log("}:%s\n", GetSize(it.first) == 1 && it.first[0] == startNode ? " [start]" : ""); log(" ctrl %s\n", log_signal(it.second.ctrl)); for (auto &edge : it.second.edges) { log(" edge %s -> {", log_signal(edge.second)); for (int i = 0; i < GetSize(edge.first); i++) log("%s%d", i ? "," : "", edge.first[i]); log("}\n"); } for (auto &value : it.second.accept) log(" accept %s\n", log_signal(value)); for (auto &value : it.second.reject) log(" reject %s\n", log_signal(value)); } } void dump() { if (!nodes.empty()) log(" number of NFSM states: %d\n", GetSize(nodes)); if (!unodes.empty()) { int count = 0; for (auto &unode : unodes) if (unode.reachable) count++; log(" number of reachable UFSM states: %d\n", count); } if (!dnodes.empty()) log(" number of DFSM states: %d\n", GetSize(dnodes)); if (verific_verbose >= 2) { dump_nodes(); dump_unodes(); dump_dnodes(); } if (trigger_sig != State::S1) log(" trigger signal: %s\n", log_signal(trigger_sig)); if (final_accept_sig != State::Sx) log(" accept signal: %s\n", log_signal(final_accept_sig)); if (final_reject_sig != State::Sx) log(" reject signal: %s\n", log_signal(final_reject_sig)); } }; PRIVATE_NAMESPACE_END YOSYS_NAMESPACE_BEGIN pool verific_sva_prims = { // Copy&paste from Verific 3.16_484_32_170630 Netlist.h PRIM_SVA_IMMEDIATE_ASSERT, PRIM_SVA_ASSERT, PRIM_SVA_COVER, PRIM_SVA_ASSUME, PRIM_SVA_EXPECT, PRIM_SVA_POSEDGE, PRIM_SVA_NOT, PRIM_SVA_FIRST_MATCH, PRIM_SVA_ENDED, PRIM_SVA_MATCHED, PRIM_SVA_CONSECUTIVE_REPEAT, PRIM_SVA_NON_CONSECUTIVE_REPEAT, PRIM_SVA_GOTO_REPEAT, PRIM_SVA_MATCH_ITEM_TRIGGER, PRIM_SVA_AND, PRIM_SVA_OR, PRIM_SVA_SEQ_AND, PRIM_SVA_SEQ_OR, PRIM_SVA_EVENT_OR, PRIM_SVA_OVERLAPPED_IMPLICATION, PRIM_SVA_NON_OVERLAPPED_IMPLICATION, PRIM_SVA_OVERLAPPED_FOLLOWED_BY, PRIM_SVA_NON_OVERLAPPED_FOLLOWED_BY, PRIM_SVA_INTERSECT, PRIM_SVA_THROUGHOUT, PRIM_SVA_WITHIN, PRIM_SVA_AT, PRIM_SVA_DISABLE_IFF, PRIM_SVA_SAMPLED, PRIM_SVA_ROSE, PRIM_SVA_FELL, PRIM_SVA_STABLE, PRIM_SVA_PAST, PRIM_SVA_MATCH_ITEM_ASSIGN, PRIM_SVA_SEQ_CONCAT, PRIM_SVA_IF, PRIM_SVA_RESTRICT, PRIM_SVA_TRIGGERED, PRIM_SVA_STRONG, PRIM_SVA_WEAK, PRIM_SVA_NEXTTIME, PRIM_SVA_S_NEXTTIME, PRIM_SVA_ALWAYS, PRIM_SVA_S_ALWAYS, PRIM_SVA_S_EVENTUALLY, PRIM_SVA_EVENTUALLY, PRIM_SVA_UNTIL, PRIM_SVA_S_UNTIL, PRIM_SVA_UNTIL_WITH, PRIM_SVA_S_UNTIL_WITH, PRIM_SVA_IMPLIES, PRIM_SVA_IFF, PRIM_SVA_ACCEPT_ON, PRIM_SVA_REJECT_ON, PRIM_SVA_SYNC_ACCEPT_ON, PRIM_SVA_SYNC_REJECT_ON, PRIM_SVA_GLOBAL_CLOCKING_DEF, PRIM_SVA_GLOBAL_CLOCKING_REF, PRIM_SVA_IMMEDIATE_ASSUME, PRIM_SVA_IMMEDIATE_COVER, OPER_SVA_SAMPLED, OPER_SVA_STABLE }; struct VerificSvaImporter { VerificImporter *importer = nullptr; Module *module = nullptr; Netlist *netlist = nullptr; Instance *root = nullptr; VerificClocking clocking; bool mode_assert = false; bool mode_assume = false; bool mode_cover = false; bool mode_trigger = false; Instance *net_to_ast_driver(Net *n) { if (n == nullptr) return nullptr; if (n->IsMultipleDriven()) return nullptr; Instance *inst = n->Driver(); if (inst == nullptr) return nullptr; if (!verific_sva_prims.count(inst->Type())) return nullptr; if (inst->Type() == PRIM_SVA_ROSE || inst->Type() == PRIM_SVA_FELL || inst->Type() == PRIM_SVA_STABLE || inst->Type() == OPER_SVA_STABLE || inst->Type() == PRIM_SVA_PAST || inst->Type() == PRIM_SVA_TRIGGERED) return nullptr; return inst; } Instance *get_ast_input(Instance *inst) { return net_to_ast_driver(inst->GetInput()); } Instance *get_ast_input1(Instance *inst) { return net_to_ast_driver(inst->GetInput1()); } Instance *get_ast_input2(Instance *inst) { return net_to_ast_driver(inst->GetInput2()); } Instance *get_ast_input3(Instance *inst) { return net_to_ast_driver(inst->GetInput3()); } Instance *get_ast_control(Instance *inst) { return net_to_ast_driver(inst->GetControl()); } // ---------------------------------------------------------- // SVA Importer struct ParserErrorException { }; [[noreturn]] void parser_error(std::string errmsg) { if (!importer->mode_keep) log_error("%s", errmsg.c_str()); log_warning("%s", errmsg.c_str()); throw ParserErrorException(); } [[noreturn]] void parser_error(std::string errmsg, linefile_type loc) { parser_error(stringf("%s at %s:%d.\n", errmsg.c_str(), LineFile::GetFileName(loc), LineFile::GetLineNo(loc))); } [[noreturn]] void parser_error(std::string errmsg, Instance *inst) { parser_error(stringf("%s at %s (%s)", errmsg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); } [[noreturn]] void parser_error(Instance *inst) { std::string msg; if (inst->Type() == PRIM_SVA_MATCH_ITEM_TRIGGER || inst->Type() == PRIM_SVA_MATCH_ITEM_ASSIGN) { msg = "SVA sequences with local variable assignments are currently not supported.\n"; } parser_error(stringf("%sVerific SVA primitive %s (%s) is currently unsupported in this context", msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); } dict check_expression_cache; bool check_expression(Net *net, bool raise_error = false) { while (!check_expression_cache.count(net)) { Instance *inst = net_to_ast_driver(net); if (inst == nullptr) { check_expression_cache[net] = true; break; } if (inst->Type() == PRIM_SVA_AT) { VerificClocking new_clocking(importer, net); log_assert(new_clocking.cond_net == nullptr); if (!clocking.property_matches_sequence(new_clocking)) parser_error("Mixed clocking is currently not supported", inst); check_expression_cache[net] = check_expression(new_clocking.body_net, raise_error); break; } if (inst->Type() == PRIM_SVA_FIRST_MATCH || inst->Type() == PRIM_SVA_NOT) { check_expression_cache[net] = check_expression(inst->GetInput(), raise_error); break; } if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_INTERSECT || inst->Type() == PRIM_SVA_WITHIN || inst->Type() == PRIM_SVA_THROUGHOUT || inst->Type() == PRIM_SVA_OR || inst->Type() == PRIM_SVA_AND) { check_expression_cache[net] = check_expression(inst->GetInput1(), raise_error) && check_expression(inst->GetInput2(), raise_error); break; } if (inst->Type() == PRIM_SVA_SEQ_CONCAT) { const char *sva_low_s = inst->GetAttValue("sva:low"); const char *sva_high_s = inst->GetAttValue("sva:high"); int sva_low = atoi(sva_low_s); int sva_high = atoi(sva_high_s); bool sva_inf = !strcmp(sva_high_s, "$"); if (sva_low == 0 && sva_high == 0 && !sva_inf) check_expression_cache[net] = check_expression(inst->GetInput1(), raise_error) && check_expression(inst->GetInput2(), raise_error); else check_expression_cache[net] = false; break; } check_expression_cache[net] = false; } if (raise_error && !check_expression_cache.at(net)) parser_error(net_to_ast_driver(net)); return check_expression_cache.at(net); } SigBit parse_expression(Net *net) { check_expression(net, true); Instance *inst = net_to_ast_driver(net); if (inst == nullptr) { return importer->net_map_at(net); } if (inst->Type() == PRIM_SVA_AT) { VerificClocking new_clocking(importer, net); log_assert(new_clocking.cond_net == nullptr); if (!clocking.property_matches_sequence(new_clocking)) parser_error("Mixed clocking is currently not supported", inst); return parse_expression(new_clocking.body_net); } if (inst->Type() == PRIM_SVA_FIRST_MATCH) return parse_expression(inst->GetInput()); if (inst->Type() == PRIM_SVA_NOT) return module->Not(NEW_ID, parse_expression(inst->GetInput())); if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_OR) return module->Or(NEW_ID, parse_expression(inst->GetInput1()), parse_expression(inst->GetInput2())); if (inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_AND || inst->Type() == PRIM_SVA_INTERSECT || inst->Type() == PRIM_SVA_WITHIN || inst->Type() == PRIM_SVA_THROUGHOUT || inst->Type() == PRIM_SVA_SEQ_CONCAT) return module->And(NEW_ID, parse_expression(inst->GetInput1()), parse_expression(inst->GetInput2())); log_abort(); } bool check_zero_consecutive_repeat(Net *net) { Instance *inst = net_to_ast_driver(net); if (inst == nullptr) return false; if (inst->Type() != PRIM_SVA_CONSECUTIVE_REPEAT) return false; const char *sva_low_s = inst->GetAttValue("sva:low"); int sva_low = atoi(sva_low_s); return sva_low == 0; } int parse_consecutive_repeat(SvaFsm &fsm, int start_node, Net *net, bool add_pre_delay, bool add_post_delay) { Instance *inst = net_to_ast_driver(net); log_assert(inst->Type() == PRIM_SVA_CONSECUTIVE_REPEAT); const char *sva_low_s = inst->GetAttValue("sva:low"); const char *sva_high_s = inst->GetAttValue("sva:high"); int sva_low = atoi(sva_low_s); int sva_high = atoi(sva_high_s); bool sva_inf = !strcmp(sva_high_s, "$"); Net *body_net = inst->GetInput(); if (add_pre_delay || add_post_delay) log_assert(sva_low == 0); if (sva_low == 0) { if (!add_pre_delay && !add_post_delay) parser_error("Possibly zero-length consecutive repeat must follow or precede a delay of at least one cycle", inst); sva_low++; } int node = fsm.createNode(start_node); start_node = node; if (add_pre_delay) { node = fsm.createNode(start_node); fsm.createEdge(start_node, node); } int prev_node = node; node = parse_sequence(fsm, node, body_net); for (int i = 1; i < sva_low; i++) { int next_node = fsm.createNode(); fsm.createEdge(node, next_node); prev_node = node; node = parse_sequence(fsm, next_node, body_net); } if (sva_inf) { log_assert(prev_node >= 0); fsm.createEdge(node, prev_node); } else { for (int i = sva_low; i < sva_high; i++) { int next_node = fsm.createNode(); fsm.createEdge(node, next_node); prev_node = node; node = parse_sequence(fsm, next_node, body_net); fsm.createLink(prev_node, node); } } if (add_post_delay) { int next_node = fsm.createNode(); fsm.createEdge(node, next_node); node = next_node; } if (add_pre_delay || add_post_delay) fsm.createLink(start_node, node); return node; } int parse_sequence(SvaFsm &fsm, int start_node, Net *net) { if (check_expression(net)) { int node = fsm.createNode(); fsm.createLink(start_node, node, parse_expression(net)); return node; } Instance *inst = net_to_ast_driver(net); if (inst->Type() == PRIM_SVA_AT) { VerificClocking new_clocking(importer, net); log_assert(new_clocking.cond_net == nullptr); if (!clocking.property_matches_sequence(new_clocking)) parser_error("Mixed clocking is currently not supported", inst); return parse_sequence(fsm, start_node, new_clocking.body_net); } if (inst->Type() == PRIM_SVA_FIRST_MATCH) { SvaFsm match_fsm(clocking); match_fsm.createLink(parse_sequence(match_fsm, match_fsm.createStartNode(), inst->GetInput()), match_fsm.acceptNode); int node = fsm.createNode(); match_fsm.getDFsm(fsm, start_node, node); if (verific_verbose) { log(" First Match FSM:\n"); match_fsm.dump(); } return node; } if (inst->Type() == PRIM_SVA_NEXTTIME || inst->Type() == PRIM_SVA_S_NEXTTIME) { const char *sva_low_s = inst->GetAttValue("sva:low"); const char *sva_high_s = inst->GetAttValue("sva:high"); int sva_low = atoi(sva_low_s); int sva_high = atoi(sva_high_s); log_assert(sva_low == sva_high); int node = start_node; for (int i = 0; i < sva_low; i++) { int next_node = fsm.createNode(); fsm.createEdge(node, next_node); node = next_node; } return parse_sequence(fsm, node, inst->GetInput()); } if (inst->Type() == PRIM_SVA_SEQ_CONCAT) { const char *sva_low_s = inst->GetAttValue("sva:low"); const char *sva_high_s = inst->GetAttValue("sva:high"); int sva_low = atoi(sva_low_s); int sva_high = atoi(sva_high_s); bool sva_inf = !strcmp(sva_high_s, "$"); int node = -1; bool past_add_delay = false; if (check_zero_consecutive_repeat(inst->GetInput1()) && sva_low > 0) { node = parse_consecutive_repeat(fsm, start_node, inst->GetInput1(), false, true); sva_low--, sva_high--; } else { node = parse_sequence(fsm, start_node, inst->GetInput1()); } if (check_zero_consecutive_repeat(inst->GetInput2()) && sva_low > 0) { past_add_delay = true; sva_low--, sva_high--; } for (int i = 0; i < sva_low; i++) { int next_node = fsm.createNode(); fsm.createEdge(node, next_node); node = next_node; } if (sva_inf) { fsm.createEdge(node, node); } else { for (int i = sva_low; i < sva_high; i++) { int next_node = fsm.createNode(); fsm.createEdge(node, next_node); fsm.createLink(node, next_node); node = next_node; } } if (past_add_delay) node = parse_consecutive_repeat(fsm, node, inst->GetInput2(), true, false); else node = parse_sequence(fsm, node, inst->GetInput2()); return node; } if (inst->Type() == PRIM_SVA_CONSECUTIVE_REPEAT) { return parse_consecutive_repeat(fsm, start_node, net, false, false); } if (inst->Type() == PRIM_SVA_NON_CONSECUTIVE_REPEAT || inst->Type() == PRIM_SVA_GOTO_REPEAT) { const char *sva_low_s = inst->GetAttValue("sva:low"); const char *sva_high_s = inst->GetAttValue("sva:high"); int sva_low = atoi(sva_low_s); int sva_high = atoi(sva_high_s); bool sva_inf = !strcmp(sva_high_s, "$"); Net *body_net = inst->GetInput(); int node = fsm.createNode(start_node); SigBit cond = parse_expression(body_net); SigBit not_cond = module->Not(NEW_ID, cond); for (int i = 0; i < sva_low; i++) { int wait_node = fsm.createNode(); fsm.createEdge(wait_node, wait_node, not_cond); if (i == 0) fsm.createLink(node, wait_node); else fsm.createEdge(node, wait_node); int next_node = fsm.createNode(); fsm.createLink(wait_node, next_node, cond); node = next_node; } if (sva_inf) { int wait_node = fsm.createNode(); fsm.createEdge(wait_node, wait_node, not_cond); fsm.createEdge(node, wait_node); fsm.createLink(wait_node, node, cond); } else { for (int i = sva_low; i < sva_high; i++) { int wait_node = fsm.createNode(); fsm.createEdge(wait_node, wait_node, not_cond); if (i == 0) fsm.createLink(node, wait_node); else fsm.createEdge(node, wait_node); int next_node = fsm.createNode(); fsm.createLink(wait_node, next_node, cond); fsm.createLink(node, next_node); node = next_node; } } if (inst->Type() == PRIM_SVA_NON_CONSECUTIVE_REPEAT) fsm.createEdge(node, node); return node; } if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_OR) { int node = parse_sequence(fsm, start_node, inst->GetInput1()); int node2 = parse_sequence(fsm, start_node, inst->GetInput2()); fsm.createLink(node2, node); return node; } if (inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_AND) { SvaFsm fsm1(clocking); fsm1.createLink(parse_sequence(fsm1, fsm1.createStartNode(), inst->GetInput1()), fsm1.acceptNode); SvaFsm fsm2(clocking); fsm2.createLink(parse_sequence(fsm2, fsm2.createStartNode(), inst->GetInput2()), fsm2.acceptNode); SvaFsm combined_fsm(clocking); fsm1.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode); fsm2.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode); int node = fsm.createNode(); combined_fsm.getDFsm(fsm, start_node, -1, node); if (verific_verbose) { log(" Left And FSM:\n"); fsm1.dump(); log(" Right And FSM:\n"); fsm1.dump(); log(" Combined And FSM:\n"); combined_fsm.dump(); } return node; } if (inst->Type() == PRIM_SVA_INTERSECT || inst->Type() == PRIM_SVA_WITHIN) { SvaFsm intersect_fsm(clocking); if (inst->Type() == PRIM_SVA_INTERSECT) { intersect_fsm.createLink(parse_sequence(intersect_fsm, intersect_fsm.createStartNode(), inst->GetInput1()), intersect_fsm.acceptNode); } else { int n = intersect_fsm.createNode(); intersect_fsm.createLink(intersect_fsm.createStartNode(), n); intersect_fsm.createEdge(n, n); n = parse_sequence(intersect_fsm, n, inst->GetInput1()); intersect_fsm.createLink(n, intersect_fsm.acceptNode); intersect_fsm.createEdge(n, n); } intersect_fsm.in_cond_mode = true; intersect_fsm.createLink(parse_sequence(intersect_fsm, intersect_fsm.createStartNode(), inst->GetInput2()), intersect_fsm.condNode); intersect_fsm.in_cond_mode = false; int node = fsm.createNode(); intersect_fsm.getDFsm(fsm, start_node, node, -1, false, true); if (verific_verbose) { log(" Intersect FSM:\n"); intersect_fsm.dump(); } return node; } if (inst->Type() == PRIM_SVA_THROUGHOUT) { SigBit expr = parse_expression(inst->GetInput1()); fsm.pushThroughout(expr); int node = parse_sequence(fsm, start_node, inst->GetInput2()); fsm.popThroughout(); return node; } parser_error(inst); } void get_fsm_accept_reject(SvaFsm &fsm, SigBit *accept_p, SigBit *reject_p, bool swap_accept_reject = false) { log_assert(accept_p != nullptr || reject_p != nullptr); if (swap_accept_reject) get_fsm_accept_reject(fsm, reject_p, accept_p); else if (reject_p == nullptr) *accept_p = fsm.getAccept(); else if (accept_p == nullptr) *reject_p = fsm.getReject(); else fsm.getFirstAcceptReject(accept_p, reject_p); } bool eventually_property(Net *&net, SigBit &trig) { Instance *inst = net_to_ast_driver(net); if (inst == nullptr) return false; if (clocking.cond_net != nullptr) { trig = importer->net_map_at(clocking.cond_net); 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) { if (mode_cover || mode_trigger) parser_error(inst); net = inst->GetInput(); clocking.cond_net = nullptr; return true; } if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION || inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) { Net *antecedent_net = inst->GetInput1(); Net *consequent_net = inst->GetInput2(); Instance *consequent_inst = net_to_ast_driver(consequent_net); if (consequent_inst == nullptr) return false; if (consequent_inst->Type() != PRIM_SVA_S_EVENTUALLY && consequent_inst->Type() != PRIM_SVA_EVENTUALLY) return false; if (mode_cover || mode_trigger) parser_error(consequent_inst); int node; SvaFsm antecedent_fsm(clocking, trig); node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net); if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) { int next_node = antecedent_fsm.createNode(); antecedent_fsm.createEdge(node, next_node); node = next_node; } antecedent_fsm.createLink(node, antecedent_fsm.acceptNode); trig = antecedent_fsm.getAccept(); net = consequent_inst->GetInput(); clocking.cond_net = nullptr; if (verific_verbose) { log(" Eventually Antecedent FSM:\n"); antecedent_fsm.dump(); } return true; } return false; } void parse_property(Net *net, SigBit *accept_p, SigBit *reject_p) { Instance *inst = net_to_ast_driver(net); SigBit trig = State::S1; 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) { 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 || inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) { Net *antecedent_net = inst->GetInput1(); Net *consequent_net = inst->GetInput2(); int node; SvaFsm antecedent_fsm(clocking, trig); node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net); if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) { int next_node = antecedent_fsm.createNode(); antecedent_fsm.createEdge(node, next_node); node = next_node; } Instance *consequent_inst = net_to_ast_driver(consequent_net); if (consequent_inst && (consequent_inst->Type() == PRIM_SVA_UNTIL || consequent_inst->Type() == PRIM_SVA_S_UNTIL || consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_ALWAYS || consequent_inst->Type() == PRIM_SVA_S_ALWAYS)) { bool until_with = consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH; Net *until_net = nullptr; if (consequent_inst->Type() == PRIM_SVA_ALWAYS || consequent_inst->Type() == PRIM_SVA_S_ALWAYS) { consequent_net = consequent_inst->GetInput(); consequent_inst = net_to_ast_driver(consequent_net); } else { until_net = consequent_inst->GetInput2(); consequent_net = consequent_inst->GetInput1(); consequent_inst = net_to_ast_driver(consequent_net); } SigBit until_sig = until_net ? parse_expression(until_net) : RTLIL::S0; SigBit not_until_sig = module->Not(NEW_ID, until_sig); antecedent_fsm.createEdge(node, node, not_until_sig); antecedent_fsm.createLink(node, antecedent_fsm.acceptNode, until_with ? State::S1 : not_until_sig); } else { antecedent_fsm.createLink(node, antecedent_fsm.acceptNode); } SigBit antecedent_match = antecedent_fsm.getAccept(); if (verific_verbose) { log(" Antecedent FSM:\n"); antecedent_fsm.dump(); } bool consequent_not = false; if (consequent_inst && consequent_inst->Type() == PRIM_SVA_NOT) { consequent_not = true; consequent_net = consequent_inst->GetInput(); consequent_inst = net_to_ast_driver(consequent_net); } SvaFsm consequent_fsm(clocking, antecedent_match); node = parse_sequence(consequent_fsm, consequent_fsm.createStartNode(), consequent_net); consequent_fsm.createLink(node, consequent_fsm.acceptNode); get_fsm_accept_reject(consequent_fsm, accept_p, reject_p, consequent_not); if (verific_verbose) { log(" Consequent FSM:\n"); consequent_fsm.dump(); } } else { bool prop_not = inst->Type() == PRIM_SVA_NOT; if (prop_not) { net = inst->GetInput(); inst = net_to_ast_driver(net); } SvaFsm fsm(clocking, trig); int node = parse_sequence(fsm, fsm.createStartNode(), net); fsm.createLink(node, fsm.acceptNode); get_fsm_accept_reject(fsm, accept_p, reject_p, prop_not); if (verific_verbose) { log(" Sequence FSM:\n"); fsm.dump(); } } } void import() { try { module = importer->module; netlist = root->Owner(); if (verific_verbose) log(" importing SVA property at root cell %s (%s) at %s:%d.\n", root->Name(), root->View()->Owner()->Name(), LineFile::GetFileName(root->Linefile()), LineFile::GetLineNo(root->Linefile())); bool is_user_declared = root->IsUserDeclared(); // FIXME if (!is_user_declared) { const char *name = root->Name(); for (int i = 0; name[i]; i++) { if (i ? (name[i] < '0' || name[i] > '9') : (name[i] != 'i')) { is_user_declared = true; break; } } } RTLIL::IdString root_name = module->uniquify(importer->mode_names || is_user_declared ? RTLIL::escape_id(root->Name()) : NEW_ID); // parse SVA sequence into trigger signal clocking = VerificClocking(importer, root->GetInput(), true); SigBit accept_bit = State::S0, reject_bit = State::S0; if (clocking.body_net == nullptr) { if (clocking.clock_net != nullptr || clocking.enable_net != nullptr || clocking.disable_net != nullptr || clocking.cond_net != nullptr) parser_error(stringf("Failed to parse SVA clocking"), root); if (mode_assert || mode_assume) { reject_bit = module->Not(NEW_ID, parse_expression(root->GetInput())); } else { accept_bit = parse_expression(root->GetInput()); } } else { Net *net = clocking.body_net; SigBit trig; if (eventually_property(net, trig)) { SigBit sig_a, sig_en = trig; parse_property(net, &sig_a, nullptr); // add final FF stage SigBit sig_a_q, sig_en_q; if (clocking.body_net == nullptr) { sig_a_q = sig_a; sig_en_q = sig_en; } else { sig_a_q = module->addWire(NEW_ID); sig_en_q = module->addWire(NEW_ID); clocking.addDff(NEW_ID, sig_a, sig_a_q, State::S0); 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; 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); if (c) importer->import_attributes(c->attributes, root); return; } else { if (mode_assert || mode_assume) { parse_property(net, nullptr, &reject_bit); } else { parse_property(net, &accept_bit, nullptr); } } } if (mode_trigger) { module->connect(importer->net_map_at(root->GetOutput()), accept_bit); } else { SigBit sig_a = module->Not(NEW_ID, reject_bit); SigBit sig_en = module->Or(NEW_ID, accept_bit, reject_bit); // add final FF stage SigBit sig_a_q, sig_en_q; if (clocking.body_net == nullptr) { sig_a_q = sig_a; sig_en_q = sig_en; } else { sig_a_q = module->addWire(NEW_ID); sig_en_q = module->addWire(NEW_ID); clocking.addDff(NEW_ID, sig_a, sig_a_q, State::S0); clocking.addDff(NEW_ID, sig_en, sig_en_q, State::S0); } // generate assert/assume/cover cell RTLIL::Cell *c = nullptr; if (mode_assert) c = module->addAssert(root_name, sig_a_q, sig_en_q); 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); if (c) importer->import_attributes(c->attributes, root); } } catch (ParserErrorException) { } } }; void verific_import_sva_assert(VerificImporter *importer, Instance *inst) { VerificSvaImporter worker; worker.importer = importer; worker.root = inst; worker.mode_assert = true; worker.import(); } void verific_import_sva_assume(VerificImporter *importer, Instance *inst) { VerificSvaImporter worker; worker.importer = importer; worker.root = inst; worker.mode_assume = true; worker.import(); } void verific_import_sva_cover(VerificImporter *importer, Instance *inst) { VerificSvaImporter worker; worker.importer = importer; worker.root = inst; worker.mode_cover = true; worker.import(); } void verific_import_sva_trigger(VerificImporter *importer, Instance *inst) { VerificSvaImporter worker; worker.importer = importer; worker.root = inst; worker.mode_trigger = true; worker.import(); } bool verific_is_sva_net(VerificImporter *importer, Verific::Net *net) { VerificSvaImporter worker; worker.importer = importer; return worker.net_to_ast_driver(net) != nullptr; } #else YOSYS_NAMESPACE_BEGIN pool verific_sva_prims = {}; #endif YOSYS_NAMESPACE_END yosys-0.52/frontends/verilog/000077500000000000000000000000001477540374200163145ustar00rootroot00000000000000yosys-0.52/frontends/verilog/.gitignore000066400000000000000000000001231477540374200203000ustar00rootroot00000000000000verilog_lexer.cc verilog_parser.output verilog_parser.tab.cc verilog_parser.tab.hh yosys-0.52/frontends/verilog/Makefile.inc000066400000000000000000000016611477540374200205300ustar00rootroot00000000000000 GENFILES += frontends/verilog/verilog_parser.tab.cc GENFILES += frontends/verilog/verilog_parser.tab.hh GENFILES += frontends/verilog/verilog_parser.output GENFILES += frontends/verilog/verilog_lexer.cc frontends/verilog/verilog_parser.tab.cc: frontends/verilog/verilog_parser.y $(Q) mkdir -p $(dir $@) $(P) $(BISON) -Wall -Werror -o $@ -d -r all -b frontends/verilog/verilog_parser $< frontends/verilog/verilog_parser.tab.hh: frontends/verilog/verilog_parser.tab.cc frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l frontends/verilog/verilog_parser.tab.cc $(Q) mkdir -p $(dir $@) $(P) flex -o frontends/verilog/verilog_lexer.cc $< frontends/verilog/verilog_parser.tab.o: CXXFLAGS += -DYYMAXDEPTH=10000000 OBJS += frontends/verilog/verilog_parser.tab.o OBJS += frontends/verilog/verilog_lexer.o OBJS += frontends/verilog/preproc.o OBJS += frontends/verilog/verilog_frontend.o OBJS += frontends/verilog/const2ast.o yosys-0.52/frontends/verilog/const2ast.cc000066400000000000000000000165151477540374200205530ustar00rootroot00000000000000/* * 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. * * --- * * The Verilog frontend. * * This frontend is using the AST frontend library (see frontends/ast/). * Thus this frontend does not generate RTLIL code directly but creates an * AST directly from the Verilog parse tree and then passes this AST to * the AST frontend library. * * --- * * This file contains an ad-hoc parser for Verilog constants. The Verilog * lexer does only recognize a constant but does not actually split it to its * components. I.e. it just passes the Verilog code for the constant to the * bison parser. The parser then uses the function const2ast() from this file * to create an AST node for the constant. * */ #include "verilog_frontend.h" #include "kernel/log.h" #include #include YOSYS_NAMESPACE_BEGIN using namespace AST; // divide an arbitrary length decimal number by two and return the rest static int my_decimal_div_by_two(std::vector &digits) { int carry = 0; for (size_t i = 0; i < digits.size(); i++) { if (digits[i] >= 10) log_file_error(current_filename, get_line_num(), "Invalid use of [a-fxz?] in decimal constant.\n"); digits[i] += carry * 10; carry = digits[i] % 2; digits[i] /= 2; } while (!digits.empty() && !digits.front()) digits.erase(digits.begin()); return carry; } // find the number of significant bits in a binary number (not including the sign bit) static int my_ilog2(int x) { int ret = 0; while (x != 0 && x != -1) { x = x >> 1; ret++; } return ret; } // parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?') static void my_strtobin(std::vector &data, const char *str, int len_in_bits, int base, char case_type, bool is_unsized) { // all digits in string (MSB at index 0) std::vector digits; while (*str) { if ('0' <= *str && *str <= '9') digits.push_back(*str - '0'); else if ('a' <= *str && *str <= 'f') digits.push_back(10 + *str - 'a'); else if ('A' <= *str && *str <= 'F') digits.push_back(10 + *str - 'A'); else if (*str == 'x' || *str == 'X') digits.push_back(0xf0); else if (*str == 'z' || *str == 'Z' || *str == '?') digits.push_back(0xf1); str++; } if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0) base = 2; data.clear(); if (base == 10) { while (!digits.empty()) data.push_back(my_decimal_div_by_two(digits) ? State::S1 : State::S0); } else { int bits_per_digit = my_ilog2(base-1); for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) { if (*it > (base-1) && *it < 0xf0) log_file_error(current_filename, get_line_num(), "Digit larger than %d used in in base-%d constant.\n", base-1, base); for (int i = 0; i < bits_per_digit; i++) { int bitmask = 1 << i; if (*it == 0xf0) data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx); else if (*it == 0xf1) data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz); else data.push_back((*it & bitmask) ? State::S1 : State::S0); } } } int len = GetSize(data); RTLIL::State msb = data.empty() ? State::S0 : data.back(); if (len_in_bits < 0) { if (len < 32) data.resize(32, msb == State::S0 || msb == State::S1 ? RTLIL::S0 : msb); return; } if (is_unsized && (len > len_in_bits)) log_file_error(current_filename, get_line_num(), "Unsized constant must have width of 1 bit, but have %d bits!\n", len); for (len = len - 1; len >= 0; len--) if (data[len] == State::S1) break; if (msb == State::S0 || msb == State::S1) { len += 1; data.resize(len_in_bits, State::S0); } else { len += 2; data.resize(len_in_bits, msb); } if (len_in_bits == 0) log_file_error(current_filename, get_line_num(), "Illegal integer constant size of zero (IEEE 1800-2012, 5.7).\n"); if (len > len_in_bits) log_warning("Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n", len_in_bits, len, current_filename.c_str(), get_line_num()); } // convert the Verilog code for a constant to an AST node AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z) { if (warn_z) { AstNode *ret = const2ast(code, case_type); if (ret != nullptr && std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end()) log_warning("Yosys has only limited support for tri-state logic at the moment. (%s:%d)\n", current_filename.c_str(), get_line_num()); return ret; } const char *str = code.c_str(); // Strings if (*str == '"') { int len = strlen(str) - 2; std::vector data; data.reserve(len * 8); for (int i = 0; i < len; i++) { unsigned char ch = str[len - i]; for (int j = 0; j < 8; j++) { data.push_back((ch & 1) ? State::S1 : State::S0); ch = ch >> 1; } } AstNode *ast = AstNode::mkconst_bits(data, false); ast->str = code; return ast; } for (size_t i = 0; i < code.size(); i++) if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n') code.erase(code.begin()+(i--)); str = code.c_str(); char *endptr; long len_in_bits = strtol(str, &endptr, 10); // Simple base-10 integer if (*endptr == 0) { std::vector data; my_strtobin(data, str, -1, 10, case_type, false); if (data.back() == State::S1) data.push_back(State::S0); return AstNode::mkconst_bits(data, true); } // unsized constant if (str == endptr) len_in_bits = -1; // The "'[sS]?[bodhBODH]" syntax if (*endptr == '\'') { std::vector data; bool is_signed = false; bool is_unsized = len_in_bits < 0; if (*(endptr+1) == 's' || *(endptr+1) == 'S') { is_signed = true; endptr++; } switch (*(endptr+1)) { case 'b': case 'B': my_strtobin(data, endptr+2, len_in_bits, 2, case_type, is_unsized); break; case 'o': case 'O': my_strtobin(data, endptr+2, len_in_bits, 8, case_type, is_unsized); break; case 'd': case 'D': my_strtobin(data, endptr+2, len_in_bits, 10, case_type, is_unsized); break; case 'h': case 'H': my_strtobin(data, endptr+2, len_in_bits, 16, case_type, is_unsized); break; default: char next_char = char(tolower(*(endptr+1))); if (next_char == '0' || next_char == '1' || next_char == 'x' || next_char == 'z') { is_unsized = true; my_strtobin(data, endptr+1, 1, 2, case_type, is_unsized); } else { return NULL; } } if (len_in_bits < 0) { if (is_signed && data.back() == State::S1) data.push_back(State::S0); } return AstNode::mkconst_bits(data, is_signed, is_unsized); } return NULL; } YOSYS_NAMESPACE_END yosys-0.52/frontends/verilog/preproc.cc000066400000000000000000000622251477540374200203040ustar00rootroot00000000000000/* * 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. * * --- * * The Verilog frontend. * * This frontend is using the AST frontend library (see frontends/ast/). * Thus this frontend does not generate RTLIL code directly but creates an * AST directly from the Verilog parse tree and then passes this AST to * the AST frontend library. * * --- * * Ad-hoc implementation of a Verilog preprocessor. The directives `define, * `include, `ifdef, `ifndef, `else and `endif are handled here. All other * directives are handled by the lexer (see verilog_lexer.l). * */ #include "preproc.h" #include "verilog_frontend.h" #include "kernel/log.h" #include #include #include #include #include YOSYS_NAMESPACE_BEGIN using namespace VERILOG_FRONTEND; static std::list output_code; static std::list input_buffer; static size_t input_buffer_charp; static void return_char(char ch) { if (input_buffer_charp == 0) input_buffer.push_front(std::string() + ch); else input_buffer.front()[--input_buffer_charp] = ch; } static void insert_input(std::string str) { if (input_buffer_charp != 0) { input_buffer.front() = input_buffer.front().substr(input_buffer_charp); input_buffer_charp = 0; } input_buffer.push_front(str); } static char next_char() { if (input_buffer.empty()) return 0; log_assert(input_buffer_charp <= input_buffer.front().size()); if (input_buffer_charp == input_buffer.front().size()) { input_buffer_charp = 0; input_buffer.pop_front(); return next_char(); } char ch = input_buffer.front()[input_buffer_charp++]; return ch == '\r' ? next_char() : ch; } static std::string skip_spaces() { std::string spaces; while (1) { char ch = next_char(); if (ch == 0) break; if (ch != ' ' && ch != '\t') { return_char(ch); break; } spaces += ch; } return spaces; } static std::string next_token(bool pass_newline = false) { std::string token; char ch = next_char(); if (ch == 0) return token; token += ch; if (ch == '\n') { if (pass_newline) { output_code.push_back(token); return ""; } return token; } if (ch == ' ' || ch == '\t') { while ((ch = next_char()) != 0) { if (ch != ' ' && ch != '\t') { return_char(ch); break; } token += ch; } } else if (ch == '"') { while ((ch = next_char()) != 0) { token += ch; if (ch == '"') break; if (ch == '\\') { if ((ch = next_char()) != 0) token += ch; } } if (token == "\"\"" && (ch = next_char()) != 0) { if (ch == '"') token += ch; else 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) { if (ch == '/') { token += '*'; char last_ch = 0; while ((ch = next_char()) != 0) { if (ch == '\n') { return_char(ch); break; } if (last_ch != '*' || ch != '/') { token += ch; last_ch = ch; } } token += " */"; } else if (ch == '*') { token += '*'; int newline_count = 0; char last_ch = 0; while ((ch = next_char()) != 0) { if (ch == '\n') { newline_count++; token += ' '; } else token += ch; if (last_ch == '*' && ch == '/') break; last_ch = ch; } while (newline_count-- > 0) return_char('\n'); } else return_char(ch); } } else { const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789"; if (ch == '`' || strchr(ok, ch) != NULL) { char first = ch; ch = next_char(); if (first == '`' && (ch == '"' || ch == '`')) { token += ch; } else do { if (strchr(ok, ch) == NULL) { return_char(ch); break; } token += ch; } while ((ch = next_char()) != 0); } } return token; } struct macro_arg_t { macro_arg_t(const std::string &name_, const char *default_value_) : name(name_), has_default(default_value_ != nullptr), default_value(default_value_ ? default_value_ : "") {} std::string name; bool has_default; std::string default_value; }; static bool all_white(const std::string &str) { for (char c : str) if (!isspace(c)) return false; return true; } struct arg_map_t { arg_map_t() {} void add_arg(const std::string &name, const char *default_value) { if (find(name)) { log_error("Duplicate macro arguments with name `%s'.\n", name.c_str()); } name_to_pos[name] = args.size(); args.push_back(macro_arg_t(name, default_value)); } // Find an argument by name; return nullptr if it doesn't exist. If pos is not null, write // the argument's position to it on success. const macro_arg_t *find(const std::string &name, int *pos = nullptr) const { auto it = name_to_pos.find(name); if (it == name_to_pos.end()) return nullptr; if (pos) *pos = it->second; return &args[it->second]; } // Construct the name for the local macro definition we use for the given argument // (something like macro_foobar_arg2). This doesn't include the leading backtick. static std::string str_token(const std::string ¯o_name, int pos) { return stringf("macro_%s_arg%d", macro_name.c_str(), pos); } // Return definitions for the macro arguments (so that substituting in the macro body and // then performing macro expansion will do argument substitution properly). std::vector> get_vals(const std::string ¯o_name, const std::vector &arg_vals) const { std::vector> ret; for (int i = 0; i < GetSize(args); ++ i) { // The SystemVerilog rules are: // // - If the call site specifies an argument and it's not whitespace, use // it. // // - Otherwise, if the argument has a default value, use it. // // - Otherwise, if the call site specified whitespace, use that. // // - Otherwise, error. const std::string *dflt = nullptr; if (args[i].has_default) dflt = &args[i].default_value; const std::string *given = nullptr; if (i < GetSize(arg_vals)) given = &arg_vals[i]; const std::string *val = nullptr; if (given && (! (dflt && all_white(*given)))) val = given; else if (dflt) val = dflt; else if (given) val = given; else log_error("Cannot expand macro `%s by giving only %d argument%s " "(argument %d has no default).\n", macro_name.c_str(), GetSize(arg_vals), (GetSize(arg_vals) == 1 ? "" : "s"), i + 1); assert(val); ret.push_back(std::make_pair(str_token(macro_name, i), * val)); } return ret; } std::vector args; std::map name_to_pos; }; struct define_body_t { define_body_t(const std::string &body, const arg_map_t *args = nullptr) : body(body), has_args(args != nullptr), args(args ? *args : arg_map_t()) {} std::string body; bool has_args; arg_map_t args; }; define_map_t::define_map_t() { add("YOSYS", "1"); } // We must define this destructor here (rather than relying on the default), because we need to // define it somewhere we've got a complete definition of define_body_t. define_map_t::~define_map_t() {} void define_map_t::add(const std::string &name, const std::string &txt, const arg_map_t *args) { 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) { // These contortions are so that we take a copy of each definition body in // map.defines. defines[pr.first] = std::unique_ptr(new define_body_t(*pr.second)); } } const define_body_t *define_map_t::find(const std::string &name) const { auto it = defines.find(name); return (it == defines.end()) ? nullptr : it->second.get(); } void define_map_t::erase(const std::string &name) { defines.erase(name); } void define_map_t::clear() { defines.clear(); } void define_map_t::log() const { for (auto &it : defines) { const std::string &name = it.first; const define_body_t &body = *it.second; Yosys::log("`define %s%s %s\n", name.c_str(), body.has_args ? "()" : "", body.body.c_str()); } } static void input_file(std::istream &f, std::string filename) { char buffer[513]; int rc; insert_input(""); auto it = input_buffer.begin(); input_buffer.insert(it, "`file_push \"" + filename + "\"\n"); while ((rc = readsome(f, buffer, sizeof(buffer)-1)) > 0) { buffer[rc] = 0; input_buffer.insert(it, buffer); } input_buffer.insert(it, "\n`file_pop\n"); } // Read tokens to get one argument (either a macro argument at a callsite or a default argument in a // macro definition). Writes the argument to dest. Returns true if we finished with ')' (the end of // the argument list); false if we finished with ','. static bool read_argument(std::string &dest) { skip_spaces(); std::vector openers; for (;;) { std::string tok = next_token(true); if (tok == ")") { 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]); openers.pop_back(); dest += tok; continue; } if (tok == "]") { char opener = openers.empty() ? '(' : openers.back(); if (opener != '[') log_error("Mismatched brackets in macro argument: %c and %c.\n", opener, tok[0]); openers.pop_back(); dest += tok; continue; } if (tok == "}") { char opener = openers.empty() ? '(' : openers.back(); if (opener != '{') log_error("Mismatched brackets in macro argument: %c and %c.\n", opener, tok[0]); openers.pop_back(); dest += tok; continue; } if (tok == "," && openers.empty()) { return false; } if (tok == "(" || tok == "[" || tok == "{") openers.push_back(tok[0]); dest += 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("\""); // Expand string literal while (!input_buffer.empty()) { std::string ntok = next_token(); if (ntok == "`\"") { insert_input(literal+"\""); return true; } else if (!try_expand_macro(defines, macro_arg_stack, ntok)) { literal += ntok; } } return false; // error - unmatched `" } if (tok == "``") { // Swallow `` in macro expansion return true; } if (tok.size() <= 1 || tok[0] != '`') return false; // This token looks like a macro name (`foo). std::string macro_name = tok.substr(1); const define_body_t *body = defines.find(tok.substr(1)); if (! body) { // Apparently not a name we know. return false; } std::string name = tok.substr(1); std::string skipped_spaces = skip_spaces(); tok = next_token(false); 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) { std::string arg; done = read_argument(arg); 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 { insert_input(tok); insert_input(skipped_spaces); } insert_input(body->body); return true; } // Read the arguments for a `define preprocessor directive with formal arguments. This is called // just after reading the token containing "(". Returns the number of newlines to emit afterwards to // keep line numbers in sync, together with the map from argument name to data (pos and default // value). static std::pair read_define_args() { // Each argument looks like one of the following: // // identifier // identifier = default_text // identifier = // // The first example is an argument with no default value. The second is an argument whose // default value is default_text. The third is an argument with default value the empty // string. int newline_count = 0; arg_map_t args; // FSM state. // // 0: At start of identifier // 1: After identifier (stored in arg_name) // 2: After closing paren int state = 0; std::string arg_name, default_val; skip_spaces(); for (;;) { if (state == 2) // We've read the closing paren. break; std::string tok = next_token(); // Cope with escaped EOLs if (tok == "\\") { char ch = next_char(); if (ch == '\n') { // Eat the \, the \n and any trailing space and keep going. skip_spaces(); continue; } else { // There aren't any other situations where a backslash makes sense. log_error("Backslash in macro arguments (not at end of line).\n"); } } switch (state) { case 0: // At start of argument. If the token is ')', we've presumably just seen // something like "`define foo() ...". Set state to 2 to finish. Otherwise, // the token should be a valid simple identifier, but we'll allow anything // here. if (tok == ")") { state = 2; } else { arg_name = tok; state = 1; } skip_spaces(); break; case 1: // After argument. The token should either be an equals sign or a comma or // closing paren. if (tok == "=") { std::string default_val; //Read an argument into default_val and set state to 2 if we're at // the end; 0 if we hit a comma. state = read_argument(default_val) ? 2 : 0; args.add_arg(arg_name, default_val.c_str()); skip_spaces(); break; } if (tok == ",") { // Take the identifier as an argument with no default value. args.add_arg(arg_name, nullptr); state = 0; skip_spaces(); break; } if (tok == ")") { // As with comma, but set state to 2 (end of args) args.add_arg(arg_name, nullptr); state = 2; skip_spaces(); break; } log_error("Trailing contents after identifier in macro argument `%s': " "expected '=', ',' or ')'.\n", arg_name.c_str()); default: // The only FSM states are 0-2 and we dealt with 2 at the start of the loop. log_assert(false); } } return std::make_pair(newline_count, args); } // Read a `define preprocessor directive. This is called just after reading the token containing // "`define". static void read_define(const std::string &filename, define_map_t &defines_map, define_map_t &global_defines_cache) { std::string name, value; arg_map_t args; skip_spaces(); name = next_token(true); bool here_doc_mode = false; int newline_count = 0; // The FSM state starts at 0. If it sees space (or enters here_doc_mode), it assumes this is // a macro without formal arguments and jumps to state 1. // // In state 0, if it sees an opening parenthesis, it assumes this is a macro with formal // arguments. It reads the arguments with read_define_args() and then jumps to state 2. // // In states 1 or 2, the FSM reads tokens to the end of line (or end of here_doc): this is // the body of the macro definition. int state = 0; if (skip_spaces() != "") state = 1; for (;;) { std::string tok = next_token(); if (tok.empty()) break; // printf("define-tok: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE"); if (tok == "\"\"\"") { here_doc_mode = !here_doc_mode; continue; } if (state == 0 && tok == "(") { auto pr = read_define_args(); newline_count += pr.first; args = pr.second; state = 2; continue; } // This token isn't an opening parenthesis immediately following the macro name, so // it's presumably at or after the start of the macro body. If state isn't already 2 // (which would mean we'd parsed an argument list), set it to 1. if (state == 0) { state = 1; } if (tok == "\n") { if (here_doc_mode) { value += " "; newline_count++; } else { return_char('\n'); break; } continue; } if (tok == "\\") { char ch = next_char(); if (ch == '\n') { value += " "; newline_count++; } else { value += std::string("\\"); return_char(ch); } continue; } // Is this token the name of a macro argument? If so, replace it with a magic symbol // that we'll replace with the argument value. int arg_pos; if (args.find(tok, &arg_pos)) { value += '`' + args.str_token(name, arg_pos); continue; } // This token is nothing special. Insert it verbatim into the macro body. value += tok; } // Append some newlines so that we don't mess up line counts in error messages. while (newline_count-- > 0) return_char('\n'); if (strchr("abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789", name[0])) { // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str()); defines_map.add(name, value, (state == 2) ? &args : nullptr); global_defines_cache.add(name, value, (state == 2) ? &args : nullptr); } else { log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name.c_str()); } } std::string frontend_verilog_preproc(std::istream &f, std::string filename, const define_map_t &pre_defines, define_map_t &global_defines_cache, const std::list &include_dirs) { define_map_t defines; 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; 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(); input_buffer_charp = 0; input_file(f, filename); while (!input_buffer.empty()) { std::string tok = next_token(); // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE"); if (tok == "`endif") { if (ifdef_fail_level > 0) ifdef_fail_level--; 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_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) { 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) { 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)) { 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)) { ifdef_fail_level++; } else { ifdef_pass_level++; ifdef_already_satisfied = true; } if (ifdef_fail_level == 1) ifdef_already_satisfied = false; continue; } if (ifdef_fail_level > 0) { if (tok == "\n") output_code.push_back(tok); continue; } if (tok == "`include") { skip_spaces(); std::string fn = next_token(true); while (try_expand_macro(defines, macro_arg_stack, fn)) { fn = next_token(); } while (1) { size_t pos = fn.find('"'); if (pos == std::string::npos) break; if (pos == 0) fn = fn.substr(1); else fn = fn.substr(0, pos) + fn.substr(pos+1); } std::ifstream ff; ff.clear(); std::string fixed_fn = fn; ff.open(fixed_fn.c_str()); bool filename_path_sep_found; bool fn_relative; #ifdef _WIN32 // Both forward and backslash are acceptable separators on Windows. filename_path_sep_found = (filename.find_first_of("/\\") != std::string::npos); // Easier just to invert the check for an absolute path (e.g. C:\ or C:/) fn_relative = !(fn[1] == ':' && (fn[2] == '/' || fn[2] == '\\')); #else filename_path_sep_found = (filename.find('/') != std::string::npos); fn_relative = (fn[0] != '/'); #endif if (ff.fail() && fn.size() > 0 && fn_relative && filename_path_sep_found) { // if the include file was not found, it is not given with an absolute path, and the // currently read file is given with a path, then try again relative to its directory ff.clear(); #ifdef _WIN32 fixed_fn = filename.substr(0, filename.find_last_of("/\\")+1) + fn; #else fixed_fn = filename.substr(0, filename.rfind('/')+1) + fn; #endif ff.open(fixed_fn); } if (ff.fail() && fn.size() > 0 && fn_relative) { // if the include file was not found and it is not given with an absolute path, then // search it in the include path for (auto incdir : include_dirs) { ff.clear(); fixed_fn = incdir + '/' + fn; ff.open(fixed_fn); if (!ff.fail()) break; } } if (ff.fail()) { output_code.push_back("`file_notfound " + fn); } else { input_file(ff, fixed_fn); yosys_input_files.insert(fixed_fn); } continue; } if (tok == "`file_push") { skip_spaces(); std::string fn = next_token(true); if (!fn.empty() && fn.front() == '"' && fn.back() == '"') fn = fn.substr(1, fn.size()-2); output_code.push_back(tok + " \"" + fn + "\""); filename_stack.push_back(filename); filename = fn; continue; } if (tok == "`file_pop") { output_code.push_back(tok); filename = filename_stack.back(); filename_stack.pop_back(); continue; } if (tok == "`define") { read_define(filename, defines, global_defines_cache); continue; } if (tok == "`undef") { std::string name; skip_spaces(); name = next_token(true); // printf("undef: >>%s<<\n", name.c_str()); defines.erase(name); global_defines_cache.erase(name); continue; } if (tok == "`timescale") { skip_spaces(); while (!tok.empty() && tok != "\n") tok = next_token(true); if (tok == "\n") return_char('\n'); continue; } if (tok == "`resetall") { default_nettype_wire = true; continue; } if (tok == "`undefineall" && sv_mode) { defines.clear(); global_defines_cache.clear(); continue; } 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; output_code.clear(); input_buffer.clear(); input_buffer_charp = 0; return output; } YOSYS_NAMESPACE_END yosys-0.52/frontends/verilog/preproc.h000066400000000000000000000044621477540374200201450ustar00rootroot00000000000000/* * 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. * * --- * * The Verilog preprocessor. * */ #ifndef VERILOG_PREPROC_H #define VERILOG_PREPROC_H #include "kernel/yosys.h" #include #include #include #include YOSYS_NAMESPACE_BEGIN struct define_body_t; struct arg_map_t; struct define_map_t { define_map_t(); ~ 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). void merge(const define_map_t &map); // Find a definition by name. If no match, returns null. const define_body_t *find(const std::string &name) const; // Erase a definition by name (no effect if not defined). void erase(const std::string &name); // Clear any existing definitions void clear(); // Print a list of definitions, using the log function void log() const; std::map> defines; }; struct define_map_t; std::string frontend_verilog_preproc(std::istream &f, std::string filename, const define_map_t &pre_defines, define_map_t &global_defines_cache, const std::list &include_dirs); YOSYS_NAMESPACE_END #endif yosys-0.52/frontends/verilog/verilog_frontend.cc000066400000000000000000000574731477540374200222110ustar00rootroot00000000000000/* * 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. * * --- * * The Verilog frontend. * * This frontend is using the AST frontend library (see frontends/ast/). * Thus this frontend does not generate RTLIL code directly but creates an * AST directly from the Verilog parse tree and then passes this AST to * the AST frontend library. * */ #if !defined(__wasm) #include #endif #include "verilog_frontend.h" #include "preproc.h" #include "kernel/yosys.h" #include "libs/sha1/sha1.h" #include YOSYS_NAMESPACE_BEGIN using namespace VERILOG_FRONTEND; // use the Verilog bison/flex parser to generate an AST and use AST::process() to convert it to RTLIL static std::vector verilog_defaults; static std::list> verilog_defaults_stack; static void error_on_dpi_function(AST::AstNode *node) { if (node->type == AST::AST_DPI_FUNCTION) log_file_error(node->filename, node->location.first_line, "Found DPI function %s.\n", node->str.c_str()); for (auto child : node->children) error_on_dpi_function(child); } static void add_package_types(dict &user_types, std::vector &package_list) { // prime the parser's user type lookup table with the package qualified names // of typedefed names in the packages seen so far. for (const auto &pkg : package_list) { log_assert(pkg->type==AST::AST_PACKAGE); for (const auto &node: pkg->children) { if (node->type == AST::AST_TYPEDEF) { std::string s = pkg->str + "::" + node->str.substr(1); user_types[s] = node; } } } } struct VerilogFrontend : public Frontend { VerilogFrontend() : Frontend("verilog", "read modules from Verilog file") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_verilog [options] [filename]\n"); log("\n"); log("Load modules from a Verilog file to the current design. A large subset of\n"); log("Verilog-2005 is supported.\n"); log("\n"); log(" -sv\n"); log(" enable support for SystemVerilog features. (only a small subset\n"); log(" of SystemVerilog is supported)\n"); log("\n"); log(" -formal\n"); 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"); log(" -noassume\n"); log(" ignore assume() statements\n"); log("\n"); log(" -norestrict\n"); log(" ignore restrict() statements\n"); log("\n"); log(" -assume-asserts\n"); log(" treat all assert() statements like assume() statements\n"); log("\n"); 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"); log(" -dump_ast1\n"); log(" dump abstract syntax tree (before simplification)\n"); log("\n"); log(" -dump_ast2\n"); log(" dump abstract syntax tree (after simplification)\n"); log("\n"); log(" -no_dump_ptr\n"); log(" do not include hex memory addresses in dump (easier to diff dumps)\n"); log("\n"); log(" -dump_vlog1\n"); log(" dump ast as Verilog code (before simplification)\n"); log("\n"); log(" -dump_vlog2\n"); log(" dump ast as Verilog code (after simplification)\n"); log("\n"); log(" -dump_rtlil\n"); log(" dump generated RTLIL netlist\n"); log("\n"); log(" -yydebug\n"); log(" enable parser debug output\n"); log("\n"); log(" -nolatches\n"); log(" usually latches are synthesized into logic loops\n"); log(" this option prohibits this and sets the output to 'x'\n"); log(" in what would be the latches hold condition\n"); log("\n"); log(" this behavior can also be achieved by setting the\n"); log(" 'nolatches' attribute on the respective module or\n"); log(" always block.\n"); log("\n"); log(" -nomem2reg\n"); log(" under certain conditions memories are converted to registers\n"); log(" early during simplification to ensure correct handling of\n"); log(" complex corner cases. this option disables this behavior.\n"); log("\n"); log(" this can also be achieved by setting the 'nomem2reg'\n"); log(" attribute on the respective module or register.\n"); log("\n"); log(" This is potentially dangerous. Usually the front-end has good\n"); log(" reasons for converting an array to a list of registers.\n"); log(" Prohibiting this step will likely result in incorrect synthesis\n"); log(" results.\n"); log("\n"); log(" -mem2reg\n"); log(" always convert memories to registers. this can also be\n"); log(" achieved by setting the 'mem2reg' attribute on the respective\n"); log(" module or register.\n"); log("\n"); log(" -nomeminit\n"); log(" do not infer $meminit cells and instead convert initialized\n"); log(" memories to registers directly in the front-end.\n"); log("\n"); log(" -ppdump\n"); log(" dump Verilog code after pre-processor\n"); log("\n"); log(" -nopp\n"); log(" do not run the pre-processor\n"); log("\n"); log(" -nodpi\n"); log(" disable DPI-C support\n"); log("\n"); log(" -noblackbox\n"); log(" do not automatically add a (* blackbox *) attribute to an\n"); log(" empty module.\n"); log("\n"); log(" -lib\n"); log(" only create empty blackbox modules. This implies -DBLACKBOX.\n"); log(" modules with the (* whitebox *) attribute will be preserved.\n"); log(" (* lib_whitebox *) will be treated like (* whitebox *).\n"); log("\n"); log(" -nowb\n"); log(" delete (* whitebox *) and (* lib_whitebox *) attributes from\n"); log(" all modules.\n"); log("\n"); log(" -specify\n"); log(" parse and import specify blocks\n"); log("\n"); log(" -noopt\n"); log(" don't perform basic optimizations (such as const folding) in the\n"); log(" high-level front-end.\n"); log("\n"); log(" -icells\n"); log(" interpret cell types starting with '$' as internal cell types\n"); log("\n"); log(" -pwires\n"); log(" add a wire for each module parameter\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 black box\n"); log(" module, and overwrite the existing module otherwise.)\n"); log("\n"); log(" -overwrite\n"); log(" overwrite existing modules with the same name\n"); log("\n"); log(" -defer\n"); log(" only read the abstract syntax tree and defer actual compilation\n"); log(" to a later 'hierarchy' command. Useful in cases where the default\n"); log(" parameters of modules yield invalid or not synthesizable code.\n"); log("\n"); log(" -noautowire\n"); log(" make the default of `default_nettype be \"none\" instead of \"wire\".\n"); log("\n"); log(" -setattr \n"); log(" set the specified attribute (to the value 1) on all loaded modules\n"); log("\n"); log(" -Dname[=definition]\n"); log(" define the preprocessor symbol 'name' and set its optional value\n"); log(" 'definition'\n"); log("\n"); log(" -Idir\n"); log(" add 'dir' to the directories which are used when searching include\n"); log(" files\n"); log("\n"); log("The command 'verilog_defaults' can be used to register default options for\n"); log("subsequent calls to 'read_verilog'.\n"); log("\n"); log("Note that the Verilog frontend does a pretty good job of processing valid\n"); log("verilog input, but has not very good error reporting. It generally is\n"); log("recommended to use a simulator (for example Icarus Verilog) for checking\n"); 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, 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"); log("\n"); } 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; bool flag_dump_vlog1 = false; bool flag_dump_vlog2 = false; bool flag_dump_rtlil = false; bool flag_nolatches = false; bool flag_nomeminit = false; bool flag_nomem2reg = false; bool flag_mem2reg = false; bool flag_ppdump = false; bool flag_nopp = false; bool flag_nodpi = false; bool flag_noopt = false; bool flag_icells = false; bool flag_pwires = false; bool flag_nooverwrite = false; bool flag_overwrite = false; 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; std::list attributes; frontend_verilog_yydebug = false; sv_mode = false; formal_mode = false; noassert_mode = false; noassume_mode = false; norestrict_mode = false; assume_asserts_mode = false; assert_assumes_mode = false; lib_mode = false; specify_mode = false; default_nettype_wire = true; args.insert(args.begin()+1, verilog_defaults.begin(), verilog_defaults.end()); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-sv") { sv_mode = true; continue; } if (arg == "-formal") { formal_mode = true; continue; } if (arg == "-nosynthesis") { flag_nosynthesis = true; continue; } if (arg == "-noassert") { noassert_mode = true; continue; } if (arg == "-noassume") { noassume_mode = true; continue; } if (arg == "-norestrict") { norestrict_mode = true; continue; } if (arg == "-assume-asserts") { assume_asserts_mode = true; continue; } if (arg == "-assert-assumes") { assert_assumes_mode = true; continue; } if (arg == "-nodisplay") { flag_nodisplay = true; continue; } if (arg == "-debug") { flag_dump_ast1 = true; flag_dump_ast2 = true; flag_dump_vlog1 = true; flag_dump_vlog2 = true; frontend_verilog_yydebug = true; continue; } if (arg == "-dump_ast1") { flag_dump_ast1 = true; continue; } if (arg == "-dump_ast2") { flag_dump_ast2 = true; continue; } if (arg == "-no_dump_ptr") { flag_no_dump_ptr = true; continue; } if (arg == "-dump_vlog1") { flag_dump_vlog1 = true; continue; } if (arg == "-dump_vlog2") { flag_dump_vlog2 = true; continue; } if (arg == "-dump_rtlil") { flag_dump_rtlil = true; continue; } if (arg == "-yydebug") { frontend_verilog_yydebug = true; continue; } if (arg == "-nolatches") { flag_nolatches = true; continue; } if (arg == "-nomeminit") { flag_nomeminit = true; continue; } if (arg == "-nomem2reg") { flag_nomem2reg = true; continue; } if (arg == "-mem2reg") { flag_mem2reg = true; continue; } if (arg == "-ppdump") { flag_ppdump = true; continue; } if (arg == "-nopp") { flag_nopp = true; continue; } if (arg == "-nodpi") { flag_nodpi = true; continue; } if (arg == "-noblackbox") { flag_noblackbox = true; continue; } if (arg == "-lib") { lib_mode = true; defines_map.add("BLACKBOX", ""); continue; } if (arg == "-nowb") { flag_nowb = true; continue; } if (arg == "-specify") { specify_mode = true; continue; } if (arg == "-noopt") { flag_noopt = true; continue; } if (arg == "-icells") { flag_icells = true; continue; } if (arg == "-pwires") { flag_pwires = true; continue; } if (arg == "-ignore_redef" || arg == "-nooverwrite") { flag_nooverwrite = true; flag_overwrite = false; continue; } if (arg == "-overwrite") { flag_nooverwrite = false; flag_overwrite = true; continue; } if (arg == "-defer") { flag_defer = true; continue; } if (arg == "-noautowire") { default_nettype_wire = false; continue; } if (arg == "-setattr" && argidx+1 < args.size()) { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; } if (arg == "-D" && argidx+1 < args.size()) { std::string name = args[++argidx], value; size_t equal = name.find('='); if (equal != std::string::npos) { value = name.substr(equal+1); name = name.substr(0, equal); } defines_map.add(name, value); continue; } if (arg.compare(0, 2, "-D") == 0) { size_t equal = arg.find('=', 2); std::string name = arg.substr(2, equal-2); std::string value; if (equal != std::string::npos) value = arg.substr(equal+1); defines_map.add(name, value); continue; } if (arg == "-I" && argidx+1 < args.size()) { include_dirs.push_back(args[++argidx]); continue; } if (arg.compare(0, 2, "-I") == 0) { include_dirs.push_back(arg.substr(2)); continue; } 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()); log("Parsing %s%s input from `%s' to AST representation.\n", formal_mode ? "formal " : "", sv_mode ? "SystemVerilog" : "Verilog", filename.c_str()); AST::current_filename = filename; AST::set_line_num = &frontend_verilog_yyset_lineno; AST::get_line_num = &frontend_verilog_yyget_lineno; current_ast = new AST::AstNode(AST::AST_DESIGN); lexin = f; std::string code_after_preproc; if (!flag_nopp) { code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, *design->verilog_defines, include_dirs); if (flag_ppdump) log("-- Verilog code after preprocessor --\n%s-- END OF DUMP --\n", code_after_preproc.c_str()); lexin = new std::istringstream(code_after_preproc); } // 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(); frontend_verilog_yylex_destroy(); for (auto &child : current_ast->children) { if (child->type == AST::AST_MODULE) for (auto &attr : attributes) if (child->attributes.count(attr) == 0) child->attributes[attr] = AST::AstNode::mkconst_int(1, false); } if (flag_nodpi) error_on_dpi_function(current_ast); 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; log("Successfully finished Verilog frontend.\n"); } } VerilogFrontend; struct VerilogDefaults : public Pass { VerilogDefaults() : Pass("verilog_defaults", "set default options for read_verilog") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" verilog_defaults -add [options]\n"); log("\n"); log("Add the specified options to the list of default options to read_verilog.\n"); log("\n"); log("\n"); log(" verilog_defaults -clear\n"); log("\n"); log("Clear the list of Verilog default options.\n"); log("\n"); log("\n"); log(" verilog_defaults -push\n"); log(" verilog_defaults -pop\n"); log("\n"); log("Push or pop the list of default options to a stack. Note that -push does\n"); log("not imply -clear.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design*) override { if (args.size() < 2) cmd_error(args, 1, "Missing argument."); if (args[1] == "-add") { verilog_defaults.insert(verilog_defaults.end(), args.begin()+2, args.end()); return; } if (args.size() != 2) cmd_error(args, 2, "Extra argument."); if (args[1] == "-clear") { verilog_defaults.clear(); return; } if (args[1] == "-push") { verilog_defaults_stack.push_back(verilog_defaults); return; } if (args[1] == "-pop") { if (verilog_defaults_stack.empty()) { verilog_defaults.clear(); } else { verilog_defaults.swap(verilog_defaults_stack.back()); verilog_defaults_stack.pop_back(); } return; } } } VerilogDefaults; struct VerilogDefines : public Pass { VerilogDefines() : Pass("verilog_defines", "define and undefine verilog defines") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" verilog_defines [options]\n"); log("\n"); log("Define and undefine verilog preprocessor macros.\n"); log("\n"); log(" -Dname[=definition]\n"); log(" define the preprocessor symbol 'name' and set its optional value\n"); log(" 'definition'\n"); log("\n"); log(" -Uname[=definition]\n"); log(" undefine the preprocessor symbol 'name'\n"); log("\n"); log(" -reset\n"); log(" clear list of defined preprocessor symbols\n"); log("\n"); log(" -list\n"); log(" list currently defined preprocessor symbols\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-D" && argidx+1 < args.size()) { std::string name = args[++argidx], value; size_t equal = name.find('='); if (equal != std::string::npos) { value = name.substr(equal+1); name = name.substr(0, equal); } design->verilog_defines->add(name, value); continue; } if (arg.compare(0, 2, "-D") == 0) { size_t equal = arg.find('=', 2); std::string name = arg.substr(2, equal-2); std::string value; if (equal != std::string::npos) value = arg.substr(equal+1); design->verilog_defines->add(name, value); continue; } if (arg == "-U" && argidx+1 < args.size()) { std::string name = args[++argidx]; design->verilog_defines->erase(name); continue; } if (arg.compare(0, 2, "-U") == 0) { std::string name = arg.substr(2); design->verilog_defines->erase(name); continue; } if (arg == "-reset") { design->verilog_defines->clear(); continue; } if (arg == "-list") { design->verilog_defines->log(); continue; } break; } if (args.size() != argidx) cmd_error(args, argidx, "Extra argument."); } } VerilogDefines; #if !defined(__wasm) static void parse_file_list(const std::string &file_list_path, RTLIL::Design *design, bool relative_to_file_list_path) { std::ifstream flist(file_list_path); if (!flist.is_open()) { log_error("Verilog file list file does not exist"); exit(1); } std::filesystem::path file_list_parent_dir = std::filesystem::path(file_list_path).parent_path(); std::string v_file_name; while (std::getline(flist, v_file_name)) { if (v_file_name.empty()) { continue; } std::filesystem::path verilog_file_path; if (relative_to_file_list_path) { verilog_file_path = file_list_parent_dir / v_file_name; } else { verilog_file_path = std::filesystem::current_path() / v_file_name; } bool is_sv = (verilog_file_path.extension() == ".sv"); std::vector read_verilog_cmd = {"read_verilog", "-defer"}; if (is_sv) { read_verilog_cmd.push_back("-sv"); } read_verilog_cmd.push_back(verilog_file_path.string()); Pass::call(design, read_verilog_cmd); } flist.close(); } struct VerilogFileList : public Pass { VerilogFileList() : Pass("read_verilog_file_list", "parse a Verilog file list") {} void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" read_verilog_file_list [options]\n"); log("\n"); log("Parse a Verilog file list, and pass the list of Verilog files to read_verilog\n"); log("command\n"); log("\n"); log(" -F file_list_path\n"); log(" File list file contains list of Verilog files to be parsed, any path is\n"); log(" treated relative to the file list file\n"); log("\n"); log(" -f file_list_path\n"); log(" File list file contains list of Verilog files to be parsed, any path is\n"); log(" treated relative to current working directroy\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-F" && argidx + 1 < args.size()) { std::string file_list_path = args[++argidx]; parse_file_list(file_list_path, design, true); continue; } if (arg == "-f" && argidx + 1 < args.size()) { std::string file_list_path = args[++argidx]; parse_file_list(file_list_path, design, false); continue; } break; } extra_args(args, argidx, design); } } VerilogFilelist; #endif YOSYS_NAMESPACE_END // the yyerror function used by bison to report parser errors void frontend_verilog_yyerror(char const *fmt, ...) { va_list ap; char buffer[1024]; char *p = buffer; va_start(ap, fmt); p += vsnprintf(p, buffer + sizeof(buffer) - p, fmt, ap); va_end(ap); p += snprintf(p, buffer + sizeof(buffer) - p, "\n"); YOSYS_NAMESPACE_PREFIX log_file_error(YOSYS_NAMESPACE_PREFIX AST::current_filename, frontend_verilog_yyget_lineno(), "%s", buffer); exit(1); } yosys-0.52/frontends/verilog/verilog_frontend.h000066400000000000000000000055761477540374200220500ustar00rootroot00000000000000/* * 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. * * --- * * The Verilog frontend. * * This frontend is using the AST frontend library (see frontends/ast/). * Thus this frontend does not generate RTLIL code directly but creates an * AST directly from the Verilog parse tree and then passes this AST to * the AST frontend library. * */ #ifndef VERILOG_FRONTEND_H #define VERILOG_FRONTEND_H #include "kernel/yosys.h" #include "frontends/ast/ast.h" #include #include #include YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { // this variable is set to a new AST_DESIGN node and then filled with the AST by the bison parser extern struct AST::AstNode *current_ast; // this function converts a Verilog constant to an AST_CONSTANT node AST::AstNode *const2ast(std::string code, char case_type = 0, bool warn_z = false); // names of locally typedef'ed types in a stack typedef std::map UserTypeMap; extern std::vector user_type_stack; // names of package typedef'ed types extern dict pkg_user_types; // state of `default_nettype extern bool default_nettype_wire; // running in SystemVerilog mode extern bool sv_mode; // running in -formal mode extern bool formal_mode; // running in -noassert mode extern bool noassert_mode; // running in -noassume mode extern bool noassume_mode; // running in -norestrict mode extern bool norestrict_mode; // running in -assume-asserts mode extern bool assume_asserts_mode; // running in -assert-assumes mode extern bool assert_assumes_mode; // running in -lib mode extern bool lib_mode; // running in -specify mode extern bool specify_mode; // lexer input stream extern std::istream *lexin; } YOSYS_NAMESPACE_END // the usual bison/flex stuff extern int frontend_verilog_yydebug; void frontend_verilog_yyerror(char const *fmt, ...); void frontend_verilog_yyrestart(FILE *f); int frontend_verilog_yyparse(void); int frontend_verilog_yylex_destroy(void); int frontend_verilog_yyget_lineno(void); void frontend_verilog_yyset_lineno (int); #endif yosys-0.52/frontends/verilog/verilog_lexer.l000066400000000000000000000440271477540374200213460ustar00rootroot00000000000000/* * 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. * * --- * * The Verilog frontend. * * This frontend is using the AST frontend library (see frontends/ast/). * Thus this frontend does not generate RTLIL code directly but creates an * AST directly from the Verilog parse tree and then passes this AST to * the AST frontend library. * * --- * * A simple lexer for Verilog code. Non-preprocessor compiler directives are * handled here. The preprocessor stuff is handled in preproc.cc. Everything * else is left to the bison parser (see verilog_parser.y). * */ %{ #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" #include "frontends/verilog/verilog_frontend.h" #include "frontends/ast/ast.h" #include "verilog_parser.tab.hh" USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; #define YYSTYPE FRONTEND_VERILOG_YYSTYPE #define YYLTYPE FRONTEND_VERILOG_YYLTYPE YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { std::vector fn_stack; std::vector ln_stack; YYLTYPE real_location; YYLTYPE old_location; } YOSYS_NAMESPACE_END #define SV_KEYWORD(_tok) \ if (sv_mode) return _tok; \ log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\ "recognized unless read_verilog is called with -sv!\n", yytext, \ AST::current_filename.c_str(), frontend_verilog_yyget_lineno()); \ yylval->string = new std::string(std::string("\\") + yytext); \ return TOK_ID; #define NON_KEYWORD() \ yylval->string = new std::string(std::string("\\") + yytext); \ return TOK_ID; #define YY_INPUT(buf,result,max_size) \ result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size) #define YY_USER_ACTION \ old_location = real_location; \ real_location.first_line = real_location.last_line; \ real_location.first_column = real_location.last_column; \ for(int i = 0; yytext[i] != '\0'; ++i){ \ if(yytext[i] == '\n') { \ real_location.last_line++; \ real_location.last_column = 1; \ } \ else { \ real_location.last_column++; \ } \ } \ (*yylloc) = real_location; #define YY_BREAK \ (*yylloc) = old_location; \ break; #undef YY_BUF_SIZE #define YY_BUF_SIZE 65536 extern int frontend_verilog_yylex(YYSTYPE *yylval_param, YYLTYPE *yyloc_param); 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) { return true; } } return false; } %} %option yylineno %option noyywrap %option nounput %option bison-locations %option bison-bridge %option prefix="frontend_verilog_yy" %x COMMENT %x STRING %x SYNOPSYS_TRANSLATE_OFF %x SYNOPSYS_FLAGS %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. int comment_caller = INITIAL; "`file_push "[^\n]* { fn_stack.push_back(current_filename); ln_stack.push_back(frontend_verilog_yyget_lineno()); current_filename = yytext+11; if (!current_filename.empty() && current_filename.front() == '"') current_filename = current_filename.substr(1); if (!current_filename.empty() && current_filename.back() == '"') current_filename = current_filename.substr(0, current_filename.size()-1); frontend_verilog_yyset_lineno(0); yylloc->first_line = yylloc->last_line = 0; real_location.first_line = real_location.last_line = 0; } "`file_pop"[^\n]*\n { current_filename = fn_stack.back(); fn_stack.pop_back(); frontend_verilog_yyset_lineno(ln_stack.back()); yylloc->first_line = yylloc->last_line = ln_stack.back(); real_location.first_line = real_location.last_line = ln_stack.back(); ln_stack.pop_back(); } "`line"[ \t]+[^ \t\r\n]+[ \t]+\"[^ \r\n]+\"[^\r\n]*\n { char *p = yytext + 5; while (*p == ' ' || *p == '\t') p++; frontend_verilog_yyset_lineno(atoi(p)); yylloc->first_line = yylloc->last_line = atoi(p); real_location.first_line = real_location.last_line = atoi(p); while (*p && *p != ' ' && *p != '\t') p++; while (*p == ' ' || *p == '\t') p++; char *q = *p ? p + 1 : p; while (*q && *q != '"') q++; current_filename = std::string(p).substr(1, q-p-1); } "`file_notfound "[^\n]* { log_error("Can't open include file `%s'!\n", yytext + 15); } "`timescale"[ \t]+[^ \t\r\n/]+[ \t]*"/"[ \t]*[^ \t\r\n]* /* ignore timescale directive */ "`celldefine"[^\n]* /* ignore `celldefine */ "`endcelldefine"[^\n]* /* ignore `endcelldefine */ "`default_nettype"[ \t]+[^ \t\r\n/]+ { char *p = yytext; while (*p != 0 && *p != ' ' && *p != '\t') p++; while (*p == ' ' || *p == '\t') p++; if (!strcmp(p, "none")) VERILOG_FRONTEND::default_nettype_wire = false; else if (!strcmp(p, "wire")) VERILOG_FRONTEND::default_nettype_wire = true; else frontend_verilog_yyerror("Unsupported default nettype: %s", p); } "`protect"[^\n]* /* ignore `protect*/ "`endprotect"[^\n]* /* ignore `endprotect*/ "`"[a-zA-Z_$][a-zA-Z0-9_$]* { frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); } "module" { return TOK_MODULE; } "endmodule" { return TOK_ENDMODULE; } "function" { return TOK_FUNCTION; } "endfunction" { return TOK_ENDFUNCTION; } "task" { return TOK_TASK; } "endtask" { return TOK_ENDTASK; } "specify" { return specify_mode ? TOK_SPECIFY : TOK_IGNORED_SPECIFY; } "endspecify" { return TOK_ENDSPECIFY; } "specparam" { return TOK_SPECPARAM; } "package" { SV_KEYWORD(TOK_PACKAGE); } "endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); } "interface" { SV_KEYWORD(TOK_INTERFACE); } "endinterface" { SV_KEYWORD(TOK_ENDINTERFACE); } "modport" { SV_KEYWORD(TOK_MODPORT); } "parameter" { return TOK_PARAMETER; } "localparam" { return TOK_LOCALPARAM; } "defparam" { return TOK_DEFPARAM; } "assign" { return TOK_ASSIGN; } "always" { return TOK_ALWAYS; } "initial" { return TOK_INITIAL; } "begin" { return TOK_BEGIN; } "end" { return TOK_END; } "if" { return TOK_IF; } "else" { return TOK_ELSE; } "for" { return TOK_FOR; } "posedge" { return TOK_POSEDGE; } "negedge" { return TOK_NEGEDGE; } "or" { return TOK_OR; } "case" { return TOK_CASE; } "casex" { return TOK_CASEX; } "casez" { return TOK_CASEZ; } "endcase" { return TOK_ENDCASE; } "default" { return TOK_DEFAULT; } "generate" { return TOK_GENERATE; } "endgenerate" { return TOK_ENDGENERATE; } "while" { return TOK_WHILE; } "repeat" { return TOK_REPEAT; } "automatic" { return TOK_AUTOMATIC; } "unique" { SV_KEYWORD(TOK_UNIQUE); } "unique0" { SV_KEYWORD(TOK_UNIQUE0); } "priority" { SV_KEYWORD(TOK_PRIORITY); } "always_comb" { SV_KEYWORD(TOK_ALWAYS_COMB); } "always_ff" { SV_KEYWORD(TOK_ALWAYS_FF); } "always_latch" { SV_KEYWORD(TOK_ALWAYS_LATCH); } /* use special token for labels on assert, assume, cover, and restrict because it's insanley complex to fix parsing of cells otherwise. (the current cell parser forces a reduce very early to update some global state.. its a mess) */ [a-zA-Z_$][a-zA-Z0-9_$]*/[ \t\r\n]*:[ \t\r\n]*(assert|assume|cover|restrict)[^a-zA-Z0-9_$\.] { if (!strcmp(yytext, "default")) return TOK_DEFAULT; yylval->string = new std::string(std::string("\\") + yytext); return TOK_SVA_LABEL; } "assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); } "assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); } "cover" { if (formal_mode) return TOK_COVER; SV_KEYWORD(TOK_COVER); } "restrict" { if (formal_mode) return TOK_RESTRICT; SV_KEYWORD(TOK_RESTRICT); } "property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); } "rand" { if (formal_mode) return TOK_RAND; SV_KEYWORD(TOK_RAND); } "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); } "bit" { SV_KEYWORD(TOK_LOGIC); } "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); } "input" { return TOK_INPUT; } "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; } "unsigned" { SV_KEYWORD(TOK_UNSIGNED); } "genvar" { return TOK_GENVAR; } "real" { return TOK_REAL; } "enum" { SV_KEYWORD(TOK_ENUM); } "typedef" { SV_KEYWORD(TOK_TYPEDEF); } "struct" { SV_KEYWORD(TOK_STRUCT); } "union" { SV_KEYWORD(TOK_UNION); } "packed" { SV_KEYWORD(TOK_PACKED); } {UNSIGNED_NUMBER} { yylval->string = new std::string(yytext); return TOK_CONSTVAL; } \'[01zxZX] { yylval->string = new std::string(yytext); return TOK_UNBASED_UNSIZED_CONSTVAL; } \'[sS]?[bodhBODH] { BEGIN(BASED_CONST); yylval->string = new std::string(yytext); return TOK_BASE; } [0-9a-fA-FzxZX?][0-9a-fA-FzxZX?_]* { BEGIN(0); yylval->string = new std::string(yytext); return TOK_BASED_CONSTVAL; } {FIXED_POINT_NUMBER_DEC} { yylval->string = new std::string(yytext); return TOK_REALVAL; } {FIXED_POINT_NUMBER_NO_DEC} { yylval->string = new std::string(yytext); return TOK_REALVAL; } \" { BEGIN(STRING); } \\. { yymore(); real_location = old_location; } \" { BEGIN(0); char *yystr = strdup(yytext); yystr[strlen(yytext) - 1] = 0; int i = 0, j = 0; while (yystr[i]) { if (yystr[i] == '\\' && yystr[i + 1]) { i++; if (yystr[i] == 'a') yystr[i] = '\a'; else if (yystr[i] == 'f') yystr[i] = '\f'; else if (yystr[i] == 'n') yystr[i] = '\n'; else if (yystr[i] == 'r') yystr[i] = '\r'; else if (yystr[i] == 't') yystr[i] = '\t'; else if (yystr[i] == 'v') yystr[i] = '\v'; else if ('0' <= yystr[i] && yystr[i] <= '7') { yystr[i] = yystr[i] - '0'; if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; i++; } if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { yystr[i + 1] = yystr[i] * 8 + yystr[i + 1] - '0'; i++; } } } yystr[j++] = yystr[i++]; } yystr[j] = 0; yylval->string = new std::string(yystr, j); free(yystr); return TOK_STRING; } . { yymore(); real_location = old_location; } and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { yylval->string = new std::string(yytext); return TOK_PRIMITIVE; } supply0 { return TOK_SUPPLY0; } supply1 { return TOK_SUPPLY1; } "$"(display[bho]?|write[bho]?|strobe|monitor|time|realtime|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { yylval->string = new std::string(yytext); return TOK_ID; } "$"(setup|hold|setuphold|removal|recovery|recrem|skew|timeskew|fullskew|nochange) { if (!specify_mode) REJECT; yylval->string = new std::string(yytext); return TOK_ID; } "$"(info|warning|error|fatal) { yylval->string = new std::string(yytext); return TOK_MSG_TASKS; } "$signed" { return TOK_TO_SIGNED; } "$unsigned" { return TOK_TO_UNSIGNED; } [a-zA-Z_][a-zA-Z0-9_]*::[a-zA-Z_$][a-zA-Z0-9_$]* { // package qualifier auto s = std::string("\\") + yytext; if (pkg_user_types.count(s) > 0) { // package qualified typedefed name yylval->string = new std::string(s); return TOK_PKG_USER_TYPE; } else { // backup before :: just return first part size_t len = strchr(yytext, ':') - yytext; yyless(len); yylval->string = new std::string(std::string("\\") + yytext); return TOK_ID; } } [a-zA-Z_$][a-zA-Z0-9_$]* { auto s = std::string("\\") + yytext; if (isUserType(s)) { // previously typedefed name yylval->string = new std::string(s); return TOK_USER_TYPE; } else { yylval->string = new std::string(std::string("\\") + yytext); return TOK_ID; } } [a-zA-Z_$][a-zA-Z0-9_$\.]* { yylval->string = new std::string(std::string("\\") + yytext); return TOK_ID; } "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { static bool printed_warning = false; if (!printed_warning) { 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); } . /* ignore synopsys translate_off body */ \n /* ignore synopsys translate_off body */ "/*"[ \t]*(synopsys|synthesis)[ \t]*"translate_on"[ \t]*"*/" { BEGIN(0); } "/*"[ \t]*(synopsys|synthesis)[ \t]+ { BEGIN(SYNOPSYS_FLAGS); } full_case { static bool printed_warning = false; if (!printed_warning) { 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; } parallel_case { static bool printed_warning = false; if (!printed_warning) { 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; } . /* ignore everything else */ "*/" { BEGIN(0); } import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { BEGIN(IMPORT_DPI); return TOK_DPI_FUNCTION; } [a-zA-Z_$][a-zA-Z0-9_$]* { yylval->string = new std::string(std::string("\\") + yytext); return TOK_ID; } [ \t\r\n] /* ignore whitespaces */ ";" { BEGIN(0); return *yytext; } . { return *yytext; } "\\"[^ \t\r\n]+ { yylval->string = new std::string(yytext); return TOK_ID; } "(*" { return ATTR_BEGIN; } "*)" { return ATTR_END; } "{*" { return DEFATTR_BEGIN; } "*}" { return DEFATTR_END; } "**" { return OP_POW; } "||" { return OP_LOR; } "&&" { return OP_LAND; } "==" { return OP_EQ; } "!=" { return OP_NE; } "<=" { return OP_LE; } ">=" { return OP_GE; } "===" { return OP_EQX; } "!==" { return OP_NEX; } "~&" { return OP_NAND; } "~|" { return OP_NOR; } "~^" { return OP_XNOR; } "^~" { return OP_XNOR; } "<<" { return OP_SHL; } ">>" { return OP_SHR; } "<<<" { return OP_SSHL; } ">>>" { return OP_SSHR; } "'" { return OP_CAST; } "::" { return TOK_PACKAGESEP; } "++" { return TOK_INCREMENT; } "--" { return TOK_DECREMENT; } "+:" { return TOK_POS_INDEXED; } "-:" { return TOK_NEG_INDEXED; } ".*" { return TOK_WILDCARD_CONNECT; } "|=" { 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_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; yylval->string = new std::string(yytext); return TOK_SPECIFY_OPER; } "&&&" { if (!specify_mode) return TOK_IGNORED_SPECIFY_AND; 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 */ "*/" { BEGIN(comment_caller); } [ \t\r\n] /* ignore whitespaces */ \\[\r\n] /* ignore continuation sequence */ "//"[^\r\n]* /* ignore one-line comments */ . { return *yytext; } <*>. { BEGIN(0); return *yytext; } %% // this is a hack to avoid the 'yyinput defined but not used' error msgs void *frontend_verilog_avoid_input_warnings() { return (void*)&yyinput; } yosys-0.52/frontends/verilog/verilog_parser.y000066400000000000000000003101721477540374200215350ustar00rootroot00000000000000/* * 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. * * --- * * The Verilog frontend. * * This frontend is using the AST frontend library (see frontends/ast/). * Thus this frontend does not generate RTLIL code directly but creates an * AST directly from the Verilog parse tree and then passes this AST to * the AST frontend library. * * --- * * This is the actual bison parser for Verilog code. The AST ist created directly * from the bison reduce functions here. Note that this code uses a few global * variables to hold the state of the AST generator and therefore this parser is * not reentrant. * */ %require "3.0" %{ #include #include #include #include "frontends/verilog/verilog_frontend.h" #include "frontends/verilog/verilog_parser.tab.hh" #include "kernel/log.h" #define YYLEX_PARAM &yylval, &yylloc USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { int port_counter; dict port_stubs; dict *attr_list, default_attr_list; std::stack *> attr_list_stack; dict *albuf; std::vector user_type_stack; dict pkg_user_types; std::vector ast_stack; struct AstNode *astbuf1, *astbuf2, *astbuf3; struct AstNode *current_function_or_task; struct AstNode *current_ast, *current_ast_mod; int current_function_or_task_port_id; std::vector case_type_stack; bool do_not_require_port_stubs; bool default_nettype_wire; bool sv_mode, formal_mode, lib_mode, specify_mode; bool noassert_mode, noassume_mode, norestrict_mode; bool assume_asserts_mode, assert_assumes_mode; bool current_wire_rand, current_wire_const; bool current_modport_input, current_modport_output; std::istream *lexin; } YOSYS_NAMESPACE_END #define SET_AST_NODE_LOC(WHICH, BEGIN, END) \ do { (WHICH)->location.first_line = (BEGIN).first_line; \ (WHICH)->location.first_column = (BEGIN).first_column; \ (WHICH)->location.last_line = (END).last_line; \ (WHICH)->location.last_column = (END).last_column; } while(0) #define SET_RULE_LOC(LHS, BEGIN, END) \ do { (LHS).first_line = (BEGIN).first_line; \ (LHS).first_column = (BEGIN).first_column; \ (LHS).last_line = (END).last_line; \ (LHS).last_column = (END).last_column; } while(0) int frontend_verilog_yylex(YYSTYPE *yylval_param, YYLTYPE *yyloc_param); static void append_attr(AstNode *ast, dict *al) { for (auto &it : *al) { if (ast->attributes.count(it.first) > 0) delete ast->attributes[it.first]; ast->attributes[it.first] = it.second; } delete al; } static void append_attr_clone(AstNode *ast, dict *al) { for (auto &it : *al) { if (ast->attributes.count(it.first) > 0) delete ast->attributes[it.first]; ast->attributes[it.first] = it.second->clone(); } } static void free_attr(dict *al) { for (auto &it : *al) delete it.second; delete al; } struct specify_target { char polarity_op; AstNode *dst, *dat; }; struct specify_triple { AstNode *t_min, *t_avg, *t_max; }; struct specify_rise_fall { specify_triple rise; 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; 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); pkg_user_types[qname] = tnode; } delete name; ast_stack.back()->children.push_back(tnode); } static void enterTypeScope() { user_type_stack.push_back(UserTypeMap()); } static void exitTypeScope() { user_type_stack.pop_back(); } 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 *makeRange(int msb = 31, int lsb = 0, bool isSigned = true) { auto range = new AstNode(AST_RANGE); range->children.push_back(AstNode::mkconst_int(msb, true)); range->children.push_back(AstNode::mkconst_int(lsb, true)); range->is_signed = isSigned; return range; } static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = true) { auto range = makeRange(msb, lsb, isSigned); parent->children.push_back(range); } static AstNode *checkRange(AstNode *type_node, AstNode *range_node) { if (type_node->range_left >= 0 && type_node->range_right >= 0) { // type already restricts the range if (range_node) { frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions."); } else { range_node = makeRange(type_node->range_left, type_node->range_right, false); } } 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 [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); } } static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode) { node->type = AST_MEMORY; if (rangeNode->type == AST_MULTIRANGE) { for (auto *itr : rangeNode->children) rewriteRange(itr); } else rewriteRange(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} %define api.pure /* The union is defined in the header, so we need to provide all the * includes it requires */ %code requires { #include #include #include "frontends/verilog/verilog_frontend.h" } %union { std::string *string; struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast; YOSYS_NAMESPACE_PREFIX dict *al; struct specify_target *specify_target_ptr; struct specify_triple *specify_triple_ptr; 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 %token TOK_SVA_LABEL TOK_SPECIFY_OPER TOK_MSG_TASKS %token TOK_BASE TOK_BASED_CONSTVAL TOK_UNBASED_UNSIZED_CONSTVAL %token TOK_USER_TYPE TOK_PKG_USER_TYPE %token TOK_ASSERT TOK_ASSUME TOK_RESTRICT TOK_COVER TOK_FINAL %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %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_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 %token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT %token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY %token TOK_IGNORED_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM TOK_SPECIFY_AND TOK_IGNORED_SPECIFY_AND %token TOK_GENERATE TOK_ENDGENERATE TOK_GENVAR TOK_REAL %token TOK_SYNOPSYS_FULL_CASE TOK_SYNOPSYS_PARALLEL_CASE %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_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 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 %type specify_rise_fall %type specify_if specify_condition %type specify_edge // operator precedence from low to high %left OP_LOR %left OP_LAND %left '|' OP_NOR %left '^' OP_XNOR %left '&' OP_NAND %left OP_EQ OP_NE OP_EQX OP_NEX %left '<' OP_LE OP_GE '>' %left OP_SHL OP_SHR OP_SSHL OP_SSHR %left '+' '-' %left '*' '/' '%' %left OP_POW %precedence OP_CAST %precedence UNARY_OPS %define parse.error verbose %define parse.lac full %precedence FAKE_THEN %precedence TOK_ELSE %debug %locations %% input: { (void)frontend_verilog_yynerrs; ast_stack.clear(); ast_stack.push_back(current_ast); } design { ast_stack.pop_back(); log_assert(GetSize(ast_stack) == 0); for (auto &it : default_attr_list) delete it.second; default_attr_list.clear(); }; design: module design | defattr design | task_func_decl design | param_decl design | localparam_decl design | typedef_decl design | package design | interface design | bind_directive design | %empty; attr: { if (attr_list != nullptr) attr_list_stack.push(attr_list); attr_list = new dict; for (auto &it : default_attr_list) (*attr_list)[it.first] = it.second->clone(); } attr_opt { $$ = attr_list; if (!attr_list_stack.empty()) { attr_list = attr_list_stack.top(); attr_list_stack.pop(); } else attr_list = nullptr; }; attr_opt: attr_opt ATTR_BEGIN opt_attr_list ATTR_END { SET_RULE_LOC(@$, @2, @$); }| %empty; defattr: DEFATTR_BEGIN { if (attr_list != nullptr) attr_list_stack.push(attr_list); attr_list = new dict; for (auto &it : default_attr_list) delete it.second; default_attr_list.clear(); } opt_attr_list { attr_list->swap(default_attr_list); delete attr_list; if (!attr_list_stack.empty()) { attr_list = attr_list_stack.top(); attr_list_stack.pop(); } else attr_list = nullptr; } DEFATTR_END; opt_attr_list: attr_list | %empty; attr_list: attr_assign | attr_list ',' attr_assign; attr_assign: hierarchical_id { if (attr_list->count(*$1) != 0) delete (*attr_list)[*$1]; (*attr_list)[*$1] = AstNode::mkconst_int(1, false); delete $1; } | hierarchical_id '=' expr { if (attr_list->count(*$1) != 0) delete (*attr_list)[*$1]; (*attr_list)[*$1] = $3; delete $1; }; hierarchical_id: TOK_ID { $$ = $1; } | hierarchical_id TOK_PACKAGESEP TOK_ID { if ($3->compare(0, 1, "\\") == 0) *$1 += "::" + $3->substr(1); else *$1 += "::" + *$3; delete $3; $$ = $1; } | hierarchical_id '.' TOK_ID { if ($3->compare(0, 1, "\\") == 0) *$1 += "." + $3->substr(1); else *$1 += "." + *$3; delete $3; $$ = $1; }; hierarchical_type_id: TOK_USER_TYPE | TOK_PKG_USER_TYPE // package qualified type name | '(' TOK_USER_TYPE ')' { $$ = $2; } // non-standard grammar ; module: attr TOK_MODULE { enterTypeScope(); } TOK_ID { do_not_require_port_stubs = false; AstNode *mod = new AstNode(AST_MODULE); ast_stack.back()->children.push_back(mod); ast_stack.push_back(mod); current_ast_mod = mod; port_stubs.clear(); port_counter = 0; mod->str = *$4; append_attr(mod, $1); } 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'.", port_stubs.begin()->first.c_str()); 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(); }; module_para_opt: '#' '(' { astbuf1 = nullptr; } module_para_list { if (astbuf1) delete astbuf1; } ')' | %empty; module_para_list: single_module_para | module_para_list ',' single_module_para; single_module_para: %empty | attr TOK_PARAMETER { if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); append_attr(astbuf1, $1); } param_type single_param_decl | attr TOK_LOCALPARAM { if (astbuf1) delete astbuf1; astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); append_attr(astbuf1, $1); } param_type single_param_decl | single_param_decl; module_args_opt: '(' ')' | %empty | '(' module_args optional_comma ')'; module_args: module_arg | module_args ',' module_arg; optional_comma: ',' | %empty; module_arg_opt_assignment: '=' expr { if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) { 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 { 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."); } | %empty; module_arg: TOK_ID { if (ast_stack.back()->children.size() > 0 && ast_stack.back()->children.back()->type == AST_WIRE) { AstNode *node = ast_stack.back()->children.back()->clone(); node->str = *$1; node->port_id = ++port_counter; ast_stack.back()->children.push_back(node); SET_AST_NODE_LOC(node, @1, @1); } else { if (port_stubs.count(*$1) != 0) frontend_verilog_yyerror("Duplicate module port `%s'.", $1->c_str()); port_stubs[*$1] = ++port_counter; } delete $1; } module_arg_opt_assignment | TOK_ID { astbuf1 = new AstNode(AST_INTERFACEPORT); astbuf1->children.push_back(new AstNode(AST_INTERFACEPORTTYPE)); astbuf1->children[0]->str = *$1; delete $1; } TOK_ID { /* SV interfaces */ if (!sv_mode) frontend_verilog_yyerror("Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", $3->c_str()); astbuf2 = astbuf1->clone(); // really only needed if multiple instances of same type. astbuf2->str = *$3; delete $3; astbuf2->port_id = ++port_counter; 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_or_multirange TOK_ID { AstNode *node = $2; node->str = *$4; SET_AST_NODE_LOC(node, @4, @4); node->port_id = ++port_counter; 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) frontend_verilog_yyerror("Input port `%s' is declared as register.", $4->c_str()); ast_stack.back()->children.push_back(node); append_attr(node, $1); delete $4; } module_arg_opt_assignment | '.' '.' '.' { do_not_require_port_stubs = true; }; package: attr TOK_PACKAGE { enterTypeScope(); } TOK_ID { AstNode *mod = new AstNode(AST_PACKAGE); ast_stack.back()->children.push_back(mod); ast_stack.push_back(mod); current_ast_mod = mod; mod->str = *$4; 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(); }; package_body: package_body package_body_stmt | %empty; package_body_stmt: typedef_decl | localparam_decl | param_decl | task_func_decl; interface: TOK_INTERFACE { enterTypeScope(); } TOK_ID { do_not_require_port_stubs = false; AstNode *intf = new AstNode(AST_INTERFACE); ast_stack.back()->children.push_back(intf); ast_stack.push_back(intf); current_ast_mod = intf; port_stubs.clear(); port_counter = 0; intf->str = *$3; delete $3; } module_para_opt module_args_opt ';' interface_body TOK_ENDINTERFACE { if (port_stubs.size() != 0) frontend_verilog_yyerror("Missing details for module port `%s'.", port_stubs.begin()->first.c_str()); ast_stack.pop_back(); log_assert(ast_stack.size() == 1); current_ast_mod = NULL; exitTypeScope(); }; interface_body: interface_body interface_body_stmt | %empty; interface_body_stmt: param_decl | localparam_decl | typedef_decl | defparam_decl | wire_decl | always_stmt | assign_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; } | // 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; 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, @$); }; 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 { astbuf3->is_input = true; } | TOK_OUTPUT { astbuf3->is_output = true; } | TOK_INOUT { astbuf3->is_input = true; astbuf3->is_output = true; }; 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_CONST { current_wire_const = 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_VAR TOK_REG { astbuf3->is_reg = true; } | // logics TOK_VAR { astbuf3->is_logic = true; } | TOK_VAR logic_type { astbuf3->is_logic = true; } | logic_type { astbuf3->is_logic = true; } | TOK_GENVAR { astbuf3->type = AST_GENVAR; astbuf3->is_reg = true; astbuf3->is_signed = true; astbuf3->range_left = 31; astbuf3->range_right = 0; }; net_type: TOK_WOR { astbuf3->is_wor = true; } | TOK_WAND { astbuf3->is_wand = true; } | TOK_WIRE; logic_type: TOK_LOGIC { } | 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); $$->children.push_back($2); $$->children.push_back($4); } | '[' expr TOK_POS_INDEXED expr ']' { $$ = new AstNode(AST_RANGE); AstNode *expr = new AstNode(AST_SELFSZ, $2); $$->children.push_back(new AstNode(AST_SUB, new AstNode(AST_ADD, expr->clone(), $4), AstNode::mkconst_int(1, true))); $$->children.push_back(new AstNode(AST_ADD, expr, AstNode::mkconst_int(0, true))); } | '[' expr TOK_NEG_INDEXED expr ']' { $$ = new AstNode(AST_RANGE); AstNode *expr = new AstNode(AST_SELFSZ, $2); $$->children.push_back(new AstNode(AST_ADD, expr, AstNode::mkconst_int(0, true))); $$->children.push_back(new AstNode(AST_SUB, new AstNode(AST_ADD, expr->clone(), AstNode::mkconst_int(1, true)), $4)); } | '[' expr ']' { $$ = new AstNode(AST_RANGE); $$->children.push_back($2); }; non_opt_multirange: non_opt_range non_opt_range { $$ = new AstNode(AST_MULTIRANGE, $1, $2); } | non_opt_multirange non_opt_range { $$ = $1; $$->children.push_back($2); }; range: non_opt_range { $$ = $1; } | %empty { $$ = NULL; }; range_or_multirange: range { $$ = $1; } | non_opt_multirange { $$ = $1; }; 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 | bind_directive | always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; checker_decl: TOK_CHECKER TOK_ID ';' { AstNode *node = new AstNode(AST_GENBLOCK); node->str = *$2; ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } module_body TOK_ENDCHECKER { delete $2; ast_stack.pop_back(); }; task_func_decl: attr TOK_DPI_FUNCTION TOK_ID TOK_ID { current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$3), AstNode::mkconst_str(*$4)); current_function_or_task->str = *$4; append_attr(current_function_or_task, $1); ast_stack.back()->children.push_back(current_function_or_task); delete $3; delete $4; } opt_dpi_function_args ';' { current_function_or_task = NULL; } | attr TOK_DPI_FUNCTION TOK_ID '=' TOK_ID TOK_ID { current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$5), AstNode::mkconst_str(*$3)); current_function_or_task->str = *$6; append_attr(current_function_or_task, $1); ast_stack.back()->children.push_back(current_function_or_task); delete $3; delete $5; delete $6; } opt_dpi_function_args ';' { current_function_or_task = NULL; } | attr TOK_DPI_FUNCTION TOK_ID ':' TOK_ID '=' TOK_ID TOK_ID { current_function_or_task = new AstNode(AST_DPI_FUNCTION, AstNode::mkconst_str(*$7), AstNode::mkconst_str(*$3 + ":" + RTLIL::unescape_id(*$5))); current_function_or_task->str = *$8; append_attr(current_function_or_task, $1); ast_stack.back()->children.push_back(current_function_or_task); delete $3; delete $5; delete $7; delete $8; } opt_dpi_function_args ';' { current_function_or_task = NULL; } | attr TOK_TASK opt_automatic TOK_ID { current_function_or_task = new AstNode(AST_TASK); current_function_or_task->str = *$4; 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 $4; } task_func_args_opt ';' task_func_body TOK_ENDTASK { current_function_or_task = NULL; ast_stack.pop_back(); } | 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 = *$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 = *$5; outreg->is_signed = false; outreg->is_reg = true; 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 $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)); delete $1; delete $2; } | TOK_ID { current_function_or_task->children.push_back(AstNode::mkconst_str(*$1)); delete $1; }; opt_dpi_function_args: '(' dpi_function_args ')' | %empty; dpi_function_args: dpi_function_args ',' dpi_function_arg | dpi_function_args ',' | dpi_function_arg | %empty; opt_automatic: TOK_AUTOMATIC | %empty; task_func_args_opt: '(' ')' | %empty | '(' { albuf = nullptr; astbuf1 = nullptr; astbuf2 = nullptr; } task_func_args optional_comma { delete astbuf1; if (astbuf2 != NULL) delete astbuf2; free_attr(albuf); } ')'; task_func_args: task_func_port | task_func_args ',' task_func_port; task_func_port: 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; free_attr(albuf); } 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) { if (!sv_mode) frontend_verilog_yyerror("task/function argument direction missing"); albuf = new dict; astbuf1 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; astbuf1->is_input = true; astbuf2 = NULL; } } wire_name; task_func_body: task_func_body behavioral_stmt | %empty; /*************************** specify parser ***************************/ specify_block: TOK_SPECIFY specify_item_list TOK_ENDSPECIFY; specify_item_list: specify_item specify_item_list | %empty; specify_item: specify_if '(' specify_edge expr TOK_SPECIFY_OPER specify_target ')' '=' specify_rise_fall ';' { AstNode *en_expr = $1; char specify_edge = $3; AstNode *src_expr = $4; string *oper = $5; specify_target *target = $6; specify_rise_fall *timing = $9; if (specify_edge != 0 && target->dat == nullptr) frontend_verilog_yyerror("Found specify edge but no data spec.\n"); AstNode *cell = new AstNode(AST_CELL); ast_stack.back()->children.push_back(cell); cell->str = stringf("$specify$%d", autoidx++); cell->children.push_back(new AstNode(AST_CELLTYPE)); cell->children.back()->str = target->dat ? "$specify3" : "$specify2"; SET_AST_NODE_LOC(cell, en_expr ? @1 : @2, @10); char oper_polarity = 0; char oper_type = oper->at(0); if (oper->size() == 3) { oper_polarity = oper->at(0); oper_type = oper->at(1); } cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_type == '*', false, 1))); cell->children.back()->str = "\\FULL"; cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity != 0, false, 1))); cell->children.back()->str = "\\SRC_DST_PEN"; cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(oper_polarity == '+', false, 1))); cell->children.back()->str = "\\SRC_DST_POL"; cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_min)); cell->children.back()->str = "\\T_RISE_MIN"; cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_avg)); cell->children.back()->str = "\\T_RISE_TYP"; cell->children.push_back(new AstNode(AST_PARASET, timing->rise.t_max)); cell->children.back()->str = "\\T_RISE_MAX"; cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_min)); cell->children.back()->str = "\\T_FALL_MIN"; cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_avg)); cell->children.back()->str = "\\T_FALL_TYP"; cell->children.push_back(new AstNode(AST_PARASET, timing->fall.t_max)); cell->children.back()->str = "\\T_FALL_MAX"; cell->children.push_back(new AstNode(AST_ARGUMENT, en_expr ? en_expr : AstNode::mkconst_int(1, false, 1))); cell->children.back()->str = "\\EN"; cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr)); cell->children.back()->str = "\\SRC"; cell->children.push_back(new AstNode(AST_ARGUMENT, target->dst)); cell->children.back()->str = "\\DST"; if (target->dat) { cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge != 0, false, 1))); cell->children.back()->str = "\\EDGE_EN"; cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(specify_edge == 'p', false, 1))); cell->children.back()->str = "\\EDGE_POL"; cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op != 0, false, 1))); cell->children.back()->str = "\\DAT_DST_PEN"; cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_int(target->polarity_op == '+', false, 1))); cell->children.back()->str = "\\DAT_DST_POL"; cell->children.push_back(new AstNode(AST_ARGUMENT, target->dat)); cell->children.back()->str = "\\DAT"; } delete oper; delete target; delete timing; } | TOK_ID '(' specify_edge expr specify_condition ',' specify_edge expr specify_condition ',' specify_triple specify_opt_triple ')' ';' { if (*$1 != "$setup" && *$1 != "$hold" && *$1 != "$setuphold" && *$1 != "$removal" && *$1 != "$recovery" && *$1 != "$recrem" && *$1 != "$skew" && *$1 != "$timeskew" && *$1 != "$fullskew" && *$1 != "$nochange") frontend_verilog_yyerror("Unsupported specify rule type: %s\n", $1->c_str()); AstNode *src_pen = AstNode::mkconst_int($3 != 0, false, 1); AstNode *src_pol = AstNode::mkconst_int($3 == 'p', false, 1); AstNode *src_expr = $4, *src_en = $5 ? $5 : AstNode::mkconst_int(1, false, 1); AstNode *dst_pen = AstNode::mkconst_int($7 != 0, false, 1); AstNode *dst_pol = AstNode::mkconst_int($7 == 'p', false, 1); AstNode *dst_expr = $8, *dst_en = $9 ? $9 : AstNode::mkconst_int(1, false, 1); specify_triple *limit = $11; specify_triple *limit2 = $12; AstNode *cell = new AstNode(AST_CELL); ast_stack.back()->children.push_back(cell); cell->str = stringf("$specify$%d", autoidx++); cell->children.push_back(new AstNode(AST_CELLTYPE)); cell->children.back()->str = "$specrule"; SET_AST_NODE_LOC(cell, @1, @14); cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_str(*$1))); cell->children.back()->str = "\\TYPE"; cell->children.push_back(new AstNode(AST_PARASET, limit->t_min)); cell->children.back()->str = "\\T_LIMIT_MIN"; cell->children.push_back(new AstNode(AST_PARASET, limit->t_avg)); cell->children.back()->str = "\\T_LIMIT_TYP"; cell->children.push_back(new AstNode(AST_PARASET, limit->t_max)); cell->children.back()->str = "\\T_LIMIT_MAX"; cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2->t_min : AstNode::mkconst_int(0, true))); cell->children.back()->str = "\\T_LIMIT2_MIN"; cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2->t_avg : AstNode::mkconst_int(0, true))); cell->children.back()->str = "\\T_LIMIT2_TYP"; cell->children.push_back(new AstNode(AST_PARASET, limit2 ? limit2->t_max : AstNode::mkconst_int(0, true))); cell->children.back()->str = "\\T_LIMIT2_MAX"; cell->children.push_back(new AstNode(AST_PARASET, src_pen)); cell->children.back()->str = "\\SRC_PEN"; cell->children.push_back(new AstNode(AST_PARASET, src_pol)); cell->children.back()->str = "\\SRC_POL"; cell->children.push_back(new AstNode(AST_PARASET, dst_pen)); cell->children.back()->str = "\\DST_PEN"; cell->children.push_back(new AstNode(AST_PARASET, dst_pol)); cell->children.back()->str = "\\DST_POL"; cell->children.push_back(new AstNode(AST_ARGUMENT, src_en)); cell->children.back()->str = "\\SRC_EN"; cell->children.push_back(new AstNode(AST_ARGUMENT, src_expr)); cell->children.back()->str = "\\SRC"; cell->children.push_back(new AstNode(AST_ARGUMENT, dst_en)); cell->children.back()->str = "\\DST_EN"; cell->children.push_back(new AstNode(AST_ARGUMENT, dst_expr)); cell->children.back()->str = "\\DST"; delete $1; delete limit; delete limit2; }; specify_opt_triple: ',' specify_triple { $$ = $2; } | %empty { $$ = nullptr; }; specify_if: TOK_IF '(' expr ')' { $$ = $3; } | %empty { $$ = nullptr; }; specify_condition: TOK_SPECIFY_AND expr { $$ = $2; } | %empty { $$ = nullptr; }; specify_target: expr { $$ = new specify_target; $$->polarity_op = 0; $$->dst = $1; $$->dat = nullptr; } | '(' expr ':' expr ')'{ $$ = new specify_target; $$->polarity_op = 0; $$->dst = $2; $$->dat = $4; } | '(' expr TOK_NEG_INDEXED expr ')'{ $$ = new specify_target; $$->polarity_op = '-'; $$->dst = $2; $$->dat = $4; } | '(' expr TOK_POS_INDEXED expr ')'{ $$ = new specify_target; $$->polarity_op = '+'; $$->dst = $2; $$->dat = $4; }; specify_edge: TOK_POSEDGE { $$ = 'p'; } | TOK_NEGEDGE { $$ = 'n'; } | %empty { $$ = 0; }; specify_rise_fall: specify_triple { $$ = new specify_rise_fall; $$->rise = *$1; $$->fall.t_min = $1->t_min->clone(); $$->fall.t_avg = $1->t_avg->clone(); $$->fall.t_max = $1->t_max->clone(); delete $1; } | '(' specify_triple ',' specify_triple ')' { $$ = new specify_rise_fall; $$->rise = *$2; $$->fall = *$4; delete $2; delete $4; } | '(' specify_triple ',' specify_triple ',' specify_triple ')' { $$ = new specify_rise_fall; $$->rise = *$2; $$->fall = *$4; delete $2; delete $4; delete $6; log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); } | '(' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ')' { $$ = new specify_rise_fall; $$->rise = *$2; $$->fall = *$4; delete $2; delete $4; delete $6; delete $8; delete $10; delete $12; log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); } | '(' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ')' { $$ = new specify_rise_fall; $$->rise = *$2; $$->fall = *$4; delete $2; delete $4; delete $6; delete $8; delete $10; delete $12; delete $14; delete $16; delete $18; delete $20; delete $22; delete $24; log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); } specify_triple: expr { $$ = new specify_triple; $$->t_min = $1; $$->t_avg = $1->clone(); $$->t_max = $1->clone(); } | expr ':' expr ':' expr { $$ = new specify_triple; $$->t_min = $1; $$->t_avg = $3; $$->t_max = $5; }; /******************** ignored specify parser **************************/ ignored_specify_block: TOK_IGNORED_SPECIFY ignored_specify_item_opt TOK_ENDSPECIFY | TOK_IGNORED_SPECIFY TOK_ENDSPECIFY ; ignored_specify_item_opt: ignored_specify_item_opt ignored_specify_item | ignored_specify_item ; ignored_specify_item: specparam_declaration // | pulsestyle_declaration // | showcancelled_declaration | path_declaration | system_timing_declaration ; specparam_declaration: TOK_SPECPARAM list_of_specparam_assignments ';' | TOK_SPECPARAM specparam_range list_of_specparam_assignments ';' ; // IEEE 1364-2005 calls this sinmply 'range' but the current 'range' rule allows empty match // and the 'non_opt_range' rule allows index ranges not allowed by 1364-2005 // exxxxtending this for SV specparam would change this anyhow specparam_range: '[' ignspec_constant_expression ':' ignspec_constant_expression ']' ; list_of_specparam_assignments: specparam_assignment | list_of_specparam_assignments ',' specparam_assignment; specparam_assignment: ignspec_id '=' ignspec_expr ; ignspec_opt_cond: TOK_IF '(' ignspec_expr ')' | %empty; path_declaration : simple_path_declaration ';' // | edge_sensitive_path_declaration // | state_dependent_path_declaration ; simple_path_declaration : ignspec_opt_cond parallel_path_description '=' path_delay_value | ignspec_opt_cond full_path_description '=' path_delay_value ; path_delay_value : '(' ignspec_expr list_of_path_delay_extra_expressions ')' | ignspec_expr | ignspec_expr list_of_path_delay_extra_expressions ; list_of_path_delay_extra_expressions : ',' ignspec_expr | ',' ignspec_expr list_of_path_delay_extra_expressions ; specify_edge_identifier : TOK_POSEDGE | TOK_NEGEDGE ; parallel_path_description : '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' | '(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor opt_polarity_operator ':' ignspec_expr ')' ')' | '(' specify_edge_identifier specify_input_terminal_descriptor '=' '>' '(' specify_output_terminal_descriptor TOK_POS_INDEXED ignspec_expr ')' ')' ; full_path_description : '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' | '(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs opt_polarity_operator ':' ignspec_expr ')' ')' | '(' specify_edge_identifier list_of_path_inputs '*' '>' '(' list_of_path_outputs TOK_POS_INDEXED ignspec_expr ')' ')' ; // This was broken into 2 rules to solve shift/reduce conflicts list_of_path_inputs : specify_input_terminal_descriptor opt_polarity_operator | specify_input_terminal_descriptor more_path_inputs opt_polarity_operator ; more_path_inputs : ',' specify_input_terminal_descriptor | more_path_inputs ',' specify_input_terminal_descriptor ; list_of_path_outputs : specify_output_terminal_descriptor | list_of_path_outputs ',' specify_output_terminal_descriptor ; opt_polarity_operator : '+' | '-' | %empty; // Good enough for the time being specify_input_terminal_descriptor : ignspec_id ; // Good enough for the time being specify_output_terminal_descriptor : ignspec_id ; system_timing_declaration : ignspec_id '(' system_timing_args ')' ';' ; system_timing_arg : TOK_POSEDGE ignspec_id | TOK_NEGEDGE ignspec_id | ignspec_expr ; system_timing_args : system_timing_arg | system_timing_args TOK_IGNORED_SPECIFY_AND system_timing_arg | system_timing_args ',' system_timing_arg ; // for the time being this is OK, but we may write our own expr here. // as I'm not sure it is legal to use a full expr here (probably not) // On the other hand, other rules requiring constant expressions also use 'expr' // (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness ignspec_constant_expression: expr { delete $1; }; ignspec_expr: expr { delete $1; } | expr ':' expr ':' expr { delete $1; delete $3; delete $5; }; ignspec_id: TOK_ID { delete $1; } range_or_multirange { delete $3; }; /**********************************************************************/ param_signed: TOK_SIGNED { astbuf1->is_signed = true; } | TOK_UNSIGNED { astbuf1->is_signed = false; } | %empty; param_integer: type_atom { astbuf1->is_reg = false; }; param_real: TOK_REAL { astbuf1->children.push_back(new AstNode(AST_REALVALUE)); }; param_range: range { if ($1 != NULL) { astbuf1->children.push_back($1); } }; param_integer_type: param_integer param_signed; 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 { addWiretypeNode($1, astbuf1); }; param_decl: attr TOK_PARAMETER { astbuf1 = new AstNode(AST_PARAMETER); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); append_attr(astbuf1, $1); } param_type param_decl_list ';' { delete astbuf1; }; localparam_decl: attr TOK_LOCALPARAM { astbuf1 = new AstNode(AST_LOCALPARAM); astbuf1->children.push_back(AstNode::mkconst_int(0, true)); append_attr(astbuf1, $1); } param_type param_decl_list ';' { delete astbuf1; }; param_decl_list: single_param_decl | param_decl_list ',' single_param_decl; single_param_decl: 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) frontend_verilog_yyerror("In pure Verilog (not SystemVerilog), parameter/localparam with an initializer must use the parameter/localparam keyword"); node = new AstNode(AST_PARAMETER); node->children.push_back(AstNode::mkconst_int(0, true)); } else { node = astbuf1->clone(); } node->str = *$1; ast_stack.back()->children.push_back(node); delete $1; SET_AST_NODE_LOC(node, @1, @1); }; defparam_decl: TOK_DEFPARAM defparam_decl_list ';'; defparam_decl_list: single_defparam_decl | defparam_decl_list ',' single_defparam_decl; single_defparam_decl: range rvalue '=' expr { AstNode *node = new AstNode(AST_DEFPARAM); node->children.push_back($2); node->children.push_back($4); if ($1 != NULL) node->children.push_back($1); ast_stack.back()->children.push_back(node); }; ///////// // enum ///////// enum_type: TOK_ENUM { static int enum_count; // create parent node for the enum astbuf2 = new AstNode(AST_ENUM); ast_stack.back()->children.push_back(astbuf2); astbuf2->str = std::string("$enum"); astbuf2->str += std::to_string(enum_count++); // 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 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: 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 ; type_signing: TOK_SIGNED { astbuf1->is_signed = true; } | TOK_UNSIGNED { astbuf1->is_signed = false; } | %empty ; enum_name_list: enum_name_decl | enum_name_list ',' enum_name_decl ; enum_name_decl: TOK_ID opt_enum_init { // put in fn log_assert(astbuf1); log_assert(astbuf2); auto node = astbuf1->clone(); node->str = *$1; delete $1; SET_AST_NODE_LOC(node, @1, @1); delete node->children[0]; node->children[0] = $2 ? $2 : new AstNode(AST_NONE); astbuf2->children.push_back(node); } ; opt_enum_init: '=' basic_expr { $$ = $2; } // TODO: restrict this | %empty { $$ = NULL; } ; enum_var_list: enum_var | enum_var_list ',' enum_var ; enum_var: TOK_ID { log_assert(astbuf1); log_assert(astbuf2); auto node = astbuf1->clone(); ast_stack.back()->children.push_back(node); node->str = *$1; delete $1; SET_AST_NODE_LOC(node, @1, @1); node->is_enum = true; } ; enum_decl: enum_type enum_var_list ';' { delete $1; } ; ////////////////// // struct or union ////////////////// struct_decl: attr struct_type { append_attr($2, $1); } struct_var_list ';' { delete astbuf2; } ; struct_type: struct_union { astbuf2 = $1; } struct_body { $$ = astbuf2; } ; struct_union: TOK_STRUCT { $$ = new AstNode(AST_STRUCT); } | TOK_UNION { $$ = new AstNode(AST_UNION); } ; struct_body: opt_packed '{' struct_member_list '}' ; opt_packed: TOK_PACKED opt_signed_struct | %empty { frontend_verilog_yyerror("Only PACKED supported at this time"); }; opt_signed_struct: TOK_SIGNED { astbuf2->is_signed = true; } | TOK_UNSIGNED { astbuf2->is_signed = false; } | %empty // default is unsigned ; struct_member_list: struct_member | struct_member_list struct_member ; struct_member: struct_member_type member_name_list ';' { delete astbuf1; } ; member_name_list: member_name | member_name_list ',' member_name ; member_name: TOK_ID { astbuf1->str = $1->substr(1); delete $1; astbuf3 = astbuf1->clone(); SET_AST_NODE_LOC(astbuf3, @1, @1); astbuf2->children.push_back(astbuf3); } range { if ($3) astbuf3->children.push_back($3); } ; struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_token ; member_type_token: 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 = $2; } struct_body { astbuf1 = astbuf2; // recover state astbuf2 = ast_stack.back(); ast_stack.pop_back(); } ; member_type: type_atom type_signing | type_vec type_signing | hierarchical_type_id { addWiretypeNode($1, astbuf1); } ; struct_var_list: struct_var | struct_var_list ',' struct_var ; struct_var: TOK_ID { auto *var_node = astbuf2->clone(); var_node->str = *$1; delete $1; SET_AST_NODE_LOC(var_node, @1, @1); ast_stack.back()->children.push_back(var_node); } ; ///////// // wire ///////// wire_decl: attr wire_type range_or_multirange { albuf = $1; astbuf1 = $2; astbuf2 = checkRange(astbuf1, $3); } delay wire_name_list { delete astbuf1; if (astbuf2 != NULL) delete astbuf2; free_attr(albuf); } ';' | attr TOK_SUPPLY0 TOK_ID { ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); ast_stack.back()->children.back()->str = *$3; append_attr(ast_stack.back()->children.back(), $1); ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(0, false, 1))); ast_stack.back()->children.back()->children[0]->str = *$3; delete $3; } opt_supply_wires ';' | attr TOK_SUPPLY1 TOK_ID { ast_stack.back()->children.push_back(new AstNode(AST_WIRE)); ast_stack.back()->children.back()->str = *$3; append_attr(ast_stack.back()->children.back(), $1); ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, new AstNode(AST_IDENTIFIER), AstNode::mkconst_int(1, false, 1))); ast_stack.back()->children.back()->children[0]->str = *$3; delete $3; } opt_supply_wires ';'; opt_supply_wires: %empty | opt_supply_wires ',' TOK_ID { AstNode *wire_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-2)->clone(); AstNode *assign_node = ast_stack.back()->children.at(GetSize(ast_stack.back()->children)-1)->clone(); wire_node->str = *$3; assign_node->children[0]->str = *$3; ast_stack.back()->children.push_back(wire_node); ast_stack.back()->children.push_back(assign_node); delete $3; }; wire_name_list: wire_name_and_opt_assign | wire_name_list ',' wire_name_and_opt_assign; wire_name_and_opt_assign: wire_name { bool attr_anyconst = false; bool attr_anyseq = false; bool attr_allconst = false; bool attr_allseq = false; if (ast_stack.back()->children.back()->get_bool_attribute(ID::anyconst)) { delete ast_stack.back()->children.back()->attributes.at(ID::anyconst); ast_stack.back()->children.back()->attributes.erase(ID::anyconst); attr_anyconst = true; } if (ast_stack.back()->children.back()->get_bool_attribute(ID::anyseq)) { delete ast_stack.back()->children.back()->attributes.at(ID::anyseq); ast_stack.back()->children.back()->attributes.erase(ID::anyseq); attr_anyseq = true; } if (ast_stack.back()->children.back()->get_bool_attribute(ID::allconst)) { delete ast_stack.back()->children.back()->attributes.at(ID::allconst); ast_stack.back()->children.back()->attributes.erase(ID::allconst); attr_allconst = true; } if (ast_stack.back()->children.back()->get_bool_attribute(ID::allseq)) { delete ast_stack.back()->children.back()->attributes.at(ID::allseq); ast_stack.back()->children.back()->attributes.erase(ID::allseq); attr_allseq = true; } if (current_wire_rand || attr_anyconst || attr_anyseq || attr_allconst || attr_allseq) { AstNode *wire = new AstNode(AST_IDENTIFIER); AstNode *fcall = new AstNode(AST_FCALL); wire->str = ast_stack.back()->children.back()->str; fcall->str = current_wire_const ? "\\$anyconst" : "\\$anyseq"; if (attr_anyconst) fcall->str = "\\$anyconst"; if (attr_anyseq) fcall->str = "\\$anyseq"; if (attr_allconst) fcall->str = "\\$allconst"; if (attr_allseq) fcall->str = "\\$allseq"; fcall->attributes[ID::reg] = AstNode::mkconst_str(RTLIL::unescape_id(wire->str)); ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, fcall)); } } | wire_name '=' expr { AstNode *wire = new AstNode(AST_IDENTIFIER); wire->str = ast_stack.back()->children.back()->str; if (astbuf1->is_input) { if (astbuf1->attributes.count(ID::defaultvalue)) delete astbuf1->attributes.at(ID::defaultvalue); astbuf1->attributes[ID::defaultvalue] = $3; } else if (astbuf1->is_reg || astbuf1->is_logic){ AstNode *assign = new AstNode(AST_ASSIGN_LE, wire, $3); AstNode *block = new AstNode(AST_BLOCK, assign); AstNode *init = new AstNode(AST_INITIAL, block); SET_AST_NODE_LOC(assign, @1, @3); SET_AST_NODE_LOC(block, @1, @3); SET_AST_NODE_LOC(init, @1, @3); ast_stack.back()->children.push_back(init); } else { AstNode *assign = new AstNode(AST_ASSIGN, wire, $3); SET_AST_NODE_LOC(assign, @1, @3); ast_stack.back()->children.push_back(assign); } }; wire_name: TOK_ID range_or_multirange { if (astbuf1 == nullptr) frontend_verilog_yyerror("Internal error - should not happen - no AST_WIRE node."); AstNode *node = astbuf1->clone(); node->str = *$1; append_attr_clone(node, albuf); if (astbuf2 != NULL) node->children.push_back(astbuf2->clone()); if ($2 != NULL) { if (node->is_input || node->is_output) frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions."); if (!astbuf2 && !node->is_custom_type) { addRange(node, 0, 0, false); } rewriteAsMemoryNode(node, $2); } 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; } if (port_stubs.count(*$1) != 0) { if (!node->is_input && !node->is_output) frontend_verilog_yyerror("Module port `%s' is neither input nor output.", $1->c_str()); if (node->is_reg && node->is_input && !node->is_output && !sv_mode) frontend_verilog_yyerror("Input port `%s' is declared as register.", $1->c_str()); node->port_id = port_stubs[*$1]; port_stubs.erase(*$1); } else { if (node->is_input || node->is_output) frontend_verilog_yyerror("Module port `%s' is not declared in module header.", $1->c_str()); } } //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); ast_stack.back()->children.push_back(node); delete $1; }; assign_stmt: TOK_ASSIGN delay assign_expr_list ';'; assign_expr_list: assign_expr | assign_expr_list ',' assign_expr; assign_expr: lvalue '=' expr { AstNode *node = new AstNode(AST_ASSIGN, $1, $3); SET_AST_NODE_LOC(node, @$, @$); ast_stack.back()->children.push_back(node); }; type_name: TOK_ID // first time seen | TOK_USER_TYPE { if (isInLocalScope($1)) frontend_verilog_yyerror("Duplicate declaration of TYPEDEF '%s'", $1->c_str()+1); } ; typedef_decl: 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 && !astbuf1->is_custom_type) { addRange(astbuf1, 0, 0, false); } rewriteAsMemoryNode(astbuf1, $5); } addTypedefNode($4, astbuf1); } | TOK_TYPEDEF enum_struct_type type_name ';' { addTypedefNode($3, $2); } ; 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 ; cell_stmt: attr TOK_ID { astbuf1 = new AstNode(AST_CELL); append_attr(astbuf1, $1); astbuf1->children.push_back(new AstNode(AST_CELLTYPE)); astbuf1->children[0]->str = *$2; delete $2; } cell_parameter_list_opt cell_list ';' { delete astbuf1; } | attr tok_prim_wrapper delay { astbuf1 = new AstNode(AST_PRIMITIVE); astbuf1->str = *$2; append_attr(astbuf1, $1); delete $2; } prim_list ';' { delete astbuf1; }; tok_prim_wrapper: TOK_PRIMITIVE { $$ = $1; } | TOK_OR { $$ = new std::string("or"); }; cell_list: single_cell | 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) astbuf2->str = *$1; delete $1; 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) astbuf2->str = *$1; delete $1; ast_stack.back()->children.push_back(new AstNode(AST_CELLARRAY, $2, astbuf2)); } '(' cell_port_list ')'{ 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; single_prim: single_cell | /* no name */ { astbuf2 = astbuf1->clone(); ast_stack.back()->children.push_back(astbuf2); } '(' cell_port_list ')' { SET_AST_NODE_LOC(astbuf2, @1, @$); } cell_parameter_list_opt: '#' '(' cell_parameter_list ')' | %empty; cell_parameter_list: cell_parameter | cell_parameter_list ',' cell_parameter; cell_parameter: %empty | expr { AstNode *node = new AstNode(AST_PARASET); astbuf1->children.push_back(node); node->children.push_back($1); } | '.' TOK_ID '(' ')' { // just ignore empty parameters } | '.' TOK_ID '(' expr ')' { AstNode *node = new AstNode(AST_PARASET); node->str = *$2; astbuf1->children.push_back(node); node->children.push_back($4); delete $2; }; cell_port_list: cell_port_list_rules { // remove empty args from end of list while (!astbuf2->children.empty()) { AstNode *node = astbuf2->children.back(); if (node->type != AST_ARGUMENT) break; if (!node->children.empty()) break; if (!node->str.empty()) break; astbuf2->children.pop_back(); delete node; } // check port types bool has_positional_args = false; bool has_named_args = false; for (auto node : astbuf2->children) { if (node->type != AST_ARGUMENT) continue; if (node->str.empty()) has_positional_args = true; else has_named_args = true; } if (has_positional_args && has_named_args) frontend_verilog_yyerror("Mix of positional and named cell ports."); }; cell_port_list_rules: cell_port | cell_port_list_rules ',' cell_port; cell_port: attr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); free_attr($1); } | attr expr { AstNode *node = new AstNode(AST_ARGUMENT); astbuf2->children.push_back(node); node->children.push_back($2); free_attr($1); } | attr '.' TOK_ID '(' expr ')' { AstNode *node = new AstNode(AST_ARGUMENT); node->str = *$3; astbuf2->children.push_back(node); node->children.push_back($5); delete $3; free_attr($1); } | attr '.' TOK_ID '(' ')' { AstNode *node = new AstNode(AST_ARGUMENT); node->str = *$3; astbuf2->children.push_back(node); delete $3; free_attr($1); } | attr '.' TOK_ID { AstNode *node = new AstNode(AST_ARGUMENT); node->str = *$3; astbuf2->children.push_back(node); node->children.push_back(new AstNode(AST_IDENTIFIER)); node->children.back()->str = *$3; delete $3; free_attr($1); } | attr TOK_WILDCARD_CONNECT { 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: TOK_ALWAYS_COMB { $$ = false; } | TOK_ALWAYS_LATCH { $$ = true; }; always_or_always_ff: TOK_ALWAYS { $$ = false; } | TOK_ALWAYS_FF { $$ = true; }; always_stmt: attr always_or_always_ff { AstNode *node = new AstNode(AST_ALWAYS); append_attr(node, $1); if ($2) node->attributes[ID::always_ff] = AstNode::mkconst_int(1, false); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } always_cond { AstNode *block = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { SET_AST_NODE_LOC(ast_stack.back(), @6, @6); ast_stack.pop_back(); SET_AST_NODE_LOC(ast_stack.back(), @2, @$); ast_stack.pop_back(); SET_RULE_LOC(@$, @2, @$); } | attr always_comb_or_latch { AstNode *node = new AstNode(AST_ALWAYS); append_attr(node, $1); if ($2) node->attributes[ID::always_latch] = AstNode::mkconst_int(1, false); else node->attributes[ID::always_comb] = AstNode::mkconst_int(1, false); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); AstNode *block = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { ast_stack.pop_back(); ast_stack.pop_back(); } | attr TOK_INITIAL { AstNode *node = new AstNode(AST_INITIAL); append_attr(node, $1); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); AstNode *block = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { ast_stack.pop_back(); ast_stack.pop_back(); }; always_cond: '@' '(' always_events ')' | '@' '(' '*' ')' | '@' ATTR_BEGIN ')' | '@' '(' ATTR_END | '@' '*' | %empty; always_events: always_event | always_events TOK_OR always_event | always_events ',' always_event; always_event: TOK_POSEDGE expr { AstNode *node = new AstNode(AST_POSEDGE); SET_AST_NODE_LOC(node, @1, @1); ast_stack.back()->children.push_back(node); node->children.push_back($2); } | TOK_NEGEDGE expr { AstNode *node = new AstNode(AST_NEGEDGE); SET_AST_NODE_LOC(node, @1, @1); ast_stack.back()->children.push_back(node); node->children.push_back($2); } | expr { AstNode *node = new AstNode(AST_EDGE); ast_stack.back()->children.push_back(node); node->children.push_back($1); }; opt_label: ':' TOK_ID { $$ = $2; } | %empty { $$ = NULL; }; opt_sva_label: TOK_SVA_LABEL ':' { $$ = $1; } | %empty { $$ = NULL; }; opt_property: TOK_PROPERTY { $$ = true; } | TOK_FINAL { $$ = false; } | %empty { $$ = false; }; modport_stmt: TOK_MODPORT TOK_ID { AstNode *modport = new AstNode(AST_MODPORT); ast_stack.back()->children.push_back(modport); ast_stack.push_back(modport); modport->str = *$2; delete $2; } modport_args_opt { ast_stack.pop_back(); log_assert(ast_stack.size() == 2); } ';' modport_args_opt: '(' ')' | '(' modport_args optional_comma ')'; modport_args: modport_arg | modport_args ',' modport_arg; modport_arg: modport_type_token modport_member | modport_member modport_member: TOK_ID { AstNode *modport_member = new AstNode(AST_MODPORTMEMBER); ast_stack.back()->children.push_back(modport_member); modport_member->str = *$1; modport_member->is_input = current_modport_input; modport_member->is_output = current_modport_output; delete $1; } modport_type_token: TOK_INPUT {current_modport_input = 1; current_modport_output = 0;} | TOK_OUTPUT {current_modport_input = 0; current_modport_output = 1;} assert: opt_sva_label TOK_ASSERT opt_property '(' expr ')' ';' { if (noassert_mode) { delete $5; } else { AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); } if ($1 != nullptr) delete $1; } | opt_sva_label TOK_ASSUME opt_property '(' expr ')' ';' { if (noassume_mode) { delete $5; } else { AstNode *node = new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); } if ($1 != nullptr) delete $1; } | opt_sva_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' { if (noassert_mode) { delete $6; } else { AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); } if ($1 != nullptr) delete $1; } | opt_sva_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' { if (noassume_mode) { delete $6; } else { AstNode *node = new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); } if ($1 != nullptr) delete $1; } | opt_sva_label TOK_COVER opt_property '(' expr ')' ';' { AstNode *node = new AstNode(AST_COVER, $5); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) { node->str = *$1; delete $1; } ast_stack.back()->children.push_back(node); } | opt_sva_label TOK_COVER opt_property '(' ')' ';' { AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @5); if ($1 != nullptr) { node->str = *$1; delete $1; } ast_stack.back()->children.push_back(node); } | opt_sva_label TOK_COVER ';' { AstNode *node = new AstNode(AST_COVER, AstNode::mkconst_int(1, false)); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @2); if ($1 != nullptr) { node->str = *$1; delete $1; } ast_stack.back()->children.push_back(node); } | opt_sva_label TOK_RESTRICT opt_property '(' expr ')' ';' { if (norestrict_mode) { delete $5; } else { AstNode *node = new AstNode(AST_ASSUME, $5); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @6); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); } if (!$3) log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n"); if ($1 != nullptr) delete $1; } | opt_sva_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' { if (norestrict_mode) { delete $6; } else { AstNode *node = new AstNode(AST_FAIR, $6); SET_AST_NODE_LOC(node, ($1 != nullptr ? @1 : @2), @7); if ($1 != nullptr) node->str = *$1; ast_stack.back()->children.push_back(node); } if (!$3) log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n"); if ($1 != nullptr) delete $1; }; assert_property: opt_sva_label TOK_ASSERT TOK_PROPERTY '(' expr ')' ';' { AstNode *node = new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5); SET_AST_NODE_LOC(node, @1, @6); ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { AstNode *node = new AstNode(AST_ASSUME, $5); SET_AST_NODE_LOC(node, @1, @6); ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { AstNode *node = new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6); SET_AST_NODE_LOC(node, @1, @7); ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { AstNode *node = new AstNode(AST_FAIR, $6); SET_AST_NODE_LOC(node, @1, @7); ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_COVER TOK_PROPERTY '(' expr ')' ';' { AstNode *node = new AstNode(AST_COVER, $5); SET_AST_NODE_LOC(node, @1, @6); ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } | opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' { if (norestrict_mode) { delete $5; } else { AstNode *node = new AstNode(AST_ASSUME, $5); SET_AST_NODE_LOC(node, @1, @6); ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } } | opt_sva_label TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { if (norestrict_mode) { delete $6; } else { AstNode *node = new AstNode(AST_FAIR, $6); SET_AST_NODE_LOC(node, @1, @7); ast_stack.back()->children.push_back(node); if ($1 != nullptr) { ast_stack.back()->children.back()->str = *$1; delete $1; } } }; simple_behavioral_stmt: attr lvalue '=' delay expr { AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $5); ast_stack.back()->children.push_back(node); SET_AST_NODE_LOC(node, @2, @5); append_attr(node, $1); } | attr lvalue attr inc_or_dec_op { addIncOrDecStmt($1, $2, $3, $4, @1, @4); } | 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); ast_stack.back()->children.push_back(node); SET_AST_NODE_LOC(node, @2, @5); append_attr(node, $1); } | 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); SET_AST_NODE_LOC(node, @1, @3); delete $1; } | non_io_wire_type range TOK_ID { frontend_verilog_yyerror("For loop variable declaration is missing initialization!"); } | 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 behavioral_stmt: defattr | assert | wire_decl | param_decl | localparam_decl | typedef_decl | non_opt_delay behavioral_stmt | simple_behavioral_stmt ';' | attr ';' { free_attr($1); } | attr hierarchical_id { AstNode *node = new AstNode(AST_TCALL); node->str = *$2; delete $2; ast_stack.back()->children.push_back(node); 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 { AstNode *node = new AstNode(AST_TCALL); node->str = *$2; delete $2; ast_stack.back()->children.push_back(node); 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 { enterTypeScope(); } opt_label { AstNode *node = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); append_attr(node, $1); if ($4 != NULL) node->str = *$4; } behavioral_stmt_list TOK_END opt_label { exitTypeScope(); 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; ast_stack.pop_back(); } | attr TOK_FOR '(' { AstNode *node = new AstNode(AST_FOR); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); append_attr(node, $1); } 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 { SET_AST_NODE_LOC(ast_stack.back(), @13, @13); ast_stack.pop_back(); SET_AST_NODE_LOC(ast_stack.back(), @2, @13); ast_stack.pop_back(); } | attr TOK_WHILE '(' expr ')' { AstNode *node = new AstNode(AST_WHILE); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); append_attr(node, $1); AstNode *block = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back($4); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { SET_AST_NODE_LOC(ast_stack.back(), @7, @7); ast_stack.pop_back(); ast_stack.pop_back(); } | attr TOK_REPEAT '(' expr ')' { AstNode *node = new AstNode(AST_REPEAT); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); append_attr(node, $1); AstNode *block = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back($4); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { SET_AST_NODE_LOC(ast_stack.back(), @7, @7); ast_stack.pop_back(); ast_stack.pop_back(); } | attr TOK_IF '(' expr ')' { AstNode *node = new AstNode(AST_CASE); AstNode *block = new AstNode(AST_BLOCK); AstNode *cond = new AstNode(AST_COND, AstNode::mkconst_int(1, false, 1), block); SET_AST_NODE_LOC(cond, @4, @4); ast_stack.back()->children.push_back(node); node->children.push_back(new AstNode(AST_REDUCE_BOOL, $4)); node->children.push_back(cond); ast_stack.push_back(node); ast_stack.push_back(block); append_attr(node, $1); } behavioral_stmt { SET_AST_NODE_LOC(ast_stack.back(), @7, @7); } optional_else { ast_stack.pop_back(); SET_AST_NODE_LOC(ast_stack.back(), @2, @9); ast_stack.pop_back(); } | case_attr case_type '(' expr ')' { AstNode *node = new AstNode(AST_CASE, $4); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); append_attr(node, $1); SET_AST_NODE_LOC(ast_stack.back(), @4, @4); } opt_synopsys_attr case_body TOK_ENDCASE { SET_AST_NODE_LOC(ast_stack.back(), @2, @9); case_type_stack.pop_back(); ast_stack.pop_back(); }; case_attr: attr { $$ = $1; } | attr TOK_UNIQUE0 { (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); $$ = $1; } | 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; }; case_type: TOK_CASE { case_type_stack.push_back(0); } | TOK_CASEX { case_type_stack.push_back('x'); } | TOK_CASEZ { case_type_stack.push_back('z'); }; opt_synopsys_attr: opt_synopsys_attr TOK_SYNOPSYS_FULL_CASE { if (ast_stack.back()->attributes.count(ID::full_case) == 0) ast_stack.back()->attributes[ID::full_case] = AstNode::mkconst_int(1, false); } | opt_synopsys_attr TOK_SYNOPSYS_PARALLEL_CASE { if (ast_stack.back()->attributes.count(ID::parallel_case) == 0) ast_stack.back()->attributes[ID::parallel_case] = AstNode::mkconst_int(1, false); } | %empty; behavioral_stmt_list: behavioral_stmt_list behavioral_stmt | %empty; optional_else: TOK_ELSE { AstNode *block = new AstNode(AST_BLOCK); AstNode *cond = new AstNode(AST_COND, new AstNode(AST_DEFAULT), block); SET_AST_NODE_LOC(cond, @1, @1); ast_stack.pop_back(); ast_stack.back()->children.push_back(cond); ast_stack.push_back(block); } behavioral_stmt { SET_AST_NODE_LOC(ast_stack.back(), @3, @3); } | %empty %prec FAKE_THEN; case_body: case_body case_item | %empty; case_item: { AstNode *node = new AstNode( case_type_stack.size() && case_type_stack.back() == 'x' ? AST_CONDX : case_type_stack.size() && case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } case_select { AstNode *block = new AstNode(AST_BLOCK); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); case_type_stack.push_back(0); } behavioral_stmt { case_type_stack.pop_back(); SET_AST_NODE_LOC(ast_stack.back(), @4, @4); ast_stack.pop_back(); ast_stack.pop_back(); }; gen_case_body: gen_case_body gen_case_item | %empty; gen_case_item: { AstNode *node = new AstNode( case_type_stack.size() && case_type_stack.back() == 'x' ? AST_CONDX : case_type_stack.size() && case_type_stack.back() == 'z' ? AST_CONDZ : AST_COND); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } case_select { case_type_stack.push_back(0); SET_AST_NODE_LOC(ast_stack.back(), @2, @2); } gen_stmt_block { case_type_stack.pop_back(); ast_stack.pop_back(); }; case_select: case_expr_list ':' | TOK_DEFAULT; case_expr_list: TOK_DEFAULT { AstNode *node = new AstNode(AST_DEFAULT); SET_AST_NODE_LOC(node, @1, @1); ast_stack.back()->children.push_back(node); } | TOK_SVA_LABEL { AstNode *node = new AstNode(AST_IDENTIFIER); SET_AST_NODE_LOC(node, @1, @1); ast_stack.back()->children.push_back(node); ast_stack.back()->children.back()->str = *$1; delete $1; } | expr { ast_stack.back()->children.push_back($1); } | case_expr_list ',' expr { ast_stack.back()->children.push_back($3); }; rvalue: hierarchical_id '[' expr ']' '.' rvalue { $$ = new AstNode(AST_PREFIX, $3, $6); $$->str = *$1; SET_AST_NODE_LOC($$, @1, @6); delete $1; } | hierarchical_id range { $$ = new AstNode(AST_IDENTIFIER, $2); $$->str = *$1; SET_AST_NODE_LOC($$, @1, @1); delete $1; if ($2 == nullptr && ($$->str == "\\$initstate" || $$->str == "\\$anyconst" || $$->str == "\\$anyseq" || $$->str == "\\$allconst" || $$->str == "\\$allseq")) $$->type = AST_FCALL; } | hierarchical_id non_opt_multirange { $$ = new AstNode(AST_IDENTIFIER, $2); $$->str = *$1; SET_AST_NODE_LOC($$, @1, @1); delete $1; }; lvalue: rvalue { $$ = $1; } | '{' lvalue_concat_list '}' { $$ = $2; }; lvalue_concat_list: expr { $$ = new AstNode(AST_CONCAT); $$->children.push_back($1); } | expr ',' lvalue_concat_list { $$ = $3; $$->children.push_back($1); }; opt_arg_list: '(' arg_list optional_comma ')' | %empty; arg_list: arg_list2 | %empty; arg_list2: single_arg | arg_list ',' single_arg; single_arg: expr { ast_stack.back()->children.push_back($1); }; module_gen_body: module_gen_body gen_stmt_or_module_body_stmt | module_gen_body gen_block | %empty; gen_stmt_or_module_body_stmt: gen_stmt | module_body_stmt | attr ';' { 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); } 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 ')' { AstNode *node = new AstNode(AST_GENIF); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); ast_stack.back()->children.push_back($3); } gen_stmt_block opt_gen_else { SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | case_type '(' expr ')' { AstNode *node = new AstNode(AST_GENCASE, $3); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } gen_case_body TOK_ENDCASE { case_type_stack.pop_back(); 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 { AstNode *node = new AstNode(AST_GENBLOCK); node->str = $3 ? *$3 : std::string(); ast_stack.back()->children.push_back(node); 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(); }; // result is wrapped in a genblock only if necessary gen_stmt_block: { AstNode *node = new AstNode(AST_GENBLOCK); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); } 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; expr: basic_expr { $$ = $1; } | basic_expr '?' attr expr ':' expr { $$ = new AstNode(AST_TERNARY); $$->children.push_back($1); $$->children.push_back($4); $$->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: rvalue { $$ = $1; } | '(' expr ')' integral_number { if ($4->compare(0, 1, "'") != 0) frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. () , while %s is not a sized constant.", $4->c_str()); AstNode *bits = $2; AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); if (val == NULL) log_error("Value conversion failed: `%s'\n", $4->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); delete $4; } | hierarchical_id integral_number { if ($2->compare(0, 1, "'") != 0) frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. \'d0, while %s is not a sized constant.", $2->c_str()); AstNode *bits = new AstNode(AST_IDENTIFIER); bits->str = *$1; SET_AST_NODE_LOC(bits, @1, @1); AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); SET_AST_NODE_LOC(val, @2, @2); if (val == NULL) log_error("Value conversion failed: `%s'\n", $2->c_str()); $$ = new AstNode(AST_TO_BITS, bits, val); delete $1; delete $2; } | integral_number { $$ = const2ast(*$1, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); SET_AST_NODE_LOC($$, @1, @1); if ($$ == NULL) log_error("Value conversion failed: `%s'\n", $1->c_str()); delete $1; } | TOK_REALVAL { $$ = new AstNode(AST_REALVALUE); char *p = (char*)malloc(GetSize(*$1) + 1), *q; for (int i = 0, j = 0; j < GetSize(*$1); j++) if ((*$1)[j] != '_') p[i++] = (*$1)[j], p[i] = 0; $$->realvalue = strtod(p, &q); SET_AST_NODE_LOC($$, @1, @1); log_assert(*q == 0); delete $1; free(p); } | TOK_STRING { $$ = AstNode::mkconst_str(*$1); SET_AST_NODE_LOC($$, @1, @1); delete $1; } | hierarchical_id attr { AstNode *node = new AstNode(AST_FCALL); node->str = *$1; delete $1; ast_stack.push_back(node); SET_AST_NODE_LOC(node, @1, @1); append_attr(node, $2); } '(' arg_list optional_comma ')' { $$ = ast_stack.back(); ast_stack.pop_back(); } | TOK_TO_SIGNED attr '(' expr ')' { $$ = new AstNode(AST_TO_SIGNED, $4); append_attr($$, $2); } | TOK_TO_UNSIGNED attr '(' expr ')' { $$ = new AstNode(AST_TO_UNSIGNED, $4); append_attr($$, $2); } | '(' expr ')' { $$ = $2; } | '(' expr ':' expr ':' expr ')' { delete $2; $$ = $4; delete $6; } | '{' concat_list '}' { $$ = $2; } | '{' expr '{' concat_list '}' '}' { $$ = new AstNode(AST_REPLICATE, $2, $4); } | '~' attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_BIT_NOT, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); } | basic_expr '&' attr basic_expr { $$ = new AstNode(AST_BIT_AND, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_NAND attr basic_expr { $$ = new AstNode(AST_BIT_NOT, new AstNode(AST_BIT_AND, $1, $4)); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '|' attr basic_expr { $$ = new AstNode(AST_BIT_OR, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_NOR attr basic_expr { $$ = new AstNode(AST_BIT_NOT, new AstNode(AST_BIT_OR, $1, $4)); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '^' attr basic_expr { $$ = new AstNode(AST_BIT_XOR, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_XNOR attr basic_expr { $$ = new AstNode(AST_BIT_XNOR, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | '&' attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_REDUCE_AND, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); } | OP_NAND attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_REDUCE_AND, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); $$ = new AstNode(AST_LOGIC_NOT, $$); } | '|' attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_REDUCE_OR, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); } | OP_NOR attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_REDUCE_OR, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); $$ = new AstNode(AST_LOGIC_NOT, $$); SET_AST_NODE_LOC($$, @1, @3); } | '^' attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_REDUCE_XOR, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); } | OP_XNOR attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_REDUCE_XNOR, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); } | basic_expr OP_SHL attr basic_expr { $$ = new AstNode(AST_SHIFT_LEFT, $1, new AstNode(AST_TO_UNSIGNED, $4)); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_SHR attr basic_expr { $$ = new AstNode(AST_SHIFT_RIGHT, $1, new AstNode(AST_TO_UNSIGNED, $4)); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_SSHL attr basic_expr { $$ = new AstNode(AST_SHIFT_SLEFT, $1, new AstNode(AST_TO_UNSIGNED, $4)); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_SSHR attr basic_expr { $$ = new AstNode(AST_SHIFT_SRIGHT, $1, new AstNode(AST_TO_UNSIGNED, $4)); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '<' attr basic_expr { $$ = new AstNode(AST_LT, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_LE attr basic_expr { $$ = new AstNode(AST_LE, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_EQ attr basic_expr { $$ = new AstNode(AST_EQ, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_NE attr basic_expr { $$ = new AstNode(AST_NE, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_EQX attr basic_expr { $$ = new AstNode(AST_EQX, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_NEX attr basic_expr { $$ = new AstNode(AST_NEX, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_GE attr basic_expr { $$ = new AstNode(AST_GE, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '>' attr basic_expr { $$ = new AstNode(AST_GT, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '+' attr basic_expr { $$ = new AstNode(AST_ADD, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '-' attr basic_expr { $$ = new AstNode(AST_SUB, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '*' attr basic_expr { $$ = new AstNode(AST_MUL, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '/' attr basic_expr { $$ = new AstNode(AST_DIV, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr '%' attr basic_expr { $$ = new AstNode(AST_MOD, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_POW attr basic_expr { $$ = new AstNode(AST_POW, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | '+' attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_POS, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); } | '-' attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_NEG, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); } | basic_expr OP_LAND attr basic_expr { $$ = new AstNode(AST_LOGIC_AND, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | basic_expr OP_LOR attr basic_expr { $$ = new AstNode(AST_LOGIC_OR, $1, $4); SET_AST_NODE_LOC($$, @1, @4); append_attr($$, $3); } | '!' attr basic_expr %prec UNARY_OPS { $$ = new AstNode(AST_LOGIC_NOT, $3); SET_AST_NODE_LOC($$, @1, @3); append_attr($$, $2); } | TOK_SIGNED OP_CAST '(' expr ')' { if (!sv_mode) frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); $$ = new AstNode(AST_TO_SIGNED, $4); SET_AST_NODE_LOC($$, @1, @4); } | TOK_UNSIGNED OP_CAST '(' expr ')' { if (!sv_mode) frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); $$ = new AstNode(AST_TO_UNSIGNED, $4); SET_AST_NODE_LOC($$, @1, @4); } | basic_expr OP_CAST '(' expr ')' { if (!sv_mode) frontend_verilog_yyerror("Static cast is only supported in SystemVerilog mode."); $$ = new AstNode(AST_CAST_SIZE, $1, $4); SET_AST_NODE_LOC($$, @1, @4); } | typedef_base_type OP_CAST '(' expr ')' { if (!sv_mode) 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: expr { $$ = new AstNode(AST_CONCAT, $1); } | expr ',' concat_list { $$ = $3; $$->children.push_back($1); }; integral_number: TOK_CONSTVAL { $$ = $1; } | TOK_UNBASED_UNSIZED_CONSTVAL { $$ = $1; } | TOK_BASE TOK_BASED_CONSTVAL { $1->append(*$2); $$ = $1; delete $2; } | TOK_CONSTVAL TOK_BASE TOK_BASED_CONSTVAL { $1->append(*$2).append(*$3); $$ = $1; delete $2; delete $3; }; yosys-0.52/guidelines/000077500000000000000000000000001477540374200147735ustar00rootroot00000000000000yosys-0.52/guidelines/GettingStarted000066400000000000000000000160371477540374200176550ustar00rootroot00000000000000Getting Started =============== Outline of a Yosys command -------------------------- Here is a the C++ code for a "hello_world" Yosys command (hello.cc): #include "kernel/yosys.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct HelloWorldPass : public Pass { HelloWorldPass() : Pass("hello_world") { } void execute(vector, Design*) override { log("Hello World!\n"); } } HelloWorldPass; PRIVATE_NAMESPACE_END This can be built into a Yosys module using the following command: yosys-config --exec --cxx --cxxflags --ldflags -o hello.so -shared hello.cc --ldlibs Or short: yosys-config --build hello.so hello.cc And then executed using the following command: yosys -m hello.so -p hello_world Yosys Data Structures --------------------- 1. Container classes based on hashing Yosys heavily relies on custom container data structures such as dict or pool defined in kernel/hashlib.h. dict is essentially a replacement for std::unordered_map and pool is a replacement for std::unordered_set. Please refer to docs/source/yosys_internals/hashing.rst for more information on those. Otherwise, Yosys makes use of the following: 2. Standard STL data types In Yosys we use std::vector and std::string whenever applicable. When dict and pool are not suitable then std::map and std::set are used instead. The types std::vector and std::string are also available as vector and string in the Yosys namespace. 3. RTLIL objects The current design (essentially a collection of modules, each defined by a netlist) is stored in memory using RTLIL object (declared in kernel/rtlil.h, automatically included by kernel/yosys.h). You should glance over at least the declarations for the following types in kernel/rtlil.h: RTLIL::IdString This is a handle for an identifier (e.g. cell or wire name). It feels a lot like a std::string, but is only a single int in size. (The actual string is stored in a global lookup table.) RTLIL::SigBit A single signal bit. I.e. either a constant state (0, 1, x, z) or a single bit from a wire. RTLIL::SigSpec Essentially a vector of SigBits. RTLIL::Wire RTLIL::Cell The building blocks of the netlist in a module. RTLIL::Module RTLIL::Design The module is a container with connected cells and wires in it. The design is a container with modules in it. All this types are also available without the RTLIL:: prefix in the Yosys namespace. 4. SigMap and other Helper Classes There are a couple of additional helper classes that are in wide use in Yosys. Most importantly there is SigMap (declared in kernel/sigtools.h). When a design has many wires in it that are connected to each other, then a single signal bit can have multiple valid names. The SigMap object can be used to map SigSpecs or SigBits to unique SigSpecs and SigBits that consistently only use one wire from such a group of connected wires. For example: SigBit a = module->addWire(NEW_ID); SigBit b = module->addWire(NEW_ID); module->connect(a, b); log("%d\n", a == b); // will print 0 SigMap sigmap(module); log("%d\n", sigmap(a) == sigmap(b)); // will print 1 Using the RTLIL Netlist Format ------------------------------ In the RTLIL netlist format the cell ports contain SigSpecs that point to the Wires. There are no references in the other direction. This has two direct consequences: (1) It is very easy to go from cells to wires but hard to go in the other way. (2) There is no danger in removing cells from the netlists, but removing wires can break the netlist format when there are still references to the wire somewhere in the netlist. The solution to (1) is easy: Create custom indexes that allow you to make fast lookups for the wire-to-cell direction. You can either use existing generic index structures to do that (such as the ModIndex class) or write your own index. For many application it is simplest to construct a custom index. For example: SigMap sigmap(module); dict sigbit_to_driver_index; for (auto cell : module->cells()) for (auto &conn : cell->connections()) if (cell->output(conn.first)) for (auto bit : sigmap(conn.second)) sigbit_to_driver_index[bit] = cell; Regarding (2): There is a general theme in Yosys that you don't remove wires from the design. You can rename them, unconnect them, but you do not actually remove the Wire object from the module. Instead you let the "clean" command take care of the dangling wires. On the other hand it is safe to remove cells (as long as you make sure this does not invalidate a custom index you are using in your code). Example Code ------------ The following yosys commands are a good starting point if you are looking for examples of how to use the Yosys API: docs/source/code_examples/stubnets/stubnets.cc docs/resources/PRESENTATION_Prog/my_cmd.cc Script Passes ------------- The ScriptPass base class can be used to implement passes that just call other passes, like a script. Examples for such passes are: techlibs/common/prep.cc techlibs/common/synth.cc In some cases it is easier to implement such a pass as regular pass, for example when ScriptPass doesn't provide the type of flow control desired. (But many of the script passes in Yosys that don't use ScriptPass simply predate the ScriptPass base class.) Examples for such passes are: passes/opt/opt.cc passes/proc/proc.cc Whether they use the ScriptPass base-class or not, a pass should always either call other passes without doing any non-trivial work itself, or should implement a non-trivial algorithm but not call any other passes. The reason for this is that this helps containing complexity in individual passes and simplifies debugging the entire system. Exceptions to this rule should be rare and limited to cases where calling other passes is optional and only happens when requested by the user (such as for example `techmap -autoproc`), or where it is about commands that are "top-level commands" in their own right, not components to be used in regular synthesis flows (such as the `bugpoint` command). A pass that would "naturally" call other passes and also do some work itself should be re-written in one of two ways: 1) It could be re-written as script pass with the parts that are not calls to other passes factored out into individual new passes. Usually in those cases the new sub passes share the same prefix as the top-level script pass. 2) It could be re-written so that it already expects the design in a certain state, expecting the calling script to set up this state before calling the pass in questions. Many back-ends are examples for the 2nd approach. For example, `write_aiger` does not convert the design into AIG representation, but expects the design to be already in this form, and prints an `Unsupported cell type` error message otherwise. 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.yosys-0.52/kernel/000077500000000000000000000000001477540374200141235ustar00rootroot00000000000000yosys-0.52/kernel/binding.cc000066400000000000000000000021001477540374200160350ustar00rootroot00000000000000/* * 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 yosys-0.52/kernel/binding.h000066400000000000000000000036331477540374200157130ustar00rootroot00000000000000/* -*- 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 yosys-0.52/kernel/bitpattern.h000066400000000000000000000074741477540374200164640ustar00rootroot00000000000000/* * 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 BITPATTERN_H #define BITPATTERN_H #include "kernel/log.h" #include "kernel/rtlil.h" YOSYS_NAMESPACE_BEGIN struct BitPatternPool { int width; struct bits_t { std::vector bitdata; mutable Hasher::hash_t cached_hash; bits_t(int width = 0) : bitdata(width), cached_hash(0) { } RTLIL::State &operator[](int index) { return bitdata[index]; } const RTLIL::State &operator[](int index) const { return bitdata[index]; } bool operator==(const bits_t &other) const { if (run_hash(*this) != run_hash(other)) return false; return bitdata == other.bitdata; } [[nodiscard]] Hasher hash_into(Hasher h) const { if (!cached_hash) cached_hash = run_hash(bitdata); h.eat(cached_hash); return h; } }; pool database; BitPatternPool(RTLIL::SigSpec sig) { width = sig.size(); if (width > 0) { bits_t pattern(width); for (int i = 0; i < width; i++) { if (sig[i].wire == NULL && sig[i].data <= RTLIL::State::S1) pattern[i] = sig[i].data; else pattern[i] = RTLIL::State::Sa; } database.insert(pattern); } } BitPatternPool(int width) { this->width = width; if (width > 0) { bits_t pattern(width); for (int i = 0; i < width; i++) pattern[i] = RTLIL::State::Sa; database.insert(pattern); } } bits_t sig2bits(RTLIL::SigSpec sig) { bits_t bits; bits.bitdata = sig.as_const().bits(); for (auto &b : bits.bitdata) if (b > RTLIL::State::S1) b = RTLIL::State::Sa; return bits; } bool match(bits_t a, bits_t b) { log_assert(int(a.bitdata.size()) == width); log_assert(int(b.bitdata.size()) == width); for (int i = 0; i < width; i++) if (a[i] <= RTLIL::State::S1 && b[i] <= RTLIL::State::S1 && a[i] != b[i]) return false; return true; } bool has_any(RTLIL::SigSpec sig) { bits_t bits = sig2bits(sig); for (auto &it : database) if (match(it, bits)) return true; return false; } bool has_all(RTLIL::SigSpec sig) { bits_t bits = sig2bits(sig); for (auto &it : database) if (match(it, bits)) { for (int i = 0; i < width; i++) if (bits[i] > RTLIL::State::S1 && it[i] <= RTLIL::State::S1) goto next_database_entry; return true; next_database_entry:; } return false; } bool take(RTLIL::SigSpec sig) { bool status = false; bits_t bits = sig2bits(sig); for (auto it = database.begin(); it != database.end();) if (match(*it, bits)) { for (int i = 0; i < width; i++) { if ((*it)[i] != RTLIL::State::Sa || bits[i] == RTLIL::State::Sa) continue; bits_t new_pattern; new_pattern.bitdata = it->bitdata; new_pattern[i] = bits[i] == RTLIL::State::S1 ? RTLIL::State::S0 : RTLIL::State::S1; database.insert(new_pattern); } it = database.erase(it); status = true; continue; } else ++it; return status; } bool take_all() { if (database.empty()) return false; database.clear(); return true; } bool empty() { return database.empty(); } }; YOSYS_NAMESPACE_END #endif yosys-0.52/kernel/calc.cc000066400000000000000000000610411477540374200153360ustar00rootroot00000000000000/* * 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. * */ // [[CITE]] Power-Modulus Algorithm // Schneier, Bruce (1996). Applied Cryptography: Protocols, Algorithms, and Source Code in C, // Second Edition (2nd ed.). Wiley. ISBN 978-0-471-11709-4, page 244 #include "kernel/yosys.h" #include "libs/bigint/BigIntegerLibrary.hh" YOSYS_NAMESPACE_BEGIN static void extend_u0(RTLIL::Const &arg, int width, bool is_signed) { RTLIL::State padding = RTLIL::State::S0; if (arg.size() > 0 && is_signed) padding = arg.back(); while (GetSize(arg) < width) arg.bits().push_back(padding); arg.bits().resize(width); } static BigInteger const2big(const RTLIL::Const &val, bool as_signed, int &undef_bit_pos) { BigUnsigned mag; BigInteger::Sign sign = BigInteger::positive; State inv_sign_bit = RTLIL::State::S1; auto num_bits = val.size(); if (as_signed && num_bits && val[num_bits-1] == RTLIL::State::S1) { inv_sign_bit = RTLIL::State::S0; sign = BigInteger::negative; num_bits--; } for (auto i = 0; i < num_bits; i++) if (val[i] == RTLIL::State::S0 || val[i] == RTLIL::State::S1) mag.setBit(i, val[i] == inv_sign_bit); else if (undef_bit_pos < 0) undef_bit_pos = i; if (sign == BigInteger::negative) mag += 1; return BigInteger(mag, sign); } static RTLIL::Const big2const(const BigInteger &val, int result_len, int undef_bit_pos) { if (undef_bit_pos >= 0) return RTLIL::Const(RTLIL::State::Sx, result_len); BigUnsigned mag = val.getMagnitude(); RTLIL::Const result(0, result_len); if (!mag.isZero()) { if (val.getSign() < 0) { mag--; for (auto i = 0; i < result_len; i++) result.bits()[i] = mag.getBit(i) ? RTLIL::State::S0 : RTLIL::State::S1; } else { for (auto i = 0; i < result_len; i++) result.bits()[i] = mag.getBit(i) ? RTLIL::State::S1 : RTLIL::State::S0; } } #if 0 if (undef_bit_pos >= 0) for (int i = undef_bit_pos; i < result_len; i++) result[i] = RTLIL::State::Sx; #endif return result; } static RTLIL::State logic_and(RTLIL::State a, RTLIL::State b) { if (a == RTLIL::State::S0) return RTLIL::State::S0; if (b == RTLIL::State::S0) return RTLIL::State::S0; if (a != RTLIL::State::S1) return RTLIL::State::Sx; if (b != RTLIL::State::S1) return RTLIL::State::Sx; return RTLIL::State::S1; } static RTLIL::State logic_or(RTLIL::State a, RTLIL::State b) { if (a == RTLIL::State::S1) return RTLIL::State::S1; if (b == RTLIL::State::S1) return RTLIL::State::S1; if (a != RTLIL::State::S0) return RTLIL::State::Sx; if (b != RTLIL::State::S0) return RTLIL::State::Sx; return RTLIL::State::S0; } static RTLIL::State logic_xor(RTLIL::State a, RTLIL::State b) { if (a != RTLIL::State::S0 && a != RTLIL::State::S1) return RTLIL::State::Sx; if (b != RTLIL::State::S0 && b != RTLIL::State::S1) return RTLIL::State::Sx; return a != b ? RTLIL::State::S1 : RTLIL::State::S0; } static RTLIL::State logic_xnor(RTLIL::State a, RTLIL::State b) { if (a != RTLIL::State::S0 && a != RTLIL::State::S1) return RTLIL::State::Sx; if (b != RTLIL::State::S0 && b != RTLIL::State::S1) return RTLIL::State::Sx; return a == b ? RTLIL::State::S1 : RTLIL::State::S0; } RTLIL::Const RTLIL::const_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) { if (result_len < 0) result_len = GetSize(arg1); RTLIL::Const arg1_ext = arg1; extend_u0(arg1_ext, result_len, signed1); RTLIL::Const result(RTLIL::State::Sx, result_len); for (auto i = 0; i < result_len; i++) { if (i >= GetSize(arg1_ext)) result.bits()[i] = RTLIL::State::S0; else if (arg1_ext.bits()[i] == RTLIL::State::S0) result.bits()[i] = RTLIL::State::S1; else if (arg1_ext.bits()[i] == RTLIL::State::S1) result.bits()[i] = RTLIL::State::S0; } return result; } static RTLIL::Const logic_wrapper(RTLIL::State(*logic_func)(RTLIL::State, RTLIL::State), RTLIL::Const arg1, RTLIL::Const arg2, bool signed1, bool signed2, int result_len = -1) { if (result_len < 0) result_len = max(GetSize(arg1), GetSize(arg2)); extend_u0(arg1, result_len, signed1); extend_u0(arg2, result_len, signed2); RTLIL::Const result(RTLIL::State::Sx, result_len); for (auto i = 0; i < result_len; i++) { RTLIL::State a = i < GetSize(arg1) ? arg1.bits()[i] : RTLIL::State::S0; RTLIL::State b = i < GetSize(arg2) ? arg2.bits()[i] : RTLIL::State::S0; result.bits()[i] = logic_func(a, b); } return result; } RTLIL::Const RTLIL::const_and(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { return logic_wrapper(logic_and, arg1, arg2, signed1, signed2, result_len); } RTLIL::Const RTLIL::const_or(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { return logic_wrapper(logic_or, arg1, arg2, signed1, signed2, result_len); } RTLIL::Const RTLIL::const_xor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { return logic_wrapper(logic_xor, arg1, arg2, signed1, signed2, result_len); } RTLIL::Const RTLIL::const_xnor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { return logic_wrapper(logic_xnor, arg1, arg2, signed1, signed2, result_len); } static RTLIL::Const logic_reduce_wrapper(RTLIL::State initial, RTLIL::State(*logic_func)(RTLIL::State, RTLIL::State), const RTLIL::Const &arg1, int result_len) { RTLIL::State temp = initial; for (auto i = 0; i < arg1.size(); i++) temp = logic_func(temp, arg1[i]); RTLIL::Const result(temp); while (GetSize(result) < result_len) result.bits().push_back(RTLIL::State::S0); return result; } RTLIL::Const RTLIL::const_reduce_and(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len) { return logic_reduce_wrapper(RTLIL::State::S1, logic_and, arg1, result_len); } RTLIL::Const RTLIL::const_reduce_or(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len) { return logic_reduce_wrapper(RTLIL::State::S0, logic_or, arg1, result_len); } RTLIL::Const RTLIL::const_reduce_xor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len) { return logic_reduce_wrapper(RTLIL::State::S0, logic_xor, arg1, result_len); } RTLIL::Const RTLIL::const_reduce_xnor(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len) { RTLIL::Const buffer = logic_reduce_wrapper(RTLIL::State::S0, logic_xor, arg1, result_len); if (!buffer.empty()) { if (buffer.front() == RTLIL::State::S0) buffer.bits().front() = RTLIL::State::S1; else if (buffer.front() == RTLIL::State::S1) buffer.bits().front() = RTLIL::State::S0; } return buffer; } RTLIL::Const RTLIL::const_reduce_bool(const RTLIL::Const &arg1, const RTLIL::Const&, bool, bool, int result_len) { return logic_reduce_wrapper(RTLIL::State::S0, logic_or, arg1, result_len); } RTLIL::Const RTLIL::const_logic_not(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) { int undef_bit_pos_a = -1; BigInteger a = const2big(arg1, signed1, undef_bit_pos_a); RTLIL::Const result(a.isZero() ? undef_bit_pos_a >= 0 ? RTLIL::State::Sx : RTLIL::State::S1 : RTLIL::State::S0); while (GetSize(result) < result_len) result.bits().push_back(RTLIL::State::S0); return result; } RTLIL::Const RTLIL::const_logic_and(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos_a = -1, undef_bit_pos_b = -1; BigInteger a = const2big(arg1, signed1, undef_bit_pos_a); BigInteger b = const2big(arg2, signed2, undef_bit_pos_b); RTLIL::State bit_a = a.isZero() ? undef_bit_pos_a >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1; RTLIL::State bit_b = b.isZero() ? undef_bit_pos_b >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1; RTLIL::Const result(logic_and(bit_a, bit_b)); while (GetSize(result) < result_len) result.bits().push_back(RTLIL::State::S0); return result; } RTLIL::Const RTLIL::const_logic_or(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos_a = -1, undef_bit_pos_b = -1; BigInteger a = const2big(arg1, signed1, undef_bit_pos_a); BigInteger b = const2big(arg2, signed2, undef_bit_pos_b); RTLIL::State bit_a = a.isZero() ? undef_bit_pos_a >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1; RTLIL::State bit_b = b.isZero() ? undef_bit_pos_b >= 0 ? RTLIL::State::Sx : RTLIL::State::S0 : RTLIL::State::S1; RTLIL::Const result(logic_or(bit_a, bit_b)); while (GetSize(result) < result_len) result.bits().push_back(RTLIL::State::S0); return result; } // Shift `arg1` by `arg2` bits. // If `direction` is +1, `arg1` is shifted right by `arg2` bits; if `direction` is -1, `arg1` is shifted left by `arg2` bits. // If `signed2` is true, `arg2` is interpreted as a signed integer; a negative `arg2` will cause a shift in the opposite direction. // Any required bits outside the bounds of `arg1` are padded with `vacant_bits` unless `sign_ext` is true, in which case any bits outside the left // bounds are filled with the leftmost bit of `arg1` (arithmetic shift). static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool sign_ext, bool signed2, int direction, int result_len, RTLIL::State vacant_bits = RTLIL::State::S0) { int undef_bit_pos = -1; BigInteger offset = const2big(arg2, signed2, undef_bit_pos) * direction; if (result_len < 0) result_len = GetSize(arg1); RTLIL::Const result(RTLIL::State::Sx, result_len); if (undef_bit_pos >= 0) return result; for (int i = 0; i < result_len; i++) { BigInteger pos = BigInteger(i) + offset; if (pos < 0) result.bits()[i] = vacant_bits; else if (pos >= BigInteger(GetSize(arg1))) result.bits()[i] = sign_ext ? arg1.back() : vacant_bits; else result.bits()[i] = arg1[pos.toInt()]; } return result; } RTLIL::Const RTLIL::const_shl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len) { RTLIL::Const arg1_ext = arg1; extend_u0(arg1_ext, result_len, signed1); return const_shift_worker(arg1_ext, arg2, false, false, -1, result_len); } RTLIL::Const RTLIL::const_shr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len) { RTLIL::Const arg1_ext = arg1; extend_u0(arg1_ext, max(result_len, GetSize(arg1)), signed1); return const_shift_worker(arg1_ext, arg2, false, false, +1, result_len); } RTLIL::Const RTLIL::const_sshl(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len) { return const_shift_worker(arg1, arg2, signed1, false, -1, result_len); } RTLIL::Const RTLIL::const_sshr(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool, int result_len) { return const_shift_worker(arg1, arg2, signed1, false, +1, result_len); } RTLIL::Const RTLIL::const_shift(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { RTLIL::Const arg1_ext = arg1; extend_u0(arg1_ext, max(result_len, GetSize(arg1)), signed1); return const_shift_worker(arg1_ext, arg2, false, signed2, +1, result_len); } RTLIL::Const RTLIL::const_shiftx(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool, bool signed2, int result_len) { return const_shift_worker(arg1, arg2, false, signed2, +1, result_len, RTLIL::State::Sx); } RTLIL::Const RTLIL::const_lt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; bool y = const2big(arg1, signed1, undef_bit_pos) < const2big(arg2, signed2, undef_bit_pos); RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); while (GetSize(result) < result_len) result.bits().push_back(RTLIL::State::S0); return result; } RTLIL::Const RTLIL::const_le(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; bool y = const2big(arg1, signed1, undef_bit_pos) <= const2big(arg2, signed2, undef_bit_pos); RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); while (GetSize(result) < result_len) result.bits().push_back(RTLIL::State::S0); return result; } RTLIL::Const RTLIL::const_eq(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { RTLIL::Const arg1_ext = arg1; RTLIL::Const arg2_ext = arg2; RTLIL::Const result(RTLIL::State::S0, result_len); int width = max(GetSize(arg1_ext), GetSize(arg2_ext)); extend_u0(arg1_ext, width, signed1 && signed2); extend_u0(arg2_ext, width, signed1 && signed2); RTLIL::State matched_status = RTLIL::State::S1; for (auto i = 0; i < arg1_ext.size(); i++) { if (arg1_ext.at(i) == RTLIL::State::S0 && arg2_ext.at(i) == RTLIL::State::S1) return result; if (arg1_ext.at(i) == RTLIL::State::S1 && arg2_ext.at(i) == RTLIL::State::S0) return result; if (arg1_ext.at(i) > RTLIL::State::S1 || arg2_ext.at(i) > RTLIL::State::S1) matched_status = RTLIL::State::Sx; } result.bits().front() = matched_status; return result; } RTLIL::Const RTLIL::const_ne(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { RTLIL::Const result = RTLIL::const_eq(arg1, arg2, signed1, signed2, result_len); if (result.front() == RTLIL::State::S0) result.bits().front() = RTLIL::State::S1; else if (result.front() == RTLIL::State::S1) result.bits().front() = RTLIL::State::S0; return result; } RTLIL::Const RTLIL::const_eqx(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { RTLIL::Const arg1_ext = arg1; RTLIL::Const arg2_ext = arg2; RTLIL::Const result(RTLIL::State::S0, result_len); int width = max(GetSize(arg1_ext), GetSize(arg2_ext)); extend_u0(arg1_ext, width, signed1 && signed2); extend_u0(arg2_ext, width, signed1 && signed2); for (auto i = 0; i < arg1_ext.size(); i++) { if (arg1_ext.at(i) != arg2_ext.at(i)) return result; } result.bits().front() = RTLIL::State::S1; return result; } RTLIL::Const RTLIL::const_nex(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { RTLIL::Const result = RTLIL::const_eqx(arg1, arg2, signed1, signed2, result_len); if (result.front() == RTLIL::State::S0) result.bits().front() = RTLIL::State::S1; else if (result.front() == RTLIL::State::S1) result.bits().front() = RTLIL::State::S0; return result; } RTLIL::Const RTLIL::const_ge(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; bool y = const2big(arg1, signed1, undef_bit_pos) >= const2big(arg2, signed2, undef_bit_pos); RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); while (GetSize(result) < result_len) result.bits().push_back(RTLIL::State::S0); return result; } RTLIL::Const RTLIL::const_gt(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; bool y = const2big(arg1, signed1, undef_bit_pos) > const2big(arg2, signed2, undef_bit_pos); RTLIL::Const result(undef_bit_pos >= 0 ? RTLIL::State::Sx : y ? RTLIL::State::S1 : RTLIL::State::S0); while (GetSize(result) < result_len) result.bits().push_back(RTLIL::State::S0); return result; } RTLIL::Const RTLIL::const_add(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) + const2big(arg2, signed2, undef_bit_pos); return big2const(y, result_len >= 0 ? result_len : max(GetSize(arg1), GetSize(arg2)), undef_bit_pos); } RTLIL::Const RTLIL::const_sub(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) - const2big(arg2, signed2, undef_bit_pos); return big2const(y, result_len >= 0 ? result_len : max(GetSize(arg1), GetSize(arg2)), undef_bit_pos); } RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger y = const2big(arg1, signed1, undef_bit_pos) * const2big(arg2, signed2, undef_bit_pos); return big2const(y, result_len >= 0 ? result_len : max(GetSize(arg1), GetSize(arg2)), min(undef_bit_pos, 0)); } // truncating division RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger a = const2big(arg1, signed1, undef_bit_pos); BigInteger b = const2big(arg2, signed2, undef_bit_pos); if (b.isZero()) return RTLIL::Const(RTLIL::State::Sx, result_len); bool result_neg = (a.getSign() == BigInteger::negative) != (b.getSign() == BigInteger::negative); a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(GetSize(arg1), GetSize(arg2)), min(undef_bit_pos, 0)); } // truncating modulo RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger a = const2big(arg1, signed1, undef_bit_pos); BigInteger b = const2big(arg2, signed2, undef_bit_pos); if (b.isZero()) return RTLIL::Const(RTLIL::State::Sx, result_len); bool result_neg = a.getSign() == BigInteger::negative; a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(GetSize(arg1), GetSize(arg2)), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_divfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger a = const2big(arg1, signed1, undef_bit_pos); BigInteger b = const2big(arg2, signed2, undef_bit_pos); if (b.isZero()) return RTLIL::Const(RTLIL::State::Sx, result_len); bool result_pos = (a.getSign() == BigInteger::negative) == (b.getSign() == BigInteger::negative); a = a.getSign() == BigInteger::negative ? -a : a; b = b.getSign() == BigInteger::negative ? -b : b; BigInteger result; if (result_pos || a == 0) { result = a / b; } else { // bigint division with negative numbers is wonky, make sure we only negate at the very end result = -((a + b - 1) / b); } return big2const(result, result_len >= 0 ? result_len : max(GetSize(arg1), GetSize(arg2)), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_modfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger a = const2big(arg1, signed1, undef_bit_pos); BigInteger b = const2big(arg2, signed2, undef_bit_pos); if (b.isZero()) return RTLIL::Const(RTLIL::State::Sx, result_len); BigInteger::Sign a_sign = a.getSign(); BigInteger::Sign b_sign = b.getSign(); a = a_sign == BigInteger::negative ? -a : a; b = b_sign == BigInteger::negative ? -b : b; BigInteger truncated = a_sign == BigInteger::negative ? -(a % b) : (a % b); BigInteger modulo; if (truncated == 0 || (a_sign == b_sign)) { modulo = truncated; } else { modulo = b_sign == BigInteger::negative ? truncated - b : truncated + b; } return big2const(modulo, result_len >= 0 ? result_len : max(GetSize(arg1), GetSize(arg2)), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) { int undef_bit_pos = -1; BigInteger a = const2big(arg1, signed1, undef_bit_pos); BigInteger b = const2big(arg2, signed2, undef_bit_pos); BigInteger y = 1; if (a == 0 && b < 0) return RTLIL::Const(RTLIL::State::Sx, result_len); if (a == 0 && b > 0) return RTLIL::Const(RTLIL::State::S0, result_len); if (b < 0) { if (a < -1 || a > 1) y = 0; if (a == -1) y = (-b % 2) == 0 ? 1 : -1; } if (b > 0) { // Power-modulo with 2^result_len as modulus BigInteger modulus = 1; int modulus_bits = (result_len >= 0 ? result_len : 1024); for (int i = 0; i < modulus_bits; i++) modulus *= 2; bool flip_result_sign = false; if (a < 0) { a *= -1; if (b % 2 == 1) flip_result_sign = true; } while (b > 0) { if (b % 2 == 1) y = (y * a) % modulus; b = b / 2; a = (a * a) % modulus; } if (flip_result_sign) y *= -1; } return big2const(y, result_len >= 0 ? result_len : max(GetSize(arg1), GetSize(arg2)), min(undef_bit_pos, 0)); } RTLIL::Const RTLIL::const_pos(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) { RTLIL::Const arg1_ext = arg1; extend_u0(arg1_ext, result_len, signed1); return arg1_ext; } RTLIL::Const RTLIL::const_buf(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) { RTLIL::Const arg1_ext = arg1; extend_u0(arg1_ext, result_len, signed1); return arg1_ext; } RTLIL::Const RTLIL::const_neg(const RTLIL::Const &arg1, const RTLIL::Const&, bool signed1, bool, int result_len) { RTLIL::Const arg1_ext = arg1; RTLIL::Const zero(RTLIL::State::S0, 1); 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 (auto i = 0; i < ret.size(); i++) if (ret[i] != arg2[i]) ret.bits()[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 (auto i = 0; i < arg3.size(); i++) if (arg3[i] == State::S1) return RTLIL::Const(std::vector(arg2.begin() + i*arg1.size(), arg2.begin() + (i+1)*arg1.size())); log_abort(); // unreachable } RTLIL::Const RTLIL::const_bmux(const RTLIL::Const &arg1, const RTLIL::Const &arg2) { std::vector t = arg1.to_bits(); for (int i = GetSize(arg2)-1; i >= 0; i--) { RTLIL::State sel = arg2.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[j] == State::S0 ? State::S0 : State::Sx); } else { for (int j = 0; j < width; j++) res.push_back(arg1[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 (auto i = 0; i < arg1.size(); i++) result.bits()[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 (auto i = 0; i < arg1.size(); i++) { if (arg3[i] != State::Sx || arg1[i] == arg2[i]) result.bits()[i] = arg3[i] == State::S1 ? arg2[i] : arg1[i]; } return result; } YOSYS_NAMESPACE_END yosys-0.52/kernel/cellaigs.cc000066400000000000000000000323221477540374200162170ustar00rootroot00000000000000/* * 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 "kernel/cellaigs.h" YOSYS_NAMESPACE_BEGIN AigNode::AigNode() { portbit = -1; inverter = false; left_parent = -1; right_parent = -1; } bool AigNode::operator==(const AigNode &other) const { if (portname != other.portname) return false; if (portbit != other.portbit) return false; if (inverter != other.inverter) return false; if (left_parent != other.left_parent) return false; if (right_parent != other.right_parent) return false; return true; } Hasher AigNode::hash_into(Hasher h) const { h.eat(portname); h.eat(portbit); h.eat(inverter); h.eat(left_parent); h.eat(right_parent); return h; } bool Aig::operator==(const Aig &other) const { return name == other.name; } Hasher Aig::hash_into(Hasher h) const { h.eat(name); return h; } struct AigMaker { Aig *aig; Cell *cell; idict aig_indices; int the_true_node; int the_false_node; AigMaker(Aig *aig, Cell *cell) : aig(aig), cell(cell) { the_true_node = -1; the_false_node = -1; } int node2index(const AigNode &node) { if (node.left_parent > node.right_parent) { AigNode n(node); std::swap(n.left_parent, n.right_parent); return node2index(n); } if (!aig_indices.count(node)) { aig_indices.expect(node, GetSize(aig->nodes)); aig->nodes.push_back(node); } return aig_indices.at(node); } int bool_node(bool value) { AigNode node; node.inverter = value; return node2index(node); } int inport(IdString portname, int portbit = 0, bool inverter = false) { if (portbit >= GetSize(cell->getPort(portname))) { if (cell->parameters.count(portname.str() + "_SIGNED") && cell->getParam(portname.str() + "_SIGNED").as_bool()) return inport(portname, GetSize(cell->getPort(portname))-1, inverter); return bool_node(inverter); } AigNode node; node.portname = portname; node.portbit = portbit; node.inverter = inverter; return node2index(node); } vector inport_vec(IdString portname, int width) { vector vec; for (int i = 0; i < width; i++) vec.push_back(inport(portname, i)); return vec; } int not_inport(IdString portname, int portbit = 0) { return inport(portname, portbit, true); } int not_gate(int A) { AigNode node(aig_indices[A]); node.outports.clear(); node.inverter = !node.inverter; return node2index(node); } int and_gate(int A, int B, bool inverter = false) { if (A == B) return inverter ? not_gate(A) : A; const AigNode &nA = aig_indices[A]; const AigNode &nB = aig_indices[B]; AigNode nB_inv(nB); nB_inv.inverter = !nB_inv.inverter; if (nA == nB_inv) return bool_node(inverter); bool nA_bool = nA.portbit < 0 && nA.left_parent < 0 && nA.right_parent < 0; bool nB_bool = nB.portbit < 0 && nB.left_parent < 0 && nB.right_parent < 0; if (nA_bool && nB_bool) { bool bA = nA.inverter; bool bB = nB.inverter; return bool_node(inverter != (bA && bB)); } if (nA_bool) { bool bA = nA.inverter; if (inverter) return bA ? not_gate(B) : bool_node(true); return bA ? B : bool_node(false); } if (nB_bool) { bool bB = nB.inverter; if (inverter) return bB ? not_gate(A) : bool_node(true); return bB ? A : bool_node(false); } AigNode node; node.inverter = inverter; node.left_parent = A; node.right_parent = B; return node2index(node); } int nand_gate(int A, int B) { return and_gate(A, B, true); } int or_gate(int A, int B) { return nand_gate(not_gate(A), not_gate(B)); } int nor_gate(int A, int B) { return and_gate(not_gate(A), not_gate(B)); } int xor_gate(int A, int B) { return nor_gate(and_gate(A, B), nor_gate(A, B)); } int xnor_gate(int A, int B) { return or_gate(and_gate(A, B), nor_gate(A, B)); } int andnot_gate(int A, int B) { return and_gate(A, not_gate(B)); } int ornot_gate(int A, int B) { return or_gate(A, not_gate(B)); } int mux_gate(int A, int B, int S) { return or_gate(and_gate(A, not_gate(S)), and_gate(B, S)); } vector adder(const vector &A, const vector &B, int carry, vector *X = nullptr, vector *CO = nullptr) { vector Y(GetSize(A)); log_assert(GetSize(A) == GetSize(B)); for (int i = 0; i < GetSize(A); i++) { Y[i] = xor_gate(xor_gate(A[i], B[i]), carry); carry = or_gate(and_gate(A[i], B[i]), and_gate(or_gate(A[i], B[i]), carry)); if (X != nullptr) X->at(i) = xor_gate(A[i], B[i]); if (CO != nullptr) CO->at(i) = carry; } return Y; } void outport(int node, IdString portname, int portbit = 0) { if (portbit < GetSize(cell->getPort(portname))) aig->nodes.at(node).outports.push_back(pair(portname, portbit)); } void outport_bool(int node, IdString portname) { outport(node, portname); for (int i = 1; i < GetSize(cell->getPort(portname)); i++) outport(bool_node(false), portname, i); } void outport_vec(const vector &vec, IdString portname) { for (int i = 0; i < GetSize(vec); i++) outport(vec.at(i), portname, i); } }; Aig::Aig(Cell *cell) { if (cell->type[0] != '$') return; AigMaker mk(this, cell); name = cell->type.str(); string mkname_last; bool mkname_a_signed = false; bool mkname_b_signed = false; bool mkname_is_signed = false; cell->parameters.sort(); for (auto p : cell->parameters) { if (p.first == ID::A_WIDTH && mkname_a_signed) { name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U'); } else if (p.first == ID::B_WIDTH && mkname_b_signed) { name = mkname_last + stringf(":%d%c", p.second.as_int(), mkname_is_signed ? 'S' : 'U'); } else { mkname_last = name; name += stringf(":%d", p.second.as_int()); } mkname_a_signed = false; mkname_b_signed = false; mkname_is_signed = false; if (p.first == ID::A_SIGNED) { mkname_a_signed = true; mkname_is_signed = p.second.as_bool(); } if (p.first == ID::B_SIGNED) { mkname_b_signed = true; mkname_is_signed = p.second.as_bool(); } } if (cell->type.in(ID($not), ID($_NOT_), ID($pos), ID($buf), ID($_BUF_))) { for (int i = 0; i < GetSize(cell->getPort(ID::Y)); i++) { int A = mk.inport(ID::A, i); int Y = cell->type.in(ID($not), ID($_NOT_)) ? mk.not_gate(A) : A; mk.outport(Y, ID::Y, i); } goto optimize; } if (cell->type.in(ID($and), ID($_AND_), ID($_NAND_), ID($or), ID($_OR_), ID($_NOR_), ID($xor), ID($xnor), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_))) { for (int i = 0; i < GetSize(cell->getPort(ID::Y)); i++) { int A = mk.inport(ID::A, i); int B = mk.inport(ID::B, i); int Y = cell->type.in(ID($and), ID($_AND_)) ? mk.and_gate(A, B) : cell->type.in(ID($_NAND_)) ? mk.nand_gate(A, B) : cell->type.in(ID($or), ID($_OR_)) ? mk.or_gate(A, B) : cell->type.in(ID($_NOR_)) ? mk.nor_gate(A, B) : cell->type.in(ID($xor), ID($_XOR_)) ? mk.xor_gate(A, B) : cell->type.in(ID($xnor), ID($_XNOR_)) ? mk.xnor_gate(A, B) : cell->type.in(ID($_ANDNOT_)) ? mk.andnot_gate(A, B) : cell->type.in(ID($_ORNOT_)) ? mk.ornot_gate(A, B) : -1; mk.outport(Y, ID::Y, i); } goto optimize; } 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++) { int A = mk.inport(ID::A, i); int B = mk.inport(ID::B, i); int Y = mk.mux_gate(A, B, S); if (cell->type == ID($_NMUX_)) Y = mk.not_gate(Y); mk.outport(Y, ID::Y, i); } goto optimize; } if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool))) { int Y = mk.inport(ID::A, 0); for (int i = 1; i < GetSize(cell->getPort(ID::A)); i++) { int A = mk.inport(ID::A, i); if (cell->type == ID($reduce_and)) Y = mk.and_gate(A, Y); if (cell->type == ID($reduce_or)) Y = mk.or_gate(A, Y); if (cell->type == ID($reduce_bool)) Y = mk.or_gate(A, Y); if (cell->type == ID($reduce_xor)) Y = mk.xor_gate(A, Y); if (cell->type == ID($reduce_xnor)) Y = mk.xor_gate(A, Y); } if (cell->type == ID($reduce_xnor)) Y = mk.not_gate(Y); mk.outport(Y, ID::Y, 0); 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.in(ID($logic_not), ID($logic_and), ID($logic_or))) { int A = mk.inport(ID::A, 0), Y = -1; for (int i = 1; i < GetSize(cell->getPort(ID::A)); i++) A = mk.or_gate(mk.inport(ID::A, i), A); if (cell->type.in(ID($logic_and), ID($logic_or))) { int B = mk.inport(ID::B, 0); for (int i = 1; i < GetSize(cell->getPort(ID::B)); i++) B = mk.or_gate(mk.inport(ID::B, i), B); if (cell->type == ID($logic_and)) Y = mk.and_gate(A, B); if (cell->type == ID($logic_or)) Y = mk.or_gate(A, B); } else { if (cell->type == ID($logic_not)) Y = mk.not_gate(A); } mk.outport_bool(Y, ID::Y); goto optimize; } if (cell->type.in(ID($add), ID($sub))) { int width = GetSize(cell->getPort(ID::Y)); vector A = mk.inport_vec(ID::A, width); vector B = mk.inport_vec(ID::B, width); int carry = mk.bool_node(false); if (cell->type == ID($sub)) { for (auto &n : B) n = mk.not_gate(n); carry = mk.not_gate(carry); } vector Y = mk.adder(A, B, carry); mk.outport_vec(Y, ID::Y); 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)); vector A = mk.inport_vec(ID::A, width); vector B = mk.inport_vec(ID::B, width); int carry = mk.inport(ID::CI); int binv = mk.inport(ID::BI); for (auto &n : B) n = mk.xor_gate(n, binv); vector X(width), CO(width); vector Y = mk.adder(A, B, carry, &X, &CO); for (int i = 0; i < width; i++) X[i] = mk.xor_gate(A[i], B[i]); mk.outport_vec(Y, ID::Y); mk.outport_vec(X, ID::X); mk.outport_vec(CO, ID::CO); goto optimize; } if (cell->type.in(ID($eq), ID($ne))) { int width = max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::B))); vector A = mk.inport_vec(ID::A, width); vector B = mk.inport_vec(ID::B, width); int Y = mk.bool_node(false); for (int i = 0; i < width; i++) Y = mk.or_gate(Y, mk.xor_gate(A[i], B[i])); if (cell->type == ID($eq)) Y = mk.not_gate(Y); mk.outport_bool(Y, ID::Y); goto optimize; } if (cell->type == ID($_AOI3_)) { int A = mk.inport(ID::A); int B = mk.inport(ID::B); int C = mk.inport(ID::C); int Y = mk.nor_gate(mk.and_gate(A, B), C); mk.outport(Y, ID::Y); goto optimize; } if (cell->type == ID($_OAI3_)) { int A = mk.inport(ID::A); int B = mk.inport(ID::B); int C = mk.inport(ID::C); int Y = mk.nand_gate(mk.or_gate(A, B), C); mk.outport(Y, ID::Y); goto optimize; } if (cell->type == ID($_AOI4_)) { int A = mk.inport(ID::A); int B = mk.inport(ID::B); int C = mk.inport(ID::C); int D = mk.inport(ID::D); int Y = mk.nor_gate(mk.and_gate(A, B), mk.and_gate(C, D)); mk.outport(Y, ID::Y); goto optimize; } if (cell->type == ID($_OAI4_)) { int A = mk.inport(ID::A); int B = mk.inport(ID::B); int C = mk.inport(ID::C); int D = mk.inport(ID::D); int Y = mk.nand_gate(mk.or_gate(A, B), mk.or_gate(C, D)); mk.outport(Y, ID::Y); goto optimize; } name.clear(); return; optimize:; pool used_old_ids; vector new_nodes; dict old_to_new_ids; old_to_new_ids[-1] = -1; for (int i = GetSize(nodes)-1; i >= 0; i--) { if (!nodes[i].outports.empty()) used_old_ids.insert(i); if (!used_old_ids.count(i)) continue; if (nodes[i].left_parent >= 0) used_old_ids.insert(nodes[i].left_parent); if (nodes[i].right_parent >= 0) used_old_ids.insert(nodes[i].right_parent); } for (int i = 0; i < GetSize(nodes); i++) { if (!used_old_ids.count(i)) continue; nodes[i].left_parent = old_to_new_ids.at(nodes[i].left_parent); nodes[i].right_parent = old_to_new_ids.at(nodes[i].right_parent); old_to_new_ids[i] = GetSize(new_nodes); new_nodes.push_back(nodes[i]); } new_nodes.swap(nodes); } YOSYS_NAMESPACE_END yosys-0.52/kernel/cellaigs.h000066400000000000000000000025571477540374200160700ustar00rootroot00000000000000/* * 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 CELLAIGS_H #define CELLAIGS_H #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN struct AigNode { IdString portname; int portbit; bool inverter; int left_parent, right_parent; vector> outports; AigNode(); bool operator==(const AigNode &other) const; [[nodiscard]] Hasher hash_into(Hasher h) const; }; struct Aig { string name; vector nodes; Aig(Cell *cell); bool operator==(const Aig &other) const; [[nodiscard]] Hasher hash_into(Hasher h) const; }; YOSYS_NAMESPACE_END #endif yosys-0.52/kernel/celledges.cc000066400000000000000000000344561477540374200163750ustar00rootroot00000000000000/* * 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 "kernel/celledges.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN void bitwise_unary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { bool is_signed = (cell->type != ID($buf)) && cell->getParam(ID::A_SIGNED).as_bool(); int a_width = GetSize(cell->getPort(ID::A)); int y_width = GetSize(cell->getPort(ID::Y)); for (int i = 0; i < y_width; i++) { if (i < a_width) db->add_edge(cell, ID::A, i, ID::Y, i, -1); else if (is_signed && a_width > 0) db->add_edge(cell, ID::A, a_width-1, ID::Y, i, -1); } } void bitwise_binary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { bool is_signed = cell->getParam(ID::A_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)); if (cell->type == ID($and) && !is_signed) { if (a_width > b_width) a_width = b_width; else b_width = a_width; } for (int i = 0; i < y_width; i++) { if (i < a_width) db->add_edge(cell, ID::A, i, ID::Y, i, -1); else if (is_signed && a_width > 0) db->add_edge(cell, ID::A, a_width-1, ID::Y, i, -1); if (i < b_width) db->add_edge(cell, ID::B, i, ID::Y, i, -1); else if (is_signed && b_width > 0) db->add_edge(cell, ID::B, b_width-1, ID::Y, i, -1); } } void arith_neg_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { bool is_signed = cell->getParam(ID::A_SIGNED).as_bool(); int a_width = GetSize(cell->getPort(ID::A)); int y_width = GetSize(cell->getPort(ID::Y)); if (is_signed && a_width == 1) y_width = std::min(y_width, 1); for (int i = 0; i < y_width; i++) for (int k = 0; k <= i && k < a_width; k++) db->add_edge(cell, ID::A, k, ID::Y, i, -1); } void arith_binary_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { bool is_signed = cell->getParam(ID::A_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)); if (!is_signed && cell->type != ID($sub)) { int ab_width = std::max(a_width, b_width); y_width = std::min(y_width, ab_width+1); } for (int i = 0; i < y_width; i++) { for (int k = 0; k <= i; k++) { if (k < a_width) db->add_edge(cell, ID::A, k, ID::Y, i, -1); if (k < b_width) db->add_edge(cell, ID::B, k, ID::Y, i, -1); } } } void reduce_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { int a_width = GetSize(cell->getPort(ID::A)); for (int i = 0; i < a_width; i++) db->add_edge(cell, ID::A, i, ID::Y, 0, -1); } void compare_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { int a_width = GetSize(cell->getPort(ID::A)); int b_width = GetSize(cell->getPort(ID::B)); for (int i = 0; i < a_width; i++) db->add_edge(cell, ID::A, i, ID::Y, 0, -1); for (int i = 0; i < b_width; i++) db->add_edge(cell, ID::B, i, ID::Y, 0, -1); } void mux_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { int a_width = GetSize(cell->getPort(ID::A)); int b_width = GetSize(cell->getPort(ID::B)); int s_width = GetSize(cell->getPort(ID::S)); for (int i = 0; i < a_width; i++) { db->add_edge(cell, ID::A, i, ID::Y, i, -1); for (int k = i; k < b_width; k += a_width) db->add_edge(cell, ID::B, 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 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_capped) db->add_edge(cell, ID::B, k, ID::Y, i, -1); } else if (is_signed) { if (i - a_width + 2 < 1 << b_width_capped) db->add_edge(cell, ID::B, k, ID::Y, i, -1); } else { if (i - a_width + 1 < 1 << b_width_capped) 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"); } } } } void packed_mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { log_assert(cell->type == ID($mem_v2)); Const rd_clk_enable = cell->getParam(ID::RD_CLK_ENABLE); int n_rd_ports = cell->getParam(ID::RD_PORTS).as_int(); int abits = cell->getParam(ID::ABITS).as_int(); int width = cell->getParam(ID::WIDTH).as_int(); for (int i = 0; i < n_rd_ports; i++) { if (rd_clk_enable[i] != State::S0) { for (int k = 0; k < width; k++) db->add_edge(cell, ID::RD_ARST, i, ID::RD_DATA, i * width + k, -1); continue; } for (int j = 0; j < abits; j++) for (int k = 0; k < width; k++) db->add_edge(cell, ID::RD_ADDR, i * abits + j, ID::RD_DATA, i * width + k, -1); } } void memrd_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { log_assert(cell->type.in(ID($memrd), ID($memrd_v2))); int abits = cell->getParam(ID::ABITS).as_int(); int width = cell->getParam(ID::WIDTH).as_int(); if (cell->getParam(ID::CLK_ENABLE).as_bool()) { if (cell->type == ID($memrd_v2)) { for (int k = 0; k < width; k++) db->add_edge(cell, ID::ARST, 0, ID::DATA, k, -1); } return; } for (int j = 0; j < abits; j++) for (int k = 0; k < width; k++) db->add_edge(cell, ID::ADDR, j, ID::DATA, k, -1); } void mem_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { if (cell->type == ID($mem_v2)) packed_mem_op(db, cell); else if (cell->type.in(ID($memrd), ID($memrd_v2))) memrd_op(db, cell); else if (cell->type.in(ID($memwr), ID($memwr_v2), ID($meminit))) return; /* no edges here */ else log_abort(); } void ff_op(AbstractCellEdgesDatabase *db, RTLIL::Cell *cell) { int width = cell->getPort(ID::Q).size(); if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr))) { for (int k = 0; k < width; k++) { db->add_edge(cell, ID::D, k, ID::Q, k, -1); db->add_edge(cell, ID::EN, 0, ID::Q, k, -1); } } if (cell->hasPort(ID::CLR)) for (int k = 0; k < width; k++) db->add_edge(cell, ID::CLR, 0, ID::Q, k, -1); if (cell->hasPort(ID::SET)) for (int k = 0; k < width; k++) db->add_edge(cell, ID::SET, 0, ID::Q, k, -1); if (cell->hasPort(ID::ALOAD)) for (int k = 0; k < width; k++) db->add_edge(cell, ID::ALOAD, 0, ID::Q, k, -1); if (cell->hasPort(ID::AD)) for (int k = 0; k < width; k++) db->add_edge(cell, ID::AD, k, ID::Q, k, -1); if (cell->hasPort(ID::ARST)) for (int k = 0; k < width; k++) db->add_edge(cell, ID::ARST, 0, ID::Q, k, -1); } PRIVATE_NAMESPACE_END bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL::Cell *cell) { if (cell->type.in(ID($not), ID($pos), ID($buf))) { bitwise_unary_op(this, cell); return true; } if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor))) { bitwise_binary_op(this, cell); return true; } if (cell->type == ID($neg)) { arith_neg_op(this, cell); return true; } if (cell->type.in(ID($add), ID($sub))) { arith_binary_op(this, cell); return true; } if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($logic_not))) { reduce_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); return true; } if (cell->type.in(ID($mux), ID($pmux))) { mux_op(this, cell); return true; } if (cell->type == ID($bmux)) { bmux_op(this, cell); return true; } if (cell->type == ID($demux)) { demux_op(this, cell); return true; } if (cell->type.in(ID($mem_v2), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2), ID($meminit))) { mem_op(this, cell); return true; } if (RTLIL::builtin_ff_cell_types().count(cell->type)) { ff_op(this, cell); return true; } // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat // FIXME: $lut $sop $alu $lcu $macc $macc_v2 $fa // 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; } yosys-0.52/kernel/celledges.h000066400000000000000000000042501477540374200162240ustar00rootroot00000000000000/* -*- 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 CELLEDGES_H #define CELLEDGES_H #include "kernel/yosys.h" #include "kernel/sigtools.h" YOSYS_NAMESPACE_BEGIN struct AbstractCellEdgesDatabase { virtual ~AbstractCellEdgesDatabase() { } virtual void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int delay) = 0; bool add_edges_from_cell(RTLIL::Cell *cell); }; struct FwdCellEdgesDatabase : AbstractCellEdgesDatabase { SigMap &sigmap; dict> db; FwdCellEdgesDatabase(SigMap &sigmap) : sigmap(sigmap) { } void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int) override { SigBit from_sigbit = sigmap(cell->getPort(from_port)[from_bit]); SigBit to_sigbit = sigmap(cell->getPort(to_port)[to_bit]); db[from_sigbit].insert(to_sigbit); } }; struct RevCellEdgesDatabase : AbstractCellEdgesDatabase { SigMap &sigmap; dict> db; RevCellEdgesDatabase(SigMap &sigmap) : sigmap(sigmap) { } void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int) override { SigBit from_sigbit = sigmap(cell->getPort(from_port)[from_bit]); SigBit to_sigbit = sigmap(cell->getPort(to_port)[to_bit]); db[to_sigbit].insert(from_sigbit); } }; YOSYS_NAMESPACE_END #endif yosys-0.52/kernel/celltypes.h000066400000000000000000000442011477540374200163010ustar00rootroot00000000000000/* * 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 CELLTYPES_H #define CELLTYPES_H #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN struct CellType { RTLIL::IdString type; pool inputs, outputs; bool is_evaluable; bool is_combinatorial; bool is_synthesizable; }; struct CellTypes { dict cell_types; CellTypes() { } CellTypes(RTLIL::Design *design) { setup(design); } void setup(RTLIL::Design *design = NULL) { if (design) setup_design(design); setup_internals(); setup_internals_mem(); setup_internals_anyinit(); setup_stdcells(); setup_stdcells_mem(); } void setup_type(RTLIL::IdString type, const pool &inputs, const pool &outputs, bool is_evaluable = false, bool is_combinatorial = false, bool is_synthesizable = false) { CellType ct = {type, inputs, outputs, is_evaluable, is_combinatorial, is_synthesizable}; cell_types[ct.type] = ct; } void setup_module(RTLIL::Module *module) { pool inputs, outputs; for (RTLIL::IdString wire_name : module->ports) { RTLIL::Wire *wire = module->wire(wire_name); if (wire->port_input) inputs.insert(wire->name); if (wire->port_output) outputs.insert(wire->name); } setup_type(module->name, inputs, outputs); } void setup_design(RTLIL::Design *design) { for (auto module : design->modules()) setup_module(module); } void setup_internals() { setup_internals_eval(); setup_type(ID($tribuf), {ID::A, ID::EN}, {ID::Y}, true); setup_type(ID($assert), {ID::A, ID::EN}, pool(), true); setup_type(ID($assume), {ID::A, ID::EN}, pool(), true); setup_type(ID($live), {ID::A, ID::EN}, pool(), true); setup_type(ID($fair), {ID::A, ID::EN}, pool(), true); setup_type(ID($cover), {ID::A, ID::EN}, pool(), true); setup_type(ID($initstate), pool(), {ID::Y}, true); setup_type(ID($anyconst), pool(), {ID::Y}, true); setup_type(ID($anyseq), pool(), {ID::Y}, true); setup_type(ID($allconst), pool(), {ID::Y}, true); setup_type(ID($allseq), pool(), {ID::Y}, true); setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, true); 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() { std::vector unary_ops = { ID($not), ID($pos), ID($buf), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($logic_not), ID($slice), ID($lut), ID($sop) }; std::vector binary_ops = { ID($and), ID($or), ID($xor), ID($xnor), 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($bweqx) }; for (auto type : unary_ops) setup_type(type, {ID::A}, {ID::Y}, true); for (auto type : binary_ops) setup_type(type, {ID::A, ID::B}, {ID::Y}, true); 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($macc_v2), {ID::A, ID::B, ID::C}, {ID::Y}, true); setup_type(ID($fa), {ID::A, ID::B, ID::C}, {ID::X, ID::Y}, true); } void setup_internals_ff() { setup_type(ID($sr), {ID::SET, ID::CLR}, {ID::Q}); setup_type(ID($ff), {ID::D}, {ID::Q}); setup_type(ID($dff), {ID::CLK, ID::D}, {ID::Q}); setup_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q}); setup_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q}); 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}); setup_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q}); setup_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q}); 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}); } void setup_stdcells() { setup_stdcells_eval(); setup_type(ID($_TBUF_), {ID::A, ID::E}, {ID::Y}, true); } void setup_stdcells_eval() { setup_type(ID($_BUF_), {ID::A}, {ID::Y}, true); setup_type(ID($_NOT_), {ID::A}, {ID::Y}, true); setup_type(ID($_AND_), {ID::A, ID::B}, {ID::Y}, true); setup_type(ID($_NAND_), {ID::A, ID::B}, {ID::Y}, true); setup_type(ID($_OR_), {ID::A, ID::B}, {ID::Y}, true); setup_type(ID($_NOR_), {ID::A, ID::B}, {ID::Y}, true); setup_type(ID($_XOR_), {ID::A, ID::B}, {ID::Y}, true); setup_type(ID($_XNOR_), {ID::A, ID::B}, {ID::Y}, true); setup_type(ID($_ANDNOT_), {ID::A, ID::B}, {ID::Y}, true); setup_type(ID($_ORNOT_), {ID::A, ID::B}, {ID::Y}, true); setup_type(ID($_MUX_), {ID::A, ID::B, ID::S}, {ID::Y}, true); setup_type(ID($_NMUX_), {ID::A, ID::B, ID::S}, {ID::Y}, true); setup_type(ID($_MUX4_), {ID::A, ID::B, ID::C, ID::D, ID::S, ID::T}, {ID::Y}, true); setup_type(ID($_MUX8_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::S, ID::T, ID::U}, {ID::Y}, true); setup_type(ID($_MUX16_), {ID::A, ID::B, ID::C, ID::D, ID::E, ID::F, ID::G, ID::H, ID::I, ID::J, ID::K, ID::L, ID::M, ID::N, ID::O, ID::P, ID::S, ID::T, ID::U, ID::V}, {ID::Y}, true); setup_type(ID($_AOI3_), {ID::A, ID::B, ID::C}, {ID::Y}, true); setup_type(ID($_OAI3_), {ID::A, ID::B, ID::C}, {ID::Y}, true); setup_type(ID($_AOI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, true); setup_type(ID($_OAI4_), {ID::A, ID::B, ID::C, ID::D}, {ID::Y}, true); } void setup_stdcells_mem() { std::vector list_np = {'N', 'P'}, list_01 = {'0', '1'}; for (auto c1 : list_np) for (auto c2 : list_np) setup_type(stringf("$_SR_%c%c_", c1, c2), {ID::S, ID::R}, {ID::Q}); setup_type(ID($_FF_), {ID::D}, {ID::Q}); for (auto c1 : list_np) setup_type(stringf("$_DFF_%c_", c1), {ID::C, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) setup_type(stringf("$_DFFE_%c%c_", c1, c2), {ID::C, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) setup_type(stringf("$_DFF_%c%c%c_", c1, c2, c3), {ID::C, ID::R, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) 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) setup_type(stringf("$_DFFSR_%c%c%c_", c1, c2, c3), {ID::C, ID::S, ID::R, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) for (auto c4 : list_np) setup_type(stringf("$_DFFSRE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::S, ID::R, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) setup_type(stringf("$_SDFF_%c%c%c_", c1, c2, c3), {ID::C, ID::R, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) for (auto c4 : list_np) setup_type(stringf("$_SDFFE_%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) for (auto c3 : list_01) for (auto c4 : list_np) setup_type(stringf("$_SDFFCE_%c%c%c%c_", c1, c2, c3, c4), {ID::C, ID::R, ID::D, ID::E}, {ID::Q}); for (auto c1 : list_np) setup_type(stringf("$_DLATCH_%c_", c1), {ID::E, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_01) setup_type(stringf("$_DLATCH_%c%c%c_", c1, c2, c3), {ID::E, ID::R, ID::D}, {ID::Q}); for (auto c1 : list_np) for (auto c2 : list_np) for (auto c3 : list_np) setup_type(stringf("$_DLATCHSR_%c%c%c_", c1, c2, c3), {ID::E, ID::S, ID::R, ID::D}, {ID::Q}); } void clear() { cell_types.clear(); } bool cell_known(RTLIL::IdString type) const { return cell_types.count(type) != 0; } bool cell_output(RTLIL::IdString type, RTLIL::IdString port) const { auto it = cell_types.find(type); return it != cell_types.end() && it->second.outputs.count(port) != 0; } bool cell_input(RTLIL::IdString type, RTLIL::IdString port) const { auto it = cell_types.find(type); return it != cell_types.end() && it->second.inputs.count(port) != 0; } bool cell_evaluable(RTLIL::IdString type) const { auto it = cell_types.find(type); return it != cell_types.end() && it->second.is_evaluable; } static RTLIL::Const eval_not(RTLIL::Const v) { for (auto &bit : v.bits()) if (bit == State::S0) bit = State::S1; else if (bit == State::S1) bit = State::S0; return v; } static RTLIL::Const eval(RTLIL::IdString type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len, bool *errp = nullptr) { if (type == ID($sshr) && !signed1) type = ID($shr); if (type == ID($sshl) && !signed1) type = ID($shl); if (type != ID($sshr) && type != ID($sshl) && type != ID($shr) && type != ID($shl) && type != ID($shift) && type != ID($shiftx) && type != ID($pos) && type != ID($buf) && type != ID($neg) && type != ID($not)) { if (!signed1 || !signed2) signed1 = false, signed2 = false; } #define HANDLE_CELL_TYPE(_t) if (type == ID($##_t)) return const_ ## _t(arg1, arg2, signed1, signed2, result_len); HANDLE_CELL_TYPE(not) HANDLE_CELL_TYPE(and) HANDLE_CELL_TYPE(or) HANDLE_CELL_TYPE(xor) HANDLE_CELL_TYPE(xnor) HANDLE_CELL_TYPE(reduce_and) HANDLE_CELL_TYPE(reduce_or) HANDLE_CELL_TYPE(reduce_xor) HANDLE_CELL_TYPE(reduce_xnor) HANDLE_CELL_TYPE(reduce_bool) HANDLE_CELL_TYPE(logic_not) HANDLE_CELL_TYPE(logic_and) HANDLE_CELL_TYPE(logic_or) HANDLE_CELL_TYPE(shl) HANDLE_CELL_TYPE(shr) HANDLE_CELL_TYPE(sshl) HANDLE_CELL_TYPE(sshr) HANDLE_CELL_TYPE(shift) HANDLE_CELL_TYPE(shiftx) HANDLE_CELL_TYPE(lt) HANDLE_CELL_TYPE(le) HANDLE_CELL_TYPE(eq) HANDLE_CELL_TYPE(ne) HANDLE_CELL_TYPE(eqx) HANDLE_CELL_TYPE(nex) HANDLE_CELL_TYPE(ge) HANDLE_CELL_TYPE(gt) HANDLE_CELL_TYPE(add) HANDLE_CELL_TYPE(sub) HANDLE_CELL_TYPE(mul) HANDLE_CELL_TYPE(div) HANDLE_CELL_TYPE(mod) HANDLE_CELL_TYPE(divfloor) HANDLE_CELL_TYPE(modfloor) HANDLE_CELL_TYPE(pow) HANDLE_CELL_TYPE(pos) HANDLE_CELL_TYPE(neg) #undef HANDLE_CELL_TYPE if (type.in(ID($_BUF_), ID($buf))) return arg1; if (type == ID($_NOT_)) return eval_not(arg1); if (type == ID($_AND_)) return const_and(arg1, arg2, false, false, 1); if (type == ID($_NAND_)) return eval_not(const_and(arg1, arg2, false, false, 1)); if (type == ID($_OR_)) return const_or(arg1, arg2, false, false, 1); if (type == ID($_NOR_)) return eval_not(const_or(arg1, arg2, false, false, 1)); if (type == ID($_XOR_)) return const_xor(arg1, arg2, false, false, 1); if (type == ID($_XNOR_)) return const_xnor(arg1, arg2, false, false, 1); if (type == ID($_ANDNOT_)) return const_and(arg1, eval_not(arg2), false, false, 1); if (type == ID($_ORNOT_)) return const_or(arg1, eval_not(arg2), false, false, 1); if (errp != nullptr) { *errp = true; return State::Sm; } log_abort(); } static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool *errp = nullptr) { if (cell->type == ID($slice)) { RTLIL::Const ret; int width = cell->parameters.at(ID::Y_WIDTH).as_int(); int offset = cell->parameters.at(ID::OFFSET).as_int(); ret.bits().insert(ret.bits().end(), arg1.begin()+offset, arg1.begin()+offset+width); return ret; } if (cell->type == ID($concat)) { RTLIL::Const ret = arg1; ret.bits().insert(ret.bits().end(), arg2.begin(), arg2.end()); 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(); std::vector t = cell->parameters.at(ID::LUT).to_bits(); while (GetSize(t) < (1 << width)) t.push_back(State::S0); t.resize(1 << width); return const_bmux(t, arg1); } if (cell->type == ID($sop)) { int width = cell->parameters.at(ID::WIDTH).as_int(); int depth = cell->parameters.at(ID::DEPTH).as_int(); std::vector t = cell->parameters.at(ID::TABLE).to_bits(); while (GetSize(t) < width*depth*2) t.push_back(State::S0); RTLIL::State default_ret = State::S0; for (int i = 0; i < depth; i++) { bool match = true; bool match_x = true; for (int j = 0; j < width; j++) { RTLIL::State a = arg1.at(j); if (t.at(2*width*i + 2*j + 0) == State::S1) { if (a == State::S1) match_x = false; if (a != State::S0) match = false; } if (t.at(2*width*i + 2*j + 1) == State::S1) { if (a == State::S0) match_x = false; if (a != State::S1) match = false; } } if (match) return State::S1; if (match_x) default_ret = State::Sx; } return default_ret; } bool signed_a = cell->parameters.count(ID::A_SIGNED) > 0 && cell->parameters[ID::A_SIGNED].as_bool(); bool signed_b = cell->parameters.count(ID::B_SIGNED) > 0 && cell->parameters[ID::B_SIGNED].as_bool(); int result_len = cell->parameters.count(ID::Y_WIDTH) > 0 ? cell->parameters[ID::Y_WIDTH].as_int() : -1; return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len, errp); } 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($_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_)) return eval_not(const_and(const_or(arg1, arg2, false, false, 1), arg3, false, false, 1)); log_assert(arg3.size() == 0); return eval(cell, arg1, arg2, errp); } static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, const RTLIL::Const &arg4, bool *errp = nullptr) { if (cell->type == ID($_AOI4_)) return eval_not(const_or(const_and(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1)); if (cell->type == ID($_OAI4_)) return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_or(arg3, arg4, false, false, 1), false, false, 1)); log_assert(arg4.size() == 0); return eval(cell, arg1, arg2, arg3, errp); } }; // initialized by yosys_setup() extern CellTypes yosys_celltypes; YOSYS_NAMESPACE_END #endif yosys-0.52/kernel/compute_graph.h000066400000000000000000000266361477540374200171460ustar00rootroot00000000000000/* * 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 COMPUTE_GRAPH_H #define COMPUTE_GRAPH_H #include #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN template< typename Fn, // Function type (deduplicated across whole graph) typename Attr = std::tuple<>, // Call attributes (present in every node) typename SparseAttr = std::tuple<>, // Sparse call attributes (optional per node) typename Key = std::tuple<> // Stable keys to refer to nodes > struct ComputeGraph { struct Ref; private: // Functions are deduplicated by assigning unique ids idict functions; struct Node { int fn_index; int arg_offset; int arg_count; Attr attr; Node(int fn_index, Attr &&attr, int arg_offset, int arg_count = 0) : fn_index(fn_index), arg_offset(arg_offset), arg_count(arg_count), attr(std::move(attr)) {} Node(int fn_index, Attr const &attr, int arg_offset, int arg_count = 0) : fn_index(fn_index), arg_offset(arg_offset), arg_count(arg_count), attr(attr) {} }; std::vector nodes; std::vector args; dict keys_; dict sparse_attrs; public: template struct BaseRef { protected: friend struct ComputeGraph; Graph *graph_; int index_; BaseRef(Graph *graph, int index) : graph_(graph), index_(index) { log_assert(index_ >= 0); check(); } void check() const { log_assert(index_ < graph_->size()); } Node const &deref() const { check(); return graph_->nodes[index_]; } public: ComputeGraph const &graph() const { return graph_; } int index() const { return index_; } int size() const { return deref().arg_count; } BaseRef arg(int n) const { Node const &node = deref(); log_assert(n >= 0 && n < node.arg_count); return BaseRef(graph_, graph_->args[node.arg_offset + n]); } std::vector::const_iterator arg_indices_cbegin() const { Node const &node = deref(); return graph_->args.cbegin() + node.arg_offset; } std::vector::const_iterator arg_indices_cend() const { Node const &node = deref(); return graph_->args.cbegin() + node.arg_offset + node.arg_count; } Fn const &function() const { return graph_->functions[deref().fn_index]; } Attr const &attr() const { return deref().attr; } bool has_sparse_attr() const { return graph_->sparse_attrs.count(index_); } SparseAttr const &sparse_attr() const { auto found = graph_->sparse_attrs.find(index_); log_assert(found != graph_->sparse_attrs.end()); return found->second; } }; using ConstRef = BaseRef; struct Ref : public BaseRef { private: friend struct ComputeGraph; Ref(ComputeGraph *graph, int index) : BaseRef(graph, index) {} Node &deref() const { this->check(); return this->graph_->nodes[this->index_]; } public: Ref(BaseRef ref) : Ref(ref.graph_, ref.index_) {} void set_function(Fn const &function) const { deref().fn_index = this->graph_->functions(function); } Attr &attr() const { return deref().attr; } void append_arg(ConstRef arg) const { log_assert(arg.graph_ == this->graph_); append_arg(arg.index()); } void append_arg(int arg) const { log_assert(arg >= 0 && arg < this->graph_->size()); Node &node = deref(); if (node.arg_offset + node.arg_count != GetSize(this->graph_->args)) move_args(node); this->graph_->args.push_back(arg); node.arg_count++; } operator ConstRef() const { return ConstRef(this->graph_, this->index_); } SparseAttr &sparse_attr() const { return this->graph_->sparse_attrs[this->index_]; } void clear_sparse_attr() const { this->graph_->sparse_attrs.erase(this->index_); } void assign_key(Key const &key) const { this->graph_->keys_.emplace(key, this->index_); } private: void move_args(Node &node) const { auto &args = this->graph_->args; int old_offset = node.arg_offset; node.arg_offset = GetSize(args); for (int i = 0; i != node.arg_count; ++i) args.push_back(args[old_offset + i]); } }; bool has_key(Key const &key) const { return keys_.count(key); } dict const &keys() const { return keys_; } ConstRef operator()(Key const &key) const { auto it = keys_.find(key); log_assert(it != keys_.end()); return (*this)[it->second]; } Ref operator()(Key const &key) { auto it = keys_.find(key); log_assert(it != keys_.end()); return (*this)[it->second]; } int size() const { return GetSize(nodes); } ConstRef operator[](int index) const { return ConstRef(this, index); } Ref operator[](int index) { return Ref(this, index); } Ref add(Fn const &function, Attr &&attr) { int index = GetSize(nodes); int fn_index = functions(function); nodes.emplace_back(fn_index, std::move(attr), GetSize(args)); return Ref(this, index); } Ref add(Fn const &function, Attr const &attr) { int index = GetSize(nodes); int fn_index = functions(function); nodes.emplace_back(fn_index, attr, GetSize(args)); return Ref(this, index); } template Ref add(Fn const &function, Attr const &attr, T &&args) { Ref added = add(function, attr); for (auto arg : args) added.append_arg(arg); return added; } template Ref add(Fn const &function, Attr &&attr, T &&args) { Ref added = add(function, std::move(attr)); for (auto arg : args) added.append_arg(arg); return added; } Ref add(Fn const &function, Attr const &attr, std::initializer_list args) { Ref added = add(function, attr); for (auto arg : args) added.append_arg(arg); return added; } Ref add(Fn const &function, Attr &&attr, std::initializer_list args) { Ref added = add(function, std::move(attr)); for (auto arg : args) added.append_arg(arg); return added; } template Ref add(Fn const &function, Attr const &attr, T begin, T end) { Ref added = add(function, attr); for (; begin != end; ++begin) added.append_arg(*begin); return added; } void compact_args() { std::vector new_args; for (auto &node : nodes) { int new_offset = GetSize(new_args); for (int i = 0; i < node.arg_count; i++) new_args.push_back(args[node.arg_offset + i]); node.arg_offset = new_offset; } std::swap(args, new_args); } void permute(std::vector const &perm) { log_assert(perm.size() <= nodes.size()); std::vector inv_perm; inv_perm.resize(nodes.size(), -1); for (int i = 0; i < GetSize(perm); ++i) { int j = perm[i]; log_assert(j >= 0 && j < GetSize(nodes)); log_assert(inv_perm[j] == -1); inv_perm[j] = i; } permute(perm, inv_perm); } void permute(std::vector const &perm, std::vector const &inv_perm) { log_assert(inv_perm.size() == nodes.size()); std::vector new_nodes; new_nodes.reserve(perm.size()); dict new_sparse_attrs; for (int i : perm) { int j = GetSize(new_nodes); new_nodes.emplace_back(std::move(nodes[i])); auto found = sparse_attrs.find(i); if (found != sparse_attrs.end()) new_sparse_attrs.emplace(j, std::move(found->second)); } std::swap(nodes, new_nodes); std::swap(sparse_attrs, new_sparse_attrs); compact_args(); for (int &arg : args) { log_assert(arg < GetSize(inv_perm)); log_assert(inv_perm[arg] >= 0); arg = inv_perm[arg]; } for (auto &key : keys_) { log_assert(key.second < GetSize(inv_perm)); log_assert(inv_perm[key.second] >= 0); key.second = inv_perm[key.second]; } } struct SccAdaptor { private: ComputeGraph const &graph_; std::vector indices_; public: SccAdaptor(ComputeGraph const &graph) : graph_(graph) { indices_.resize(graph.size(), -1); } typedef int node_type; struct node_enumerator { private: friend struct SccAdaptor; int current, end; node_enumerator(int current, int end) : current(current), end(end) {} public: bool finished() const { return current == end; } node_type next() { log_assert(!finished()); node_type result = current; ++current; return result; } }; node_enumerator enumerate_nodes() { return node_enumerator(0, GetSize(indices_)); } struct successor_enumerator { private: friend struct SccAdaptor; std::vector::const_iterator current, end; successor_enumerator(std::vector::const_iterator current, std::vector::const_iterator end) : current(current), end(end) {} public: bool finished() const { return current == end; } node_type next() { log_assert(!finished()); node_type result = *current; ++current; return result; } }; successor_enumerator enumerate_successors(int index) const { auto const &ref = graph_[index]; return successor_enumerator(ref.arg_indices_cbegin(), ref.arg_indices_cend()); } int &dfs_index(node_type const &node) { return indices_[node]; } std::vector const &dfs_indices() { return indices_; } }; }; YOSYS_NAMESPACE_END #endif yosys-0.52/kernel/consteval.h000066400000000000000000000251601477540374200162760ustar00rootroot00000000000000/* * 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 CONSTEVAL_H #define CONSTEVAL_H #include "kernel/rtlil.h" #include "kernel/sigtools.h" #include "kernel/celltypes.h" #include "kernel/macc.h" YOSYS_NAMESPACE_BEGIN struct ConstEval { RTLIL::Module *module; SigMap assign_map; SigMap values_map; SigPool stop_signals; SigSet sig2driver; std::set busy; std::vector stack; RTLIL::State defaultval; ConstEval(RTLIL::Module *module, RTLIL::State defaultval = RTLIL::State::Sm) : module(module), assign_map(module), defaultval(defaultval) { CellTypes ct; ct.setup_internals(); ct.setup_stdcells(); for (auto &it : module->cells_) { if (!ct.cell_known(it.second->type)) continue; for (auto &it2 : it.second->connections()) if (ct.cell_output(it.second->type, it2.first)) sig2driver.insert(assign_map(it2.second), it.second); } } void clear() { values_map.clear(); stop_signals.clear(); } void push() { stack.push_back(values_map); } void pop() { values_map.swap(stack.back()); stack.pop_back(); } void set(RTLIL::SigSpec sig, RTLIL::Const value) { assign_map.apply(sig); #ifndef NDEBUG RTLIL::SigSpec current_val = values_map(sig); for (int i = 0; i < GetSize(current_val); i++) log_assert(current_val[i].wire != NULL || current_val[i] == value[i]); #endif values_map.add(sig, RTLIL::SigSpec(value)); } void stop(RTLIL::SigSpec sig) { assign_map.apply(sig); stop_signals.add(sig); } bool eval(RTLIL::Cell *cell, RTLIL::SigSpec &undef) { if (cell->type == ID($lcu)) { RTLIL::SigSpec sig_p = cell->getPort(ID::P); RTLIL::SigSpec sig_g = cell->getPort(ID::G); RTLIL::SigSpec sig_ci = cell->getPort(ID::CI); RTLIL::SigSpec sig_co = values_map(assign_map(cell->getPort(ID::CO))); if (sig_co.is_fully_const()) return true; if (!eval(sig_p, undef, cell)) return false; if (!eval(sig_g, undef, cell)) return false; if (!eval(sig_ci, undef, cell)) return false; if (sig_p.is_fully_def() && sig_g.is_fully_def() && sig_ci.is_fully_def()) { RTLIL::Const coval(RTLIL::Sx, GetSize(sig_co)); bool carry = sig_ci.as_bool(); for (int i = 0; i < GetSize(coval); i++) { carry = (sig_g[i] == State::S1) || (sig_p[i] == RTLIL::S1 && carry); coval.bits()[i] = carry ? State::S1 : State::S0; } set(sig_co, coval); } else set(sig_co, RTLIL::Const(RTLIL::Sx, GetSize(sig_co))); return true; } RTLIL::SigSpec sig_a, sig_b, sig_s, sig_y; log_assert(cell->hasPort(ID::Y)); sig_y = values_map(assign_map(cell->getPort(ID::Y))); if (sig_y.is_fully_const()) return true; if (cell->hasPort(ID::S)) { sig_s = cell->getPort(ID::S); } if (cell->hasPort(ID::A)) sig_a = cell->getPort(ID::A); if (cell->hasPort(ID::B)) sig_b = cell->getPort(ID::B); if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_), ID($_NMUX_))) { std::vector y_candidates; 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().at(0); RTLIL::SigSpec b_slice = sig_b.extract(sig_y.size()*i, sig_y.size()); if (s_bit == RTLIL::State::Sx || s_bit == RTLIL::State::S1) y_candidates.push_back(b_slice); if (s_bit == RTLIL::State::S1) count_set_s_bits++; } if (count_set_s_bits == 0) y_candidates.push_back(sig_a); std::vector y_values; log_assert(y_candidates.size() > 0); for (auto &yc : y_candidates) { if (!eval(yc, undef, cell)) return false; if (cell->type == ID($_NMUX_)) y_values.push_back(RTLIL::const_not(yc.as_const(), Const(), false, false, GetSize(yc))); else y_values.push_back(yc.as_const()); } if (y_values.size() > 1) { std::vector master_bits = y_values.at(0).to_bits(); for (size_t i = 1; i < y_values.size(); i++) { std::vector slave_bits = y_values.at(i).to_bits(); log_assert(master_bits.size() == slave_bits.size()); for (size_t j = 0; j < master_bits.size(); j++) if (master_bits[j] != slave_bits[j]) master_bits[j] = RTLIL::State::Sx; } set(sig_y, RTLIL::Const(master_bits)); } 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); RTLIL::SigSpec sig_x = cell->getPort(ID::X); int width = GetSize(sig_c); if (!eval(sig_a, undef, cell)) return false; if (!eval(sig_b, undef, cell)) return false; if (!eval(sig_c, undef, cell)) return false; RTLIL::Const t1 = const_xor(sig_a.as_const(), sig_b.as_const(), false, false, width); RTLIL::Const val_y = const_xor(t1, sig_c.as_const(), false, false, width); RTLIL::Const t2 = const_and(sig_a.as_const(), sig_b.as_const(), false, false, width); RTLIL::Const t3 = const_and(sig_c.as_const(), t1, false, false, width); RTLIL::Const val_x = const_or(t2, t3, false, false, width); for (int i = 0; i < GetSize(val_y); i++) if (val_y[i] == RTLIL::Sx) val_x.bits()[i] = RTLIL::Sx; set(sig_y, val_y); set(sig_x, val_x); } else if (cell->type == ID($alu)) { bool signed_a = cell->parameters.count(ID::A_SIGNED) > 0 && cell->parameters[ID::A_SIGNED].as_bool(); bool signed_b = cell->parameters.count(ID::B_SIGNED) > 0 && cell->parameters[ID::B_SIGNED].as_bool(); RTLIL::SigSpec sig_ci = cell->getPort(ID::CI); RTLIL::SigSpec sig_bi = cell->getPort(ID::BI); if (!eval(sig_a, undef, cell)) return false; if (!eval(sig_b, undef, cell)) return false; if (!eval(sig_ci, undef, cell)) return false; if (!eval(sig_bi, undef, cell)) return false; RTLIL::SigSpec sig_x = cell->getPort(ID::X); RTLIL::SigSpec sig_co = cell->getPort(ID::CO); bool any_input_undef = !(sig_a.is_fully_def() && sig_b.is_fully_def() && sig_ci.is_fully_def() && sig_bi.is_fully_def()); sig_a.extend_u0(GetSize(sig_y), signed_a); sig_b.extend_u0(GetSize(sig_y), signed_b); bool carry = sig_ci[0] == State::S1; bool b_inv = sig_bi[0] == State::S1; for (int i = 0; i < GetSize(sig_y); i++) { RTLIL::SigSpec x_inputs = { sig_a[i], sig_b[i], sig_bi[0] }; if (!x_inputs.is_fully_def()) { set(sig_x[i], RTLIL::Sx); } else { bool bit_a = sig_a[i] == State::S1; bool bit_b = (sig_b[i] == State::S1) != b_inv; bool bit_x = bit_a != bit_b; set(sig_x[i], bit_x ? State::S1 : State::S0); } if (any_input_undef) { set(sig_y[i], RTLIL::Sx); set(sig_co[i], RTLIL::Sx); } else { bool bit_a = sig_a[i] == State::S1; bool bit_b = (sig_b[i] == State::S1) != b_inv; bool bit_y = (bit_a != bit_b) != carry; carry = (bit_a && bit_b) || (bit_a && carry) || (bit_b && carry); set(sig_y[i], bit_y ? State::S1 : State::S0); set(sig_co[i], carry ? State::S1 : State::S0); } } } else if (cell->type.in(ID($macc), ID($macc_v2))) { Macc macc; macc.from_cell(cell); for (auto &port : macc.ports) { if (!eval(port.in_a, undef, cell)) return false; if (!eval(port.in_b, undef, cell)) return false; } RTLIL::Const result(0, GetSize(cell->getPort(ID::Y))); if (!macc.eval(result)) log_abort(); set(cell->getPort(ID::Y), result); } else { RTLIL::SigSpec sig_c, sig_d; if (cell->type.in(ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) { if (cell->hasPort(ID::C)) sig_c = cell->getPort(ID::C); if (cell->hasPort(ID::D)) sig_d = cell->getPort(ID::D); } if (sig_a.size() > 0 && !eval(sig_a, undef, cell)) return false; if (sig_b.size() > 0 && !eval(sig_b, undef, cell)) return false; if (sig_c.size() > 0 && !eval(sig_c, undef, cell)) return false; if (sig_d.size() > 0 && !eval(sig_d, undef, cell)) return false; bool eval_err = false; RTLIL::Const eval_ret = CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(), sig_c.as_const(), sig_d.as_const(), &eval_err); if (eval_err) return false; set(sig_y, eval_ret); } return true; } bool eval(RTLIL::SigSpec &sig, RTLIL::SigSpec &undef, RTLIL::Cell *busy_cell = NULL) { assign_map.apply(sig); values_map.apply(sig); if (sig.is_fully_const()) return true; if (stop_signals.check_any(sig)) { undef = stop_signals.extract(sig); return false; } if (busy_cell) { if (busy.count(busy_cell) > 0) { undef = sig; return false; } busy.insert(busy_cell); } std::set driver_cells; sig2driver.find(sig, driver_cells); for (auto cell : driver_cells) { if (!eval(cell, undef)) { if (busy_cell) busy.erase(busy_cell); return false; } } if (busy_cell) busy.erase(busy_cell); values_map.apply(sig); if (sig.is_fully_const()) return true; if (defaultval != RTLIL::State::Sm) { for (auto &bit : sig) if (bit.wire) bit = defaultval; return true; } for (auto &c : sig.chunks()) if (c.wire != NULL) undef.append(c); return false; } bool eval(RTLIL::SigSpec &sig) { RTLIL::SigSpec undef; return eval(sig, undef); } }; YOSYS_NAMESPACE_END #endif yosys-0.52/kernel/constids.inc000066400000000000000000000071561477540374200164550ustar00rootroot00000000000000X(A) X(abc9_box) X(abc9_box_id) X(abc9_box_seq) X(abc9_bypass) X(abc9_carry) X(abc9_flop) X(abc9_keep) X(abc9_lut) 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) 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) X(chain) X(CI) X(CLK) X(clkbuf_driver) X(clkbuf_inhibit) X(clkbuf_inv) X(clkbuf_sink) X(CLK_ENABLE) X(CLK_POLARITY) X(CLR) X(CLR_POLARITY) X(CO) X(COLLISION_X_MASK) X(CONFIG) X(CONFIG_WIDTH) X(CTRL_IN) X(CTRL_IN_WIDTH) X(CTRL_OUT) X(CTRL_OUT_WIDTH) X(D) X(DAT) X(DATA) X(DAT_DST_PEN) X(DAT_DST_POL) X(defaultvalue) X(DELAY) X(DEPTH) X(DST) X(DST_EN) X(DST_PEN) X(DST_POL) X(DST_WIDTH) X(dynports) X(E) X(EDGE_EN) X(EDGE_POL) X(EN) X(EN_DST) X(EN_POLARITY) X(EN_SRC) X(enum_base_type) X(enum_type) X(equiv_merged) X(equiv_region) X(extract_order) X(F) X(FLAVOR) X(FORMAT) X(force_downto) X(force_upto) X(fsm_encoding) X(fsm_export) X(FULL) X(full_case) X(G) X(gclk) X(gentb_clock) X(gentb_constant) X(gentb_skip) X(H) X(hdlname) X(hierconn) X(I) X(INIT) X(INIT_VALUE) X(init) X(initial_top) X(interface_modport) X(interfaces_replaced_in_module) X(interface_type) X(invertible_pin) X(iopad_external_pin) X(is_interface) X(J) X(K) X(keep) X(keep_hierarchy) X(L) X(lib_whitebox) X(localparam) X(logic_block) X(lram) X(LUT) X(lut_keep) X(M) X(maximize) X(mem2reg) X(MEMID) X(minimize) X(module_not_derived) X(N) X(NAME) X(noblackbox) X(nolatches) X(nomem2init) 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(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) X(SIZE) X(SRC) X(src) X(SRC_DST_PEN) X(SRC_DST_POL) X(SRC_EN) X(SRC_PEN) X(SRC_POL) 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_) X(_TECHMAP_CELLTYPE_) X(techmap_celltype) X(_TECHMAP_FAIL_) X(techmap_maccmap) X(_TECHMAP_REPLACE_) X(techmap_simplemap) X(_techmap_special_) X(techmap_wrap) X(_TECHMAP_PLACEHOLDER_) X(techmap_chtype) X(T_FALL_MAX) X(T_FALL_MIN) X(T_FALL_TYP) X(T_LIMIT) X(T_LIMIT2) X(T_LIMIT2_MAX) X(T_LIMIT2_MIN) X(T_LIMIT2_TYP) X(T_LIMIT_MAX) X(T_LIMIT_MIN) 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) X(TYPE) X(U) X(unique) X(unused_bits) X(V) X(via_celltype) X(wand) X(whitebox) X(WIDTH) X(wildcard_port_conns) X(wiretype) X(wor) X(WORDS) X(WR_ADDR) X(WR_CLK) X(WR_CLK_ENABLE) 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) X(area) X(capacitance) X(NPRODUCTS) X(NADDENDS) X(PRODUCT_NEGATED) X(ADDEND_NEGATED) X(A_WIDTHS) X(B_WIDTHS) X(C_WIDTHS) X(C_SIGNED) yosys-0.52/kernel/cost.cc000066400000000000000000000132221477540374200154020ustar00rootroot00000000000000#include "kernel/cost.h" USING_YOSYS_NAMESPACE unsigned int CellCosts::get(RTLIL::Module *mod) { if (mod_cost_cache_.count(mod->name)) return mod_cost_cache_.at(mod->name); unsigned int module_cost = 1; for (auto c : mod->cells()) { unsigned int new_cost = module_cost + get(c); module_cost = new_cost >= module_cost ? new_cost : INT_MAX; } mod_cost_cache_[mod->name] = module_cost; return module_cost; } static unsigned int y_coef(RTLIL::IdString type) { if ( // equality type.in(ID($bweqx), ID($nex), ID($eqx)) || // basic logic type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($not)) || // mux type.in(ID($bwmux), ID($mux)) || // others type == ID($tribuf)) { return 1; } else if (type == ID($neg)) { return 4; } else if (type == ID($demux)) { return 2; } else if (type == ID($fa)) { return 5; } else if (type.in(ID($add), ID($sub), ID($alu))) { // multi-bit adders return 8; } else if (type.in(ID($shl), ID($sshl))) { // left shift return 10; } return 0; } static unsigned int max_inp_coef(RTLIL::IdString type) { if ( // binop reduce type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool)) || // others type.in(ID($logic_not), ID($pmux), ID($bmux))) { return 1; } else if ( // equality type.in(ID($eq), ID($ne)) || // logic type.in(ID($logic_and), ID($logic_or))) { return 2; } else if (type == ID($lcu)) { return 5; } else if (type.in(ID($lt), ID($le), ID($ge), ID($gt))) { // comparison return 7; } return 0; } static unsigned int sum_coef(RTLIL::IdString type) { if (type.in(ID($shr), ID($sshr))) { // right shift return 4; } else if (type.in(ID($shift), ID($shiftx))) { // shift return 8; } return 0; } static unsigned int is_div_mod(RTLIL::IdString type) { return (type == ID($div) || type == ID($divfloor) || type == ID($mod) || type == ID($modfloor)); } static bool is_free(RTLIL::IdString type) { return ( // tags type.in(ID($overwrite_tag), ID($set_tag), ID($original_tag), ID($get_tag)) || // formal type.in(ID($check), ID($equiv), ID($initstate), ID($assert), ID($assume), ID($live), ID($cover), ID($fair)) || type.in(ID($allseq), ID($allconst), ID($anyseq), ID($anyconst), ID($anyinit)) || // utilities type.in(ID($scopeinfo), ID($print)) || // real but free type.in(ID($concat), ID($slice), ID($pos)) || // specify type.in(ID($specrule), ID($specify2), ID($specify3))); } unsigned int max_inp_width(RTLIL::Cell *cell) { unsigned int max = 0; RTLIL::IdString input_width_params[] = { ID::WIDTH, ID::A_WIDTH, ID::B_WIDTH, ID::S_WIDTH, }; if (cell->type == ID($bmux)) return cell->getParam(ID::WIDTH).as_int() << cell->getParam(ID::S_WIDTH).as_int(); for (RTLIL::IdString param : input_width_params) if (cell->hasParam(param)) max = std::max(max, (unsigned int)cell->getParam(param).as_int()); return max; } unsigned int port_width_sum(RTLIL::Cell *cell) { unsigned int sum = 0; RTLIL::IdString port_width_params[] = { ID::WIDTH, ID::A_WIDTH, ID::B_WIDTH, ID::S_WIDTH, ID::Y_WIDTH, }; for (auto param : port_width_params) if (cell->hasParam(param)) sum += cell->getParam(param).as_int(); return sum; } unsigned int CellCosts::get(RTLIL::Cell *cell) { // simple 1-bit cells if (cmos_gate_cost().count(cell->type)) return 1; if (design_ && design_->module(cell->type) && cell->parameters.empty()) { log_debug("%s is a module, recurse\n", cell->name.c_str()); return get(design_->module(cell->type)); } else if (RTLIL::builtin_ff_cell_types().count(cell->type)) { log_assert(cell->hasPort(ID::Q) && "Weird flip flop"); log_debug("%s is ff\n", cell->name.c_str()); return cell->getParam(ID::WIDTH).as_int(); } else if (y_coef(cell->type)) { // linear with Y_WIDTH or WIDTH log_assert((cell->hasParam(ID::Y_WIDTH) || cell->hasParam(ID::WIDTH)) && "Unknown width"); auto param = cell->hasParam(ID::Y_WIDTH) ? ID::Y_WIDTH : ID::WIDTH; int width = cell->getParam(param).as_int(); if (cell->type == ID($demux)) width <<= cell->getParam(ID::S_WIDTH).as_int(); log_debug("%s Y*coef %d * %d\n", cell->name.c_str(), width, y_coef(cell->type)); return width * y_coef(cell->type); } else if (sum_coef(cell->type)) { // linear with sum of port widths unsigned int sum = port_width_sum(cell); log_debug("%s sum*coef %d * %d\n", cell->name.c_str(), sum, sum_coef(cell->type)); return sum * sum_coef(cell->type); } else if (max_inp_coef(cell->type)) { // linear with largest input width unsigned int max = max_inp_width(cell); log_debug("%s max*coef %d * %d\n", cell->name.c_str(), max, max_inp_coef(cell->type)); return max * max_inp_coef(cell->type); } else if (is_div_mod(cell->type) || cell->type == ID($mul)) { // quadratic with sum of port widths unsigned int sum = port_width_sum(cell); unsigned int coef = cell->type == ID($mul) ? 3 : 5; log_debug("%s coef*(sum**2) %d * %d\n", cell->name.c_str(), coef, sum * sum); return coef * sum * sum; } else if (cell->type == ID($lut)) { int width = cell->getParam(ID::WIDTH).as_int(); unsigned int cost = 1U << (unsigned int)width; log_debug("%s is 2**%d\n", cell->name.c_str(), width); return cost; } else if (cell->type == ID($sop)) { int width = cell->getParam(ID::WIDTH).as_int(); int depth = cell->getParam(ID::DEPTH).as_int(); log_debug("%s is (2*%d + 1)*%d\n", cell->name.c_str(), width, depth); return (2 * width + 1) * depth; } else if (is_free(cell->type)) { log_debug("%s is free\n", cell->name.c_str()); return 0; } // TODO: $fsm $mem.* $macc // ignored: $pow log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(cell->type), GetSize(cell->parameters)); return 1; } yosys-0.52/kernel/cost.h000066400000000000000000000055511477540374200152520ustar00rootroot00000000000000/* * 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 COST_H #define COST_H #include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN struct CellCosts { private: dict mod_cost_cache_; Design *design_ = nullptr; public: CellCosts(RTLIL::Design *design) : design_(design) { } static const dict& default_gate_cost() { // Default size heuristics for several common PDK standard cells // used by abc and stat static const dict db = { { ID($_BUF_), 1 }, { ID($_NOT_), 2 }, { ID($_AND_), 4 }, { ID($_NAND_), 4 }, { ID($_OR_), 4 }, { ID($_NOR_), 4 }, { ID($_ANDNOT_), 4 }, { ID($_ORNOT_), 4 }, { ID($_XOR_), 5 }, { ID($_XNOR_), 5 }, { ID($_AOI3_), 6 }, { ID($_OAI3_), 6 }, { ID($_AOI4_), 7 }, { ID($_OAI4_), 7 }, { ID($_MUX_), 4 }, { ID($_NMUX_), 4 }, }; return db; } static const dict& cmos_gate_cost() { // Estimated CMOS transistor counts for several common PDK standard cells // used by stat and optionally by abc static const dict db = { { ID($_BUF_), 1 }, { ID($_NOT_), 2 }, { ID($_AND_), 6 }, { ID($_NAND_), 4 }, { ID($_OR_), 6 }, { ID($_NOR_), 4 }, { ID($_ANDNOT_), 6 }, { ID($_ORNOT_), 6 }, { ID($_XOR_), 12 }, { ID($_XNOR_), 12 }, { ID($_AOI3_), 6 }, { ID($_OAI3_), 6 }, { ID($_AOI4_), 8 }, { ID($_OAI4_), 8 }, { ID($_MUX_), 12 }, { ID($_NMUX_), 10 }, { ID($_DFF_P_), 16 }, { ID($_DFF_N_), 16 }, }; return db; } // Get the cell cost for a cell based on its parameters. // This cost is an *approximate* upper bound for the number of gates that // the cell will get mapped to with "opt -fast; techmap" // The intended usage is for flattening heuristics and similar situations unsigned int get(RTLIL::Cell *cell); // Sum up the cell costs of all cells in the module // and all its submodules recursively unsigned int get(RTLIL::Module *mod); }; YOSYS_NAMESPACE_END #endif yosys-0.52/kernel/driver.cc000066400000000000000000000635471477540374200157440ustar00rootroot00000000000000/* * 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 "kernel/yosys.h" #include "kernel/hashlib.h" #include "libs/sha1/sha1.h" #include "libs/cxxopts/include/cxxopts.hpp" #include #ifdef YOSYS_ENABLE_READLINE # include # include #endif #ifdef YOSYS_ENABLE_EDITLINE # include #endif #ifdef YOSYS_ENABLE_TCL # include #endif #include #include #include #include #ifndef __STDC_FORMAT_MACROS # define __STDC_FORMAT_MACROS #endif #include #if defined (__linux__) || defined(__FreeBSD__) # include # include # include #endif #ifdef __FreeBSD__ # include # include #endif #if !defined(_WIN32) || defined(__MINGW32__) # include #endif USING_YOSYS_NAMESPACE #ifdef EMSCRIPTEN # include # include # include extern "C" int main(int, char**); extern "C" void run(const char*); extern "C" const char *errmsg(); extern "C" const char *prompt(); int main(int argc, char **argv) { EM_ASM( if (ENVIRONMENT_IS_NODE) { FS.mkdir('/hostcwd'); FS.mount(NODEFS, { root: '.' }, '/hostcwd'); FS.mkdir('/hostfs'); FS.mount(NODEFS, { root: '/' }, '/hostfs'); } ); mkdir("/work", 0777); chdir("/work"); log_files.push_back(stdout); log_error_stderr = true; yosys_banner(); yosys_setup(); #ifdef WITH_PYTHON PyRun_SimpleString(("sys.path.append(\""+proc_self_dirname()+"\")").c_str()); PyRun_SimpleString(("sys.path.append(\""+proc_share_dirname()+"plugins\")").c_str()); #endif if (argc == 2) { // Run the first argument as a script file run_frontend(argv[1], "script"); } } void run(const char *command) { int selSize = GetSize(yosys_get_design()->selection_stack); try { log_last_error = "Internal error (see JavaScript console for details)"; run_pass(command); log_last_error = ""; } catch (...) { while (GetSize(yosys_get_design()->selection_stack) > selSize) yosys_get_design()->selection_stack.pop_back(); throw; } } const char *errmsg() { return log_last_error.c_str(); } const char *prompt() { const char *p = create_prompt(yosys_get_design(), 0); while (*p == '\n') p++; return p; } #else /* EMSCRIPTEN */ #if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) int yosys_history_offset = 0; std::string yosys_history_file; #endif #if defined(__wasm) extern "C" { // FIXME: WASI does not currently support exceptions. void* __cxa_allocate_exception(size_t thrown_size) throw() { return malloc(thrown_size); } bool __cxa_uncaught_exception() throw(); void __cxa_throw(void* thrown_exception, struct std::type_info * tinfo, void (*dest)(void*)) { std::terminate(); } } #endif void yosys_atexit() { #if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) if (!yosys_history_file.empty()) { #if defined(YOSYS_ENABLE_READLINE) if (yosys_history_offset > 0) { history_truncate_file(yosys_history_file.c_str(), 100); append_history(where_history() - yosys_history_offset, yosys_history_file.c_str()); } else write_history(yosys_history_file.c_str()); #else write_history(yosys_history_file.c_str()); #endif } clear_history(); #if defined(YOSYS_ENABLE_READLINE) HIST_ENTRY **hist_list = history_list(); if (hist_list != NULL) free(hist_list); #endif #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::vector special_args; std::string output_filename = ""; std::string scriptfile = ""; std::string depsfile = ""; std::string topmodule = ""; std::string perffile = ""; bool scriptfile_tcl = false; bool scriptfile_python = 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; cxxopts::Options options(argv[0], "Yosys Open SYnthesis Suite"); options.set_width(SIZE_MAX); options.add_options("operation") ("b,backend", "use for the output file specified on the command line", cxxopts::value(), "") ("f,frontend", "use for the input files on the command line", cxxopts::value(), "") ("s,scriptfile", "execute the commands in ", cxxopts::value(), "") #ifdef YOSYS_ENABLE_TCL ("c,tcl-scriptfile", "execute the commands in the TCL (see 'help tcl' for details)", cxxopts::value(),"") ("C,tcl-interactive", "enters TCL interactive shell mode") #endif // YOSYS_ENABLE_TCL #ifdef WITH_PYTHON ("y,py-scriptfile", "execute the Python

YosysJS Example Application #01

[ load example ]

Loading...
yosys-0.52/misc/yosysjs/demo02.html000066400000000000000000000076261477540374200173100ustar00rootroot00000000000000 YosysJS Example Application #02

YosysJS Example Application #02

yosys-0.52/misc/yosysjs/demo03.html000066400000000000000000000102251477540374200172760ustar00rootroot00000000000000 YosysJS Example Application #02

YosysJS Example Application #03

Your mission: Create a behavioral Verilog model for the following circuit:

yosys-0.52/misc/yosysjs/yosysjs.js000066400000000000000000000177221477540374200174130ustar00rootroot00000000000000var YosysJS = new function() { this.script_element = document.currentScript; this.viz_element = undefined; this.viz_ready = true; this.url_prefix = this.script_element.src.replace(/[^/]+$/, '') this.load_viz = function() { if (this.viz_element) return; this.viz_element = document.createElement('iframe') this.viz_element.style.display = 'none' document.body.appendChild(this.viz_element); this.viz_element.contentWindow.document.open(); this.viz_element.contentWindow.document.write('